public IEnumerable<Point> PossibleMoves(int x, int y, bool ignoreWalls) { Point move = new Point(); for(int i=0; i<Map.MOVES.Length; i++) { move.X = x; move.Y = y; move.MoveInDirection(Map.MOVES[i]); if (!ignoreWalls && !IsWall(move.X, move.Y)) { yield return move; } else if (ignoreWalls) { yield return move; } } }
// Determine which direction has the most available spaces and fill // as efficiently as possible private static string PerformSurvivalMove() { float score = 0; float bestScore = 0; int depth = 0; GameState gs = new GameState(); Point p = new Point(); string bestMove = Map.MOVES[0]; List<string> ties = new List<string>(); for(int i=0; i<Map.MOVES.Length; i++) { p.X = Map.MyX(); p.Y = Map.MyY(); p.MoveInDirection(Map.MOVES[i]); if (!Map.IsWall(p.X, p.Y)) { //score = FloodFill(gs.ApplyMoveToMeAndCreate(Map.MOVES[i]), p.X, p.Y); score = FloodFillDepthFirst(gs.ApplyMoveToMeAndCreate(Map.MOVES[i])); } else { score = 0; } //Console.Error.WriteLine("Far:" + Map.MOVES[i] + ":" + score); if (score > bestScore) { bestMove = Map.MOVES[i]; bestScore = score; ties.Clear(); ties.Add(bestMove); } else if (score == bestScore) { ties.Add(Map.MOVES[i]); } } // break ties // hug closest wall if (ties.Count > 0) { bestScore = int.MaxValue; foreach(string move in ties) { p.X = Map.MyX(); p.Y = Map.MyY(); p.MoveInDirection(move); // use shortest distance to closest wall score = int.MaxValue; int tmp; foreach (string direction in Map.MOVES) { Point q = new Point(p.X, p.Y); q.MoveInDirection(direction); if (q.X == Map.MyX() && q.Y == Map.MyY()) { continue; } q.X = p.X; q.Y = p.Y; tmp = ScoreStraightPath(direction, q); if (tmp < score) { score = tmp; } } //Console.Error.WriteLine("Far tie break:" + move + ":" + score); if (score < bestScore) { bestScore = score; bestMove = move; } } } return bestMove; }
private static int ScoreStraightPath(string direction, Point p) { int score = 0; p.MoveInDirection(direction); while(!Map.IsWall(p.X, p.Y)) { score++; p.MoveInDirection(direction); } return score; }
// alpha beta with iterative deepening private static string PerformNearMove(Path shortestPath) { int depth = 0; float time = 0; float score, bestScore; GameState gs = new GameState(); Point p = new Point(); // default to something that won't kill us - server sometimes // runs out of time WAY early resulting in no time to perform alpha beta // iterations string bestMove = Map.MOVES[0]; //PerformFoolishRandomMove(); DateTime lastAlphaBeta; List<string> ties = new List<string>(); string[] moves = new string[4]; float[] scores = new float[4]; // 0 north, 1 south, 2 east, 3 west scores[0]=3; scores[1]=2; scores[2]=1; scores[3]=0; // used to adjust time estimate for next depth so we don't go over time limit float timebase = ((float)Map.Width() * (float)Map.Height()) / (15f * 15f); while(Duration() + time < (TIME_LIMIT - TIME_THRESHOLD) && depth <= 12) { score = int.MinValue; bestScore = int.MinValue; depth++; // order moves by previous iterations scores // TODO this really does nothing. Cache of game states is needed // for quick eval retrival and move ordering int length = scores.Length; bool swapped = true; moves[0] = "North"; moves[1] = "South"; moves[2] = "East"; moves[3] = "West"; while(swapped) { swapped = false; for(int b=0; b<length - 1; b++) { if (scores[b] < scores[b+1]) { string tmp = moves[b]; float ftmp = scores[b]; moves[b] = moves[b+1]; scores[b] = scores[b+1]; moves[b+1] = tmp; scores[b+1] = ftmp; swapped = true; } } length -= 1; //Console.Error.WriteLine("best:" + best + " score:" + scores[best]); } for(int i=0; i<moves.Length; i++) { string move = moves[i]; p.X = Map.MyX(); p.Y = Map.MyY(); p.MoveInDirection(move); if (!Map.IsWall(p.X, p.Y)) { // negate since starting with opponents moves lastAlphaBeta = DateTime.Now; score = -AlphaBeta(gs.ApplyMoveToMeAndCreate(move), depth, -int.MaxValue, -(-int.MaxValue), false); // estimate time for next depth TimeSpan ts = DateTime.Now - lastAlphaBeta; time = (float)ts.Milliseconds * (depth * timebase); } else { score = int.MinValue; } //Console.Error.WriteLine("alphabeta:" + move + ":" + score + " depth:" + depth); if (score > bestScore) { bestMove = move; bestScore = score; ties.Clear(); ties.Add(bestMove); } else if (score == bestScore) { ties.Add(move); } // track score string temp = move.Substring(0, 1).ToUpper(); int firstChar = (int)temp[0]; switch(firstChar) { case 'N': scores[0] = score; break; case 'S': scores[1] = score; break; case 'E': scores[2] = score; break; case 'W': scores[3] = score; break; } } depth++; } List<string> secondaryTies = new List<string>(); // break ties if (ties.Count > 1) { bestScore = int.MinValue; foreach(string move in ties) { //Console.Error.WriteLine("alpha tie break:" + move); p.X = Map.MyX(); p.Y = Map.MyY(); p.MoveInDirection(move); if (Map.IsWall(p.X, p.Y)) { continue; } Territory room = new Territory(gs.ApplyMoveToMeAndCreate(move)); room.DetermineTerritories(); score = (float)room.GetMySize() - (float)room.GetOpponentSize(); //Console.Error.WriteLine("alpha tie break:" + move + ":" + score); if (score > bestScore) { bestScore = score; bestMove = move; secondaryTies.Clear(); secondaryTies.Add(move); } else if (score == bestScore) { secondaryTies.Add(move); } } } // kinda lame, but need another tie breaker...quick and dirty if (secondaryTies.Count > 1) { bestScore = int.MinValue; foreach(string move in ties) { if (shortestPath != null) { if (move.Equals(shortestPath.direction)) { bestMove = shortestPath.direction; break; } } p.X = Map.MyX(); p.Y = Map.MyY(); p.MoveInDirection(move); if (Map.IsWall(p.X, p.Y)) { continue; } score = -GetEuclideanOpponentDistance(p.X, p.Y); //Console.Error.WriteLine("alpha tie break:" + move + ":" + score); if (score > bestScore) { bestScore = score; bestMove = move; } } } return bestMove; }