protected override void Solve() { for (int i = 0; i < 50; ++i) { dp[i].Score = int.MinValue; dp[i].Ways = new Unsafe8Array <Way>(); } int deepness = StartDepth; int maxDepth = (TurnCount - CurrentTurn) + 1; //PointEvaluator.Base evaluator = (TurnCount / 3 * 2) < CurrentTurn ? PointEvaluator_Normal : PointEvaluator_Dispersion; PointEvaluator.Base evaluator = PointEvaluator_Normal; SearchState state = new SearchState(MyBoard, EnemyBoard, MyAgents, EnemyAgents); Log("TurnCount = {0}, CurrentTurn = {1}", TurnCount, CurrentTurn); for (int agent = 0; agent < AgentsCount; ++agent) { Unsafe8Array <Way> nextways = dp[0].Ways; NegaMax(deepness, state, int.MinValue + 1, 0, evaluator, null, nextways, agent); } if (CancellationToken.IsCancellationRequested == false) { SolverResult = new Decision(Unsafe8Array <VelocityPoint> .Create(dp[0].Ways.GetEnumerable(AgentsCount).Select(x => x.Direction).ToArray())); } }
//1ターン = 深さ2 protected override void Solve() { for (int i = 0; i < 50; ++i) { dp[i].Score = int.MinValue; } int deepness = StartDepth; int maxDepth = (TurnCount - CurrentTurn) * 2; PointEvaluator.Base evaluator = (TurnCount / 3 * 2) < CurrentTurn ? PointEvaluator_Normal : PointEvaluator_Dispersion; SearchState state = new SearchState(MyBoard, EnemyBoard, new Player(MyAgent1, MyAgent2), new Player(EnemyAgent1, EnemyAgent2), WaysPool); for (; deepness <= 1; deepness++) { NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator); if (CancellationToken.IsCancellationRequested == false) { SolverResult = new Decided(dp[0].Agent1Way, dp[0].Agent2Way); } else { break; } Log("[SOLVER] deepness = {0}", deepness); } }
//1ターン = 深さ2 protected override void Solve() { for (int i = 0; i < 50; ++i) { dp[i].Score = int.MinValue; } int deepness = StartDepth; int maxDepth = (TurnCount - CurrentTurn) * 2; PointEvaluator.Base evaluator = (TurnCount / 3 * 2) < CurrentTurn ? PointEvaluator_Normal : PointEvaluator_Dispersion; SearchState state = new SearchState(MyBoard, EnemyBoard, MyAgents, EnemyAgents); for (; deepness <= 1; deepness++) { NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator); if (CancellationToken.IsCancellationRequested == false) { SolverResult = new Decision(Unsafe8Array <VelocityPoint> .Create(dp[0].Ways.GetEnumerable(AgentsCount).Select(x => x.Direction).ToArray())); } else { break; } Log("[SOLVER] deepness = {0}", deepness); } }
//Meが動くとする。「Meのスコア - Enemyのスコア」の最大値を返す。 //NegaMaxではない private int NegaMax(int deepness, SearchState state, int alpha, int count, PointEvaluator.Base evaluator, Decision ngMove, Unsafe8Array <Way> nextways, int nowAgent) { var sw = System.Diagnostics.Stopwatch.StartNew(); if (deepness == 0) { return(evaluator.Calculate(ScoreBoard, state.MeBoard, 0, state.Me, state.Enemy) - evaluator.Calculate(ScoreBoard, state.EnemyBoard, 0, state.Enemy, state.Me)); } Ways ways = state.MakeMoves(AgentsCount, ScoreBoard); int i = 0; foreach (var way in ways.Data[nowAgent]) { if (CancellationToken.IsCancellationRequested == true) { return(alpha); } //何を返しても良いのでとにかく返す if (way.Direction == new VelocityPoint()) { continue; } i++; int j = 0; for (j = 0; j < nowAgent; ++j) { if (dp[0].Ways[j].Locate == way.Locate) { break; } } if (j != nowAgent) { continue; } Unsafe8Array <Way> newways = new Unsafe8Array <Way>(); newways[nowAgent] = way; SearchState backup = state; state = state.GetNextState(AgentsCount, newways); int res = NegaMax(deepness - 1, state, alpha, count + 1, evaluator, ngMove, nextways, nowAgent); if (alpha < res) { nextways[nowAgent] = way; alpha = res; dp[count].UpdateScore(alpha, nextways); } state = backup; } sw.Stop(); //Log("NODES : {0} nodes, elasped {1} ", i, sw.Elapsed); ways.End(); return(alpha); }
//1ターン = 深さ2 protected override void Solve() { for (int i = 0; i < 50; ++i) { dp1[i].Score = int.MinValue; dp2[i].Score = int.MinValue; } int deepness = StartDepth; int maxDepth = (TurnCount - CurrentTurn) * 2 + 2; PointEvaluator.Base evaluator = (TurnCount / 3 * 2) < CurrentTurn ? PointEvaluator_Normal : PointEvaluator_Dispersion; SearchState state = new SearchState(MyBoard, EnemyBoard, new Player(MyAgent1, MyAgent2), new Player(EnemyAgent1, EnemyAgent2), WaysPool); int score = PointEvaluator_Normal.Calculate(ScoreBoard, state.MeBoard, 0) - PointEvaluator_Normal.Calculate(ScoreBoard, state.EnemyBoard, 0); Log("TurnCount = {0}, CurrentTurn = {1}", TurnCount, CurrentTurn); if (!(lastTurnDecided is null)) { Log("IsAgent1Moved = {0}, IsAgent2Moved = {1}, lastTurnDecided = {2}", IsAgent1Moved, IsAgent2Moved, lastTurnDecided); } if (!(lastTurnDecided is null) && IsAgent1Moved == false && IsAgent2Moved == false && score > 0) //勝っている状態で競合していたら { SolverResultList.Add(lastTurnDecided); return; } for (; deepness <= maxDepth; deepness++) { DecidedEx resultList = new DecidedEx(); int greedyDepth = Math.Min(greedyMaxDepth, maxDepth - deepness); if ((deepness + greedyDepth) % 2 == 1 && greedyDepth > 0) { greedyDepth--; } //普通にNegaMaxをして、最善手を探す NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator, null, greedyDepth); Decided best1 = new Decided(dp1[0].Agent1Way, dp1[0].Agent2Way); resultList.Add(best1); //競合手.Agent1 == 最善手.Agent1 && 競合手.Agent2 == 最善手.Agent2になった場合、競合手をngMoveとして探索をおこない、最善手を探す if ((IsAgent1Moved == false && lastTurnDecided.MeAgent1.Equals(best1.MeAgent1)) && (IsAgent2Moved == false && lastTurnDecided.MeAgent2.Equals(best1.MeAgent2))) { NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator, best1, greedyDepth); Decided best2 = new Decided(dp2[0].Agent1Way, dp2[0].Agent2Way); resultList.Add(best2); } if (CancellationToken.IsCancellationRequested == false) { SolverResultList = resultList; if (SolverResultList.Count == 2 && score <= 0) //現時点で引き分けか負けていたら競合を避けるのを優先してみる(デバッグ用) { var tmp = SolverResultList[0]; SolverResultList[0] = SolverResultList[1]; SolverResultList[1] = tmp; Log("[SOLVER] Swaped! {0} {1}", SolverResult.MeAgent1, SolverResult.MeAgent2); } Log("[SOLVER] SolverResultList.Count = {0}, score = {1}", SolverResultList.Count, score); } else { return; } Log("[SOLVER] deepness = {0}", deepness); } }
//Meが動くとする。「Meのスコア - Enemyのスコア」の最大値を返す。 private int NegaMax(int deepness, SearchState state, int alpha, int beta, int count, PointEvaluator.Base evaluator, Decided ngMove, int greedyDepth) { if (deepness == 0) { for (int j = 0; j < greedyDepth; j++) { Way move = state.MakeGreedyMove(ScoreBoard, WayEnumerator); state.Move(move.Agent1Way, move.Agent2Way); //Ways moves = state.MakeMoves(WayEnumerator); //SortMoves(ScoreBoard, state, moves, 49, null); //state.Move(moves[0].Agent1Way, moves[1].Agent2Way); } int score = evaluator.Calculate(ScoreBoard, state.MeBoard, 0) - evaluator.Calculate(ScoreBoard, state.EnemyBoard, 0); if (greedyDepth % 2 == 1) { return(-score); } return(score); } Ways ways = state.MakeMoves(WayEnumerator); SortMoves(ScoreBoard, state, ways, count, ngMove); for (int i = 0; i < ways.Count; i++) { if (CancellationToken.IsCancellationRequested == true) { return(alpha); } //何を返しても良いのでとにかく返す //if (count == 0 && !(ngMove is null) && new Decided(ways[i].Agent1Way, ways[i].Agent2Way).Equals(ngMove)) { continue; } //競合手を避ける場合 if (count == 0 && !(ngMove is null) && (ways[i].Agent1Way.Equals(ngMove.MeAgent1) || ways[i].Agent2Way.Equals(ngMove.MeAgent2))) { continue; } //2人とも競合手とは違う手を指す SearchState nextState = state; nextState.Move(ways[i].Agent1Way, ways[i].Agent2Way); int res = -NegaMax(deepness - 1, nextState, -beta, -alpha, count + 1, evaluator, ngMove, greedyDepth); if (alpha < res) { alpha = res; if (ngMove is null) { dp1[count].UpdateScore(alpha, ways[i].Agent1Way, ways[i].Agent2Way); } else { dp2[count].UpdateScore(alpha, ways[i].Agent1Way, ways[i].Agent2Way); } if (alpha >= beta) { return(beta); //βcut } } } ways.Erase(); WaysPool.Return(ways); return(alpha); }
//1ターン = 深さ2 protected override void Solve() { for (int i = 0; i < 50; ++i) { dp1[i].Score = int.MinValue; dp2[i].Score = int.MinValue; } int deepness = StartDepth; int maxDepth = (TurnCount - CurrentTurn) * 2 + 2; PointEvaluator.Base evaluator = (TurnCount / 3 * 2) < CurrentTurn ? PointEvaluator_Normal : PointEvaluator_Dispersion; SearchState state = new SearchState(MyBoard, EnemyBoard, MyAgents, EnemyAgents); int score = PointEvaluator_Normal.Calculate(ScoreBoard, state.MeBoard, 0, state.Me, state.Enemy) - PointEvaluator_Normal.Calculate(ScoreBoard, state.EnemyBoard, 0, state.Enemy, state.Me); Log("TurnCount = {0}, CurrentTurn = {1}", TurnCount, CurrentTurn); //if (!(lastTurnDecided is null)) Log("IsAgent1Moved = {0}, IsAgent2Moved = {1}, lastTurnDecided = {2}", IsAgent1Moved, IsAgent2Moved, lastTurnDecided); if (!(lastTurnDecided is null) && score > 0) //勝っている状態で競合していたら { int i; for (i = 0; i < AgentsCount; ++i) { if (IsAgentsMoved[i]) { break; } } if (i == AgentsCount) { SolverResultList.Add(lastTurnDecided); return; } } for (; deepness <= maxDepth; deepness++) { Decided resultList = new Decided(); int greedyDepth = Math.Min(greedyMaxDepth, maxDepth - deepness); if ((deepness + greedyDepth) % 2 == 1 && greedyDepth > 0) { greedyDepth--; } //普通にNegaMaxをして、最善手を探す NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator, null, greedyDepth); Decision best1 = new Decision(Unsafe8Array <VelocityPoint> .Create(dp1[0].Ways.GetEnumerable(AgentsCount).Select(x => x.Direction).ToArray())); resultList.Add(best1); //競合手.Agent1 == 最善手.Agent1 && 競合手.Agent2 == 最善手.Agent2になった場合、競合手をngMoveとして探索をおこない、最善手を探す for (int i = 0; i < AgentsCount; ++i) { if (IsAgentsMoved[i] || !lastTurnDecided.Agents[i].Equals(best1.Agents[i])) { break; } if (i < AgentsCount - 1) { continue; } NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator, best1, greedyDepth); Decision best2 = new Decision(Unsafe8Array <VelocityPoint> .Create(dp2[0].Ways.GetEnumerable(AgentsCount).Select(x => x.Direction).ToArray())); resultList.Add(best2); } if (CancellationToken.IsCancellationRequested == false) { SolverResultList = resultList; if (SolverResultList.Count == 2 && score <= 0) //現時点で引き分けか負けていたら競合を避けるのを優先してみる(デバッグ用) { var tmp = SolverResultList[0]; SolverResultList[0] = SolverResultList[1]; SolverResultList[1] = tmp; Log("[SOLVER] Swaped! {0} {1}", SolverResult.Agents[0], SolverResult.Agents[1]); } Log("[SOLVER] SolverResultList.Count = {0}, score = {1}", SolverResultList.Count, score); } else { return; } Log("[SOLVER] deepness = {0}", deepness); } }
//Meが動くとする。「Meのスコア - Enemyのスコア」の最大値を返す。 private int NegaMax(int deepness, SearchState state, int alpha, int beta, int count, PointEvaluator.Base evaluator, Decision ngMove, int greedyDepth) { if (deepness == 0) { //for (int j = 0; j < greedyDepth; j++) //{ //Unsafe8Array<VelocityPoint> move = state.MakeGreedyMove(ScoreBoard, WayEnumerator, AgentsCount); //state.Move(move, AgentsCount); //Ways moves = state.MakeMoves(AgentsCount, ScoreBoard); //SortMoves(ScoreBoard, state, moves, 49, null); //state.Move(moves[0].Agent1Way, moves[1].Agent2Way); //} int score = evaluator.Calculate(ScoreBoard, state.MeBoard, 0, state.Me, state.Enemy) - evaluator.Calculate(ScoreBoard, state.EnemyBoard, 0, state.Enemy, state.Me); if (greedyDepth % 2 == 1) { return(-score); } return(score); } Ways ways = state.MakeMoves(AgentsCount, ScoreBoard); SmallWays sways = new SmallWays(AgentsCount); //ways => sways foreach (var way in ways.GetEnumerator(AgentsCount)) { sways.Add(new SmallWay(way)); } SortMoves(ScoreBoard, state, sways, count, ngMove); for (int i = 0; i < sways.Count; ++i) { if (CancellationToken.IsCancellationRequested == true) { return(alpha); } //何を返しても良いのでとにかく返す //if (count == 0 && !(ngMove is null) && new Decided(ways[i].Agent1Way, ways[i].Agent2Way).Equals(ngMove)) { continue; } //競合手を避ける場合 if (count == 0 && !(ngMove is null)) //2人とも競合手とは違う手を指す { int j; for (j = 0; j < AgentsCount; ++j) { if (sways[i].Equals(ngMove.Agents[j])) { break; } } if (j != AgentsCount) { continue; } } SearchState nextState = state; nextState = nextState.GetNextState(AgentsCount, sways[i].AgentsWay); nextState = nextState.ChangeTurn(); int res = -NegaMax(deepness - 1, nextState, -beta, -alpha, count + 1, evaluator, ngMove, greedyDepth); if (alpha < res) { alpha = res; if (ngMove is null) { dp1[count].UpdateScore(alpha, sways[i].AgentsWay); } else { dp2[count].UpdateScore(alpha, sways[i].AgentsWay); } if (alpha >= beta) { return(beta); //βcut } } } sways.Erase(); //WaysPool.Return(ways); return(alpha); }
//1ターン = 深さ2 protected override void Solve() { int i; for (i = 0; i < 50; ++i) { dp[i].Score = int.MinValue; } int deepness = StartDepth; int maxDepth = (TurnCount - CurrentTurn) * 2; PointEvaluator.Base evaluator = (TurnCount / 3 * 2) < CurrentTurn ? PointEvaluator_Normal : PointEvaluator_AreaCount; SearchState state = new SearchState(MyBoard, EnemyBoard, new Player(MyAgent1, MyAgent2), new Player(EnemyAgent1, EnemyAgent2), WaysPool); //競合判定 int K = KyogoTurn; //Kは1以上の整数 ngMove = null; for (i = 0; i < K; i++) { if (historyStates.Count < K) { break; } int id = historyStates.Count - 1 - i; if (id < 0 || state.Equals(historyStates[id]) == false) { break; } if (i + 1 < K && id > 0 && historyDecides[id].Equals(historyDecides[id - 1]) == false) { break; } } if (i == K) { int score = PointEvaluator_Normal.Calculate(ScoreBoard, state.MeBoard, 0) - PointEvaluator_Normal.Calculate(ScoreBoard, state.EnemyBoard, 0); if (score <= 0) { ngMove = historyDecides[historyDecides.Count - 1]; Log("[SOLVER] conclusion!, you cannot move to ", ngMove); } } //反復深化 for (; deepness <= maxDepth; deepness++) { NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator, Math.Min(maxDepth - deepness, GreedyMaxDepth)); if (CancellationToken.IsCancellationRequested == false) { SolverResult = new Decided(dp[0].Agent1Way, dp[0].Agent2Way); } else { break; } Log("[SOLVER] deepness = {0}", deepness); } //履歴の更新 historyStates.Add(state); historyDecides.Add(SolverResult); }
//Meが動くとする。「Meのスコア - Enemyのスコア」の最大値を返す。 private int NegaMax(int deepness, SearchState state, int alpha, int beta, int count, PointEvaluator.Base evaluator, int greedyDepth) { if (deepness == 0) { //深さgreedyDepth分だけ貪欲をしてから、評価関数を呼び出す for (int i = 0; i < greedyDepth; i++) { Ways moves = state.MakeMoves(WayEnumerator); SortMoves(ScoreBoard, state, moves, dp.Length - 1); state.Move(moves[0].Agent1Way, moves[0].Agent2Way); } int eval = evaluator.Calculate(ScoreBoard, state.MeBoard, 0) - evaluator.Calculate(ScoreBoard, state.EnemyBoard, 0); if (greedyDepth % 2 == 1) { return(-eval); } return(eval); } Ways ways = state.MakeMoves(WayEnumerator); SortMoves(ScoreBoard, state, ways, count); for (int i = 0; i < ways.Count; i++) { if (CancellationToken.IsCancellationRequested == true) { return(alpha); } //何を返しても良いのでとにかく返す if (count == 0 && ngMove != null && new Decided(ways[i].Agent1Way, ways[i].Agent2Way).Equals(ngMove) == true) { continue; } //競合手は指さない SearchState backup = state; state.Move(ways[i].Agent1Way, ways[i].Agent2Way); int res = -NegaMax(deepness - 1, state, -beta, -alpha, count + 1, evaluator, greedyDepth); if (alpha < res) { alpha = res; dp[count].UpdateScore(alpha, ways[i].Agent1Way, ways[i].Agent2Way); if (alpha >= beta) { return(beta); //βcut } } state = backup; } ways.Erase(); WaysPool.Return(ways); return(alpha); }
//1ターン = 深さ2 protected override void Solve() { for (int i = 0; i < 50; ++i) { dp1[i].Score = int.MinValue; dp2[i].Score = int.MinValue; } int deepness = StartDepth; int maxDepth = (TurnCount - CurrentTurn) * 2 + 2; PointEvaluator.Base evaluator = (TurnCount / 3 * 2) < CurrentTurn ? PointEvaluator_Normal : PointEvaluator_Distance; SearchState state = new SearchState(MyBoard, EnemyBoard, MyAgents, EnemyAgents); int score = PointEvaluator_Normal.Calculate(ScoreBoard, state.MeBoard, 0, MyAgents, EnemyAgents) - PointEvaluator_Normal.Calculate(ScoreBoard, state.EnemyBoard, 0, EnemyAgents, MyAgents); Log("TurnCount = {0}, CurrentTurn = {1}", TurnCount, CurrentTurn); if (!(lastTurnDecided is null)) { StringBuilder sb = new StringBuilder("AgentMoved: {"); for (int i = 0; i < AgentsCount; ++i) { sb.Append(IsAgentsMoved[i]); sb.Append(", "); } string ismoved = sb.ToString(); Log("{0}}}, lastTurnDecided = {2}", ismoved.Substring(0, ismoved.Length - 2), lastTurnDecided); } if (!(lastTurnDecided is null) && IsAgentsMoved.GetEnumerable(AgentsCount).All(b => b == false) && score > 0) //勝っている状態で競合していたら { SolverResultList.Add(lastTurnDecided); return; } for (; deepness <= maxDepth; deepness++) { Decided resultList = new Decided(); int greedyDepth = Math.Min(greedyMaxDepth, maxDepth - deepness); if ((deepness + greedyDepth) % 2 == 1 && greedyDepth > 0) { greedyDepth--; } //普通にNegaMaxをして、最善手を探す NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator, null, greedyDepth); Decision best1 = new Decision(Unsafe8Array <VelocityPoint> .Create(dp1[0].Ways.GetEnumerable(AgentsCount).Select(x => x.Direction).ToArray())); resultList.Add(best1); //競合手.Agent == 最善手.Agent の数が半数以上になった場合、競合手をngMoveとして探索をおこない、最善手を探す int UnMoveAgentNum = 0; for (int i = 0; i < AgentsCount; ++i) { if (IsAgentsMoved[i] == false && lastTurnDecided.Agents[i] == best1.Agents[i]) { ++UnMoveAgentNum; } } if (UnMoveAgentNum > AgentsCount / 2) { NegaMax(deepness, state, int.MinValue + 1, int.MaxValue, 0, evaluator, best1, greedyDepth); Decision best2 = new Decision(Unsafe8Array <VelocityPoint> .Create(dp2[0].Ways.GetEnumerable(AgentsCount).Select(x => x.Direction).ToArray())); resultList.Add(best2); } if (CancellationToken.IsCancellationRequested == false) { SolverResultList = resultList; if (SolverResultList.Count == 2 && score <= 0) //現時点で引き分けか負けていたら競合を避けるのを優先してみる(デバッグ用) { var tmp = SolverResultList[0]; SolverResultList[0] = SolverResultList[1]; SolverResultList[1] = tmp; Log("[SOLVER] Swaped! {0} {1}", SolverResult.Agents[0], SolverResult.Agents[1]); } Log("[SOLVER] SolverResultList.Count = {0}, score = {1}", SolverResultList.Count, score); } else { return; } Log("[SOLVER] deepness = {0}", deepness); } }
//Meが動くとする。「Meのスコア - Enemyのスコア」の最大値を返す。 private int NegaMax(int deepness, SearchState state, int alpha, int beta, int count, PointEvaluator.Base evaluator) { if (deepness == 0) { return(evaluator.Calculate(ScoreBoard, state.MeBoard, 0) - evaluator.Calculate(ScoreBoard, state.EnemyBoard, 0)); } Ways ways = state.MakeMoves(WayEnumerator); SortMoves(ScoreBoard, state, ways, count); for (int i = 0; i < ways.Count; i++) { if (CancellationToken.IsCancellationRequested == true) { return(alpha); } //何を返しても良いのでとにかく返す SearchState backup = state; state.Move(ways[i].Agent1Way, ways[i].Agent2Way); int res = -NegaMax(deepness - 1, state, -beta, -alpha, count + 1, evaluator); if (alpha < res) { alpha = res; dp[count].UpdateScore(alpha, ways[i].Agent1Way, ways[i].Agent2Way); if (alpha >= beta) { return(beta); //βcut } } state = backup; } ways.Erase(); WaysPool.Return(ways); return(alpha); }
//Meが動くとする。「Meのスコア - Enemyのスコア」の最大値を返す。 //NegaMaxではない private int NegaMax(int deepness, SearchState state, int alpha, int count, PointEvaluator.Base evaluator, Decision ngMove, int nowAgent) { if (deepness == 0) { return(evaluator.Calculate(ScoreBoard, state, 0)); } if (CancellationToken.IsCancellationRequested == true) { return(alpha); } //何を返しても良いのでとにかく返す SingleAgentWays ways = state.MakeMovesSingle(AgentsCount, nowAgent, ScoreBoard); for (int i = 0; i < ways.Count; ++i) { var way = ways.Data[i]; if (count == 0 && !(ngMove is null)) //競合手とは違う手を指す { if (way == ngMove.Agents[nowAgent]) { continue; } } SearchState newState = state.GetNextStateSingle(nowAgent, way, ScoreBoard, deepness); //自エージェントとの衝突を防ぐ if (count == 0) { int j = 0; for (j = 0; j < AgentsCount; ++j) { if (j == nowAgent) { continue; } if (ngMove is null && dp1[count][j] == way) { break; } if (!(ngMove is null) && dp2[count][j] == way) { break; } } if (j != AgentsCount) { continue; } } int res = NegaMax(deepness - 1, newState, alpha, count + 1, evaluator, ngMove, nowAgent); if (alpha < res) { alpha = res; if (ngMove is null) { dp1[count][nowAgent] = way; } else { dp2[count][nowAgent] = way; } } } //Log("NODES : {0} nodes, elasped {1} ", i, sw.Elapsed); ways.End(); return(alpha); }
protected override void Solve() { var myAgents = SearchFirstPlace(); Array.Clear(dp1, 0, dp1.Length); Array.Clear(dp2, 0, dp2.Length); int deepness = StartDepth; int maxDepth = (TurnCount - CurrentTurn) + 1; PointEvaluator.Base evaluator = (TurnCount / 3 * 2) < CurrentTurn ? PointEvaluator_Normal : PointEvaluator_Dispersion; SearchState state = new SearchState(MyBoard, EnemyBoard, myAgents, EnemyAgents, MySurroundedBoard, EnemySurroundedBoard); int score = 0; for (uint x = 0; x < ScoreBoard.GetLength(0); ++x) { for (uint y = 0; y < ScoreBoard.GetLength(1); ++y) { if (MyBoard[x, y]) { score += ScoreBoard[x, y]; } else if (MySurroundedBoard[x, y]) { score += Math.Abs(ScoreBoard[x, y]); } else if (EnemyBoard[x, y]) { score -= ScoreBoard[x, y]; } else if (EnemySurroundedBoard[x, y]) { score -= Math.Abs(ScoreBoard[x, y]); } } } Log("TurnCount = {0}, CurrentTurn = {1}", TurnCount, CurrentTurn); //if (!(lastTurnDecided is null)) Log("IsAgent1Moved = {0}, IsAgent2Moved = {1}, lastTurnDecided = {2}", IsAgent1Moved, IsAgent2Moved, lastTurnDecided); if (!(lastTurnDecided is null) && score > 0) //勝っている状態で競合していたら { int i; for (i = 0; i < AgentsCount; ++i) { if (IsAgentsMoved[i]) { break; } } if (i == AgentsCount) { SolverResultList.Add(lastTurnDecided); return; } } for (; deepness <= maxDepth; deepness++) { Decided resultList = new Decided(); //普通にNegaMaxをして、最善手を探す for (int agent = 0; agent < AgentsCount; ++agent) { if (MyAgentsState[agent] == AgentState.NonPlaced) { continue; } NegaMax(deepness, state, int.MinValue + 1, 0, evaluator, null, agent); } var res = dp1[0]; for (int agent = 0; agent < AgentsCount; ++agent) { if (MyAgentsState[agent] == AgentState.NonPlaced) { res[agent] = myAgents[agent]; } } Decision best1 = new Decision((byte)AgentsCount, res, agentStateAry); resultList.Add(best1); //競合手.Agent1 == 最善手.Agent1 && 競合手.Agent2 == 最善手.Agent2になった場合、競合手をngMoveとして探索をおこない、最善手を探す for (int i = 0; i < AgentsCount; ++i) { if (IsAgentsMoved[i] || (!(lastTurnDecided is null) && lastTurnDecided.Agents[i] != best1.Agents[i])) { break; } if (i < AgentsCount - 1) { continue; } for (int agent = 0; agent < AgentsCount; ++agent) { if (MyAgentsState[agent] == AgentState.NonPlaced) { continue; } NegaMax(deepness, state, int.MinValue + 1, 0, evaluator, best1, agent); } res = dp2[0]; for (int agent = 0; agent < AgentsCount; ++agent) { if (MyAgentsState[agent] == AgentState.NonPlaced) { res[agent] = myAgents[agent]; } } Decision best2 = new Decision((byte)AgentsCount, res, agentStateAry); resultList.Add(best2); } if (CancellationToken.IsCancellationRequested == false) { SolverResultList = resultList; if (SolverResultList.Count == 2 && score <= 0) //現時点で引き分けか負けていたら競合を避けるのを優先してみる(デバッグ用) { var tmp = SolverResultList[0]; SolverResultList[0] = SolverResultList[1]; SolverResultList[1] = tmp; Log("[SOLVER] Swaped! {0} {1}", SolverResult.Agents[0], SolverResult.Agents[1]); } Log("[SOLVER] SolverResultList.Count = {0}, score = {1}", SolverResultList.Count, score); } else { return; } Log("[SOLVER] deepness = {0}", deepness); } }
//Meが動くとする。「Meのスコア - Enemyのスコア」の最大値を返す。 private int NegaMax(int deepness, SearchState state, int alpha, int beta, int count, PointEvaluator.Base evaluator) { var sw = System.Diagnostics.Stopwatch.StartNew(); if (deepness == 0) { return(evaluator.Calculate(ScoreBoard, state.MeBoard, 0, state.Me, state.Enemy) - evaluator.Calculate(ScoreBoard, state.EnemyBoard, 0, state.Enemy, state.Me)); } Ways ways = state.MakeMoves(AgentsCount, ScoreBoard); int i = 0; foreach (var way in ways.GetEnumerator(AgentsCount)) { i++; if (CancellationToken.IsCancellationRequested == true) { return(alpha); } //何を返しても良いのでとにかく返す SearchState backup = state; state = state.GetNextState(AgentsCount, way); state = state.ChangeTurn(); int res = -NegaMax(deepness - 1, state, -beta, -alpha, count + 1, evaluator); if (alpha < res) { alpha = res; dp[count].UpdateScore(alpha, way); if (alpha >= beta) { return(beta); //βcut } } state = backup; } sw.Stop(); Log("NODES : {0} nodes, elasped {1} ", i, sw.Elapsed); ways.End(); return(alpha); }