// Calls minimax for each possible move and returns best move public static int FindBestMove(Connect4Game connect4, Connect4Board startBoard, int maxDepth) { MiniMaxVanilla.maxDepth = maxDepth; MiniMaxVanilla.connect4 = connect4; int player = MAXIMIZING; List <MoveScore> results = new List <MoveScore>(); Console.WriteLine("Move scores {move, score}: "); foreach (int move in connect4.GetPossibleMoves(startBoard)) { Connect4Board nextBoard = connect4.SimulateMove(startBoard, player, move); int searchScore = MiniMaxSearch(nextBoard, player * -1, 1); Console.Write("{" + move + ", " + searchScore + "} , "); results.Add(new MoveScore() { move = move, score = searchScore }); } int decidedMove = DecideMove(results); Console.WriteLine("Move chosen: " + decidedMove); return(decidedMove); }
static int FindMove(Connect4Board board) { int iterativeDepth = 0; int bestMove = 0, tempBestMove = 0; while (true) { iterativeDepth++; Console.WriteLine("Depth: " + (iterativeDepth)); tempBestMove = MiniMaxAlphaBetaHeuristic.FindBestMove(connect4, new Con4Board(board), iterativeDepth); if (timesUp) { timesUp = false; averageDepth += iterativeDepth - 1; averageDepthCounts++; Console.WriteLine("Depth " + iterativeDepth + " aborted!"); return(bestMove); } bestMove = tempBestMove; if (gameOutcomeFinal) // 100% win/lose in sight, don't search deeper. Or if only 1 viable option { gameOutcomeFinal = false; t.Abort(); return(bestMove); } } //return MiniMaxAlphaBetaHeuristic.FindBestMove(connect4, new Con4Board(board), depth); }
public void ColumnFullReturnsTrueIfFull() { var board = new Connect4Board(2, 2); board.Columns[0][0].PlayerIndex = 1; board.Columns[0][1].PlayerIndex = 1; Assert.IsTrue(board.ColumnFull(1)); }
// Returns best score found for current player private static int MiniMaxSearch(Connect4Board currentBoard, int player, int depth) { BoardState boardState = connect4.EvaluateBoard(currentBoard); // End game if max depth reached or game is over if (depth == maxDepth || boardState != BoardState.ongoing) { return(Evaluate.EvaluateBoardSimple(boardState, depth)); } int searchScore = player == MAXIMIZING ? int.MinValue : int.MaxValue; foreach (int move in connect4.GetPossibleMoves(currentBoard)) { Connect4Board nextBoard = connect4.SimulateMove(currentBoard, player, move); if (player == MAXIMIZING) { searchScore = Math.Max(searchScore, MiniMaxSearch(nextBoard, MINIMIZING, depth + 1)); } else { searchScore = Math.Min(searchScore, MiniMaxSearch(nextBoard, MAXIMIZING, depth + 1)); } } return(searchScore); }
protected override async Task <InnerMoveResult> HandleAsync(Connect4MoveData moveData, int playerId) { var serializedBoard = await boardLoader.LoadByMatchIdAsync(moveData.MatchId); var board = new Connect4Board(); board.DeserializeContent(serializedBoard); var item = playerId == 0 ? Item.Red : Item.Yellow; board.PlaceItem(item, moveData.Column); serializedBoard.BoardData = board.SerializeContent().BoardData; var result = new InnerMoveResult { MoveResult = new Connect4MoveResult { Board = board.Board } }; if (board.CheckWinner() == item) { result.Status = Status.Win; return(result); } if (board.Full) { result.Status = Status.Draw; return(result); } result.Status = Status.Success; return(result); }
private void Connect4BoardOnMouseMove(object sender, MouseEventArgs e) { if (Game.GameStatus != GameStatusType.BlackWin && Game.GameStatus != GameStatusType.RedWin) { CurrentHover = GetAvailableInRow(e.Location.X); } Connect4Board.Refresh(); }
public void AddCounterAddsToCorrectRowAndColumnPopulatedBoard() { var board = new Connect4Board(5, 2); board.Columns[0][0].PlayerIndex = 1; board.Columns[0][1].PlayerIndex = 2; board.AddCounter(1, 1); Assert.AreEqual(1, board.Columns[0][2].PlayerIndex); }
public override int GetColumnSelected(Func<string> QueryPlayer, Action<object> WriteLinetoDisplay, Connect4Board board) { WriteLinetoDisplay("The computer is thinking"); Thread.Sleep(2000); int column = rand.Next(1, board.ColumnCount+1); while (board.ColumnFull(column)) column = rand.Next(1, board.ColumnCount+1); return column; }
public void CreateBuildsCorrectArrays() { var board = new Connect4Board(5, 2); Assert.AreEqual(2, board.ColumnCount); Assert.AreEqual(5, board.RowCount); Assert.AreEqual(2,board.Columns.Length); Assert.AreEqual(5, board.Columns[0].Length); Assert.AreEqual(5, board.Columns[1].Length); }
// Use this for initialization void Start() { _board = new Connect4Board(COLUMNS, COLUMN_SIZE); mouseCell = new Vector3Int(-1, COLUMN_SIZE, 0); exitBtn.onClick.AddListener(OnExitBtnClick); resetBtn.onClick.AddListener(OnResetBtnClick); difficultyDropdown.onValueChanged.AddListener(OnDifficultyChange); }
public void AddMultipleCounters() { var board = new Connect4Board(5, 2); board.AddCounter(1, 1); board.AddCounter(2, 1); board.AddCounter(3, 2); Assert.AreEqual(1, board.Columns[0][0].PlayerIndex); Assert.AreEqual(2, board.Columns[0][1].PlayerIndex); Assert.AreEqual(3, board.Columns[1][0].PlayerIndex); }
private static void DrawMove(Connect4Board board, int selectedColumn, int cursorLeft, int cursorTop, Player humanPlayer) { var boardState = board.Clone().State; Console.CursorVisible = false; var emptyPositions = boardState.Columns[selectedColumn].Count(player => player == Player.None); Console.SetCursorPosition(cursorLeft, cursorTop + emptyPositions); Console.Write(Connect4Board.FromPlayerToString(humanPlayer)); }
public override Connect4MoveResult Convert(SerializedConnect4Board serializedBoard) { var board = new Connect4Board(); board.DeserializeContent(serializedBoard); return(new Connect4MoveResult { Board = board.Board }); }
public Con4Board(Connect4Board board) { grid = new int[WIDTH, HEIGHT]; for (int x = 0; x < WIDTH; x++) { for (int y = 0; y < HEIGHT; y++) { grid[x, y] = board.GetCell(x, y); } } }
private void RestartButton_Click(object sender, EventArgs e) { Game.RestartGame(); Connect4Board.Refresh(); var AIColour = UserPlayer == PlayerColor.Red ? PlayerColor.Black : PlayerColor.Red; AI = new MCTSAI(false, AIColour); if (AIColour == PlayerColor.Red) { AI.MakeMove(Game); } }
// Use this for initialization void Start() { players = FindObjectsOfType <Player>(); if (players.Length == 0) { players = new Player[1]; players[0] = Instantiate(dummyPlayer); } players[activePlayerIndex].isActive = true; board = FindObjectOfType <Connect4Board>(); }
protected override void Start() { base.Start(); for (int i = 0; i < actions.Length; i++) { actions[i].action = i; } policyValueNet = new Connect4PolicyValueNet(); mctsPlayer = new MCTSPlayer(policyValueNet); board = new Connect4Board(); }
public override int GetColumnSelected(Func<string> QueryPlayer, Action<object> WriteLineToDisplay, Connect4Board board) { var columnSelected = QueryPlayer(); int column = 0; while (!Int32.TryParse(columnSelected, out column) || column < 1|| column > board.ColumnCount || board.ColumnFull(column)) { WriteLineToDisplay(String.Format("Please enter a number between 1 and {0} where column is not full.", board.ColumnCount)); columnSelected = QueryPlayer(); } return column; }
// Returns best score found for current player private static int MiniMaxSearch(Connect4Board currentBoard, int player, int depth, int a, int b) { BoardState boardState = connect4.EvaluateBoard(currentBoard); // End game if max depth reached or game is over if (depth == maxDepth || boardState != BoardState.ongoing) return Evaluate.EvaluateBoardSimple(boardState, depth); int searchScore = player == MAXIMIZING ? int.MinValue : int.MaxValue; foreach (int move in connect4.GetPossibleMoves(currentBoard).OrderBy(x => rand.Next()).ToList()) { Connect4Board nextBoard = connect4.SimulateMove(currentBoard, player, move); if (player == MAXIMIZING) { searchScore = Math.Max(searchScore, MiniMaxSearch(nextBoard, MINIMIZING, depth + 1, a, b)); a = Math.Max(a, searchScore); if (a >= b) break; /* if (searchScore >= b) // Minimizing parent will not choose this path, beta cutoff break; a = Math.Max(a, searchScore); */ } else { searchScore = Math.Min(searchScore, MiniMaxSearch(nextBoard, MAXIMIZING, depth + 1, a, b)); b = Math.Min(b, searchScore); if (b <= a) break; /* if (searchScore <= a) // Maximizing parent will not choose this path, alpha cutoff break; b = Math.Min(b, searchScore); */ } } return searchScore; }
private static void PrintBoard(Connect4Board board, Player humanPlayer) { Console.Clear(); var cursorTop = Console.WindowHeight - 9; Console.SetCursorPosition(0, cursorTop); Console.Write("CONNECT 4\n"); Console.Write("\n"); Console.Write("Players:\n"); Console.Write("\n"); Console.Write($"* You: {Connect4Board.FromPlayerToString(humanPlayer)}\n"); Console.Write($"* Computer: {Connect4Board.FromPlayerToString(humanPlayer.ToOppositePlayer())}\n"); Console.Write("\n"); Console.Write("How to play:\n"); Console.Write("\n"); Console.Write("* Select a column and click enter to drop your chip.\n"); Console.Write("* Type Q to exit.\n"); Console.Write("\n\n"); Console.Write(board); Console.Write("\n"); }
private void Connect4Board_Click(object sender, MouseEventArgs e) { var available = GetAvailableInRow(e.Location.X); if (available != null && Game.GameStatus != GameStatusType.BlackWin && Game.GameStatus != GameStatusType.RedWin) { Game.PutInColumn(available.Item2, UserPlayer); Connect4Board.Refresh(); if (Game.GameStatus != GameStatusType.Started && Game.GameStatus != GameStatusType.Initialised) { MessageBox.Show("Koniec gry. Wygrały " + (Game.GameStatus == GameStatusType.BlackWin ? "czerwone" : "zolte") + "."); } else { AI.MakeMove(Game); Connect4Board.Refresh(); if (Game.GameStatus != GameStatusType.Started && Game.GameStatus != GameStatusType.Initialised) { MessageBox.Show("Koniec gry. Wygrały " + (Game.GameStatus == GameStatusType.BlackWin ? "czerwone" : "zolte") + "."); } } } }
// Calls minimax for each possible move and returns best move public static int FindBestMove(Connect4Game connect4, Connect4Board startBoard, int maxDepth) { MiniMaxAlphaBeta.maxDepth = maxDepth; MiniMaxAlphaBeta.connect4 = connect4; int player = MAXIMIZING; int a = int.MinValue; int b = int.MaxValue; List<MoveScore> results = new List<MoveScore>(); Console.WriteLine("\nMove scores {move, score, a}: "); foreach (int move in connect4.GetPossibleMoves(startBoard).OrderBy(x => rand.Next()).ToList()) { Connect4Board nextBoard = connect4.SimulateMove(startBoard, player, move); int searchScore = MiniMaxSearch(nextBoard, MINIMIZING, 1, a, b); // UPDATE ALPHA a = Math.Max(a, searchScore); Console.Write("{" + move + ", " + searchScore + ", " + a + "} , "); results.Add(new MoveScore() { move = move, score = searchScore }); } int decidedMove = DecideMove(results); Console.WriteLine("Move chosen: " + decidedMove); return decidedMove; }
public abstract int GetColumnSelected(Func<string> QueryPlayer, Action<object> WriteLineToDisplay, Connect4Board board);
public void ColumnFullReturnsFalseIfNotFull() { var board = new Connect4Board(2, 2); Assert.IsFalse(board.ColumnFull(1)); }
public void CreateBuildsEmptyBoard() { var board = new Connect4Board(5, 2); //check board is empty Assert.IsTrue(board.Columns.All(x => x.All(y => y.PlayerIndex ==0))); }
private static async Task PlayConnect4(TwoPlayerZeroSumGameMovesEngine gameEngine, bool humanIsFirstPlayer = true) { var humanPlayer = humanIsFirstPlayer ? Player.First : Player.Second; var board = new Connect4Board(gameEngine); PrintBoard(board, humanPlayer); if (!humanIsFirstPlayer) { Console.CursorVisible = false; Console.SetCursorPosition(0, Console.WindowHeight); Console.Write("Calculating first move..."); var firstMoveResult = await gameEngine.CalculateBestMove <Connect4Board, Connect4MoveInfo, Connect4BoardState>(board, Player.First).ConfigureAwait(false); var firstMove = firstMoveResult.BestMove; gameEngine.TryMakeMove <Connect4Board, Connect4MoveInfo, Connect4BoardState>(board, firstMove, false); PrintBoard(board, humanPlayer); } var selectedColumn = 0; var gameResult = GameResult.StillGame; while (gameResult == GameResult.StillGame) { var cursorLeft = 2 + (2 * selectedColumn); var cursorTop = Console.WindowHeight - 9; Console.SetCursorPosition(cursorLeft, cursorTop); Console.CursorVisible = true; var keyInfo = Console.ReadKey(); Console.SetCursorPosition(cursorLeft, cursorTop); switch (keyInfo.Key) { case ConsoleKey.LeftArrow: selectedColumn = selectedColumn > 0 ? selectedColumn - 1 : selectedColumn; break; case ConsoleKey.RightArrow: selectedColumn = selectedColumn < (board.Clone().State.Columns.Length - 1) ? selectedColumn + 1 : selectedColumn; break; case ConsoleKey.Enter: DrawMove(board, selectedColumn, cursorLeft, cursorTop, humanPlayer); Console.SetCursorPosition(0, Console.WindowHeight); Console.Write("Calculating move..."); var humanMove = new Connect4MoveInfo(humanPlayer, selectedColumn); await gameEngine.MakeMoveAgainstMachine <Connect4Board, Connect4MoveInfo, Connect4BoardState>(board, humanMove).ConfigureAwait(false); gameResult = board.GetGameResult(humanMove.Player); PrintBoard(board, humanPlayer); break; case ConsoleKey.Q: PrintBoard(board, humanPlayer); return; default: PrintBoard(board, humanPlayer); break; } Console.SetCursorPosition(cursorLeft, cursorTop); Console.CursorVisible = true; } PrintGameResult(gameResult); await PlayConnect4(gameEngine, !humanIsFirstPlayer); }
public void Init() { board = new Connect4Board(Width, Height); }
public Albot.BoardState EvaluateBoard() // SHOULD BE LOCAL IMPLEMENTATION { Connect4Board board = new Connect4Board(grid); return(Program.connect4.EvaluateBoard(board)); }
private static int DecideMove(Connect4Board board) { Con4Board con4Board = new Con4Board(board); return(MCTS.FindMove(con4Board, 9000)); }
Move MaxPlay(Connect4Board board, int depth, int alpha = int.MinValue, int beta = int.MaxValue) { if (depth == 0 || board.Done) { return new Move { Column = -1, Score = board.EvalScore() } } ; var maxMove = new Move { Column = -1, Score = int.MinValue }; var possibleColumns = board.GetAvailableColumns(); for (var i = 0; i < possibleColumns.Count; i++) { var col = possibleColumns[i]; var newBoard = board.GetNewState(BoardTile.CPU, col); var nextMove = MinPlay(newBoard, depth - 1, alpha, beta); if (maxMove.Column == -1 || nextMove.Score > maxMove.Score) { maxMove.Column = col; maxMove.Score = nextMove.Score; alpha = nextMove.Score; } if (alpha >= beta) { return(maxMove); } } return(maxMove); } Move MinPlay(Connect4Board board, int depth, int alpha = int.MinValue, int beta = int.MaxValue) { if (depth == 0 || board.Done) { return new Move { Column = -1, Score = board.EvalScore() } } ; var minMove = new Move { Column = -1, Score = int.MaxValue }; var possibleColumns = board.GetAvailableColumns(); for (var i = 0; i < possibleColumns.Count; i++) { var col = possibleColumns[i]; var newBoard = board.GetNewState(BoardTile.Player, col); var nextMove = MaxPlay(newBoard, depth - 1, alpha, beta); if (minMove.Column == -1 || nextMove.Score < minMove.Score) { minMove.Column = col; minMove.Score = nextMove.Score; beta = nextMove.Score; } if (alpha >= beta) { return(minMove); } } return(minMove); } }