public override void Move(Board b) { { //Create a new game state for the root of the state space GameState gameState = new GameState(GameState.State.initial, b, this, null, cell, true); //Set the state as the root MiniMaxTree m = new MiniMaxTree(gameState); //Find children states if they exist m.GenerateStates(gameState, 4, true); //The value returned by the recursive minimax function int value = m.MiniMax(gameState, 4, true); cell = null; foreach(GameState child in gameState.GetChildren()) { if (child.GetHeuristicValue() == value) { cell = child.GetCell(); } } //Set the cell on the playing board b.getCell(cell.getRow(), cell.getColumn()).setState((int)cell.getState()); //if a row exists above this cell, make it playable if (cell.getRow() != 0) { b.getCell(cell.getRow() - 1, cell.getColumn()).isPlayable(true); } //Test if the game is over if(MiniMaxTree.TerminalTest(cell)) { b.printBoard(); Console.WriteLine("GameOver: " + this.getColorToString() + " wins"); b.isGameOver(true); } moveCount++; } }
public GameState(State s, Board b, Player p, GameState parent, Cell c, Boolean mp) { maxPlayer = mp; state = s; cell = c; board = b; player = p; this.parent = parent; children = new List<GameState>(); Region.findConnectedCells(b); b.UpdateCellObservers(); if(MiniMaxTree.TerminalTest(cell) && state != GameState.State.initial) { state = GameState.State.terminal; if (maxPlayer) { heuristicValue = MiniMaxTree.MIN_VALUE; } else heuristicValue = MiniMaxTree.MAX_VALUE; } }
public int FindHeuristicValue(GameState gs) { int value = 0; //The count of connectCells a cell contains int bestCount = 0; int connectR = Board.GetConnectR(); if(gs.GetState().Equals(GameState.State.terminal)) { return gs.GetHeuristicValue(); } //if true it is maxPlayers turn and the board needs to be evaluate to minimize min's score if(gs.isMaxPlayer()) { //check if max can make a killer move foreach(Cell cell in gs.GetBoard().getPlayerCells(gs.GetPlayer())) { if(cell.isTerminal() > 0) { value = MAX_VALUE; return value; } } //check if it is impossible to prevent min from winning if(gs.GetCell().isTerminal() > 1) { value = MIN_VALUE; return value; } //check if min can make a killer move foreach (Cell cell in gs.GetBoard().getPlayerCells(gs.GetPlayer().getOpponent())) { if (cell.isTerminal() == 1) { value = MIN_VALUE / 2; return value; } } //last resort - find the move that gives the most possibilites for connect 4 foreach(Cell cell in gs.GetBoard().getPlayerCells(gs.GetPlayer())) { int count = 0; foreach(KeyValuePair<int, HashSet<Cell>> c in cell.GetObservers()) { count = count + c.Value.Count; } foreach(KeyValuePair<int, List<Cell>> k in cell.GetConnectedCells()) { count = count + k.Value.Count; } if(count > bestCount) { bestCount = count; } } value = bestCount; return value; } //It is not max's turn else { //check if min can make a killer move foreach (Cell cell in gs.GetBoard().getPlayerCells(gs.GetPlayer().getOpponent())) { if (cell.isTerminal() > 0) { value = MIN_VALUE; return value; } } //check if it is impossible to prevent max from winning if (gs.GetCell().isTerminal() > 1) { value = MAX_VALUE; return value; } //check if max can make a killer move foreach (Cell cell in gs.GetBoard().getPlayerCells(gs.GetPlayer())) { if (cell.isTerminal() == 1) { value = MAX_VALUE / 2; return value; } } } //last resort - find the move that gives the most possibilites for connect 4 foreach (Cell cell in gs.GetBoard().getPlayerCells(gs.GetPlayer().getOpponent())) { int count = 0; foreach (KeyValuePair<int, HashSet<Cell>> c in cell.GetObservers()) { count = count + c.Value.Count; } foreach (KeyValuePair<int, List<Cell>> k in cell.GetConnectedCells()) { count = count + k.Value.Count; } if (count > bestCount) { bestCount = count; } } value = bestCount; return value; }
public MiniMaxTree(GameState gs) { root = gs; }
public int MiniMax(GameState gs, int depth, Boolean maxPlayer) { //base case if (depth == 0 || gs.GetState().Equals(GameState.State.terminal)) { return FindHeuristicValue(gs); } if(maxPlayer) { int bestValue = MIN_VALUE; foreach(GameState child in gs.GetChildren()) { int value = MiniMax(child, depth - 1, false); if (value.CompareTo(bestValue) > 0) { gs.SetHeuristicValue(value); bestValue = value; } } return bestValue; } else { int bestValue = MAX_VALUE; foreach (GameState child in gs.GetChildren()) { int value = MiniMax(child, depth - 1, true); if(value.CompareTo(bestValue) < 0) { gs.SetHeuristicValue(value); bestValue = value; } } return bestValue; } }
public void GenerateStates(GameState gs, int depth, Boolean maxPlayer) { if(depth == 0 || gs.GetState().Equals(GameState.State.terminal)) { return; } gs.FindChildrenStates(maxPlayer); foreach(GameState child in gs.GetChildren()) { if (maxPlayer) { GenerateStates(child, depth - 1, false); } else GenerateStates(child, depth - 1, true); } }
// private methods private void gameOverCheck(int row, int column, out GameState gameState) { gameState = GameState.notWon; // check horizontal for a win checkHorizontal(row, column, out gameState); // check vertical for a win if (gameState == GameState.notWon) { checkVertical(row, column, out gameState); } // end if // check diagonal (up and right, down and left) for a win if (gameState == GameState.notWon) { checkDiagonal(row, column, out gameState); } // end if // check antidiagonal (up and left, down and right) for a win if (gameState == GameState.notWon) { checkAntiDiagonal(row, column, out gameState); } // end if // check to see if it is a draw if (gameState == GameState.notWon && numberOfMoves >= 42) { gameState = GameState.draw; } // end if }
private void checkVertical(int row, int column, out GameState gameState) { gameState = GameState.notWon; int numberInARow = 1; bool noBreaks = true; // compare to the next one up int nextRow = row + 1; // row is y int nextColumn = column; // column is x while ((nextRow < gameBoard.GetLength(ROW)) && (numberInARow < 4) && (noBreaks)) // check up { compareColors(row, column, nextRow, nextColumn, ref numberInARow, ref noBreaks); nextRow++; // look at the next space up } // end while // reset the row to compare to the next one down nextRow = row - 1; // row is y noBreaks = true; // reset so we can look the other direction while ((nextRow >= 0) && (numberInARow < 4) && (noBreaks)) // check down { compareColors(row, column, nextRow, nextColumn, ref numberInARow, ref noBreaks); nextRow--; // look at the next space down } // end while if (numberInARow >= 4) { gameState = GameState.won; } // end if }
private void checkHorizontal(int row, int column, out GameState gameState) { gameState = GameState.notWon; int numberInARow = 1; bool noBreaks = true; // compare to the next one on the right int nextRow = row; // row is y int nextColumn = column + 1; // column is x while ((nextColumn < gameBoard.GetLength(COLUMN)) && (numberInARow < 4) && (noBreaks)) // check to the right { compareColors(row, column, nextRow, nextColumn, ref numberInARow, ref noBreaks); nextColumn++; // look at the next space to the right } // end while // reset the column to compare to the next one on the left nextColumn = column - 1; // column is x noBreaks = true; // reset so we can look the other direction while ((nextColumn >= 0) && (numberInARow < 4) && (noBreaks)) // check to the left { compareColors(row, column, nextRow, nextColumn, ref numberInARow, ref noBreaks); nextColumn--; // look at the next space to the left } // end while if (numberInARow >= 4) { gameState = GameState.won; } // end if }
private void checkDiagonal(int row, int column, out GameState gameState) { gameState = GameState.notWon; int numberInARow = 1; bool noBreaks = true; // row is y, column is x if (((row - column) < 3) && ((row + (6 - column)) > 2)) // make sure we are not comparing if it is in the top left or bottom right corner { // compare to the next one up and right int nextRow = row + 1; // row is y int nextColumn = column + 1; // column is x while (((nextRow < gameBoard.GetLength(ROW)) && (nextColumn < gameBoard.GetLength(COLUMN))) && (numberInARow < 4) && (noBreaks)) // check up and right { compareColors(row, column, nextRow, nextColumn, ref numberInARow, ref noBreaks); nextRow++; // look at the next space up nextColumn++; // look at the next space to the right } // end while // reset the row to compare to the next one down and left nextRow = row - 1; // row is y nextColumn = column - 1; noBreaks = true; // reset so we can look the other direction while (((nextRow >= 0) && (nextColumn >= 0)) && (numberInARow < 4) && (noBreaks)) // check down and left { compareColors(row, column, nextRow, nextColumn, ref numberInARow, ref noBreaks); nextRow--; // look at the next space down nextColumn--; // look at the next space to the left } // end while if (numberInARow >= 4) { gameState = GameState.won; } // end if } // end if }
public void FindChildrenStates(Boolean maxPlayer) { GameState gs; for(int i = 0; i < board.GetWidth(); i++) { for(int j = 0; j < board.GetLength(); j++) { if(board.getCell(j,i).getState().Equals(Cell.CellState.empty)) { if (board.getCell(j, i).isPlayable()) { Board b = new Board(board); Cell cell = b.getCell(j, i); if (maxPlayer) { cell.setState(player.getColor()); } else { cell.setState(player.getOpponent().getColor()); } if (j != 0) { b.getCell(j - 1, i).isPlayable(true); } gs = new GameState(GameState.State.transition, b, player, this, cell, !maxPlayer); children.Add(gs); } } else break; } } }
public void SetState(GameState.State s) { state = s; }