//遷移順を決める. 「この関数においては」MeBoard…手番プレイヤのボード, Me…手番プレイヤ、とします。 //引数: stateは手番プレイヤが手を打つ前の探索状態、(way1[i], way2[i])はi番目の合法手(移動量)です。 //以下のルールで優先順を決めます. //ルール1. Killer手(優先したい手)があれば、それを優先する //ルール2. 次のmoveで得られる「タイルポイント」の合計値が大きい移動(の組み合わせ)を優先する。 //ルール2では, タイル除去によっても「タイルポイント」が得られるとして計算する。 private void SortMoves(sbyte[,] ScoreBoard, SearchState state, SmallWays ways, int deep, Decision ngMove) { Unsafe8Array <Point> Killer = new Unsafe8Array <Point>(); if (ngMove is null) { if (dp1[deep].Score == int.MinValue) { for (int i = 0; i < AgentsCount; ++i) { Killer[i] = new Point(114, 191); } } else { for (int i = 0; i < AgentsCount; ++i) { Killer[i] = state.Me[i] + dp1[deep].Ways[i].Direction; } } } else { if (dp2[deep].Score == int.MinValue) { for (int i = 0; i < AgentsCount; ++i) { Killer[i] = new Point(114, 191); } } else { for (int i = 0; i < AgentsCount; ++i) { Killer[i] = state.Me[i] + dp2[deep].Ways[i].Direction; } } } for (int i = 0; i < ways.Count; ++i) { sbyte score = 0; Unsafe8Array <Point> next = new Unsafe8Array <Point>(); for (int j = 0; j < AgentsCount; ++j) { next[j] = state.Me[j] + ways[i].AgentsWay[j].Direction; } for (int j = 0; j < AgentsCount; ++j) { if (Killer[j] != next[j]) { break; } if (j == AgentsCount - 1) { score = 100; } } for (int j = 0; j < AgentsCount; ++j) { if (state.EnemyBoard[next[j]]) { score += ScoreBoard[next[j].X, next[j].Y]; //タイル除去によって有利になる } else if (!state.MeBoard[next[j]]) { score += ScoreBoard[next[j].X, next[j].Y]; //移動でMeの陣地が増えて有利になる } } } ways.Sort(); }
//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); }