public TakAI2(int boardSize, int maxDepth = DefaultMaxDepth) { _maxDepth = maxDepth; _rand = new Random(); _singleThreadData = new AIThreadData(boardSize); // Initialize list of all legal board positions _normalPositions = new BoardPosition[boardSize * boardSize]; for (int i = 0; i < _normalPositions.Length; i++) { _normalPositions[i] = new BoardPosition(i % boardSize, i / boardSize); } // Copy board positions into an alternate array that can be randomized to make the AI more interesting _randomPositions = new BoardPosition[boardSize * boardSize]; Array.Copy(_normalPositions, _randomPositions, _normalPositions.Length); }
public IMove FindGoodMove(GameState game) { // Clear cancelation state before beginning to process the move _canceled = _canceling = false; _evals = 0; _timer.Stop(); _timer.Reset(); _timer.Restart(); // Randomize order of board positions so that the AI appears less predictable // in the early phases of the game _randomPositions.RandomizeOrder(_rand); IMove bestMove = null; int bestEval; if (_singleThreadData == null) { _singleThreadData = new AIThreadData(game.Size, _maxDepth, Evaluator); } _singleThreadData.Reset(game); for (_deepenTo = 0; _deepenTo <= _maxDepth; _deepenTo++) { FindGoodMove(_singleThreadData, 0, null, out bestMove, out bestEval); if (Math.Abs(bestEval) >= Evaluation.FlatWinEval) { break; } LastEvaluation = bestEval; } _timer.Stop(); if (Properties.Settings.Default.debug) { System.IO.File.AppendAllText(DebugLogFilePath, string.Format("{0}\t{1}\t{2:0.0000}\t{3:0.0000}\r\n", Evaluator.Name, _evals, _timer.Elapsed.TotalSeconds, (double)_evals / _timer.Elapsed.TotalSeconds)); } return(bestMove); }
void FindGoodMove(AIThreadData data, int depth, int?prune, out IMove bestMove, out int bestEval) { bestMove = null; bestEval = int.MinValue; if (_canceling) { _canceled = true; return; } var depthMoves = data.DepthMoves; while (depth >= depthMoves.Count) { depthMoves.Add(new List <IMove>()); } var moves = depthMoves[depth]; moves.Clear(); var game = data.Game; EnumerateMoves(moves, game, _randomPositions); foreach (var move in moves) { move.MakeMove(game); game.Ply++; int eval; bool gameOver; data.Evaluator.Evaluate(game, out eval, out gameOver); if (0 == (game.Ply & 1)) { eval *= -1; } if (!(depth == _maxDepth || gameOver)) { IMove opmove; int opeval; FindGoodMove(data, depth + 1, bestMove == null ? (int?)null : -bestEval, out opmove, out opeval); eval = opeval * -1; } if (eval > bestEval) { bestMove = move; bestEval = eval; } game.Ply--; move.TakeBackMove(game); if (gameOver && eval > 0) { break; } if (prune.HasValue && eval >= prune.Value) { break; } } }
void FindGoodMove(AIThreadData data, int depth, int?prune, out IMove bestMove, out int bestEval) { bestMove = null; bestEval = -999999; if (_canceling) { _canceled = true; return; } var moves = data.DepthMoves[depth]; moves.Clear(); var game = data.Game; Helper.EnumerateMoves(moves, game, _randomPositions); // on the very first ply we only need to consider moves in one quadrant of the board because of rotational symmetry if (data.Game.Ply == 0) { var maxcoord = (data.Game.Size + 1) >> 1; for (int i = 0; i < moves.Count; i++) { var m = (PlacePieceMove)moves[i]; if (m.Pos.X >= maxcoord || m.Pos.Y >= maxcoord) { moves[i] = moves[moves.Count - 1]; moves.RemoveAt(moves.Count - 1); } } } { int writeto = 0; if (data.KillerMoves1[depth] != null) { var killer = data.KillerMoves1[depth]; for (int i = writeto + 1; i < moves.Count; i++) { if (moves[i].CompareTo(killer)) { var swap = moves[writeto]; moves[writeto] = moves[i]; moves[i] = swap; writeto++; break; } } } if (data.KillerMoves2[depth] != null) { var killer = data.KillerMoves2[depth]; for (int i = writeto + 1; i < moves.Count; i++) { if (moves[i].CompareTo(killer)) { var swap = moves[writeto]; moves[writeto] = moves[i]; moves[i] = swap; writeto++; break; } } } } foreach (var move in moves) { move.MakeMove(game); game.Ply++; int eval; bool gameOver; if (depth != _deepenTo) { data.SimpleEvaluator.Evaluate(game, out eval, out gameOver); } else { data.Evaluator.Evaluate(game, out eval, out gameOver); } _evals++; if (0 == (game.Ply & 1)) { eval *= -1; } if (!(depth == _deepenTo || gameOver)) { IMove opmove; int opeval; FindGoodMove(data, depth + 1, bestMove == null ? (int?)null : -bestEval, out opmove, out opeval); eval = opeval * -1; } if (eval > bestEval) { bestMove = move; bestEval = eval; data.KillerMoves2[depth] = data.KillerMoves1[depth]; data.KillerMoves1[depth] = bestMove; } game.Ply--; move.TakeBackMove(game); if (gameOver && eval > 0) { break; } if (prune.HasValue && eval >= prune.Value) { break; } } }