/// <summary> /// Called when the user clicks a square button in the game board visual area /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void square_clicked(object sender, EventArgs e) { Button squareClicked = (Button)sender; if (squareClicked.Enabled && gameActive != null) { Square squareObject = null; for (int iX = 0; iX < 3; iX++) { for (int iY = 0; iY < 3; iY++) { if (squareButtons[iX,iY] == squareClicked) { squareObject = gameActive.Board.Squares[iX, iY]; break; } } } if (squareObject != null) { Move move = new Move(gameActive.ActivePlayer, squareObject); gameActive.PlayMove(move); } } }
/// <summary> /// Method called when the game passes control to this player /// </summary> /// <param name="game">Game object that this player belongs to</param> public override void PromptForMove(Game game) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Square bestSquare = null; float bestValue = float.MinValue; _playerOpponent = game.Players[0]; if (_playerOpponent == this) { _playerOpponent = game.Players[1]; } Random rand = new Random(); int equalSquares = 0; foreach (Square square in game.Board.Squares) { if (square.Owner == null) { square.Owner = this; float curValue = AlphaBetaPrune(game, int.MinValue, int.MaxValue, false); square.Owner = null; if (bestSquare == null || curValue > bestValue) { bestValue = curValue; bestSquare = square; equalSquares = 0; } else if (curValue == bestValue) { equalSquares++; if (rand.Next(0,equalSquares + 1) == 0) { bestValue = curValue; bestSquare = square; } } } } int curTurnTime = (int)stopwatch.ElapsedMilliseconds; int turnTimeDiff = _minTurnTime - curTurnTime; Move move = new Move(this, bestSquare); Action playMove = () => game.PlayMove(move); int sleepMs = Math.Max(0, turnTimeDiff); // Ensure that the AI waits at least the minTurnTime before playing a move, // making the AI seem more human-like in that it won't make all of its moves near instantaniously. _timer = new Timer(PlayMoveAfterDelayCallback, playMove, sleepMs, Timeout.Infinite); }
/// <summary> /// Attempt to play the given move /// </summary> /// <param name="move">Move to try and play</param> public void PlayMove(Move move) { if (move == null) { throw new InvalidMoveException("Cannot play null move."); } if (move.Player != ActivePlayer) { throw new InvalidMoveException("It is not this player's [" + move.Player.ToString() + "] turn."); } if (move.Square.Owner != null) { throw new InvalidMoveException("Board square [" + move.Square.ToString() + "] already has an owner."); } move.Square.Owner = move.Player; OnGameUpdate(); AdvanceTurn(); if (GetGameState().IsCompleted == false) { ActivePlayer.PromptForMove(this); } }
/// <summary> /// Assigns arbitrary weights to a move. Used for computing a winner. /// </summary> /// <param name="move"> /// The move whose weight to get. /// </param> /// <returns> /// An integer value weight for the move. /// </returns> private int GetWeightForMove(Move move) { int value = 0; switch (move) { case Move.X: value = 1; break; case Move.O: value = -1; break; case Move.Undefined: value = 0; break; } return value; }
/// <summary> /// Sets the target row and column in the board to the passed in move. /// </summary> /// <param name="slot"> /// The slot to place the move in. /// </param> /// <param name="move"> /// The move to make. X or O. /// </param> public void MakeMove(int slot, Move move) { if (slot < 1 || slot > 9) { throw new IndexOutOfRangeException("Slot numbers must be between 1 and 9"); } var index = slot - 1; if (moves[index] == Move.Undefined) { this.moves[index] = move; this.winner = this.CheckForWinner(); } else { throw new ArgumentException("Someone already moved there!"); } }
/// <summary> /// Swaps X's to O's and O's to X's. Allows for alternating moves. /// </summary> /// <param name="currentPlayer"> /// The current move to be made by a player. X or O. /// </param> /// <returns> /// If X, then O. If O, then X. /// </returns> private Move FlipCurrentPlayer(Move currentPlayer) { return currentPlayer == Move.X ? Move.O : Move.X; }
/// <summary> /// Prints out a move. /// </summary> /// <param name="move"> /// The move to print. /// </param> /// <returns> /// Prints the symbol for the move. /// </returns> private string PrintMove(Move move) { return move == Move.X ? "X" : "O"; }
/// <summary> /// Sets the target row and column in the board to the passed in move. /// </summary> /// <param name="slot"> /// The slot to place the move in. /// </param> /// <param name="move"> /// The move to make. X or O. /// </param> public void MakeMove(int slot, Move move) { throw new NotImplementedException(); }