public Node(Move m) { Pos = new Move(m); Winrate = 0; // Wins = 0; // Simulations = 0; }
public Move GetMove() { Board cloneBoard = b.Clone(); while (true) { Console.WriteLine("Enter row and column for move coordinates, split by space"); string moveCoordsString = Console.ReadLine(); if (String.IsNullOrWhiteSpace(moveCoordsString) == true) continue; string[] coords = moveCoordsString.Split(' '); if (coords.Length != 2) continue; int row; int column; if (int.TryParse(coords[0], out row) == true) { if (int.TryParse(coords[1], out column) == true) { Move result = new Move(row, column); if (cloneBoard.PlaceStone(result) == true) return result; } } } }
public UCTNode(UCTNode parent, Move m, Board boardState) { if (m == null || boardState == null) throw new ArgumentNullException("m"); BoardState = boardState.Clone(); Parent = parent; Children = null; Position = new Move(m); Wins = 0; Visits = 0; }
private int PlayRandomGame(UCTNode node) { _boardClone.CopyStateFrom(node.BoardState); int turnsSimulated = 0; while (turnsSimulated < GameParameters.GameDepth && _boardClone.IsGameOver() == false) { turnsSimulated++; Move m = new Move(-5, -5); do { m.row = RandomGen.Next(-1, GameParameters.BoardSize); m.column = RandomGen.Next(-1, GameParameters.BoardSize); } while (_boardClone.PlaceStone(m) == false); } int winner = _boardClone.DetermineWinner(); return winner; }
int GetAvailableMoves(Board b) { if (_availableMoves == null) _availableMoves = new Move[Size*Size+1]; int moveCount = 0; for (int i = 0; i < Size; i++) { for (int j = 0; j < Size; j++) { //is on empty space on the board and not a friendly eye if (b[i, j] == 0 && b.IsEye(i, j) != b.ActivePlayer) { _availableMoves[moveCount++] = new Move(i, j); } } } return moveCount; }
public int PlaySimulation() { if (_testingBoard == null) _testingBoard = new Board(); _testingBoard.CopyStateFrom(_startingTestingBoard); int turnsSimulated = 0; while (turnsSimulated < GameParameters.GameDepth && _testingBoard.IsGameOver() == false) { turnsSimulated++; Move m = new Move(-1, -1); do { m.row= RandomGen.Next(-1, GameParameters.BoardSize); m.column = RandomGen.Next(-1, GameParameters.BoardSize); } while (_testingBoard.PlaceStone(m) == false); } int winner = _testingBoard.DetermineWinner(); return winner; }
double GetWinrate(Move move) { if (_startingTestingBoard == null) _startingTestingBoard = new Board(); _startingTestingBoard.CopyStateFrom(_actualBoard); if (_startingTestingBoard.PlaceStone(move) == false) return -1; UInt64 sim = 0; int wins = 0; while (sim < GameParameters.RandomSimulations) { int winner = PlaySimulation(); if (winner != 0) { sim++; if (winner == _actualBoard.ActivePlayer) wins++; } } return sim > 0 ? (double)wins / sim : -1; }
public int PlaySimulation() { if (_testingBoard == null) _testingBoard = new Board(); _testingBoard.CopyStateFrom(_startingTestingBoard); int turnsSimulated = 0; while (turnsSimulated < GameParameters.GameDepth && _testingBoard.IsGameOver() == false) { turnsSimulated++; int moveCount = GetAvailableMoves(_testingBoard); Move pass = new Move(-1, -1); //добавить в список возможных ходов пас _availableMoves[moveCount++] = pass; _availableMoves.Shuffle(moveCount); for (int i = 0; i < moveCount; i++) { if (_testingBoard.PlaceStone(_availableMoves[i]) == true) { break; } } } int winner = _testingBoard.DetermineWinner(); return winner; }
public bool IsOnBoard(Move m) { return m.row >= 0 & m.row < Size && m.column >= 0 && m.column < Size; }
public int IsEye(Move move) //false eyes fixed { return IsEye(move.row, move.column); }
int RemoveDragon(Move m) { return RemoveDragon(m.row, m.column); }
public bool IsFree(Move m) { return IsOnBoard(m) && _board[m.row, m.column] == 0; }
public bool ReceiveTurn(Move m) { return _actualBoard.PlaceStone(m); }
public Move GetMove() { DateTime start = DateTime.Now; int turnCount = GetAvailableMoves(_actualBoard); //most simple logic for the first couple of turns //reduces required computations and forbids AI from making stupid turns (should not do them anyway) turnCount = ApplyHeuristics(_actualBoard, turnCount); Node[] nodes = new Node[turnCount]; for (int i = 0; i < turnCount; i++) nodes[i] = new Node(_availableMoves[i]); Parallel.For(0, turnCount, (i) => { if (_availableMoves == null) _availableMoves = new Move[Size*Size + 1]; nodes[i].Winrate = GetWinrate(nodes[i].Pos); }); // for (int i = 0; i < turnCount; i++) // { // if (_availableMoves == null) // _availableMoves = new Move[Size * Size + 1]; // nodes[i].Winrate = GetWinrate(nodes[i].Pos, b); // } double maxWin = -1; int maxWinIndex = -1; for (int i = 0; i < turnCount; i++) { if (nodes[i].Winrate > maxWin && nodes[i].Winrate >= 0) { maxWin = nodes[i].Winrate; maxWinIndex = i; } } DateTime end = DateTime.Now; TimeSpan ts = end - start; Move bestMove; if (maxWin < 0) { bestMove = new Move(-1, -1); } else { bestMove = nodes[maxWinIndex].Pos; } Console.WriteLine("StupidTurbo-{1} has found move {2}({3},{4}) in {0} after {5} total sims", ts, _actualBoard.ActivePlayer == 1 ? "Black" : "White", _actualBoard.TurnNumber, bestMove.row, bestMove.column, GameParameters.RandomSimulations*turnCount); return bestMove; }
public bool ReceiveTurn(Move m) { if (Root.Children != null) { foreach (UCTNode child in Root.Children) { if (child.Position.Equals(m)) { Console.WriteLine("UCTTurbo-{0} had {1} nodes, lost {2} nodes and now has {3} nodes", _player==1?"Black":"White", Root.MeasureTree(), Root.MeasureTree()-child.MeasureTree(), child.MeasureTree()); Root = child; Root.Parent.Children = null; child.Parent = null; if (child.Children == null) child.CreateChildren(); return true; } } } Board newBoard = Root.BoardState.Clone(); if (newBoard.PlaceStone(m) == false) throw new ArgumentException("invalid turn"); Console.WriteLine("UCTTurbo-{0} had {1} nodes, lost {1} nodes and now has {2} nodes", _player == 1 ? "Black" : "White", Root.MeasureTree(), 1); Root.Children = null; //break the link for garbage collection UCTNode newRoot = new UCTNode(null, new Move(m), newBoard); newRoot.CreateChildren(); Root = newRoot; return true; }
private int PlayLessRandomGame(UCTNode node) { if (_availableMoves == null) _availableMoves = new Move[Size*Size+1]; _boardClone.CopyStateFrom(node.BoardState); int turnsSimulated = 0; while (turnsSimulated < GameParameters.GameDepth && _boardClone.IsGameOver() == false) { turnsSimulated++; int moveCount = GetAvailableMoves(_boardClone); _availableMoves.Shuffle(moveCount); Move pass = new Move(-1, -1); //add pass to possible moves _availableMoves[moveCount++] = pass; for (int i = 0; i < moveCount; i++) { if (_boardClone.PlaceStone(_availableMoves[i]) == true) { break; } } } int winner = _boardClone.DetermineWinner(); return winner; }
public bool ReceiveTurn(Move m) { return(_actualBoard.PlaceStone(m)); }
public void CreateChildren() { lock (this) { int size = Board.Size; Board b = BoardState; if (Children != null) return; if (Parent == null || Parent.Children == null) { Children = new List<UCTNode>(size*size); } else { Children = new List<UCTNode>(Parent.Children.Count); } for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { //is on empty space on the board if (b[i, j] == 0 && b.IsEye(i, j) != b.ActivePlayer) { Board anotherCloneBoard = b.Clone(); Move m = new Move(i, j); if (anotherCloneBoard.PlaceStone(m) == true) Children.Add(new UCTNode(this, m, anotherCloneBoard)); } } } Children.Shuffle(); HasChildren = true; } }
public byte this[Move m] { get { return _board[m.row, m.column]; } set { _board[m.row, m.column] = value; } }
public Move(Move m) { row = m.row; column = m.column; }
// generate a move, using the uct algorithm public Move GetMove() { Move bestMove; DateTime start = DateTime.Now; if (_resetTreeEachTurn == true) //EXPERIMENTAL, HAS NOT BEEN TESTED AND MIGHT NOT WORK Root = new UCTNode(null, new Move(-5, -5), Root.BoardState.Clone()); Console.WriteLine("Starting Tree size == {0}", Root.MeasureTree()); _doneSims = 0; if (Root.Children.Any(_x => _x.IsSolved == true && _x.SolvedWinner == _player)) { bestMove = Root.Children.First(_x => _x.IsSolved == true && _x.SolvedWinner == _player).Position; } else { for (int i = 0; i < Environment.ProcessorCount; i++) new Task(ParallelSimulations).Start(); while (_doneSims < _sims) { Thread.Sleep(100); } UCTNode n = GetBestChild(Root); if (n == null) bestMove = new Move(-1, -1); else bestMove = new Move(n.Position); } TimeSpan ts = DateTime.Now - start; Root.Children.Sort((_x, _y) => _x.Visits.CompareTo(_y.Visits)); foreach (UCTNode child in Root.Children) { Console.WriteLine(child); } Root.Children.Shuffle(); Console.WriteLine("Current tree size == {0}, and there are {1} solved nodes", Root.MeasureTree(), Root.CountSolvedNodes()); Console.WriteLine("UCTTurbo-{1} has found move {2}({3},{4}) in {0} after {5} sims", ts, Root.BoardState.ActivePlayer == 1 ? "Black" : "White", Root.BoardState.TurnNumber, bestMove.row, bestMove.column, _doneSims); return bestMove; }
public bool PlaceStone(Move move) { if (State != GameState.GameIsNotOver) return false; if (move.row == -1 && move.column == -1) { Pass(); return true; } //check if the move is on the board if (IsOnBoard(move) == false) { return false; } //check if the intersection is already occupied if (_board[move.row, move.column] != 0) { return false; } //check if the move is forbidden because of the Ko rule if (IsKo(move.row, move.column)) { return false; } Array.Copy(_board, _buffer, _board.Length); this[move] = ActivePlayer; //if there is an enemy dragon nearby, it won't contain newly-placed stone - have to check each one individually Array.Clear(_visited, 0, _visited.Length); bool isSuicide = true; //для более быстрого определения возможности хода. Если рядом с новым камнем есть свободное пересечение, то это точно не суицид for (int i = 0; i < DirectionCount; i++) //first check opponent's dragons { int testRow = move.row + CardinalDirections[i, 0]; int testCol = move.column + CardinalDirections[i, 1]; //если клетка находится за доской или на ней стоит союзный камень - пропускаем. проверка союзных камней будет позже //if an intersection is outside the board or has allied stone - skip it for now. allied checks will come later if (IsOnBoard(testRow, testCol) == false || _board[testRow, testCol] == ActivePlayer) continue; else if (_board[testRow, testCol] == 0) //if a neighbouring intersection is empty, then the new stone will definitely have at least 1 liberty { isSuicide = false; continue; } if (_board[testRow, testCol] == OppositePlayer) { if (_visited[testRow, testCol] == true) continue; else { Array.Clear(_visited, 0, _visited.Length); if (IsDragonDead(testRow, testCol) == true) { RemoveDragon(testRow, testCol); } } } } Array.Clear(_visited, 0, _visited.Length); //если рядом с новым камнем есть дракон, то он включает в себя и этот камень //if there is a nearby friendly dragon, it will contain newly-placed stone if (isSuicide == true && IsDragonDead(move.row, move.column) == true) { this[move] = 0; return false; } ActivePlayer = OppositePlayer; //TODO: possibly add ko checks Passes = 0; TurnNumber++; Array.Copy(_buffer, _lastPosition, _lastPosition.Length); return true; }
public bool ReceiveTurn(Move m) { return b.PlaceStone(m); }