public int GetMove(BoardModel boardModel) { var openIndicies = boardModel.GetOpenIndicies(); if (openIndicies.Count == 0) { return -1; } HypotheticalBoard best = null; //Generate a tree of HypotheticalBoards for each open spot to determine which move is the best. foreach (var index in openIndicies) { var board = new HypotheticalBoard(new BoardModel(boardModel), Player.Computer, index); if ((best == null) || board.GetScore() > best.GetScore()) { best = board; } else if (best.GetScore() == board.GetScore()) { //Randomly decide whether to use the new board, or the existing best board. //This will make the game a bit more random, while still using strategy. if (_random == null) { _random = new Random(); } if (_random.NextDouble() < 0.25) { best = board; } } } //best can never be null, it will be set in the first pass of the for loop. return best.Index; }
/// <summary> /// Keeping depth private, because external uses do not need to worry about it. /// </summary> /// <param name="depth"></param> /// <param name="board"></param> /// <param name="player"></param> /// <param name="index"></param> private HypotheticalBoard(int depth, BoardModel board, Player player, int index) { _depth = depth; _board = board; _player = player; Index = index; _board.UpdatePositionAtIndex(Index, _player); // Continue to fill the tree of next possible moves // Stop the recursion when there are no open spots left, // or we have reached the max depth of recursion. if (_depth < maxDepth) { // Get the open indicies for the board where there are moves are still available var openIndicies = _board.GetOpenIndicies(); if (openIndicies.Count > 0) { ChildBoards = new List<HypotheticalBoard>(); foreach (var openIndex in openIndicies) { ChildBoards.Add(new HypotheticalBoard(depth + 1, new BoardModel(board), Utilities.SwitchPlayer(_player), openIndex)); } } } }
public int GetMove(BoardModel boardModel) { var openIndicies = boardModel.GetOpenIndicies(); if(_randomMove == null) { _randomMove = new Random(); } return openIndicies[_randomMove.Next(openIndicies.Count)]; }
/// <summary> /// Construct a BoardModel copy of the given baseBoardModel /// </summary> /// <param name="baseBoardModel">The BoardModel to copy.</param> public BoardModel(BoardModel baseBoardModel) { _board = new List<Player>(baseBoardModel._board); }
/// <summary> /// Creates a board using the given BoardModel in which the given player /// will take the given index. /// </summary> /// <param name="board"></param> /// <param name="player"></param> /// <param name="index"></param> public HypotheticalBoard(BoardModel board, Player player, int index) : this(0, board, player, index) { }
/// <summary> /// Begin new game. /// </summary> /// <param name="userInterface">Instance of form that the controller will send various game state updates to</param> /// <param name="startingPlayer">Who begins the game, must be Human or Computer</param> /// <param name="difficulty">The level of difficulty</param> public void StartNewGame(TicTacToeForm userInterface, Player startingPlayer = Player.Human, Difficulty difficulty=Difficulty.Easy) { if (startingPlayer == Player.None) { throw new ArgumentException("Player must be either human or computer."); } _boardModel = new BoardModel(); _userInterface = userInterface; _playerTurn = startingPlayer; //If the difficulty is easy, then the computer will choose indicies at random. //If the diffuculty is hard, the computer uses a min-max algorithm to choose the optimal index. switch(difficulty) { case Difficulty.Easy: _moveChooser = new RandomComputerMoveChooser(); break; case Difficulty.Hard: _moveChooser = new MinMaxComputerMoveChooser(); break; } if(_playerTurn == Player.Computer) { DoComputerTurn(); } }