/// <summary> /// Finds enemy cell(s) which are closest to specified cell, or to any player cell if targetCell is null. /// if targetCell is not specified, all player-owned cells are used as targets. /// </summary> /// <returns></returns> private IEnumerable <IKlopCell> FindNearestEnemyCells(IKlopCell targetCell = null) { if (targetCell == null) { var availableCellsWithDistances = Model.Cells.Where(c => c.Available).Select(c => new { c, d = _distanceMap[c.X, c.Y] }).ToArray(); var minDistance = availableCellsWithDistances.Select(c => c.d).Min(); // Go over all available cells with the same minimum distance foreach (var c in availableCellsWithDistances.Where(c => c.d == minDistance).SelectMany(c => FindNearestEnemyCells(c.c))) { yield return(c); } yield break; } var cell = targetCell; var cellDistance = _distanceMap[cell.X, cell.Y]; if (cellDistance == 0) { // End of recursion, we have found enemy cell. yield return(cell); } else { foreach (var c in Model.GetNeighborCells(cell).Where(c => _distanceMap[c.X, c.Y] < cellDistance).SelectMany(FindNearestEnemyCells)) { yield return(c); } } }
private IKlopCell FindEnemyCellToAttack(IKlopCell targetCell = null) { var nearestEnemyCells = FindNearestEnemyCells(targetCell); // There could be several enemy cells with equal distances. Find the one closer to enemy base! var pathLengths = nearestEnemyCells.Select(c => new { c, pathLength = _pathFinder.FindPath(c.X, c.Y, c.Owner.BasePosX, c.Owner.BasePosY, this).Count }); return(pathLengths.Highest((c1, c2) => c1.pathLength < c2.pathLength).c); }
private void OnKlopCellChanged(DependencyPropertyChangedEventArgs e) { var oldCell = e.OldValue as IKlopCell; if (oldCell != null) { oldCell.PropertyChanged -= Cell_PropertyChanged; } _cell = Cell; // Cache value for faster access _cell.PropertyChanged += Cell_PropertyChanged; UpdateBrushes(); }
/// <summary> /// Gets the cell cost. /// </summary> /// <param name="cell">The cell.</param> /// <param name="klopPlayer">The klop player.</param> /// <returns></returns> private double GetCellCost(IKlopCell cell, IKlopPlayer klopPlayer) { if (cell.Owner == klopPlayer) { return 0; // Zero cost for owned cell } if (cell.State == ECellState.Dead) { return TurnBlockedCost; // Can't move into own dead cell or base cell } //TODO: Additive cost! E.g. near own clop + near enemy clop!! if (cell.Owner != null && cell.State == ECellState.Alive) { if (IsCellNearBase(cell, klopPlayer)) { return TurnEatOwnbaseCost; } if (_klopModel.Players.Where(p => p != klopPlayer).Any(enemy => IsCellNearBase(cell, enemy))) { return TurnEatEnemyBaseCost; } return TurnEatCost; } if (IsCellNearBase(cell, klopPlayer)) { return TurnNearOwnBaseCost; } var neighbors = _klopModel.GetNeighborCells(cell).ToArray(); if (neighbors.Any(c => c.State == ECellState.Base)) { return TurnNearEnemyBaseCost; } var enemyCount = neighbors.Count(c => c.Owner != null && c.Owner != klopPlayer); if (enemyCount > 0) { //return TurnNearEnemyEmptyCost*(1 + (double) enemyCount/2); // Turn near enemy klop costs a bit more. return TurnEmptyCost + TurnNearEnemyEmptyCostAddition*enemyCount; } var neighborCount = neighbors.Count(c => c.Owner != null); return TurnEmptyCost*(1 + (double) neighborCount/2); // Default - turn into empty cell. }
/// <summary> /// Gets the neighbor cells. /// </summary> /// <param name="cell">The cell.</param> /// <param name="model">The model.</param> /// <returns></returns> private static IEnumerable <IKlopCell> GetNeighborCells(IKlopCell cell, IKlopModel model) { for (int x = -1; x < 1; x++) { for (int y = -1; y < 1; y++) { var xx = cell.X + x; var yy = cell.Y + y; if ((x == y && x == 0) || xx < 0 || yy < 0 || xx >= model.FieldWidth || yy > model.FieldHeight) { continue; } yield return(model[xx, yy]); } } }
/// <summary> /// Gets the neighbor cells. /// </summary> /// <param name="cell">The cell.</param> /// <returns></returns> public IEnumerable <IKlopCell> GetNeighborCells(IKlopCell cell) { // This method is called very often. Here is fastest implementation for now: var cx = cell.X; var cy = cell.Y; var xNotMin = cx != 0; var xNotMax = cx < _fieldWidth - 1; if (cy != 0) { if (xNotMin) { yield return(_cells[cx - 1, cy - 1]); } yield return(_cells[cx, cy - 1]); if (xNotMax) { yield return(_cells[cx + 1, cy - 1]); } } if (xNotMin) { yield return(_cells[cx - 1, cy]); } if (xNotMax) { yield return(_cells[cx + 1, cy]); } if (cy != _fieldHeight - 1) { if (xNotMin) { yield return(_cells[cx - 1, cy + 1]); } yield return(_cells[cx, cy + 1]); if (xNotMax) { yield return(_cells[cx + 1, cy + 1]); } } }
public void HighlightPath(IKlopCell cell) { if (!_model.CurrentPlayer.Human && _highlightedCells.Count == 0) { return; } _highlightedCells.Clear(); if (_model.CurrentPlayer.Human) { var path = PathFinder.FindPath(_model.CurrentPlayer.BasePosX, _model.CurrentPlayer.BasePosY, cell.X, cell.Y, _model.CurrentPlayer); var i = path.Count; foreach (var klopCell in path) { _highlightedCells[klopCell] = i--; } } InvokeHighlightChanged(); }
private void MakeTurn(IKlopCell cell) { if (cell.Available) { Model.MakeTurn(cell); } else if (PathHighlighter.IsHighlighted(cell)) { // Cell is highlighted - perform multiple turns: while (Model.RemainingKlops > 1) //TODO: Configurable whether leave one clop or not { var currentCell = Model.Cells.FirstOrDefault(c => c.Available && PathHighlighter.IsHighlighted(c)); if (currentCell == null) { break; } Model.MakeTurn(currentCell); if (currentCell == cell) { break; // Destination reached } } } }
/// <summary> /// Determines whether the specified cell is highlighted. /// </summary> /// <param name="cell">The cell.</param> /// <returns> /// <c>true</c> if the specified cell is highlighted; otherwise, <c>false</c>. /// </returns> public bool IsHighlighted(IKlopCell cell) { return(cell != null && _highlightedCells.ContainsKey(cell)); }
/// <summary> /// Determines whether the specified cell is highlighted. When highlighted, returns positive number indicating path length. /// If not highlighted, returns -1. /// </summary> /// <param name="cell">The cell.</param> public int GetPathLength(IKlopCell cell) { return(cell != null && _highlightedCells.ContainsKey(cell) ? _highlightedCells[cell] : -1); }
private static bool IsCellNearBase(IKlopCell cell, IKlopPlayer baseOwner) { return Math.Max(Math.Abs(cell.X - baseOwner.BasePosX), Math.Abs(cell.Y - baseOwner.BasePosY)) == 1; }
/// <summary> /// Makes the turn to the specified cell. /// </summary> /// <param name="cell">The cell.</param> public void MakeTurn(IKlopCell cell) { MakeTurn(cell.X, cell.Y); }
/// <summary> /// Gets the distance to the closest enemy cell. /// </summary> private double GetEnemyDistance(IKlopCell cell) { return(_distanceMap[cell.X, cell.Y]); }