/// <summary> /// Eliminate will remove the value from the tile and propagate changes via calls to Assign /// if there is only a single value remaining /// NOTE: This method mutates the CPBoard cpb parameter /// </summary> /// <param name="cpb"></param> /// <param name="tile"></param> /// <param name="value"></param> /// <returns></returns> static public bool Eliminate(CPBoard cpb, int tileIndex, string value) { var values = cpb.Get(tileIndex); CDebug.Assert(values.Length > 0, "values.Length > 0"); if (values.Length == 1) { // Reached a contradiction, eliminating this value will leave the tile EMPTY! if (values == value) { return(false); } // Value has already been eliminated from this tile return(true); } var newValues = values.Replace(value, ""); cpb.Set(tileIndex, newValues); // If there is only one possible value after the change // Call assign to propagate changes if (newValues.Length == 1) { return(Assign(cpb, tileIndex, newValues)); } return(true); }
/// <summary> /// Function that initializes the internal representation of the constraint propagotion board /// by relating all the tiles to each other via their units /// Each tile will have 20 peers comprised of the row, column and square peers /// </summary> static private void populatePeers() { var indices = Enumerable.Range(0, Const.N); // Helper function to reduce code duplication // The index is for determining the offset in the array, // basically row units are first 9, columns are second 9, squares are third 9 Action <HashSet <int>, int> relatePeers = (peers, unitIndex) => { foreach (var peerIndex in peers) { // Except statement is to exclude self from the relation Peers[peerIndex] = Peers[peerIndex].Union(peers).Except(new int[] { peerIndex }).ToHashSet(); Units[unitIndex] = peers; } }; foreach (var i in indices) { var rowPeers = (from c in indices select Utils.CalculateTileIndex(i, c)).ToHashSet(); relatePeers(rowPeers, i); var columnPeers = (from r in indices select Utils.CalculateTileIndex(r, i)).ToHashSet(); relatePeers(columnPeers, i + Const.N); int rs = i / Const.W; int cs = i % Const.W; var subRows = Enumerable.Range(0, Const.W).Select(j => rs * Const.W + j); var subColumns = Enumerable.Range(0, Const.W).Select(j => cs * Const.W + j); var squarePeers = (from r in subRows from c in subColumns select Utils.CalculateTileIndex(r, c)).ToHashSet(); relatePeers(squarePeers, i + Const.N * 2); CDebug.Assert(Peers[i].Count == 20, $"Should be 20 peers each tile- {string.Join(',', Peers[i])}"); } }
/// <summary> /// Exhaustively try all possible values for the tiles, using constraints to /// reduce the search space /// </summary> /// <param name="cpb"></param> /// <param name="depth"></param> Parameter debugging and curiousity /// <returns></returns> static private SearchResult search(CPBoard cpb, int depth) { var freeTile = cpb.GetFreeTile(); // No free tile available if (freeTile.Value == null) { return(new SearchResult(cpb, IsSolved(cpb))); } var possibleValues = freeTile.Value; foreach (var c in possibleValues) { // Make a copy in case we reach contradiction and need to revert back var copy = cpb.Copy(); bool didAssign = Assign(copy, freeTile.Key, c.ToString()); CDebug.Assert(copy.Get(freeTile.Key) != cpb.Get(freeTile.Key), "Original CPB mutated!"); if (didAssign) { SearchResult sr = search(copy, depth + 1); if (sr.DidSolve) { return(sr); } } } return(new SearchResult(cpb, false)); }
public CPBoard(string board) { var indices = Enumerable.Range(0, Const.N); CDebug.Assert(board.Length == Const.N2, "board.Length == Const.N2"); for (int i = 0; i < board.Length; i++) { int n = Utils.parseChar(board[i]); if (n != 0) { Tiles[i] = n.ToString(); } else { Tiles[i] = string.Join("", indices.Select((num) => num + 1)); } } }
/// <summary> /// Assign a single value to the tile, propagate the changes via calls to Eliminate /// NOTE: Method mutates the CPBoard cpb parameter /// </summary> /// <param name="cpb"></param> /// <param name="tileIndex"></param> /// <param name="value"></param> /// <returns></returns> static public bool Assign(CPBoard cpb, int tileIndex, string value) { CDebug.Assert(value.Length == 1, "Cannot assign multiple values"); var values = cpb.Get(tileIndex); // Cannot assign a value that is a not possible value of the tile if (!values.Contains(value)) { return(false); } // Attempt to eliminate values from peers cpb.Set(tileIndex, value); foreach (var peer in Peers[tileIndex]) { if (!Eliminate(cpb, peer, value)) { return(false); } } return(true); }