public void Update(double value, UTTTMove bestMove, UTTTState currentState, Dictionary <int, StateRecord> statesSeen, int iteration) { this.value = value; this.bestMove = bestMove; this.iteration = iteration; statesSeen.Add(currentState.GetHashCode(), this); }
public void DoMove(UTTTMove move) { // Set to 1 / 2 depending on player field[move.x, move.y] = (activePlayer ? 1 : 2); hashCode = zobristHasher.getNewHashcode(hashCode, new Tuple <int, int>(move.x, move.y), activePlayer ? 1 : 0); UpdateState(move); }
public void UpdateState(UTTTMove move) { // Check fields for winner CheckField(move.x / 3 + move.y / 3 * 3); // Check macro for winner CheckMacro(); // Alternate active player activePlayer ^= true; }
// Finds a copy of a Move in a list of Moves, then swaps the found copy with another given move private void FindCopyAndSwap(UTTTMove toFind, List <UTTTMove> moves, UTTTMove toSwapWith) { UTTTMove moveCopy = null; foreach (UTTTMove move in moves) { if (move.Equals(toFind)) { moveCopy = move; break; } } if (moveCopy == null) { return; } toFind = toSwapWith; toSwapWith = moveCopy; }
// Returns the best move from some particular state for the player which has the turn, within the time given // Returns random move if not enough time is given // pre: startState != null && !startState.isFinal() public UTTTMove FindBestMove(UTTTState startState, int msGiven) { if (startState == null || startState.IsFinal()) { throw new ArgumentException("TimedSearcher.FindBestMove: Invalid startState passed."); } // stopwatch keeps track of how much time we have left Stopwatch sw = new Stopwatch(); sw.Start(); // iterative deepening UTTTMove bestMove = null; double bestScore = 0; TimedAlphaBetaWIP timedAB = new TimedAlphaBetaWIP(sw, msGiven, startState); for (int depthLeft = 1; sw.ElapsedMilliseconds < msGiven; depthLeft++) { List <UTTTMove> newMoveList = new List <UTTTMove>(); TimedAlphaBetaWIP.AlphaBetaIterationResult res = timedAB.computeNextIteration(); if (res.outOfTime) { break; } else { bestMove = res.bestMove; bestScore = res.bestMoveValue; } } // if no time to find any move, return random move if (bestMove == null) { bestMove = startState.GetPossibleMoves().First(); } sw.Stop(); Console.Error.WriteLine(bestScore); return(bestMove); }
// Generate state from given engine string // Generally always used to parse public UTTTState(String fieldString, String macroString, ZobristHasher zobristHasher, bool weArePlayerOne) { String[] fieldArray = fieldString.Split(','); for (int i = 0; i != 81; i++) { field[i % 9, i / 9] = Int32.Parse(fieldArray[i]); } String[] macroArray = macroString.Split(','); for (int i = 0; i != 9; i++) { macro[i % 3, i / 3] = Int32.Parse(macroArray[i]); } for (int i = 0; i != 81; i++) { moveArray[i % 9, i / 9] = new UTTTMove(i % 9, i / 9); } //Our turn playerNum = (weArePlayerOne ? 1 : 2); this.zobristHasher = zobristHasher; this.hashCode = zobristHasher.getOriginalHashcode(); }
private static void RealMainEatCheese() { TimedSearcher searcher = new TimedSearcher(); ZobristHasher zobristHasher = new ZobristHasher(9, 9, 2); UTTTState currentState = null; int timePerMove; int timebank; String field = null; String macroboard = null; bool weArePlayerOne = false; while (true) { Thread.Sleep(10); String line = Console.In.ReadLine(); if (line.Length == 0) { continue; } Console.Error.WriteLine(line); String[] parts = line.Split(' '); switch (parts[0]) { case "settings": switch (parts[1]) { case "time_per_move": timePerMove = int.Parse(parts[2]); break; case "timebank": timebank = int.Parse(parts[2]); break; case "field": field = parts[2]; break; case "macroboard": macroboard = parts[2]; break; case "your_botid": weArePlayerOne = (int.Parse(parts[2]) == 1); break; } break; case "update": switch (parts[2]) { case "field": field = parts[3]; break; case "macroboard": macroboard = parts[3]; break; } break; case "action": currentState = new UTTTState(field, macroboard, zobristHasher, weArePlayerOne); UTTTMove chosen = searcher.FindBestMove(currentState, 2000); Console.WriteLine($"place_move {chosen.x} {chosen.y}"); Console.Out.Flush(); break; default: // error break; } } }
// state : The root of the (sub)-tree this search expands // alpha : Value used for AlphaBeta pruning - maximum value maximizing player can definitely get // beta : Value used for AlphaBeta pruning - minimum value minimizing player can definitely get // depthLeft : How many more depths we are allowed to explore private double AlphaBetaSearch(UTTTState state, double alpha, double beta, int depthLeft) { // throw an exception if our player is forced to stop if (sw.ElapsedMilliseconds > msGiven) { throw new OutOfTimeException(); } // if final depth reached, then return the value of this leaf if (depthLeft == 0 || state.IsFinal()) { return(state.Evaluate()); } // if not final depth, then generate all possible branches List <UTTTMove> moves = state.GetPossibleMoves(); // if (moves.Count == 0) { throw new OutOfTimeException(); } // if only a single move is possible, don't decrease depth if (moves.Count == 1) { depthLeft++; } // if the state has been seen before, then continue // tracing the best move of the previous iteration by putting the old move // in the first position to be evaluated. This leads to better pruning. StateRecord rec = null; statesSeen.TryGetValue(state.GetHashCode(), out rec); if (rec != null) { if (rec.iteration == iteration) { return(rec.value); } if (rec.bestMove != null) { FindCopyAndSwap(rec.bestMove, moves, moves.First()); } statesSeen.Remove(state.GetHashCode()); } else { rec = new StateRecord(); } // keep track of the best move // update the StateRecord in the dictionary after we have finished analyzing the state Boolean min = state.MinimizingHasTurn(); UTTTMove bestMove = null; foreach (UTTTMove move in moves) { state.DoMove(move); double result = AlphaBetaSearch(state, alpha, beta, depthLeft - 1); state.UndoMove(move); if ((min && result < beta) || (!min && result > alpha)) { bestMove = move; if (min) { beta = result; } else { alpha = result; } } if (alpha >= beta) { // update record and return double res = (alpha + beta) / 2; rec.Update(res, bestMove, state, statesSeen, iteration); return(res); } } // update record and return if (min) { rec.Update(beta, bestMove, state, statesSeen, iteration); return(beta); } else { rec.Update(alpha, bestMove, state, statesSeen, iteration); return(alpha); } }
public void UndoMove(UTTTMove move) { field[move.x, move.y] = 0; hashCode = zobristHasher.getNewHashcode(hashCode, new Tuple <int, int> (move.x, move.y), activePlayer ? 0 : 1); UpdateState(move); }
public bool Equals(UTTTMove other) { return((other.x == this.x) && (other.y == this.y)); }