public Board(Board b) { this.WhiteBitBoard = b.WhiteBitBoard; this.BlackBitBoard = b.BlackBitBoard; this.CurrentPlayer = b.CurrentPlayer; this.Evaluation = b.Evaluation; }
public Board PlayFirstMove() { if (Color != PieceColor.White) throw new InvalidOperationException("Can't play first move as black"); Board b = new Board(); Random rnd = new Random(); int x = rnd.Next(1, 5); int y = rnd.Next(1, 5); b.PlacePieceAt(x, y, PieceColor.White); b.CurrentPlayer = PieceColor.Black; return b; }
// Gives high score to opposite color of .CurrentPlayer, // which is the color which presumably just played a move. // Negamax requires the evaluation function to keep track of // appropriately assigning +/- evaluations depending on who // is playing. public static int Evaluate(ref Board b) { int multiplier = (b.CurrentPlayer == PieceColor.White) ? -1 : 1; switch (b.GetWinner()) { case PieceColor.Empty: if (b.NumBlack() == 18) return EVAL_DRAW; break; case PieceColor.White: return multiplier * EVAL_WHITE_WIN; case PieceColor.Black: return multiplier * EVAL_BLACK_WIN; case PieceColor.Both: return EVAL_DRAW; } return multiplier * EvaluateChains(ref b); }
public static bool HasCheckmate(Board b, PieceColor pieceColor) { int whiteTemp = 0; int blackTemp = 0; UInt64 whiteLine = 0; UInt64 blackLine = 0; List<UInt64> lines = new List<UInt64>(); for (int i = 0; i < BoardHelper.WinningLines.Length; i++) { whiteLine = b.WhiteBitBoard & BoardHelper.WinningLines[i]; whiteTemp = BoardHelper.NumOneBits(whiteLine); blackLine = b.BlackBitBoard & BoardHelper.WinningLines[i]; blackTemp = BoardHelper.NumOneBits(blackLine); if (pieceColor == PieceColor.White && blackTemp == 0 && whiteTemp == 4) { foreach (UInt64 line in lines) { if (line == whiteLine) return true; } lines.Add(whiteLine); } else if (pieceColor == PieceColor.Black && whiteTemp == 0 && blackTemp == 4) { foreach (UInt64 line in lines) { if (line == blackLine) return true; } lines.Add(blackLine); } } return false; }
public abstract Board Play(Board b);
private static int CompareEvaluations(Board a, Board b) { return -a.Evaluation.CompareTo(b.Evaluation); }
/* Evaluation heurisitc: * Consider each potential winning line. If there is a black piece * in the line, then it cannot be won by white, so score that line as zero. * Otherwise, score the line as the # of white pieces residing in the line. * Keep track of the highest scoring chain, and how many were found with * that score. Do the same for black. * * Give most favor to longer chains, then secondary favor to the number of * such chains present. Create a positive white score this way, then * subtract off the corresponding black score using the same logic. * * Always gives positive score to white, negative to black. Up to the * caller to multiply by -1 if needed. */ private static int EvaluateChains(ref Board b) { int longestWhiteChain = 0; int numLongestWhiteChain = 0; int longestBlackChain = 0; int numLongestBlackChain = 0; UInt64 whiteTemp, blackTemp; int temp; for (int i = 0; i < BoardHelper.WinningLines.Length; i++) { // how many white pieces are in this line whiteTemp = b.WhiteBitBoard & BoardHelper.WinningLines[i]; // how many black pieces are in this line blackTemp = b.BlackBitBoard & BoardHelper.WinningLines[i]; if (blackTemp == 0 && whiteTemp != 0) { temp = BoardHelper.NumCommonOneBits(b.WhiteBitBoard, BoardHelper.WinningLines[i]); if (temp >= longestWhiteChain) { if (temp == longestWhiteChain) numLongestWhiteChain++; else { longestWhiteChain = temp; numLongestWhiteChain = 1; } } continue; // no need to evaluate black } if (whiteTemp == 0 && blackTemp != 0) { temp = BoardHelper.NumCommonOneBits(b.BlackBitBoard, BoardHelper.WinningLines[i]); if (temp >= longestBlackChain) { if (temp == longestBlackChain) numLongestBlackChain++; else { longestBlackChain = temp; numLongestBlackChain = 1; } } } } return (100 * longestWhiteChain + numLongestWhiteChain) - (100 * longestBlackChain + numLongestBlackChain); }
private void DoAiMoveComplete(Board aiBoard) { FadeBottomText.Stop(); Move aiMove = GetMoveFromBoards(masterBoard, aiBoard); Debug.Assert(false, String.Format("x: {0} y:{1} r:{2}", aiMove.xCoord, aiMove.yCoord, aiMove.rotation)); if (aiMove.pieceColor != PieceColor.Empty) { AnimateMove(aiMove); masterBoard = new Board(aiBoard); } if (masterBoard.IsDraw()) { okToClickDots = false; okToRotateQuadrant = false; BottomText.Text = ""; PopupTextBoxText.Text = "draw!"; Thread.Sleep(1000); PopupPanel.Visibility = System.Windows.Visibility.Visible; } else if (masterBoard.GetWinner() != PieceColor.Empty) { okToClickDots = false; okToRotateQuadrant = false; BottomText.Text = ""; PopupTextBoxText.Text = String.Format("{0} wins!", masterBoard.GetWinner() == PieceColor.White ? "white" : "black"); PopupPanel.Visibility = System.Windows.Visibility.Visible; } else { BottomText.Text = "your move"; okToClickDots = true; } aiThinking = false; }
private void DoAiMoveComplete(Board aiBoard) { FadeBottomText.Stop(); Move aiMove = GetMoveFromBoards(masterBoard, aiBoard); Debug.Assert(false, String.Format("x: {0} y:{1} r:{2}", aiMove.xCoord, aiMove.yCoord, aiMove.rotation)); if (aiMove.pieceColor != PieceColor.Empty) { AnimateMove(aiMove); masterBoard = new Board(aiBoard); } if (masterBoard.IsDraw()) { BottomText.Text = "Draw!"; okToClickDots = false; okToRotateQuadrant = false; } else if (masterBoard.GetWinner() != PieceColor.Empty) { if (masterBoard.GetWinner() == playerColor) { BottomText.Text = "You win!"; } else { BottomText.Text = "Computer wins!"; } okToClickDots = false; okToRotateQuadrant = false; } else { BottomText.Text = "Your move"; okToClickDots = true; } aiThinking = false; }
private void Button_Click(object sender, RoutedEventArgs e) { // ignore new game requests if the ai is running if (aiThinking) return; HowTo.Visibility = Visibility.Collapsed; HowToText.Visibility = Visibility.Collapsed; okToRotateQuadrant = false; okToClickDots = false; UpperLeft.Reset(); UpperRight.Reset(); LowerLeft.Reset(); LowerRight.Reset(); masterBoard = new Board(); if ((bool)playerBlackRadioButton.IsChecked) { playerColor = PieceColor.Black; if ((bool)aiEasyRadioButton.IsChecked) { Debug.Assert(false, "Easy"); masterAI = new FixedDepthPlayer(PieceColor.White, 0); } else if ((bool)aiMediumRadioButton.IsChecked) { Debug.Assert(false,"Med"); masterAI = new FixedDepthPlayer(PieceColor.White, 2); } else { Debug.Assert(false, "Hard"); masterAI = new FixedTimePlayer(PieceColor.White, 5); } aiThinking = true; DoAiMove(); } else { playerColor = PieceColor.White; if ((bool)aiEasyRadioButton.IsChecked) masterAI = new FixedDepthPlayer(PieceColor.Black, 0); else if ((bool)aiMediumRadioButton.IsChecked) masterAI = new FixedDepthPlayer(PieceColor.Black, 2); else masterAI = new FixedTimePlayer(PieceColor.Black, 5); okToClickDots = true; okToRotateQuadrant = false; BottomText.Text = "Your move"; } }
// Finds all unique boards possible after applying a move by the current player public List<Board> GenerateSubBoards() { Dictionary<Board, int> boards = new Dictionary<Board, int>(256); if (this.GetWinner() != PieceColor.Empty) return new List<Board>(); Board b = new Board(this); for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { if (this.GetColorAt(i, j) == PieceColor.Empty) { b.Copy(this); b.ApplyMoveFast(i, j, this.CurrentPlayer, Rotation.LowerLeftAntiClockwise); if (!boards.ContainsKey(b)) { b.Evaluation = BoardEvaluator.Evaluate(ref b); boards.Add(new Board(b), 0); } b.Copy(this); b.ApplyMoveFast(i, j, this.CurrentPlayer, Rotation.LowerLeftClockwise); if (!boards.ContainsKey(b)) { b.Evaluation = BoardEvaluator.Evaluate(ref b); boards.Add(new Board(b), 0); } b.Copy(this); b.ApplyMoveFast(i, j, this.CurrentPlayer, Rotation.LowerRightAntiClockwise); if (!boards.ContainsKey(b)) { b.Evaluation = BoardEvaluator.Evaluate(ref b); boards.Add(new Board(b), 0); } b.Copy(this); b.ApplyMoveFast(i, j, this.CurrentPlayer, Rotation.LowerRightClockwise); if (!boards.ContainsKey(b)) { b.Evaluation = BoardEvaluator.Evaluate(ref b); boards.Add(new Board(b), 0); } b.Copy(this); b.ApplyMoveFast(i, j, this.CurrentPlayer, Rotation.UpperLeftAntiClockwise); if (!boards.ContainsKey(b)) { b.Evaluation = BoardEvaluator.Evaluate(ref b); boards.Add(new Board(b), 0); } b.Copy(this); b.ApplyMoveFast(i, j, this.CurrentPlayer, Rotation.UpperLeftClockwise); if (!boards.ContainsKey(b)) { b.Evaluation = BoardEvaluator.Evaluate(ref b); boards.Add(new Board(b), 0); } b.Copy(this); b.ApplyMoveFast(i, j, this.CurrentPlayer, Rotation.UpperRightAntiClockwise); if (!boards.ContainsKey(b)) { b.Evaluation = BoardEvaluator.Evaluate(ref b); boards.Add(new Board(b), 0); } b.Copy(this); b.ApplyMoveFast(i, j, this.CurrentPlayer, Rotation.UpperRightClockwise); if (!boards.ContainsKey(b)) { b.Evaluation = BoardEvaluator.Evaluate(ref b); boards.Add(new Board(b), 0); } } } } return new List<Board>(boards.Keys); }
public List<Board> GenerateSafeBoards(bool includeCheckmates) { List<Board> boards = this.GenerateSubBoards(); Board workingBoard = new Board(); for (int k = 0; k < boards.Count; k++) { workingBoard.Copy(boards[k]); foreach (Board b in workingBoard.GenerateSubBoards()) { if (b.GetWinner() == workingBoard.CurrentPlayer) { boards.RemoveAt(k); k--; break; } if(includeCheckmates && BoardEvaluator.HasCheckmate(b, workingBoard.CurrentPlayer)) { boards.RemoveAt(k); k--; break; } } } return boards; }
private void NewGameButton_Click(object sender, EventArgs e) { // ignore new game requests if the ai is running if (aiThinking) return; okToRotateQuadrant = false; okToClickDots = false; UpperLeft.Reset(); UpperRight.Reset(); LowerLeft.Reset(); LowerRight.Reset(); PopupPanel.Visibility = System.Windows.Visibility.Collapsed; masterBoard = new Board(); this.currentGameStyle = settings.GameStyleSetting; if (this.currentGameStyle == GameStyle.VsAi && settings.PlayerColorSetting == PieceColor.Black) { playerColor = PieceColor.Black; if (0 == settings.AiStrengthSetting) { Debug.Assert(false, "Easy"); masterAI = new FixedDepthPlayer(PieceColor.White, 0); } else if (1 == settings.AiStrengthSetting) { Debug.Assert(false, "Med"); masterAI = new FixedDepthPlayer(PieceColor.White, 2); } else { Debug.Assert(false, "Hard"); masterAI = new FixedTimePlayer(PieceColor.White, 5); } aiThinking = true; DoAiMove(); } else if (this.currentGameStyle == GameStyle.VsAi && settings.PlayerColorSetting == PieceColor.White) { playerColor = PieceColor.White; if (0 == settings.AiStrengthSetting) masterAI = new FixedDepthPlayer(PieceColor.Black, 0); else if (1 == settings.AiStrengthSetting) masterAI = new FixedDepthPlayer(PieceColor.Black, 2); else masterAI = new FixedTimePlayer(PieceColor.Black, 5); okToClickDots = true; okToRotateQuadrant = false; BottomText.Text = "your move"; } else if (this.currentGameStyle == GameStyle.VsHuman) { playerColor = PieceColor.White; okToClickDots = true; okToRotateQuadrant = false; BottomText.Text = "white's move"; } }
// negamax style alpha beta pruning search private int AlphaBeta(Board b, int depth, int alpha, int beta) { DateTime currentTime = DateTime.Now; if ((currentTime - startTime).TotalSeconds > thinkTimeSeconds) return TIMEUP; if (depth == 0) return -b.Evaluation; switch (b.GetWinner()) { case PieceColor.White: return (b.CurrentPlayer == PieceColor.White) ? BoardEvaluator.EVAL_WHITE_WIN : -BoardEvaluator.EVAL_WHITE_WIN; case PieceColor.Black: return (b.CurrentPlayer == PieceColor.White) ? BoardEvaluator.EVAL_BLACK_WIN : -BoardEvaluator.EVAL_BLACK_WIN; case PieceColor.Both: return BoardEvaluator.EVAL_DRAW; case PieceColor.Empty: break; } List<Board> boards = b.GenerateSubBoards(); if (boards.Count == 0) return NOMOVES; BoardEvaluator.SortBoards(ref boards); int current; int best = MINSCORE; int localAlpha = alpha; for (int i = 0; i < boards.Count; i++) { current = -AlphaBeta(boards[i], depth - 1, -beta, -localAlpha); if (current == -NOMOVES) current = boards[i].Evaluation; else if (current == -TIMEUP) return TIMEUP; best = Math.Max(current, best); if (best >= beta) return beta; if (best > localAlpha) localAlpha = best; } return best; }
private Move GetMoveFromBoards(Board currentBoard, Board aiBoard) { Move returnMove = new Move(); if (currentBoard.Equals(aiBoard)) { returnMove.pieceColor = PieceColor.Empty; return returnMove; } PieceColor moveColor = (aiBoard.CurrentPlayer == PieceColor.White) ? PieceColor.Black: PieceColor.White; returnMove.pieceColor = moveColor; Board b = new Board(currentBoard); for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { b.Copy(currentBoard); if (b.GetColorAt(i, j) == PieceColor.Empty) { returnMove.xCoord = i; returnMove.yCoord = j; b.Copy(currentBoard); b.PlacePieceAt(i, j, moveColor); b.DoRotation(Rotation.LowerLeftAntiClockwise); if (b.Equals(aiBoard)) { returnMove.rotation = Rotation.LowerLeftAntiClockwise; return returnMove; } b.Copy(currentBoard); b.PlacePieceAt(i, j, moveColor); b.DoRotation(Rotation.LowerLeftClockwise); if (b.Equals(aiBoard)) { returnMove.rotation = Rotation.LowerLeftClockwise; return returnMove; } b.Copy(currentBoard); b.PlacePieceAt(i, j, moveColor); b.DoRotation(Rotation.LowerRightAntiClockwise); if (b.Equals(aiBoard)) { returnMove.rotation = Rotation.LowerRightAntiClockwise; return returnMove; } b.Copy(currentBoard); b.PlacePieceAt(i, j, moveColor); b.DoRotation(Rotation.LowerRightClockwise); if (b.Equals(aiBoard)) { returnMove.rotation = Rotation.LowerRightClockwise; return returnMove; } b.Copy(currentBoard); b.PlacePieceAt(i, j, moveColor); b.DoRotation(Rotation.UpperLeftAntiClockwise); if (b.Equals(aiBoard)) { returnMove.rotation = Rotation.UpperLeftAntiClockwise; return returnMove; } b.Copy(currentBoard); b.PlacePieceAt(i, j, moveColor); b.DoRotation(Rotation.UpperLeftClockwise); if (b.Equals(aiBoard)) { returnMove.rotation = Rotation.UpperLeftClockwise; return returnMove; } b.Copy(currentBoard); b.PlacePieceAt(i, j, moveColor); b.DoRotation(Rotation.UpperRightAntiClockwise); if (b.Equals(aiBoard)) { returnMove.rotation = Rotation.UpperRightAntiClockwise; return returnMove; } b.Copy(currentBoard); b.PlacePieceAt(i, j, moveColor); b.DoRotation(Rotation.UpperRightClockwise); if (b.Equals(aiBoard)) { returnMove.rotation = Rotation.UpperRightClockwise; return returnMove; } } } } // shouldn't ever get here Debug.Assert(false, "Bogus move \r\n" + masterBoard.ToString()); return returnMove; }
public override Board Play(Board b) { if (b.CurrentPlayer != this.Color) throw new InvalidOperationException(); List<Board> boards = b.GenerateSafeBoards(true); Debug.WriteLine("Safe moves: " + boards.Count.ToString()); // if opponent wins on their next move no matter what if (boards.Count == 0) { // if we have a legal move, do it even though it's a loser boards = b.GenerateSubBoards(); if(boards.Count == 0) return b; return boards[0]; } BoardEvaluator.SortBoards(ref boards); int current; int localAlpha = MINSCORE; Board bestBoard = boards[0]; Board bestBoardLastCompletedDepth = boards[0]; int lastCompletedDepth = 1; int lastCompletedDepthScore = 0; startTime = DateTime.Now; for (int depth = 1; depth < 36; depth++) { localAlpha = MINSCORE; int i; for (i = 0; i < boards.Count; i++) { current = -AlphaBeta(boards[i], depth, MINSCORE, -localAlpha); if (current == -TIMEUP) break; if (current == -NOMOVES) current = boards[i].Evaluation; boards[i].Evaluation = current; if (current > localAlpha) { localAlpha = current; bestBoard = boards[i]; if (current == BoardEvaluator.EVAL_WHITE_WIN) { lastCompletedDepth = depth; lastCompletedDepthScore = current; bestBoardLastCompletedDepth = bestBoard; break; } } } // bail from the loop if we didn't make it // all the way through the search at this depth if (i != boards.Count) break; // otherwise save the best move lastCompletedDepthScore = localAlpha; lastCompletedDepth = depth; bestBoardLastCompletedDepth = bestBoard; // re-sort the boards after each completed depth // to increase the number of cutoffs at the next depth BoardEvaluator.SortBoards(ref boards); } Debug.WriteLine(String.Format("Depth: {0} Score: {1}", lastCompletedDepth, lastCompletedDepthScore)); return bestBoardLastCompletedDepth; }
public override Board Play(Board b) { if (b.CurrentPlayer != this.Color) throw new InvalidOperationException(); Debug.Assert(false, b.ToString()); List<Board> boards; if (maxDepth == 2) boards = b.GenerateSafeBoards(true); else boards = b.GenerateSafeBoards(); Debug.WriteLine("Safe moves: " + boards.Count.ToString()); // if opponent wins on their next move no matter what if (boards.Count == 0) { // if we have a legal move, do it even though it's a loser boards = b.GenerateSubBoards(); if(boards.Count == 0) return b; return boards[0]; } BoardEvaluator.SortBoards(ref boards); int current; int localAlpha = MINSCORE; Board bestBoard = boards[0]; DateTime startTime = DateTime.Now; positionsEvaluated = 0; for (int i = 0; i < boards.Count; i++) { // Console.Write("+"); current = -AlphaBeta(boards[i], maxDepth, MINSCORE, -localAlpha); if (current == -NOMOVES) current = boards[i].Evaluation; if (current > localAlpha) { localAlpha = current; bestBoard = boards[i]; if (current == BoardEvaluator.EVAL_WHITE_WIN) break; } } DateTime endTime = DateTime.Now; TimeSpan moveTime = endTime - startTime; //Console.WriteLine(" Score: " + localAlpha.ToString()); //Console.WriteLine("Time: {0}", moveTime.TotalSeconds); //Console.WriteLine("Moves evaluated: {0}", positionsEvaluated); //Console.WriteLine("Moves/sec: {0}", positionsEvaluated / moveTime.TotalSeconds); Debug.Assert(false, bestBoard.ToString()); return bestBoard; }