public static Ship Parse(string s) { string[] parts = s.Split(delimiters); if (parts.Length != 3) throw new ArgumentException("s"); Position p1 = Position.Parse(parts[1]); Position p2 = Position.Parse(parts[2]); if (p2 < p1) { Position swap = p1; p1 = p2; p2 = swap; } Ship ship = new Ship() { Position = p1, Size = p1 - p2 + 1, Orientation = (p1.Row == p2.Row) ? Orientation.Horizontal : Orientation.Vertical, Code = parts[0] }; return ship; }
/// <summary> /// Whether or not a ship intersects or is next to another ship /// </summary> /// <param name="ship"></param> /// <returns></returns> public bool IntersectsOrAdjacent(Ship ship) { foreach (Position p in ship.Positions) { if (AtOrAdjacent(p)) return true; } return false; }
/// <summary> /// Adjust position possibilies based on the fact that a ship was sunk /// </summary> /// <param name="size">size of ship</param> void Sink(Ship ship) { // Remove this ship type from ship position consideration for (var i = 0; i < AdaptivePlayer.Width; ++i) { for (var j = 0; j < AdaptivePlayer.Width; ++j) { if (_score[i, j].Score > 0) _score[i, j].Sink(ship.Size); } } _sunkSpots += ship.Size; // We sunk a ship, go back to search mode if we don't have any pending hit boats if (_sunkSpots == _hits.Count) { _sunkSpots = 0; List<Position> adjacent = new List<Position>(); // We can now adjust scores for these hits foreach (Position p in _hits) { AdjustScore(p); if (!_assumeAdjacent) { AdjustNotAdjacentScore(p); adjacent.AddRange(AdjacentPositions(p)); } } if (!_assumeAdjacent) { adjacent = adjacent.Except(_hits).Distinct().ToList(); foreach (Position p in adjacent) { AdjustNotAdjacentScore(p); } } _hits.Clear(); _targettingMode = TargettingMode.Search; } else { // We found two ships that are adjacent _assumeAdjacent = true; // Let's see if we can remove some hits from further consideration List<Ship> possibleLayouts = new List<Ship>(); if ((_destroySearchOrientation & Orientation.Horizontal) == Orientation.Horizontal) { Ship layout = ship.Clone() as Ship; layout.Orientation = Orientation.Horizontal; // Let's see if we can find the end points of the ship var possibilities = _hits.Where(p => p.Row == _lastShot.Row && Math.Abs(p.Column - _lastShot.Column) < ship.Size); if (possibilities.Count() == ship.Size) { layout.Position = possibilities.Min(); if (!layout.Positions.Except(_hits).Any()) { // If all positions in the layout are contained in hits possibleLayouts.Add(layout); } } } if ((_destroySearchOrientation & Orientation.Vertical) == Orientation.Vertical) { Ship layout = ship.Clone() as Ship; layout.Orientation = Orientation.Vertical; // Let's see if we can find the end points of the ship var possibilities = _hits.Where(p => p.Column == _lastShot.Column && Math.Abs(p.Row - _lastShot.Row) < ship.Size); if (possibilities.Count() == ship.Size) { layout.Position = possibilities.Min(); if (!layout.Positions.Except(_hits).Any()) { // If all positions in the layout are contained in hits possibleLayouts.Add(layout); } } } if (possibleLayouts.Count == 1) { // Remove these positions from consideration _hits = _hits.Except(possibleLayouts[0].Positions).ToList(); _sunkSpots -= ship.Size; foreach (Position p in possibleLayouts[0].Positions) AdjustScore(p); } } // Reset our destroy orientation pattern _destroySearchOrientation = Orientation.Horizontal | Orientation.Vertical; }
/// <summary> /// Returns true if a ship is adjacent to this ship /// </summary> /// <param name="ship"></param> /// <returns></returns> public bool Adjacent(Ship ship) { foreach (Position p in ship.Positions) { if (Adjacent(p)) return true; } return false; }
/// <summary> /// Place ships on board /// </summary> /// <returns>List of ship placements</returns> public List<Ship> PlaceShips() { // We want to be both random and smart // Generate a series of positions, score them based on position probability and opponent shooting data List<Ship> placedShips = new List<Ship>(AdaptivePlayer.Ships.Count); List<Ship> bestShipPlacement = new List<Ship>(AdaptivePlayer.Ships.Count); double minimumScore = double.MaxValue; bool allowTouching = true; // Randomly decide to disallow touching ships if (BattleshipGame.Random.NextDouble() < 0.6d) { allowTouching = false; } // Randomly choose 1000 different layouts, choose the layout with the lowest score for (int sample = 0; sample < _samples; ++sample) { double currentScore = 0.0d; placedShips.Clear(); foreach (Ship ship in AdaptivePlayer.Ships) { bool intersects = false; Ship placedShip = new Ship() { Size = ship.Size, Code = ship.Code }; // Keep generating positions until we have a set that has no intersecting ships do { intersects = false; placedShip.Place(AdaptivePlayer.Width, AdaptivePlayer.Height); foreach (Ship checkShip in placedShips) { if (allowTouching) { if (placedShip.Intersects(checkShip)) { intersects = true; break; } } else { if (placedShip.IntersectsOrAdjacent(checkShip)) { intersects = true; break; } } } } while (intersects); // Score the positions of each ship foreach(Position p in placedShip.Positions) { // Default to probability distribution, otherwise use previous shot history currentScore += (double)AdaptivePlayer.Data.IncomingShots[p.Row][p.Column] / (double)_totalPositions; } placedShips.Add(placedShip); } if (currentScore < minimumScore) { minimumScore = currentScore; bestShipPlacement.Clear(); foreach (Ship ship in placedShips) bestShipPlacement.Add(ship); } } // We want to avoid using this placement again, add some points to the current location int totalShipSize = bestShipPlacement.Sum(x => x.Size); foreach (Ship ship in bestShipPlacement) { foreach (Position p in ship.Positions) { AdaptivePlayer.Data.IncomingShots[p.Row][p.Column] += (int)((double)ship.Size / (double)totalShipSize * 1000.0d); } } return bestShipPlacement; }