public void WinForPlayerYIsNotWinForPlayerX() { int score = MoveScoreConverter.ConvertWin(Occupied.PlayerY, 0); bool result = MoveScoreConverter.IsImmediateWin(Occupied.PlayerX, score); Assert.IsFalse(result); }
public void DescribeYWinner() { int score = MoveScoreConverter.ConvertWin(Occupied.PlayerY, 2); string description = MoveScoreConverter.DescribeScore(score); Assert.AreEqual("Win by Y in 2 moves", description); }
public void EqualScoresAreNotBetterForEitherPlayer() { const int Score = 0; Assert.IsFalse(MoveScoreConverter.IsBetterFor(Score, Score, true)); Assert.IsFalse(MoveScoreConverter.IsBetterFor(Score, Score, false)); }
/// <summary> /// public wrapper for recursion /// try each possible move, find the one with the best score /// </summary> /// <param name="lookahead">how far to look ahead</param> /// <param name="playerX">is this for player x</param> /// <returns>The data on the best move location and score</returns> public MinimaxResult DoMinimax(int lookahead, bool playerX) { // set up inital state DateTime startTime = DateTime.Now; if (lookahead < 1) { throw new Exception("Invalid lookahead of " + lookahead); } this.debugDataItems.Clear(); Occupied player = playerX.ToPlayer(); int alpha = MoveScoreConverter.ConvertWin(player.Opponent(), 0); int beta = MoveScoreConverter.ConvertWin(player, 0); MinimaxResult bestMove = this.ScoreBoard(lookahead, this.ActualBoard, playerX, alpha, beta); if (bestMove.Move != Location.Null) { GoodMoves.AddGoodMove(0, bestMove.Move); } DateTime endTime = DateTime.Now; this.MoveTime = endTime - startTime; return(bestMove); }
public void IsImmediateWinSuccessForPlayerX() { int score = MoveScoreConverter.ConvertWin(Occupied.PlayerX, 0); bool result = MoveScoreConverter.IsImmediateWin(Occupied.PlayerX, score); Assert.IsTrue(result); }
public void WinIsBetterForPlayerY() { int winScoreX = MoveScoreConverter.ConvertWin(Occupied.PlayerX, 2); int winScoreY = MoveScoreConverter.ConvertWin(Occupied.PlayerY, 2); Assert.IsTrue(MoveScoreConverter.IsBetterFor(winScoreY, winScoreX, false)); Assert.IsFalse(MoveScoreConverter.IsBetterFor(winScoreY, winScoreX, true)); }
public void LowerScoreIsBetterForPlayerX() { const int PlayerXScore = 1; const int PlayerYScore = -1; Assert.IsFalse(MoveScoreConverter.IsBetterFor(PlayerYScore, PlayerXScore, true)); Assert.IsTrue(MoveScoreConverter.IsBetterFor(PlayerYScore, PlayerXScore, false)); }
public void WinForPlayerXIsGreaterThanWinForPlayerY() { int playerXWin = MoveScoreConverter.ConvertWin(Occupied.PlayerX, 0); int playerYWin = MoveScoreConverter.ConvertWin(Occupied.PlayerY, 0); Assert.Greater(playerXWin, playerYWin); Assert.Greater(playerXWin, 0); Assert.Less(playerYWin, 0); }
public void NegativeScoreIsNotImmediateWin() { const int Score = -12; bool resultForX = MoveScoreConverter.IsImmediateWin(Occupied.PlayerX, Score); bool resultForY = MoveScoreConverter.IsImmediateWin(Occupied.PlayerY, Score); Assert.IsFalse(resultForX, "X"); Assert.IsFalse(resultForY, "Y"); }
public void LaterWinForPLayerXIsNotImmediateWin() { int score = MoveScoreConverter.ConvertWin(Occupied.PlayerX, 1); bool resultForX = MoveScoreConverter.IsImmediateWin(Occupied.PlayerX, score); bool resultForY = MoveScoreConverter.IsImmediateWin(Occupied.PlayerY, score); Assert.IsFalse(resultForX, "X"); Assert.IsFalse(resultForY, "Y"); }
public void NearWinForPlayerXIsGreater() { int playerXWin = MoveScoreConverter.ConvertWin(Occupied.PlayerX, 1); int playerXFarWin = MoveScoreConverter.ConvertWin(Occupied.PlayerX, 2); Assert.Greater(playerXWin, playerXFarWin); Assert.Greater(playerXFarWin, 0); Assert.Greater(playerXWin, 0); }
public void NearWinForPlayerYIsLessNegative() { int playerYWin = MoveScoreConverter.ConvertWin(Occupied.PlayerY, 1); int playerYFarWin = MoveScoreConverter.ConvertWin(Occupied.PlayerY, 2); Assert.Less(playerYWin, playerYFarWin); Assert.Less(playerYFarWin, 0); Assert.Less(playerYWin, 0); }
public void MoveWins() { if (MoveScoreConverter.IsWinForPlayer(this.Score, Occupied.PlayerX)) { this.Score--; } else if (MoveScoreConverter.IsWinForPlayer(this.Score, Occupied.PlayerY)) { this.Score++; } }
private static void TestBestMove(HexGame game, int level, Location expectedBestMove) { Minimax hexPlayer = new Minimax(game.Board, game.GoodMoves, new CandidateMovesAll()); MinimaxResult bestMove = hexPlayer.DoMinimax(level, true); // test the location of the move Assert.AreEqual(expectedBestMove, bestMove.Move, "Wrong move at level " + level); // test the expected score if (level >= 3) { Assert.AreEqual(Occupied.PlayerX, MoveScoreConverter.Winner(bestMove.Score)); } }
public void TestCalculateMove2MinimaxPlayerY() { HexBoard board = new HexBoard(3); PlayTwoMoves(board); Minimax minimax = MakeMinimaxForBoard(board); MinimaxResult secondPlayerResult = minimax.DoMinimax(2, false); Location expectedPlay = new Location(0, 2); Assert.AreEqual(expectedPlay, secondPlayerResult.Move, "Wrong play location"); Assert.IsFalse(MoveScoreConverter.IsWin(secondPlayerResult.Score)); }
public MinimaxResult DoMinimax(int depth, bool isComputer) { // megmondja, hogy ki a jatekos, ha a gep, akkor az isComputerben true van, es akkor a plyaerben xPlayer lesz. Occupied player = isComputer.ToPlayer(); // a gep az alpha this.alpha = MoveScoreConverter.ConvertWin(player.Opponent(), 0); // ember a beta this.beta = MoveScoreConverter.ConvertWin(player, 0); // a minimax algoritmus magaban MinimaxResult bestMove = this.MiniMaxAlg(depth, isComputer, board); if (bestMove.Move != Location.Null) { // killer heurisztika GoodMoves.AddGoodMove(0, bestMove.Move); } return(bestMove); }
public void TestMinimax3() { HexBoard board = new HexBoard(5); Minimax minimax = MakeMinimaxForBoard(board); /* * on a 5 * 5 board, red(playerx) has 3, 0 and 1, 4 * needs to play 2,2 to win - should know this at look ahead 5 */ board.PlayMove(3, 0, true); board.PlayMove(1, 4, true); MinimaxResult bestMove = minimax.DoMinimax(5, true); Location expectedMove = new Location(2, 2); Assert.IsTrue(MoveScoreConverter.IsWin(bestMove.Score), "No win " + bestMove.Score); Assert.AreEqual(expectedMove, bestMove.Move, "Wrong expected move"); }
public int SituationScore() { int playerXScore = this.PlayerScore(true); if (playerXScore == 0) { // player x wins return(MoveScoreConverter.ConvertWin(Occupied.PlayerX, 0)); } if (playerXScore == PathLengthConstants.OffPath) { // player X loses -> player y wins return(MoveScoreConverter.ConvertWin(Occupied.PlayerY, 0)); } // no win yet. score for the situation int playerYScore = this.PlayerScore(false); return(playerYScore - playerXScore); }
private Location GetBestMove(MinimaxResult playResult) { Location result = playResult.Move; int moveScore = playResult.Score; this.IsGameWon = MoveScoreConverter.IsWin(moveScore) && MoveScoreConverter.WinDepth(moveScore) == 1; Occupied opponent = (!this.hexGame.PlayerX).ToPlayer(); bool losingMove = MoveScoreConverter.Winner(playResult.Score) == opponent; if (losingMove) { Location losingLocation = this.MakeLosingMove(); if (losingLocation != Location.Null) { result = losingLocation; } } return(result); }
private static void AssertWinner(int score, Occupied winner) { Assert.IsTrue(MoveScoreConverter.IsWin(score), "Should have winner"); Assert.AreEqual(winner, MoveScoreConverter.Winner(score), "Wrong winner"); }
/// <summary> /// private recursive worker - does the minimax algorithm /// </summary> /// <param name="lookahead">the current ply, counts down to zero</param> /// <param name="stateBoard">the current board</param> /// <param name="isPlayerX">player X or player Y</param> /// <param name="alpha">alpha value used in alpha-beta pruning</param> /// <param name="beta">beta value used in alpha-beta pruning</param> /// <returns>the score of the board and best move location</returns> private MinimaxResult ScoreBoard( int lookahead, HexBoard stateBoard, bool isPlayerX, int alpha, int beta) { this.CountBoards++; MinimaxResult bestResult = null; Location cutoffMove = Location.Null; var possibleMoves = this.candidateMovesFinder.CandidateMoves(stateBoard, lookahead); foreach (Location move in possibleMoves) { // end on null loc if (move.IsNull()) { break; } if (this.GenerateDebugData) { this.AddDebugDataItem(lookahead, move, isPlayerX, alpha, beta); } // make a speculative board, like the current, but with this cell played HexBoard testBoard = this.boardCache.GetBoard(); testBoard.CopyStateFrom(stateBoard); testBoard.PlayMove(move, isPlayerX); MinimaxResult moveScore; PathLengthBase staticAnalysis = this.pathLengthFactory.CreatePathLength(testBoard); int situationScore = staticAnalysis.SituationScore(); if (lookahead <= 1) { // we have reached the limits of lookahead - return the situation score moveScore = new MinimaxResult(situationScore); } else if (MoveScoreConverter.IsWin(situationScore)) { // stop - someone has won moveScore = new MinimaxResult(situationScore); } else { // recurse moveScore = this.ScoreBoard(lookahead - 1, testBoard, !isPlayerX, beta, alpha); moveScore.MoveWins(); } moveScore.Move = move; this.boardCache.Release(testBoard); // higher scores are good for player x, lower scores for player y if (bestResult == null || MoveScoreConverter.IsBetterFor(moveScore.Score, bestResult.Score, isPlayerX)) { bestResult = new MinimaxResult(move, moveScore); } // do the alpha-beta pruning alpha = CheckAlpha(alpha, moveScore.Score, isPlayerX); if (IsAlphaBetaCutoff(isPlayerX, alpha, beta)) { cutoffMove = move; bestResult.Score = alpha; break; } // end a-b pruning } if (bestResult != null) { GoodMoves.AddGoodMove(lookahead, bestResult.Move); } if (cutoffMove != Location.Null) { GoodMoves.AddGoodMove(lookahead, cutoffMove); } return(bestResult); }
public void DescribeNegativeScore() { string description = MoveScoreConverter.DescribeScore(-13); Assert.AreEqual("Y ahead with -13", description); }
public void DescribePositiveScore() { string description = MoveScoreConverter.DescribeScore(12); Assert.AreEqual("X ahead with 12", description); }
public void DescribeZeroScore() { string description = MoveScoreConverter.DescribeScore(0); Assert.AreEqual("Score: 0", description); }
private MinimaxResult MiniMaxAlg(int depth, bool isComputer, HexBoard board) { BoardCache boardCache = new BoardCache(board.Size); MinimaxResult bestResult = null; Location cutOffMove = Location.Null; // az ures cellakat tartalmazza var possibleMoves = candidateMovesFinder.CandidateMoves(board, depth); foreach (Location move in possibleMoves) { if (!move.IsNull()) { // nem az eredetit modositom meg, hanem letrehozok egyet a peldajara HexBoard board1 = new HexBoard(board.Size); board1.CopyStateFrom(board); board1.PlayMove(move, isComputer); // itt szamolja ki az allast PathLengthBase staticAnalysis = this.pathLengthFactory.CreatePathLength(board1); int situationScore = staticAnalysis.SituationScore(); MinimaxResult moveScore = new MinimaxResult(situationScore); if (depth <= 1 || MoveScoreConverter.IsWin(situationScore)) { moveScore = new MinimaxResult(situationScore); } else { if (depth > 1) { // rekurzio moveScore = MiniMaxAlg(depth--, !isComputer, board1); moveScore.MoveWins(); } } moveScore.Move = move; // Itt nezem meg, hogy a minimum kell nekunk, vagy a maximum if (bestResult == null || MoveScoreConverter.MinOrMax(moveScore.Score, bestResult.Score, isComputer)) { bestResult = new MinimaxResult(move, moveScore); } alpha = CheckAlpha(moveScore.Score, isComputer); if (IsAlphaBetaCutoff(isComputer)) { cutOffMove = move; bestResult.Score = alpha; break; } } else { break; } } if (bestResult != null) { GoodMoves.AddGoodMove(depth, bestResult.Move); } if (cutOffMove != Location.Null) { GoodMoves.AddGoodMove(depth, cutOffMove); } return(bestResult); }
public void NoWinner() { Assert.AreEqual(Occupied.Empty, MoveScoreConverter.Winner(0)); }
public void WinnerYIsWinner() { int score = MoveScoreConverter.ConvertWin(Occupied.PlayerY, 1); Assert.AreEqual(Occupied.PlayerY, MoveScoreConverter.Winner(score)); }