/// <summary> /// Color the puzzle according to the "Test" value of the cell /// </summary> /// <param name="mode">Solve mode colors the whole puzzle, while hint mode colors only one cell</param> private void ColorTest(SolvingMode mode) { for (int i = 0; i < SizeX; i++) { for (int j = 0; j < SizeY; j++) { if (this[i, j].Test && this[i, j].State != CellState.filled) { this[i, j].State = CellState.filled; if (mode == SolvingMode.hint) { return; } } if (!this[i, j].Test && this[i, j].State != CellState.empty) { this[i, j].State = CellState.empty; if (mode == SolvingMode.hint) { return; } } } } }
/// <summary> /// Solve for the last unfinished value in checksum /// </summary> /// <param name="mode">Solve mode colors the whole puzzle, while hint mode colors only one cell</param> /// <returns>True if change occured, null if error occured</returns> private bool?SolveLast(SolvingMode mode) { bool change = false; int position = (int)PutOneFromEnd(_lastUnfinished, _unfinishedRangeEnd); var lastFilled = LastFilled(); // position now marks where the put end is // we can now possibly shrink unfinished range change |= Color(position + _checksum[_lastUnfinished], _unfinishedRangeEnd, CellState.empty, mode); if (change && mode == SolvingMode.hint) { return(true); } // unfinished range begins after last colored cell _unfinishedRangeEnd = position + _checksum[_lastUnfinished] - 1; //coloring if (lastFilled - position >= _checksum[_lastUnfinished]) { // when this happens, error occured (something is wrong with the puzzle) return(null); } if (lastFilled - position >= 0) { change |= Color(position, lastFilled, CellState.filled, mode); if (mode == SolvingMode.hint && change) { return(true); } // if the whole value is finished, put an empty cell before it, decrease end of an unfinished range and change last unfinished value to the next one if (lastFilled - position + 1 == _checksum[_lastUnfinished]) { if (position - 1 > 0 && this[position - 1].State != CellState.empty) { this[position - 1].State = CellState.empty; if (mode == SolvingMode.hint) { return(true); } } // position marks the end of first value, position - 1 is an empty space and position - 2 is a new beginning of an unfinished range _unfinishedRangeEnd = position - 2; _lastUnfinished--; } } return(change); }
/// <summary> /// Solve for the first unfinished value in checksum /// </summary> /// <param name="mode">Solve mode colors the whole puzzle, while hint mode colors only one cell</param> /// <returns></returns> private bool?SolveFirst(SolvingMode mode) { bool change = false; int position = (int)PutOneFromBeginning(_firstUnfinished, _unfinishedRangeBeginning); var firstFilled = FirstFilled(); // position now marks where the put end is // we can now possibly shrink unfinished range change |= Color(_unfinishedRangeBeginning, position - _checksum[_firstUnfinished], CellState.empty, mode); if (change && mode == SolvingMode.hint) { return(true); } // unfinished range begins after last colored cell _unfinishedRangeBeginning = position - _checksum[_firstUnfinished] + 1; //coloring if (position - firstFilled >= _checksum[_firstUnfinished]) { // when this happens, error occured (something is wrong with the puzzle) return(null); } else if (position - firstFilled >= 0) { change |= Color(firstFilled, position, CellState.filled, mode); if (change && mode == SolvingMode.hint) { return(true); } // if the whole value is finished, put an empty cell after it, increase beginning of an unfinished range and change first unfinished value to the next one if (position - firstFilled + 1 == _checksum[_firstUnfinished]) { if (position + 1 < _size && this[position + 1].State != CellState.empty) { this[position + 1].State = CellState.empty; if (mode == SolvingMode.hint) { return(true); } change = true; } // position marks the end of first value, position + 1 is an empty space and position + 2 is a new beginning of an unfinished range _unfinishedRangeBeginning = position + 2; _firstUnfinished++; } } return(change); }
/// <summary> /// Solves a single line (row or column) of a puzzle using only one iteration and several methods of solving puzzle /// </summary> /// <param name="mode">Solving mode fills all of them, hint mode only fills one</param> /// <returns>True if a change occured, null if puzzle doesn't have a solution</returns> public bool?Solve(SolvingMode mode = SolvingMode.solve) { if (Check()) { return(FinishLine(mode)); } bool?change = false; if (_firstUnfinished > _lastUnfinished) { return(FillWithEmpty(mode)); } var newChange = SolveRowIntersect(mode); if (mode == SolvingMode.hint && newChange == true) { return(true); } if (newChange == null) { return(null); } change |= newChange; newChange = SolveFirst(mode); if (mode == SolvingMode.hint && newChange == true) { return(true); } if (newChange == null) { return(null); } change |= newChange; if (_firstUnfinished >= _checksum.Count) { return(change); } newChange = SolveLast(mode); if (mode == SolvingMode.hint && newChange == true) { return(true); } if (newChange == null) { return(null); } change |= newChange; return(change); }
/// <summary> /// Set state of cells in a given range to a given state /// </summary> /// <param name="start">Beginning of a range to color</param> /// <param name="finish">End of a range to color</param> /// <param name="state">State to which cells will be set</param> /// <param name="mode">Solve mode colors the whole range, while hint mode only colors the first cell</param> /// <returns>True if something changed</returns> private bool Color(int start, int finish, CellState state, SolvingMode mode) { bool change = false; for (int i = start; i <= finish; i++) { if (this[i].State != state) { this[i].State = state; if (mode == SolvingMode.hint) { return(true); } change = true; } } return(change); }
/// <summary> /// This function is called after the line is properly colored and only fills unknown spaces with empty values /// </summary> /// <param name="mode">Solving mode fills all of them, hint mode only fills one</param> /// <returns>True if change occured</returns> private bool FinishLine(SolvingMode mode) { bool change = false; for (int i = 0; i < _size; i++) { if (this[i].State == CellState.unknown) { this[i].State = CellState.empty; change = true; if (mode == SolvingMode.hint) { return(change); } } } return(change); }
/// <summary> /// Solve the puzzle using state space search /// </summary> /// <param name="mode">Solve mode colors the whole puzzle, while hint mode colors only one cell</param> private bool SolveStateSpace(SolvingMode mode) { StateSpaceSearchLine[] lines; Func <bool> check; Orientation offsetsOrientation; // choose to solve in an orientation that has less lines if (SizeX > SizeY) { lines = new StateSpaceSearchLine[SizeY]; offsetsOrientation = Orientation.Horizontal; check = CheckVerticalTest; } else { lines = new StateSpaceSearchLine[SizeX]; offsetsOrientation = Orientation.Vertical; check = CheckHorizontalTest; } for (int i = 0; i < SizeY; i++) { lines[i] = new StateSpaceSearchLine(this, i, offsetsOrientation); } while (true) { if (check()) { break; } int lastIncreased = 0; while (!lines[lastIncreased].IncreaseOffsets()) { lines[lastIncreased].ResetOffsets(); lastIncreased++; if (lastIncreased >= SizeX) { return(false); } } } ColorTest(mode); return(true); }
/// <summary> /// Solve the puzzle /// </summary> /// <param name="mode">Solve mode colors the whole puzzle, while hint mode colors only one cell</param> /// <returns>Returns false if the puzzle in its current state doesn't have a solution</returns> public bool Solve(SolvingMode mode = SolvingMode.solve) { bool?change = false; var lines = new DirectSolveLine[SizeX + SizeY]; for (int i = 0; i < SizeY; i++) { lines[i] = new DirectSolveLine(this, Orientation.Horizontal, i); } for (int i = SizeY; i < SizeX + SizeY; i++) { lines[i] = new DirectSolveLine(this, Orientation.Vertical, i - SizeY); } // Iterate through all rows and collumns and solve them as long as change occurs do { change = false; for (int i = 0; i < SizeX + SizeY; i++) { var newChange = lines[i].Solve(mode); if (mode == SolvingMode.hint && newChange == true) { return(true); } if (newChange == null) { return(false); } change |= newChange; } } while ((bool)change); // After the direct solving method fails, solve with state space search if (!IsSolved()) { return(SolveStateSpace(mode)); } return(true); }
/// <summary> /// Solves the line using intersection method /// </summary> /// <param name="mode">Can be either solveall which solves the whole puzzle, or hint, which only finds a single tile</param> /// <returns></returns> private bool?SolveRowIntersect(SolvingMode mode) { bool change = false; var putBeg = PutFromBeginning(); if (putBeg == null) { // this means puzzle doesn't have a solution return(null); } var putEnd = PutFromEnd(); if (putEnd == null) { return(null); } // coloring for (int i = _firstUnfinished; i <= _lastUnfinished; i++) { if (putBeg[i - _firstUnfinished] >= putEnd[i - _firstUnfinished]) { for (int j = putEnd[i - _firstUnfinished]; j <= putBeg[i - _firstUnfinished]; j++) { if (this[j].State != CellState.filled) { this[j].State = CellState.filled; if (mode == SolvingMode.hint) { return(true); } change = true; } } } } return(change); }
/// <summary> /// Sets the state of every cell in line to empty /// </summary> /// <returns></returns> private bool?FillWithEmpty(SolvingMode mode = SolvingMode.solve) { bool change = false; for (int i = _unfinishedRangeBeginning; i <= _unfinishedRangeEnd; i++) { if (this[i].State == CellState.unknown) { this[i].State = CellState.empty; change = true; if (mode == SolvingMode.hint) { return(change); } } // preasummably empty line shouldn't contain filled cell if (this[i].State == CellState.filled) { return(null); } } return(change); }
/// <summary> /// Constructor for testing purpose. /// </summary> /// <param name="maxNumberOfAdditionalSantas"></param> /// <param name="mode"></param> public GoogleRoutingConfig(int maxNumberOfAdditionalSantas, SolvingMode mode) { MaxNumberOfAdditionalSantas = maxNumberOfAdditionalSantas; Mode = mode; }
public WrongSolverChoosenException( SolvingMode typeInUse) : base(String.Format("{0}{1}{2}", defaultMessage[0], typeNames[(int)typeInUse], defaultMessage[1])) { }