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)); } } }
public void ApplyReverseMove(Move move) { Owner owner = PreviousPlayer; if (PieceGrid.PointPieces[move.AmazonsPoint].Owner != owner) { throw new ArgumentException($"Reverse move given doesn't correspond to {owner} previous turn. Move: {move}"); } if (PieceGrid.PointPieces[move.AmazonsPoint].Name != PieceName.Amazon) { throw new ArgumentException($"Reverse move given isn't on an Amazon. You cannot move a {PieceGrid.PointPieces[move.Origin].Name}. Move: {move}"); } PieceGrid.ApplyReverseMove(move); if (owner == Owner.Player1) { Player1MoveCount--; } else { Player2MoveCount--; } _moves.Clear(); }
/// <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); }
public Board Clone() { Board newBoard = new Board(); newBoard.PieceGrid = PieceGrid.Clone(); newBoard.Player1MoveCount = Player1MoveCount; newBoard.Player2MoveCount = Player2MoveCount; return(newBoard); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { PieceGrid pieceGrid = (PieceGrid)value; writer.WriteStartObject(); writer.WritePropertyName("Size"); writer.WriteValue(pieceGrid.Size); writer.WritePropertyName("PointPieces"); serializer.Serialize(writer, pieceGrid.PointPieces); writer.WriteEndObject(); }
/// <summary> /// Calculate the mobility score for all amazons on the PieceGrid /// </summary> /// <param name="pieceGrid">PieceGrid to analyze</param> private void CalculateAllAmazonMobility(PieceGrid pieceGrid) { AmazonMobilityScores.Clear(); foreach (Point p in pieceGrid.Amazon1Points) { AmazonMobilityScores.Add(p, CalculateAmazonMobility(p, Owner.Player1, pieceGrid)); } foreach (Point p in pieceGrid.Amazon2Points) { AmazonMobilityScores.Add(p, CalculateAmazonMobility(p, Owner.Player2, pieceGrid)); } }
/// <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); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { Newtonsoft.Json.Linq.JObject o = Newtonsoft.Json.Linq.JObject.Load(reader); int size = o.Value <int>("Size"); PieceGrid newPieceGrid = new PieceGrid(size); var pieceGridJson = o.Value <JObject>("PointPieces"); Dictionary <Point, Piece> pointPiecesDictionary = new Dictionary <Point, Piece>(); foreach (KeyValuePair <string, JToken> kvp in pieceGridJson) { Point point = Point.Get(kvp.Key); Piece piece = Piece.Get(kvp.Value.Value <string>()); pointPiecesDictionary.Add(point, piece); } newPieceGrid.Initialize(pointPiecesDictionary); return(newPieceGrid); }
/// <summary> /// Build <see cref="LocalAdvantage"/> objects for each open space on the PieceGrid /// </summary> /// <param name="pieceGrid">PieceGrid to analyze</param> /// <param name="playerToMove">Player whos turn it is</param> private void CalculateLocalAdvantages(PieceGrid pieceGrid, Owner playerToMove) { LocalAdvantages.Clear(); foreach (var kvp in pieceGrid.PointPieces) { if (!(kvp.Value is Open)) { continue; } double player1QueenDistance = Player1QueenMinDistances[kvp.Key] ?? 1000d; double player2QueenDistance = Player2QueenMinDistances[kvp.Key] ?? 1000d; LocalAdvantage advantageValue = new LocalAdvantage { Player1QueenDistance = player1QueenDistance, Player2QueenDistance = player2QueenDistance, PlayerToMove = playerToMove, }; LocalAdvantages[kvp.Key] = advantageValue; } }
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> /// Calculate the mobility score for a specific amazon point on the PieceGrid. /// </summary> /// <remarks> /// Mobility is based on the breathing space of each space that can be moved to by an /// amazon at the given point. Closer (king distance) spaces are more valuable than /// distant ones. /// </remarks> /// <param name="p">Point containing an amazon</param> /// <param name="pieceGrid">PieceGrid to analyze</param> /// <returns>Numeric mobility score, higher numbers = more mobile</returns> private double CalculateAmazonMobility(Point p, Owner owner, PieceGrid pieceGrid) { IDictionary <Point, double?> minDistancesOppositePlayer; if (owner == Owner.Player1) { minDistancesOppositePlayer = Player2QueenMinDistances; } else if (owner == Owner.Player2) { minDistancesOppositePlayer = Player1QueenMinDistances; } else { throw new ArgumentException($"Point {p} doesn't have an amazon for either player!"); } double mobility = 0d; foreach (Point target in pieceGrid.GetOpenPointsOutFrom(p)) { int degree = target.GetAdjacentPoints() .Where(adj => pieceGrid.PointPieces.ContainsKey(adj) && !pieceGrid.PointPieces[adj].Impassible) .Count(); if (!minDistancesOppositePlayer.ContainsKey(target)) { continue; } if (owner == Owner.Player1 && !LocalAdvantages[target].Player2Reachable) { continue; } if (owner == Owner.Player2 && !LocalAdvantages[target].Player1Reachable) { continue; } double localMobility = Math.Pow(2, -(SpecificKingDistances[p][target])) * degree; mobility += localMobility; } return(mobility); }
/// <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; }
public IEnumerable <Move> GetAvailableMoves(Owner owner = Owner.None, bool reverse = false) { bool cached = false; if (owner == Owner.None) { cached = _moves.ContainsKey(Owner.Player1) && _moves.ContainsKey(Owner.Player2); } else { cached = _moves.ContainsKey(owner); } if (!cached) { IEnumerable <Point> sourceSet; if (owner == Owner.Player1) { List <Move> results = new List <Move>(); foreach (Point p in PieceGrid.Amazon1Points) { if (reverse) { results.AddRange(PieceGrid.GetReverseMovesFromPoint(p, owner)); } else { results.AddRange(PieceGrid.GetMovesFromPoint(p)); } } _moves[owner] = results; } else if (owner == Owner.Player2) { List <Move> results = new List <Move>(); foreach (Point p in PieceGrid.Amazon2Points) { if (reverse) { results.AddRange(PieceGrid.GetReverseMovesFromPoint(p, owner)); } else { results.AddRange(PieceGrid.GetMovesFromPoint(p)); } } _moves[owner] = results; } else { List <Move> results1 = new List <Move>(); List <Move> results2 = new List <Move>(); sourceSet = PieceGrid.Amazon1Points.Union(PieceGrid.Amazon2Points); foreach (Point p in PieceGrid.Amazon1Points) { if (reverse) { results1.AddRange(PieceGrid.GetReverseMovesFromPoint(p, Owner.Player1)); } else { results1.AddRange(PieceGrid.GetMovesFromPoint(p)); } } _moves[Owner.Player1] = results1; foreach (Point p in PieceGrid.Amazon2Points) { if (reverse) { results2.AddRange(PieceGrid.GetReverseMovesFromPoint(p, Owner.Player2)); } else { results2.AddRange(PieceGrid.GetMovesFromPoint(p)); } } _moves[Owner.Player2] = results2; } } if (owner == Owner.None) { return(_moves[Owner.Player1].Union(_moves[Owner.Player2])); } else { return(_moves[owner]); } }
public Board(PieceGrid grid) { PieceGrid = grid; Player1MoveCount = grid.PointPieces.Count(kvp => kvp.Value is ArrowPlayer1); Player2MoveCount = grid.PointPieces.Count(kvp => kvp.Value is ArrowPlayer2); }
public Board(int size) { PieceGrid = new PieceGrid(size, PieceHelpers.GetInitialAmazonPositions(size)); }