/// <summary> /// Converts board to neurel net training example. Returns example corresopnding to normalized board (current player's checker color is blue, last player is green) /// </summary> /// <param name="lastPlayerToGo">Current Player which corresponds to last checker placed on board.</param> public static Example ToNormalizedExample(Board board, Checker lastPlayerToGo) { Debug.Assert(lastPlayerToGo != Checker.Empty); List<double> boardState = board.Cells.Cast<Checker>().Select(c=>Transform.ToNormalizedValue(c, lastPlayerToGo)).ToList(); List<int> features = new List<int>(); // 42 Input Units - Board State Only // return new Example(boardState); foreach (Checker checker in new List<Checker> { lastPlayerToGo, Board.Toggle(lastPlayerToGo) }) { features.AddRange(board.LineOfX(checker)); features.AddRange(board.LineOfX(checker, potential: true)); features.AddRange(board.NumbersInColumns(checker)); features.AddRange(board.NumbersInRows(checker)); features.Add(board.NumberOnBoard(checker)); } boardState.AddRange(features.Select(e => (double)e)); // 40 Input Units - Features Only //return new Example(features.Select(e => (double)e).ToList()); // 82 Input Units - Board State and Features return new Example(boardState); }
public override void AddCells(Board board, Cell cell, connectedCells c) { switch(c) { case connectedCells.north: for (int i = 1; i < connectR; i++) { cells.Add(board.getCell(cell.getRow() - i, cell.getColumn())); } cell.AddConnectedCells((int)connectedCells.north, cells); break; case connectedCells.south: for (int i = 1; i < connectR; i++) { cells.Add(board.getCell(cell.getRow() + i, cell.getColumn())); } cell.AddConnectedCells((int)connectedCells.south, cells); break; } }
public override void Move(Board b) { { //Create a new game state for the root of the state space GameState gameState = new GameState(GameState.State.initial, b, this, null, cell, true); //Set the state as the root MiniMaxTree m = new MiniMaxTree(gameState); //Find children states if they exist m.GenerateStates(gameState, 4, true); //The value returned by the recursive minimax function int value = m.MiniMax(gameState, 4, true); cell = null; foreach(GameState child in gameState.GetChildren()) { if (child.GetHeuristicValue() == value) { cell = child.GetCell(); } } //Set the cell on the playing board b.getCell(cell.getRow(), cell.getColumn()).setState((int)cell.getState()); //if a row exists above this cell, make it playable if (cell.getRow() != 0) { b.getCell(cell.getRow() - 1, cell.getColumn()).isPlayable(true); } //Test if the game is over if(MiniMaxTree.TerminalTest(cell)) { b.printBoard(); Console.WriteLine("GameOver: " + this.getColorToString() + " wins"); b.isGameOver(true); } moveCount++; } }
/// <summary> /// Parses the validation set from connect-4 8-ply database. /// </summary> /// <returns>Validation set</returns> public static List<Example> Parse() { ValidationSet.Clear(); using (StringReader reader = new StringReader(Properties.Resources.connect_4)) { string line; while ((line = reader.ReadLine()) != null) { string[] values = line.Split(','); Board board = new Board(); for (int i = 0; i < values.Length - 1; ++i) { string x = values[i].ToLower().Trim(); Checker checker = Checker.Empty; switch (x) { case "x": checker = Checker.Blue; break; case "o": checker = Checker.Green; break; case "b": checker = Checker.Empty; break; } // Format of linear board data in connect-4.txt is bottom to top, left to right board.AddChecker(checker, i/6); } // In connect-4.txt, it is X's turn to go next, which means // player O has just went. Player O == Green, therefore // we use Checker.Green in the following line. Example example = Transform.ToNormalizedExample(board, Checker.Green); string result = values[values.Length - 1].ToLower().Trim(); // Current values denote next player that goes will be guaranteed to win/lose/draw given he/she plays optimally... // We need to normalize this for our network... Ie, the label should instead denote if last player that went for given board position win/loses/ties if he/she plays optimally. GameResult gr = result == "win" ? GameResult.Loss : result == "loss" ? GameResult.Win : GameResult.Draw; example.Labels.Add(Transform.ToValue(gr)); ValidationSet.Add(example); } } return ValidationSet; }
//Copy constructor public Board(Board b) { length = b.GetLength(); width = b.GetWidth(); connectR = Board.GetConnectR(); board = new Cell[length, width]; for (int i = 0; i < length; i++) { for (int j = 0; j < width; j++) { Cell cell = new Cell(b.getCell(i, j)); board[i, j] = cell; } } }
public void BatchAddCheckers(Checker start, List<int> columnHistory, bool updateBoard = true, bool delay = true, Board completedBoard = null) { Storyboard story = new Storyboard(); Checker checker = start; int i = 0; foreach (int column in columnHistory) { Image image = new Image(); image.Stretch = Stretch.Uniform; image.Source = (checker == Checker.Blue ? new BitmapImage(new Uri("/Icons/orbz_water.ico", UriKind.Relative)) : new BitmapImage(new Uri("/Icons/orbz_spirit.ico", UriKind.Relative))); image.SetValue(Grid.ColumnProperty, column); int? minRow = gridBoard.Children.OfType<Image>().Where(e => (int)e.GetValue(Grid.ColumnProperty) == column).Select(e => (int?)e.GetValue(Grid.RowProperty)).Min(); if (minRow.HasValue && minRow == 0) throw new Exception("Cannot add checker to full column"); int row = (int)(minRow.HasValue ? minRow - 1 : 5); image.SetValue(Grid.ZIndexProperty, -1); image.SetValue(Grid.RowProperty, row); image.Opacity = 0; image.Height = gridBoard.RowDefinitions[0].ActualHeight; gridBoard.Children.Add(image); if (updateBoard) CurrentBoard.AddChecker(checker, column); ThicknessAnimation animation = new ThicknessAnimation(new Thickness(0, -gridBoard.ActualHeight * 2 * Settings.Default.DropHeightRatio, 0, 0), new Thickness(0, 0, 0, 0), TimeSpan.FromMilliseconds(Settings.Default.DropSpeed)); animation.EasingFunction = new BounceEase() { Bounces = 3, Bounciness = 5, EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut }; animation.BeginTime = TimeSpan.FromMilliseconds(i * Settings.Default.MoveDelay); Storyboard.SetTarget(animation, image); Storyboard.SetTargetProperty(animation, new PropertyPath(Image.MarginProperty)); story.Children.Add(animation); DoubleAnimation fade = (completedBoard != null && !completedBoard.WinningSequence.Any(t => t.Item1 == row && t.Item2 == column) ? Fade(image, 1, Settings.Default.FadeTo, i * Settings.Default.MoveDelay, Settings.Default.FadeSpeed) : Fade(image, 0, 1, i * Settings.Default.MoveDelay, 0)); story.Children.Add(fade); story.Completed += new EventHandler(story_Completed); checker = Board.Toggle(checker); ++i; } story.Begin(); }
public GameState(State s, Board b, Player p, GameState parent, Cell c, Boolean mp) { maxPlayer = mp; state = s; cell = c; board = b; player = p; this.parent = parent; children = new List<GameState>(); Region.findConnectedCells(b); b.UpdateCellObservers(); if(MiniMaxTree.TerminalTest(cell) && state != GameState.State.initial) { state = GameState.State.terminal; if (maxPlayer) { heuristicValue = MiniMaxTree.MIN_VALUE; } else heuristicValue = MiniMaxTree.MAX_VALUE; } }
public void recSelectMove(Board board, int depth, bool max, out int column, out double score, out List<double> columnEvaluations) { int bestX = 0; columnEvaluations = Enumerable.Repeat(double.NegativeInfinity, board.Columns).ToList(); double bestV = max ? Double.NegativeInfinity : Double.PositiveInfinity; ; for (int x = 0; x < board.Columns; x++) { if (!board.IsColumnFull(x)) { board.AddChecker(MyColor, x); int col; double v; if (depth <= 1 || board.IsGameOver) { col = x; v = EvaluateBoard(board); } else { List<double> ignore = (new double[board.Columns]).ToList(); recSelectMove(board, depth - 1, !max, out col, out v, out ignore); } board.RemoveChecker(x); columnEvaluations[x] = v; if (v > bestV && max || v < bestV && !max) { bestV = v; bestX = col; } } } column = bestX; score = bestV; }
public void SelectMove(Board board, out int column, out double score, int depth = 1) { // Lambda percent of the time, select a random move. if (LambdaType == LambdaType.Threshold && RANDOM.NextDouble() <= Lambda) { int[] cols = Enumerable.Range(0, board.Columns).Where(x => !board.IsColumnFull(x)).ToArray(); column = cols[RANDOM.Next(cols.Count())]; board.AddChecker(MyColor, column); score = EvaluateBoard(board); board.RemoveChecker(column); return; } List<double> columnEvaluations; recSelectMove(board, depth, true, out column, out score, out columnEvaluations); //we pick a move randomly, using a probability distribution such that the moves with the "best" board positions have a // higher probability of being selected higher values of Lambda mean choices will have more equal probability, even if they had different // low values of Lambda will have the opposite effect Lambda should be positive number. Otherwise, no exploration will take place. // If non-positive, just return the "best" move now, to avoid divide-by-zero type issues. if (LambdaType == LambdaType.ProbabilityDistribution && Lambda > 0) { double sum = 0.0; double[] weights = new double[columnEvaluations.Count]; for (int i = 0; i < columnEvaluations.Count; i++) { // the closer this column's evaluation to the "best", the // greater weight it will have double w = 1 / (Lambda + (score - columnEvaluations[i])); weights[i] = w; sum += w; } double r = RANDOM.NextDouble() * sum; int c; for (c = 0; c + 1 < weights.Length; c++) { r -= weights[c]; if (r <= 0) break; } column = c; score = columnEvaluations[c]; } }
public override void Move(Board b) { if(moveCount == b.GetLength() * b.GetWidth()) { Console.WriteLine("Game Over, you both lose"); } Boolean moveMade = false; b.printBoard(); if(this.getOpponent() != null) { Console.WriteLine(" "); } Console.WriteLine(this.getColorToString() + " player's turn"); while(!moveMade) { Console.Write("Select a Move: " + "0 - " + (b.GetWidth() - 1) + ": "); int choice = Convert.ToInt32(Console.ReadLine()); for (int i = b.GetLength() - 1; i >= 0; i--) { if(choice < 0 || choice > b.GetWidth() - 1) { break; } cell = b.getCell(i, choice); if (cell.isPlayable()) { cell.setState((int)color); cell.isPlayable(false); if (i != 0) { b.getCell(i - 1, choice).isPlayable(true); } moveMade = true; moveCount++; if (MiniMaxTree.TerminalTest(cell)) { b.printBoard(); Console.WriteLine("GameOver: " + this.getColorToString() + " wins"); b.isGameOver(true); } break; } } } }
public abstract void Move(Board board);
static void Main(string[] args) { Console.Write("Board Width? "); colNum = Convert.ToInt32(Console.ReadLine()); Console.WriteLine(); Console.Write("Board Height? "); rowNum = Convert.ToInt32(Console.ReadLine()); Console.WriteLine(); Console.Write("Connect? "); r = Convert.ToInt32(Console.ReadLine()); Console.WriteLine(); Console.WriteLine("1: Human vs Human"); Console.WriteLine("2: Human vs AI"); Console.WriteLine("3: AI vs AI"); Console.Write("Select Players: "); int choice = Convert.ToInt32(Console.ReadLine()); Console.WriteLine(); Board board = new Board(rowNum, colNum, r); Region.findConnectedCells(board); board.UpdateCellObservers(); Player p1 = null; Player p2 = null; switch (choice) { case 1: p1 = new HumanPlayer(); p1.setAsRed(); p2 = new HumanPlayer(); p2.setAsBlack(); break; case 2: Console.WriteLine("1: Human 2: AI"); Console.Write("Who will go first? "); choice = Convert.ToInt32(Console.ReadLine()); if(choice == 1) { p1 = new HumanPlayer(); p1.setAsRed(); p2 = new AIPlayer(); p2.setAsBlack(); } else { p1 = new AIPlayer(); p1.setAsRed(); p2 = new HumanPlayer(); p2.setAsBlack(); } p1.setOpponent(p2); p2.setOpponent(p1); break; case 3: break; } int turn = RED; while(!board.isGameOver()) { switch (turn) { case RED: p1.Move(board); turn = BLACK; break; case BLACK: p2.Move(board); turn = RED; break; } } Console.Read(); }
public static void findConnectedCells(Board board) { for(int i = length - 1; i >= 0; i--) { for(int j = 0; j < width; j++) { Cell cell = board.getCell(i, j); //has r north cells if(i >= connectR - 1) { region = new Vertical(); region.AddCells(board, cell, connectedCells.north); //has r northeast cells if(j + connectR - 1 < width) { region = new Diagonal(); region.AddCells(board, cell, connectedCells.northEast); } //has r northwest cells if(j >= connectR - 1) { region = new Diagonal(); region.AddCells(board, cell, connectedCells.northWest); } } //has r south cells if(i + connectR - 1 < length) { region = new Vertical(); region.AddCells(board, cell, connectedCells.south); //has r southwest cells if(j - connectR + 1 >= 0) { region = new Diagonal(); region.AddCells(board, cell, connectedCells.southWest); } //has r southeast cells if(j <= connectR - 1) { region = new Diagonal(); region.AddCells(board, cell, connectedCells.southEast); } } //has r east cells if(j <= connectR - 1) { region = new Horizontal(); region.AddCells(board, cell, connectedCells.east); } //has r west cells if(j - connectR + 1 >= 0) { region = new Horizontal(); region.AddCells(board, cell, connectedCells.west); } } } }
public abstract void AddCells(Board board, Cell cell, connectedCells c);
protected override double EvaluateBoard(Board board) { Example example = MakeExample(board, MyColor); Network.PropogateInput(example); return example.Predictions[0]; }
public override Example MakeExample(Board board, Checker color) { return Transform.ToNormalizedExample(board, color); }
public void Restart(GameMode mode, bool startup = false) { gridBoard.Children.RemoveRange(7, gridBoard.Children.Count - 7); // Don't remove the 7 borders. Mode = mode; Checker = Checker.Blue; CurrentBoard = new Board(); if (!startup) GetValidNetwork(); // Assert valid current network be means of a messagebox. }
/// <summary> /// Simulate a game until completion. /// </summary> /// <param name="board">Starting board that the bots will play on. This need not be empty!</param> /// <param name="network">Neural network that provides the AI for gameplay.</param> /// <returns>Trace of game sequence, each board state stored as a Neural Net Example</returns> public List<Example> Play(Board board, Network network) { Bot allen = new NeuralNetBot(Checker.Blue, network); // <-- you know he will win :) Bot jason = new NeuralNetBot(Checker.Green, network); List<Example> trace = new List<Example>(); Turns = 0; Bot current = allen.MyColor == board.NextPlayer ? allen : jason; while (!board.IsGameOver) { int column; double score; current.SelectMove(board, out column, out score); Log(String.Format("{0} picks column {1} (Score: {2:f2})", (current == allen ? "Allen" : "Jason"), column, score)); board.AddChecker(current.MyColor, column); Example example = Transform.ToNormalizedExample(board, current.MyColor); example.Predictions.Add(score); trace.Add(example); current = (current == allen ? jason : allen); ++Turns; } if (Viewer != null) Viewer.BatchAddCheckers(Checker.Blue, board.MoveHistory,completedBoard:board); TotalTurns += Turns; Checker winner; if (board.TryGetWinner(out winner)) { //The game is over, there was a winner. //This means the last element of "trace" represents a won //board state (i.e. there is a four-in-a-row with color //'winner'). if (trace.Count > 0) trace[trace.Count - 1].Predictions[0] = Transform.ToValue(GameResult.Win); if (trace.Count > 1) trace[trace.Count - 2].Predictions[0] = Transform.ToValue(GameResult.Loss); if (winner == allen.MyColor) { Log("WINNER: Allen"); ++AllenWon; } else { Log("WINNER: Jason"); ++JasonWon; } } else { if (trace.Count > 0) trace[trace.Count - 1].Predictions[0] = Transform.ToValue(GameResult.Draw); if (trace.Count > 1) trace[trace.Count - 2].Predictions[0] = Transform.ToValue(GameResult.Draw); Log("TIE"); ++Ties; } ++TotalGames; Log(string.Format("Turns: {0} ({1:f2})", Turns, (double)TotalTurns / TotalGames)); Log(string.Format("Allen: {0}({1:f2}) Jason: {2}({3:f2}) Ties {4}({5:f2}) TOTAL: {6}", AllenWon, (double)AllenWon / TotalGames, JasonWon, (double)JasonWon / TotalGames, Ties, (double)Ties / TotalGames, TotalGames)); Log(""); List<Example> trace1 = new List<Example>(), trace2 = new List<Example>(); for (int i = 0; i < trace.Count; ++i) { if (i % 2 == 0) trace1.Add(trace[i]); else trace2.Add(trace[i]); } double lambda = .7; double alpha = .1; double gamma = .5; UpdateTraceLabels(trace1, lambda, alpha, gamma); UpdateTraceLabels(trace2, lambda, alpha, gamma); return trace1.Union(trace2).ToList(); }
public void FindChildrenStates(Boolean maxPlayer) { GameState gs; for(int i = 0; i < board.GetWidth(); i++) { for(int j = 0; j < board.GetLength(); j++) { if(board.getCell(j,i).getState().Equals(Cell.CellState.empty)) { if (board.getCell(j, i).isPlayable()) { Board b = new Board(board); Cell cell = b.getCell(j, i); if (maxPlayer) { cell.setState(player.getColor()); } else { cell.setState(player.getOpponent().getColor()); } if (j != 0) { b.getCell(j - 1, i).isPlayable(true); } gs = new GameState(GameState.State.transition, b, player, this, cell, !maxPlayer); children.Add(gs); } } else break; } } }
public abstract Example MakeExample(Board board, Checker color);
protected abstract double EvaluateBoard(Board board);