private static RowNode CopyRow(RowNode columnA) => new RowNode(columnA.Row.ToList());
private static void GroupsOfFour(IList <Tile> allTiles) { // get all stateful tiles Console.WriteLine("Getting all stateful tiles..."); var allStatefullTiles = GetAllStatefulTiles(allTiles); Console.WriteLine($"Got {allStatefullTiles.Count()} stateful tiles."); // get all rows Console.WriteLine("Getting rows..."); var validRows = new List <RowNode>(); // for each possible row combination foreach (var statefulTileCombination in new Combinations <StatefulTile>(allStatefullTiles, 4)) { if (ValidRow(statefulTileCombination)) { // row is valid // create row node var rowNode = new RowNode(statefulTileCombination); // add row node to list of valid rows validRows.Add(rowNode); foreach (var tile in rowNode.Row) { // link tile back to row that it is in tile.RowNodes.Add(rowNode); } } } Console.WriteLine($"Got {validRows.Count} rows."); // find valid grids Console.WriteLine("Finding valid grids...\n"); // for each valid row... foreach (var rowA in validRows) { // choose a tile for col A var tileA = rowA.Row[0]; // find all possible rows that could be the col for this tile // of the rows that include tile A... var validColAs = tileA.RowNodes.Where(x => // where row is not row A and its not the case that... x != rowA && !x.Row.Any(y => // there is a tile other than tile A that is in both row A and the col A y != tileA && rowA.Row.Any(z => IsSameTile(y, z)))); foreach (var colA in validColAs) { // do the same for another tile from row A var tileB = rowA.Row[1]; var validColBs = tileB.RowNodes.Where(x => // row must now also not be col A x != rowA && x != colA && !x.Row.Any(y => y != tileB // also check col A for matching tiles && (rowA.Row.Any(z => IsSameTile(y, z)) || colA.Row.Any(z => IsSameTile(y, z))))); foreach (var colB in validColBs) { // third tile var tileC = rowA.Row[2]; var validColCs = tileC.RowNodes.Where(x => x != rowA && x != colA && x != colB && !x.Row.Any(y => y != tileC && (rowA.Row.Any(z => IsSameTile(y, z)) || colA.Row.Any(z => IsSameTile(y, z)) || colB.Row.Any(z => IsSameTile(y, z))))); foreach (var colC in validColCs) { // fourth and last tile var tileD = rowA.Row[3]; var validColDs = tileD.RowNodes.Where(x => x != rowA && x != colA && x != colB && x != colC && !x.Row.Any(y => y != tileD && (rowA.Row.Any(z => IsSameTile(y, z)) || colA.Row.Any(z => IsSameTile(y, z)) || colB.Row.Any(z => IsSameTile(y, z)) || colC.Row.Any(z => IsSameTile(y, z))))); foreach (var colD in validColDs) { // create new objects and remove row A from the cols var colARowB = CopyAndRemove(colA, tileA); var colBRowB = CopyAndRemove(colB, tileB); var colCRowB = CopyAndRemove(colC, tileC); var colDRowB = CopyAndRemove(colD, tileD); var validRowBs = validRows.Except( new List <RowNode> { rowA, colA, colB, colC, colD }) .Where(x => colARowB.Row.Any(y => x.Row.Contains(y)) && colBRowB.Row.Any(y => x.Row.Contains(y)) && colCRowB.Row.Any(y => x.Row.Contains(y)) && colDRowB.Row.Any(y => x.Row.Contains(y))); foreach (var rowB in validRowBs) { var colARowC = CopyRow(colARowB); var colBRowC = CopyRow(colBRowB); var colCRowC = CopyRow(colCRowB); var colDRowC = CopyRow(colDRowB); foreach (var tile in rowB.Row) { colARowC.Row.Remove(tile); colBRowC.Row.Remove(tile); colCRowC.Row.Remove(tile); colDRowC.Row.Remove(tile); } var validRowCs = validRows.Except( new List <RowNode> { rowA, rowB, colA, colB, colC, colD }) .Where(x => colARowC.Row.Any(y => x.Row.Contains(y)) && colBRowC.Row.Any(y => x.Row.Contains(y)) && colCRowC.Row.Any(y => x.Row.Contains(y)) && colDRowC.Row.Any(y => x.Row.Contains(y))); foreach (var rowC in validRowCs) { var colARowD = CopyRow(colARowC); var colBRowD = CopyRow(colBRowC); var colCRowD = CopyRow(colCRowC); var colDRowD = CopyRow(colDRowC); foreach (var tile in rowC.Row) { colARowD.Row.Remove(tile); colBRowD.Row.Remove(tile); colCRowD.Row.Remove(tile); colDRowD.Row.Remove(tile); } var rowD = validRows.Except( new List <RowNode> { rowA, rowB, rowC, colA, colB, colC, colD }) .SingleOrDefault(x => colARowD.Row.Any(y => x.Row.Contains(y)) && colBRowD.Row.Any(y => x.Row.Contains(y)) && colCRowD.Row.Any(y => x.Row.Contains(y)) && colDRowD.Row.Any(y => x.Row.Contains(y))); if (rowD != null) { // create and organise board as 2D array var board = new StatefulTile[4, 4]; var i = 0; // apply row A foreach (var tile in rowA.Row) { board[0, i++] = tile; } // apply row B board[1, 0] = colA.Row.Single(x => rowB.Row.Contains(x)); board[1, 1] = colB.Row.Single(x => rowB.Row.Contains(x)); board[1, 2] = colC.Row.Single(x => rowB.Row.Contains(x)); board[1, 3] = colD.Row.Single(x => rowB.Row.Contains(x)); // apply row C board[2, 0] = colA.Row.Single(x => rowC.Row.Contains(x)); board[2, 1] = colB.Row.Single(x => rowC.Row.Contains(x)); board[2, 2] = colC.Row.Single(x => rowC.Row.Contains(x)); board[2, 3] = colD.Row.Single(x => rowC.Row.Contains(x)); // apply row D board[3, 0] = colA.Row.Single(x => rowD.Row.Contains(x)); board[3, 1] = colB.Row.Single(x => rowD.Row.Contains(x)); board[3, 2] = colC.Row.Single(x => rowD.Row.Contains(x)); board[3, 3] = colD.Row.Single(x => rowD.Row.Contains(x)); //PrintBoard(board); var possibleDiagAs = validRows.Except(new List <RowNode> { rowA, rowB, rowC, rowD, colA, colB, colC, colD }); foreach (var possibleDiagA in possibleDiagAs) { CheckDiag( validRows, rowA, rowB, rowC, rowD, colA, colB, colC, colD, possibleDiagA, (StatefulTile[, ])board.Clone()); } } } } } } } } } Console.WriteLine($"Found {validSolutions} valid grids."); }