////// END: very easy ai functionality ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// ////// START: easy ai functionality #region easy AI public void invokeEasyAI() { VirtualBoard virtualBoard = game.getVirtualBoard(); int rowCount = virtualBoard.getRowCount(); int colCount = virtualBoard.getColCount(); // [SC] using copy since indices in playerTiles may change due to removed tiles List <TileZeroTile> tempPlayerTiles = playerTiles.listShallowClone(); bool tilePlacedFlag = false; bool shouldDropFlag = true; foreach (TileZeroTile tile in tempPlayerTiles) { // [SC] check if the tile is playable if (!tile.getPlayable()) { continue; } // [SC] add some daly before making next move if (tilePlacedFlag) { tilePlacedFlag = false; } for (int currRowIndex = 0; currRowIndex < rowCount && !tilePlacedFlag; currRowIndex++) { for (int currColIndex = 0; currColIndex < colCount; currColIndex++) { int resultScore = virtualBoard.isValidMove(currRowIndex, currColIndex, tile, true, null, false); if (resultScore != Cfg.NONE) { setSelectedTile(tile); game.setSelectedCell(currRowIndex, currColIndex, playerIndex); game.placePlayerTileOnBoard(playerIndex); tilePlacedFlag = true; shouldDropFlag = false; break; } } } } if (shouldDropFlag) { // [SC] dropping a random tile setSelectedTile(playerTiles.getRandomElement()); game.dropPlayerTile(playerIndex); //Cfg.showMsg(getPlayerName() + " dropped a tile."); } }
public Game() { virtualBoard = new VirtualBoard(this); rnd = new Random(); }
// [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()); }
///////////////////////////////////////////////////////////////// ////// START: A code for creating all possible permutations of board positions ////// of tiles in given list, starting from left-most tile in the array private void boardPosPermAddChildNodes(CandidateTileSeq candTileSeq, int currTileIndex, TreeNode parentNode, TileZeroTile[,] tileArray, VirtualBoard virtualBoard) { if (currTileIndex >= candTileSeq.getTileCount()) { return; } TileZeroTile tile = candTileSeq.getTileAt(currTileIndex); for (int currRowIndex = 0; currRowIndex < tileArray.GetLength(0); currRowIndex++) { for (int currColIndex = 0; currColIndex < tileArray.GetLength(1); currColIndex++) { int resultScore = virtualBoard.isValidMove(currRowIndex, currColIndex, tile, true, tileArray, false); if (resultScore != Cfg.NONE) { TileZeroTile[,] newTileArray = Cfg.createBoardCopy(tileArray); virtualBoard.addTile(currRowIndex, currColIndex, tile, false, newTileArray); TreeNode childNode = parentNode.addChildNodeValue(new AbstractPos(currTileIndex, currRowIndex, currColIndex, resultScore)); boardPosPermAddChildNodes(candTileSeq, currTileIndex + 1, childNode, newTileArray, virtualBoard); } } } boardPosPermAddChildNodes(candTileSeq, currTileIndex + 1, parentNode, tileArray, virtualBoard); // [SC][2016.12.08] new code }