//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); } }
public void UpdateScore(int score, Unsafe8Array <Way> ways) { if (Score < score) { Ways = ways; } }
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())); } }
//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); }
public void EqualPoint3Test() { Unsafe8Array <Point> x = Unsafe8Array <Point> .Create(new Point[] { new Point(0, 1), new Point(2, 3), new Point(4, 5), new Point(6, 7) }); Unsafe8Array <Point> y = Unsafe8Array <Point> .Create(new Point[] { new Point(0, 1), new Point(2, 3), new Point(4, 5), new Point(8, 9) }); Assert.True(Unsafe8Array <Point> .Equals(x, y, 3)); y[2] = new Point(10, 11); Assert.False(Unsafe8Array <Point> .Equals(x, y, 3)); }
//遷移順を決める. 「この関数においては」MeBoard…手番プレイヤのボード, Me…手番プレイヤ、とします。 //引数: stateは手番プレイヤが手を打つ前の探索状態、(way1[i], way2[i])はi番目の合法手(移動量)です。 //以下のルールで優先順を決めます. //ルール1. Killer手(優先したい手)があれば、それを優先する //ルール2. 次のmoveで得られる「タイルポイント」の合計値が大きい移動(の組み合わせ)を優先する。 //ルール2では, タイル除去によっても「タイルポイント」が得られるとして計算する。 private void SortMoves(sbyte[,] ScoreBoard, SearchState state, Ways way, int deep, Decision ngMove) { Unsafe8Array <Point> Killer; DP[] dp = ngMove is null ? dp1 : dp2; if (dp[deep].Score == int.MinValue) { Killer = Unsafe8Array <Point> .Create(new Point(114, 191), new Point(114, 191), new Point(114, 191), new Point(114, 191), new Point(114, 191), new Point(114, 191), new Point(114, 191), new Point(114, 191)); } else { Killer = new Unsafe8Array <Point>(); for (int i = 0; i < AgentsCount; ++i) { Killer[i] = state.Me[i] + dp[deep].AgentsWay[i]; } } for (int i = 0; i < way.Count; i++) { int score = 0; Unsafe8Array <Point> nexts = new Unsafe8Array <Point>(); for (int n = 0; n < AgentsCount; ++i) { nexts[n] = state.Me[n] + way[i].AgentWays[n]; } if (Killer.Agent1 == next1 && Killer.Agent2 == next2) { score = 100; } if (state.EnemyBoard[next1]) { score += ScoreBoard[next1.X, next1.Y]; } //タイル除去によって有利になる else if (!state.MeBoard[next1]) { score += ScoreBoard[next1.X, next1.Y]; } //移動でMeの陣地が増えて有利になる if (state.EnemyBoard[next2]) { score += ScoreBoard[next2.X, next2.Y]; } else if (!state.MeBoard[next2]) { score += ScoreBoard[next2.X, next2.Y]; } way[i].Point = score; } way.Sort(); }
public void PutAndGetVelocityPointTest() { Unsafe8Array <VelocityPoint> x = new Unsafe8Array <VelocityPoint>(); x[0] = new VelocityPoint(1, 2); x[1] = new VelocityPoint(3, 4); x[2] = new VelocityPoint(5, 6); x[3] = new VelocityPoint(7, 8); x[4] = new VelocityPoint(-1, -2); x[5] = new VelocityPoint(-3, -4); x[6] = new VelocityPoint(-5, -6); x[7] = new VelocityPoint(-7, -8); Assert.True(x[0] == new VelocityPoint(1, 2)); Assert.True(x[1] == new VelocityPoint(3, 4)); Assert.True(x[2] == new VelocityPoint(5, 6)); Assert.True(x[3] == new VelocityPoint(7, 8)); Assert.True(x[4] == new VelocityPoint(-1, -2)); Assert.True(x[5] == new VelocityPoint(-3, -4)); Assert.True(x[6] == new VelocityPoint(-5, -6)); Assert.True(x[7] == new VelocityPoint(-7, -8)); }
public void PutAndGetPointTest() { Unsafe8Array <Point> x = new Unsafe8Array <Point>(); x[0] = new Point(1, 2); x[1] = new Point(3, 4); x[2] = new Point(5, 6); x[3] = new Point(7, 8); x[4] = new Point(9, 10); x[5] = new Point(11, 12); x[6] = new Point(13, 14); x[7] = new Point(15, 16); Assert.True(x[0] == new Point(1, 2)); Assert.True(x[1] == new Point(3, 4)); Assert.True(x[2] == new Point(5, 6)); Assert.True(x[3] == new Point(7, 8)); Assert.True(x[4] == new Point(9, 10)); Assert.True(x[5] == new Point(11, 12)); Assert.True(x[6] == new Point(13, 14)); Assert.True(x[7] == new Point(15, 16)); }
//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); } }
//遷移順を決める. 「この関数においては」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(); }
public SmallWay(Unsafe8Array <Way> a) { AgentsWay = a; Point = 0; }
//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); } }