private void FloodFillMinDistancesQueen(Point point, PieceGrid pieceGrid, PointSquareArray <double?> result) { ISet <Point> visited = new HashSet <Point>(); Queue <(Point, double)> toVisit = new Queue <(Point, double)>(); foreach (Point p in pieceGrid.GetOpenPointsOutFrom(point)) { toVisit.Enqueue((p, 1)); } while (toVisit.Any()) { (Point, double)p = toVisit.Dequeue(); if (visited.Contains(p.Item1)) { continue; } if (result[p.Item1].HasValue) { result[p.Item1] = Math.Min(p.Item2, result[p.Item1].Value); } else { result.Add(p.Item1, p.Item2); } visited.Add(p.Item1); foreach (Point pNext in pieceGrid.GetOpenPointsOutFrom(p.Item1) .Where(adj => !visited.Contains(adj))) { toVisit.Enqueue((pNext, p.Item2 + 1)); } } }
/// <summary> /// Create a dictionary of points mapped to the minimum distance to that point for the given player and move type. /// As a side effect, this fills the Specific*Distances dictionary for the given <paramref name="owner"/> /// </summary> /// <param name="pieceGrid">PieceGrid to analyze</param> /// <param name="owner">Player whos amazons we are calculating distance for</param> /// <param name="queen">True for Queen distances, false for King distances</param> /// <returns>Dictionary of Points with their minimum distance values for the given player and move type</returns> private PointSquareArray <double?> BuildDistancesDictionary(PieceGrid pieceGrid, Owner owner, bool queen) { var distancesDictionary = new PointSquareArray <double?>(pieceGrid.Size); ISet <Point> amazonPoints; if (owner == Owner.Player1) { amazonPoints = pieceGrid.Amazon1Points; } else { amazonPoints = pieceGrid.Amazon2Points; } foreach (Point amazonPoint in amazonPoints) { if (queen) { FloodFillMinDistancesQueen(amazonPoint, pieceGrid, distancesDictionary); } else { FloodFillMinDistancesKing(amazonPoint, pieceGrid, distancesDictionary); } } return(distancesDictionary); }
/// <summary> /// Build all the internal analysis data for the given PieceGrid /// </summary> /// <param name="pieceGrid">PieceGrid to analyze</param> /// <param name="playerToMove">Player whos turn it is</param> public void BuildAnalysis(PieceGrid pieceGrid, Owner playerToMove) { if (pieceGrid.Id == LastAnalyzedPieceGridId) { return; } LastAnalyzedPieceGridId = pieceGrid.Id; LocalAdvantages = new PointSquareArray <LocalAdvantage>(pieceGrid.Size); Player1QueenMinDistances = BuildDistancesDictionary(pieceGrid, Owner.Player1); Player2QueenMinDistances = BuildDistancesDictionary(pieceGrid, Owner.Player2); CalculateLocalAdvantages(pieceGrid, playerToMove); T1 = LocalAdvantages.Where(a => a.Value != null).Sum(a => a.Value.DeltaQueen); }
private void FloodFillMinDistancesKing(Point point, PieceGrid pieceGrid, PointSquareArray <double?> result) { ISet <Point> visited = new HashSet <Point>(); Queue <(Point, double)> toVisit = new Queue <(Point, double)>(); foreach (Point p in point.GetAdjacentPoints().Where(adj => !pieceGrid.IsOutOfBounds(adj) && !pieceGrid.PointPieces[adj].Impassible)) { toVisit.Enqueue((p, 1)); } while (toVisit.Any()) { (Point, double)p = toVisit.Dequeue(); if (visited.Contains(p.Item1)) { continue; } if (result[p.Item1].HasValue) { result[p.Item1] = Math.Min(p.Item2, result[p.Item1].Value); } else { result.Add(p.Item1, p.Item2); } if (SpecificKingDistances[point] == null) { SpecificKingDistances.Add(point, new PointSquareArray <double>(pieceGrid.Size)); } SpecificKingDistances[point].Add(p.Item1, p.Item2); visited.Add(p.Item1); foreach (Point pNext in p.Item1.GetAdjacentPoints() .Where(adj => !pieceGrid.IsOutOfBounds(adj) && !pieceGrid.PointPieces[adj].Impassible && !visited.Contains(adj))) { toVisit.Enqueue((pNext, p.Item2 + 1)); } } }
/// <summary> /// Build all the internal analysis data for the given PieceGrid /// </summary> /// <param name="pieceGrid">PieceGrid to analyze</param> /// <param name="playerToMove">Player whos turn it is</param> public void BuildAnalysis(PieceGrid pieceGrid, Owner playerToMove) { if (pieceGrid.Id == LastAnalyzedPieceGridId) { return; } LastAnalyzedPieceGridId = pieceGrid.Id; LocalAdvantages = new PointSquareArray <LocalAdvantage>(pieceGrid.Size); AmazonMobilityScores = new PointSquareArray <double>(pieceGrid.Size); SpecificQueenDistances = new PointSquareArray <PointSquareArray <double> >(pieceGrid.Size); SpecificKingDistances = new PointSquareArray <PointSquareArray <double> >(pieceGrid.Size); SpecificQueenDistances.Clear(); SpecificKingDistances.Clear(); Player1QueenMinDistances = BuildDistancesDictionary(pieceGrid, Owner.Player1, true); Player1KingMinDistances = BuildDistancesDictionary(pieceGrid, Owner.Player1, false); Player2QueenMinDistances = BuildDistancesDictionary(pieceGrid, Owner.Player2, true); Player2KingMinDistances = BuildDistancesDictionary(pieceGrid, Owner.Player2, false); CalculateLocalAdvantages(pieceGrid, playerToMove); CalculateAllAmazonMobility(pieceGrid); W = LocalAdvantages.Where(a => a.Value != null && a.Value.Player1Reachable && a.Value.Player2Reachable) .Sum(a => Math.Pow(2, -(Math.Abs(a.Value.Player1QueenDistance - a.Value.Player2QueenDistance)))); C1 = 2 * LocalAdvantages.Where(a => a.Value != null).Sum(a => Math.Pow(2, -(a.Value.Player1QueenDistance)) - Math.Pow(2, -(a.Value.Player2QueenDistance))); C2 = LocalAdvantages.Where(a => a.Value != null).Sum(a => Math.Min(1, Math.Max(-1, (a.Value.Player2KingDistance - a.Value.Player1KingDistance) / 6d))); T1 = LocalAdvantages.Where(a => a.Value != null).Sum(a => a.Value.DeltaQueen); T2 = LocalAdvantages.Where(a => a.Value != null).Sum(a => a.Value.DeltaKing); T = (F1(W) * T1) + (F2(W) * C1) + (F3(W) * C2) + (F4(W) * T2); double player1MobilitySum = AmazonMobilityScores.Where(s => pieceGrid.Amazon1Points.Contains(s.Key)) .Sum(s => IndividualWeightForM(W, s.Value)); double player2MobilitySum = AmazonMobilityScores.Where(s => pieceGrid.Amazon2Points.Contains(s.Key)) .Sum(s => IndividualWeightForM(W, s.Value)); M = player2MobilitySum - player1MobilitySum; }