//コンストラクタ public BruteForceSearchAgentHandler(Game game, Agent agent, int depth = 4) : base(game, agent) { Depth = depth; Map = game.Field.Map; Width = game.Field.Width; Height = game.Field.Height; CalculateExistenceProbability = new CalculateExistenceProbability(depth); Way = new Way(agent, Direction.None); BestDeltaScore = 0; }
protected override ScoringEfficiency EvaluateGame() { //移動によるスコアを計算 if (BetterRoute != null) { int nowIndex = BetterRoute.ToList().FindIndex((n) => n.Point == Agent.Position); int beforeTeam, beforeEnemy, nextTeam, nextEnemy; int needTurn = 0; double delta = 0.0; //移動させて推定得点を出すために、フィールドを複製 Field field = Game.Field.Clone(true, true, true); //敵の存在する確率を求めるクラスを保持 var calculateExistenceProbability = new CalculateExistenceProbability(1); //初期値を設定 beforeTeam = field.EvaluateMap(TeamEnum); beforeEnemy = field.EvaluateMap(EnemyEnum); //一つづつ移動してみて、推定能率を計算 foreach (var node1 in BetterRoute.ToList().Skip(nowIndex)) { //ターン追加 needTurn++; //自チームにタイルを置く field.Map[node1.Point.Y, node1.Point.X].SetState(TeamEnum, CellState.Occupied); //相手に占有されていたらその分ターンが必要なので if (field.Map[node1.Point.Y, node1.Point.X].GetState(EnemyEnum) == CellState.Occupied) { needTurn++; } field.Map[node1.Point.Y, node1.Point.X].SetState(EnemyEnum, CellState.None); //値の変化を見る nextTeam = field.GetScore(TeamEnum); nextEnemy = field.GetScore(EnemyEnum); delta += ((nextTeam - beforeTeam) - (nextEnemy - beforeEnemy)) * (1 - calculateExistenceProbability.MapPossibilities(Enemy.Agent1.Position, Enemy.Agent2.Position, field.Width, field.Height, needTurn)[node1.Point.Y, node1.Point.X]); beforeTeam = nextTeam; beforeEnemy = nextEnemy; } //得点を計算 return(new ScoringEfficiency(delta, needTurn)); } //経路がない場合 return(new ScoringEfficiency(0, 0)); }
public override Intent Search(params Point[] prohibitedPoints) { //探索内最高点差を保持 double bestScoreDifference = double.MinValue; //一時的な差を保持 double delta; //エージェントの経路を保持 Direction[] route; Direction[] bestDirections = new Direction[Depth]; /*探索した部分をもとに戻すためのリスト*/ List <Point> points = new List <Point>(); //nターンで取れる行動を列挙するクラスEnumerateRoutesのインスタンスを作成 EnumerateRoutes enumerateRoutes = new EnumerateRoutes { Turn = Depth }; //作業用変数 int i; Agent agent = new Agent(); /*探索用*/ Point agentNextPos; ICell[,] map = Game.Field.Map.Clones(); int startTeam = EvaluateMap(map, TeamEnum), startEnemy = EvaluateMap(map, EnemyEnum), beforeTeam, beforeEnemy, nextTeam, nextEnemy; //Listで敵エージェントがそこに存在する確率を保持することで、オーバーヘッドを軽減 List <double[, ]> agentsProbabilities = new List <double[, ]>(); i = 1; //敵エージェント存在確率保持 while (Depth >= i) { agentsProbabilities.Add( CalculateExistenceProbability.MapPossibilities( Enemy.Agent1.Position, Enemy.Agent2.Position, Game.Field.Width, Game.Field.Height, i) ); i++; } //最善手を探す while (enumerateRoutes.HasNext) { //経路の取得 route = enumerateRoutes.Next(); //初手 移動 or タイル除去 ができる場合のみを考える if (Game.CanMove(Agent, route[0]) || Game.CanRemove(Agent, route[0])) { //エージェント情報のコピー agent.Position = Agent.Position; //マップをコピー foreach (var p in points) { map[p.Y, p.X].State1 = Map[p.Y, p.X].State1; map[p.Y, p.X].State2 = Map[p.Y, p.X].State2; } //書き換えた経路をリセット points.Clear(); delta = 0.0; beforeTeam = startTeam; beforeEnemy = startEnemy; //取り出した経路にしたがってエージェントを動かす for (i = 0; i < Depth; i++) { //移動した場合の場所を検索 agentNextPos = agent.Position.FastMove(route[i]); //移動した先がちゃんとマップ内であり、禁止領域でなければ移動 if (!(agentNextPos.X < 0 || agentNextPos.Y < 0 || agentNextPos.X >= Width || agentNextPos.Y >= Height) && prohibitedPoints.All(p => p != agentNextPos)) { points.Add(agentNextPos); AgentMove(map, TeamEnum, agent, route[i]); nextTeam = EvaluateMap(map, TeamEnum); nextEnemy = EvaluateMap(map, EnemyEnum); delta += ((nextTeam - beforeTeam) - (nextEnemy - beforeEnemy)) * (1.0 - agentsProbabilities[i][agentNextPos.Y, agentNextPos.X]); beforeTeam = nextTeam; beforeEnemy = nextEnemy; } else { //マップ外にでてしまった場合 goto NextRoutes; //マップ外に出るような移動は考えない } } //より高い得点を得られる経路であれば if (delta > bestScoreDifference) { //方向の更新 route.CopyTo(bestDirections, 0); //最高得点の更新 bestScoreDifference = delta; } } NextRoutes :; } BestDeltaScore = bestScoreDifference; //最善経路の保持 Way = new Way(Agent, bestDirections); Way.Next(out agentNextPos, out Direction direction); Intent Intent = new Intent() { Intention = Intentions.Stay, Direction = direction }; //移動先に移動できれば移動する意志に、敵タイルがあれば除去 if (Game.CanMove(Agent, direction)) { Intent.Intention = Intentions.Move; } else if (Game.CanRemove(Agent, direction)) { Intent.Intention = Intentions.Remove; } return(Intent); }
public override Intent Search(params Point[] prohibitedPoints) { //問題は、二回目以降の探索を行うときに起こる //Agent agent = new Agent(); Field field = Game.Field.Clone(true, true, true); int startTeam, startEnemy, beforeTeam, beforeEnemy, nextTeam, nextEnemy; int needTurn; double delta; startTeam = field.EvaluateMap(TeamEnum); startEnemy = field.EvaluateMap(EnemyEnum); //良いとされるルートを保持 Node[] betterRoute = new Node[1]; //敵の存在する確率を求めるクラスを保持 var calculateExistenceProbability = new CalculateExistenceProbability(1); //Listで敵エージェントがそこに存在する確率を保持することで、オーバーヘッドを軽減 List <double[, ]> agentsProbabilities = new List <double[, ]>(); BetterRoute = new Node[1]; BetterRoute[0] = new Node(); BetterEfficiency = double.MinValue; AStarSearch.Nodes.ForEach((r, n) => { //エージェントの位置以外探索 //おもすぎるので、相対位置が--の場合に変えるかも? if (n.Point != Agent.Position) { AStarSearch.ResetResult(); AStarSearch.StartNode = AStarSearch.Nodes[Agent.Position.Y, Agent.Position.X]; AStarSearch.EndNodes = new Node[] { n }; //禁止領域は予めクローズすることで対応 foreach (var p in prohibitedPoints) { AStarSearch.Nodes[p.Y, p.X].Status = NodeStatus.Closed; } //探索開始 AStarSearch.Search((c) => { Point p = c.Point; //まだ敵エージェントが存在する確率を計算していない if (c.Turn >= agentsProbabilities.Count) { agentsProbabilities.Add( calculateExistenceProbability.MapPossibilities(Enemy.Agent1.Position, Enemy.Agent2.Position, Game.Field.Width, Game.Field.Height, c.Turn) ); } //Score計算 ICell cell = Game.Field.GetCell(p); //移動成功確率をセルに保持。移動成功確率は前の成功確率とこの移動ができる確率の積 c.SuccessProbability = (1.0 - agentsProbabilities[c.Turn - 1][p.Y, p.X]) * c.Parent.SuccessProbability; //移動に伴うコストは、盤面の平均値とする //要改善、失敗確率の累積を考えていない //敵のタイル情報で場合分け switch (cell.GetState(EnemyEnum)) { case CellState.Occupied: c.Turn++; //敵のセルがある場合は、ターンが余計にかかるので、その分 agentsProbabilities.Add( calculateExistenceProbability.MapPossibilities(Enemy.Agent1.Position, Enemy.Agent2.Position, Game.Field.Width, Game.Field.Height, c.Turn) ); //2ターン必要ということで、成功確率も減る c.SuccessProbability *= (1.0 - agentsProbabilities[c.Turn - 1][p.Y, p.X]); return(16 - cell.Score * c.SuccessProbability + 2 * averageScore); case CellState.InRegion: return(16 - cell.Score * c.SuccessProbability + 1 * averageScore); } //自チームのタイル状況で場合分け switch (cell.GetState(TeamEnum)) { case CellState.None: return(16 - cell.Score * c.SuccessProbability + 1 * averageScore); case CellState.Occupied: return(16 + 0 * c.SuccessProbability + 1 * averageScore); case CellState.InRegion: //取るとかえって損なセルがある if (cell.Score >= 0) { return(16 + 0 * c.SuccessProbability + 1 * averageScore); } else { return(16 - cell.Score * c.SuccessProbability + 1 * averageScore); } default: //ここに来ることはありえない return(16 - cell.Score * c.SuccessProbability + 1 * averageScore); } }, (c) => { //HeuristicScore計算 Point pos = c.Point - Agent.Position; double minSteps = Math.Max(Math.Abs(pos.X), Math.Abs(pos.Y)); return((int)((16 - averageScore + 1) * minSteps)); }); //実際に移動させてみて得点がどうなるかを見る field = Game.Field.Clone(true, true, true); field.AutoEvaluate = false; nextTeam = beforeTeam = startTeam; nextEnemy = beforeEnemy = startEnemy; needTurn = 0; delta = 0; //一つづつチェック foreach (var node1 in AStarSearch.BestWayNodes) { if (field.Map[node1.Point.Y, node1.Point.X].GetState(TeamEnum) != CellState.Occupied) { field.Map[node1.Point.Y, node1.Point.X].SetState(TeamEnum, CellState.Occupied); //値の変化を見る nextTeam = field.EvaluateMap(TeamEnum); } //相手に占有されていたらその分ターンが必要なので if (field.Map[node1.Point.Y, node1.Point.X].GetState(EnemyEnum) == CellState.Occupied) { needTurn++; field.Map[node1.Point.Y, node1.Point.X].SetState(EnemyEnum, CellState.None); nextEnemy = field.EvaluateMap(EnemyEnum); } delta += ((nextTeam - beforeTeam) - (nextEnemy - beforeEnemy)) * node1.SuccessProbability; beforeTeam = nextTeam; beforeEnemy = nextEnemy; needTurn++;//必要ターン加算 } //よりよい経路が見つかったら if (BetterEfficiency < delta / needTurn) { betterRoute = AStarSearch.BestWayNodes.ToArray(); //最後のノードに推定能率を保持 BetterEfficiency = delta / needTurn; NeedTurn = needTurn; } } }); //結果を保存 BetterRoute = betterRoute; //得られた結果をWayクラスに変換 Way = new Way(Agent, BetterRoute.Select(node2 => node2.Point).ToArray()); Point agentNextPos; //次を取り出す Way.Peep(out agentNextPos, out Direction direction); return(Game.GetIntent(Agent, direction)); }