// [SC] 1. Create lists of tiles where each list is a group of tiles with the same color or shape. // [SC] 2. For each group of tiles, create lists of tile sequences where each list contains a sequence of tiles in a unique order. // [SC] 3. For each tile sequence, create a combination of unique board positions. public CandidateTilePos calculateMoves(bool considerColor, bool considerShape, bool suboptiomal) { List <CandidateTileSeq> candTileSeqList = new List <CandidateTileSeq>(); // [TODO] ////////////////////////////////////////////////////////////////////////////// // [2016.12.08] new code List <List <TileZeroTile> > colorTileLists = new List <List <TileZeroTile> >(); // [SC] each list contains player's tiles of the same color List <List <TileZeroTile> > shapeTileLists = new List <List <TileZeroTile> >(); // [SC] each list contains player's tiles of the same shape for (int index = 0; index < Cfg.MAX_VAL_INDEX; index++) { colorTileLists.Add(new List <TileZeroTile>()); shapeTileLists.Add(new List <TileZeroTile>()); } foreach (TileZeroTile tile in playerTiles) { colorTileLists[tile.getColorIndex()].Add(tile); shapeTileLists[tile.getShapeIndex()].Add(tile); } colorTileLists.RemoveAll(p => p.Count == 0); // [SC] remove empty tile combos shapeTileLists.RemoveAll(p => p.Count == 0); // [SC] remove empty tile combos colorTileLists = colorTileLists.OrderBy(p => p.Count).ToList(); // [SC] order by size of tile combos shapeTileLists = shapeTileLists.OrderBy(p => p.Count).ToList(); // [SC] order by size of tile combos if (considerColor) { // [SC] remove all color tile combos that are subsets of another color or shape combo for (int indexOne = 0; indexOne < colorTileLists.Count; indexOne++) { List <TileZeroTile> tileComboOne = colorTileLists[indexOne]; for (int indexTwo = indexOne + 1; indexTwo < colorTileLists.Count; indexTwo++) { List <TileZeroTile> tileComboTwo = colorTileLists[indexTwo]; if (tileComboOne.All(p => tileComboTwo.Contains(p))) { tileComboOne.Clear(); continue; } } if (tileComboOne.Count == 0) { continue; } if (considerShape) { for (int indexTwo = 0; indexTwo < shapeTileLists.Count; indexTwo++) { List <TileZeroTile> tileComboTwo = shapeTileLists[indexTwo]; // [SC] true if combo one is a subset of combo two if (tileComboOne.Count <= tileComboTwo.Count && tileComboOne.All(p => tileComboTwo.Contains(p))) { tileComboOne.Clear(); continue; } } } } colorTileLists.RemoveAll(p => p.Count == 0); // [SC] remove empty tile combos } if (considerShape) { // [SC] remove all shape tile combos that are subsets of another color or shape combo for (int indexOne = 0; indexOne < shapeTileLists.Count; indexOne++) { List <TileZeroTile> tileComboOne = shapeTileLists[indexOne]; for (int indexTwo = indexOne + 1; indexTwo < shapeTileLists.Count; indexTwo++) { List <TileZeroTile> tileComboTwo = shapeTileLists[indexTwo]; if (tileComboOne.All(p => tileComboTwo.Contains(p))) { tileComboOne.Clear(); continue; } } if (tileComboOne.Count == 0) { continue; } if (considerColor) { for (int indexTwo = 0; indexTwo < colorTileLists.Count; indexTwo++) { List <TileZeroTile> tileComboTwo = colorTileLists[indexTwo]; // [SC] true if combo one is a subset of combo two if (tileComboOne.Count <= tileComboTwo.Count && tileComboOne.All(p => tileComboTwo.Contains(p))) { tileComboOne.Clear(); continue; } } } } shapeTileLists.RemoveAll(p => p.Count == 0); // [SC] remove empty tile combos } // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // [2016.12.08] new code if (considerColor) { // [SC] iterate through list of tiles with one particular color foreach (List <TileZeroTile> tileList in colorTileLists) { int colorIndex = tileList[0].getColorIndex(); TreeNode rootNode = new TreeNode(null); for (int tileIndex = 0; tileIndex < tileList.Count; tileIndex++) { tileListPermAddChildNodes(tileList, tileIndex, rootNode); } tileListPermTraverseTreePaths(rootNode, new List <TileZeroTile>(), colorIndex, Cfg.COLOR_ATTR, candTileSeqList); } } if (considerShape) { // [SC] iterate through list of tiles with one particular shape foreach (List <TileZeroTile> tileList in shapeTileLists) { int shapeIndex = tileList[0].getShapeIndex(); TreeNode rootNode = new TreeNode(null); for (int tileIndex = 0; tileIndex < tileList.Count; tileIndex++) { tileListPermAddChildNodes(tileList, tileIndex, rootNode); } tileListPermTraverseTreePaths(rootNode, new List <TileZeroTile>(), shapeIndex, Cfg.SHAPE_ATTR, candTileSeqList); } } // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // [2016.12.08] new code int maxCTS = 10; if (candTileSeqList.Count > maxCTS) { candTileSeqList = candTileSeqList.OrderByDescending(p => p.getTileCount()).ToList(); candTileSeqList.RemoveRange(maxCTS, candTileSeqList.Count - maxCTS); } // ////////////////////////////////////////////////////////////////////////////// VirtualBoard virtualBoard = game.getVirtualBoard(); TileZeroTile[,] tileArray = virtualBoard.getBoardCopy(); List <CandidateTilePos> chosenPosComboList = new List <CandidateTilePos>(); List <CandidateTilePos> maxScorePosComboList = new List <CandidateTilePos>(); foreach (CandidateTileSeq candTileSeq in candTileSeqList) { TreeNode rootNode = new TreeNode(null); boardPosPermAddChildNodes(candTileSeq, 0, rootNode, tileArray, virtualBoard); if (suboptiomal) { boardPosPermTraverseTreePaths(rootNode, new List <AbstractPos>(), candTileSeq, 0, chosenPosComboList, maxScorePosComboList); } else { boardPosPermTraverseTreePaths(rootNode, new List <AbstractPos>(), candTileSeq, 0, chosenPosComboList); } } return(chosenPosComboList.getRandomElement()); }