From e02a4ca5da107752ccbe61d0ff4e58197a303868 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sat, 20 May 2023 17:12:48 +1000 Subject: [PATCH] Tidy up shot selection --- 77_Salvo/csharp/Game.cs | 18 ++++++------ 77_Salvo/csharp/Grid.cs | 14 +--------- .../csharp/Targetting/ComputerShotSelector.cs | 18 ++++++------ .../csharp/Targetting/HumanShotSelector.cs | 8 +++--- .../KnownHitsShotSelectionStrategy.cs | 10 +++---- .../Targetting/SearchPatternShotSelector.cs | 22 +++++++-------- .../Targetting/ShotSelectionStrategy.cs | 13 +++++---- 77_Salvo/csharp/Targetting/ShotSelector.cs | 28 ++++++++++++++----- 8 files changed, 67 insertions(+), 64 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 95ccef23..6c0add12 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -22,8 +22,8 @@ internal class Game var computerGrid = new Grid(_random); var humanGrid = new Grid(_io); - var humanShotSelector = new HumanShotSelector(humanGrid, computerGrid, _io); - var computerShotSelector = new ComputerShotSelector(computerGrid, humanGrid, _random); + var humanShotSelector = new HumanShotSelector(humanGrid, _io); + var computerShotSelector = new ComputerShotSelector(computerGrid, _random); var startResponse = _io.ReadString(Prompts.Start); while (startResponse == Strings.WhereAreYourShips) { @@ -43,14 +43,14 @@ L1980: _io.Write(Strings.Turn(turnNumber)); L1990: var numberOfShots = humanShotSelector.NumberOfShots; L2220: _io.Write(Strings.YouHaveShots(numberOfShots)); if (numberOfShots == 0) { goto L2270; } -L2230: if (numberOfShots > computerGrid.UntriedSquareCount) +L2230: if (humanShotSelector.CanTargetAllRemainingSquares) { _io.WriteLine(Streams.YouHaveMoreShotsThanSquares); L2250: goto L2890; } - foreach (var shot1 in humanShotSelector.GetShots()) + foreach (var shot1 in humanShotSelector.GetShots(turnNumber)) { - if (computerGrid.IsHit(shot1, turnNumber, out var ship)) + if (computerGrid.IsHit(shot1, out var ship)) { _io.Write(Strings.YouHit(ship.Name)); } @@ -60,15 +60,15 @@ L2640: turnNumber++; L2660: _io.Write(Strings.Turn(turnNumber)); L2670: numberOfShots = computerShotSelector.NumberOfShots; L2840: _io.Write(Strings.IHaveShots(numberOfShots)); -L2850: if (humanGrid.UntriedSquareCount > numberOfShots) { goto L2880; } +L2850: if (!computerShotSelector.CanTargetAllRemainingSquares) { goto L2880; } L2860: _io.Write(Streams.IHaveMoreShotsThanSquares); L2270: _io.Write(Streams.IWon); return; -L2880: if (numberOfShots == 0) { goto L2960; } +L2880: if (numberOfShots > 0) { goto L2960; } L2890: _io.Write(Streams.YouWon); L2900: return; -L2960: temp = computerShotSelector.GetShots().ToArray(); +L2960: temp = computerShotSelector.GetShots(turnNumber).ToArray(); // display shots L3380: if (seeShotsResponse == "YES") { @@ -79,7 +79,7 @@ L3380: if (seeShotsResponse == "YES") } foreach (var shot in temp) { - if (humanGrid.IsHit(shot, turnNumber, out var ship)) + if (humanGrid.IsHit(shot, out var ship)) { _io.Write(Strings.IHit(ship.Name)); computerShotSelector.RecordHit(ship, turnNumber); diff --git a/77_Salvo/csharp/Grid.cs b/77_Salvo/csharp/Grid.cs index 0c84d688..f91ae43a 100644 --- a/77_Salvo/csharp/Grid.cs +++ b/77_Salvo/csharp/Grid.cs @@ -6,12 +6,6 @@ namespace Salvo; internal class Grid { private readonly List _ships; - private readonly Dictionary _shots = new(); - - internal Grid() - { - _ships = new(); - } internal Grid(IReadWrite io) { @@ -57,16 +51,10 @@ internal class Grid } } - internal int UntriedSquareCount => 100 - _shots.Count; internal IEnumerable Ships => _ships.AsEnumerable(); - internal bool WasTargetedAt(Position position, out int turnTargeted) - => _shots.TryGetValue(position, out turnTargeted); - - internal bool IsHit(Position position, int turnNumber, [NotNullWhen(true)] out Ship? ship) + internal bool IsHit(Position position, [NotNullWhen(true)] out Ship? ship) { - _shots[position] = turnNumber; - ship = _ships.FirstOrDefault(s => s.IsHit(position)); if (ship == null) { return false; } diff --git a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs index 41b27a6d..5a5214dd 100644 --- a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs +++ b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs @@ -3,19 +3,19 @@ namespace Salvo.Targetting; internal class ComputerShotSelector : ShotSelector { private readonly KnownHitsShotSelectionStrategy _knownHitsStrategy; - private readonly SearchPatternShotSelector _searchPatternShotSelector; + private readonly SearchPatternShotSelectionStrategy _searchPatternStrategy; - internal ComputerShotSelector(Grid source, Grid target, IRandom random) - : base(source, target) + internal ComputerShotSelector(Grid source, IRandom random) + : base(source) { - _knownHitsStrategy = new KnownHitsShotSelectionStrategy(target); - _searchPatternShotSelector = new SearchPatternShotSelector(source, target, random); + _knownHitsStrategy = new KnownHitsShotSelectionStrategy(this); + _searchPatternStrategy = new SearchPatternShotSelectionStrategy(this, random); } - internal override IEnumerable GetShots() - { - return _knownHitsStrategy.GetShots(NumberOfShots) ?? _searchPatternShotSelector.GetShots(); - } + protected override IEnumerable GetShots() => GetSelectionStrategy().GetShots(NumberOfShots); internal void RecordHit(Ship ship, int turn) => _knownHitsStrategy.RecordHit(ship, turn); + + private ShotSelectionStrategy GetSelectionStrategy() + => _knownHitsStrategy.KnowsOfDamagedShips ? _knownHitsStrategy : _searchPatternStrategy; } diff --git a/77_Salvo/csharp/Targetting/HumanShotSelector.cs b/77_Salvo/csharp/Targetting/HumanShotSelector.cs index b4a0db13..f3e5fba2 100644 --- a/77_Salvo/csharp/Targetting/HumanShotSelector.cs +++ b/77_Salvo/csharp/Targetting/HumanShotSelector.cs @@ -4,13 +4,13 @@ internal class HumanShotSelector : ShotSelector { private readonly IReadWrite _io; - internal HumanShotSelector(Grid source, Grid target, IReadWrite io) - : base(source, target) + internal HumanShotSelector(Grid source, IReadWrite io) + : base(source) { _io = io; } - internal override IEnumerable GetShots() + protected override IEnumerable GetShots() { var shots = new Position[NumberOfShots]; @@ -19,7 +19,7 @@ internal class HumanShotSelector : ShotSelector while (true) { var position = _io.ReadValidPosition(); - if (Target.WasTargetedAt(position, out var turnTargeted)) + if (WasSelectedPreviously(position, out var turnTargeted)) { _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {turnTargeted}"); continue; diff --git a/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs b/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs index 917b232e..5e0b9d2c 100644 --- a/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs +++ b/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs @@ -4,15 +4,15 @@ internal class KnownHitsShotSelectionStrategy : ShotSelectionStrategy { private readonly List<(int Turn, Ship Ship)> _damagedShips = new(); - internal KnownHitsShotSelectionStrategy(Grid target) - : base(target) + internal KnownHitsShotSelectionStrategy(ShotSelector shotSelector) + : base(shotSelector) { } - internal IEnumerable? GetShots(int numberOfShots) - { - if (!_damagedShips.Any()) { return null; } + internal bool KnowsOfDamagedShips => _damagedShips.Any(); + internal override IEnumerable GetShots(int numberOfShots) + { var tempGrid = Position.All.ToDictionary(x => x, _ => 0); var shots = Enumerable.Range(1, numberOfShots).Select(x => new Position(x, x)).ToArray(); diff --git a/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs b/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs index 329a0a98..1eae88f8 100644 --- a/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs +++ b/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs @@ -1,30 +1,30 @@ namespace Salvo.Targetting; -internal class SearchPatternShotSelector : ShotSelector +internal class SearchPatternShotSelectionStrategy : ShotSelectionStrategy { private const int MaxSearchPatternAttempts = 100; private readonly IRandom _random; private readonly SearchPattern _searchPattern = new(); private readonly List _shots = new(); - internal SearchPatternShotSelector(Grid source, Grid target, IRandom random) - : base(source, target) + internal SearchPatternShotSelectionStrategy(ShotSelector shotSelector, IRandom random) + : base(shotSelector) { _random = random; } - internal override IEnumerable GetShots() + internal override IEnumerable GetShots(int numberOfShots) { _shots.Clear(); - while(_shots.Count < NumberOfShots) + while(_shots.Count < numberOfShots) { var (seed, _) = _random.NextShipPosition(); - SearchFrom(seed); + SearchFrom(numberOfShots, seed); } return _shots; } - private void SearchFrom(Position candidateShot) + private void SearchFrom(int numberOfShots, Position candidateShot) { var attemptsLeft = MaxSearchPatternAttempts; while (true) @@ -32,18 +32,18 @@ internal class SearchPatternShotSelector : ShotSelector _searchPattern.Reset(); if (attemptsLeft-- == 0) { return; } candidateShot = candidateShot.BringIntoRange(_random); - if (FindValidShots(ref candidateShot)) { return; } + if (FindValidShots(numberOfShots, ref candidateShot)) { return; } } } - private bool FindValidShots(ref Position candidateShot) + private bool FindValidShots(int numberOfShots, ref Position candidateShot) { while (true) { if (IsValidShot(candidateShot)) { _shots.Add(candidateShot); - if (_shots.Count == NumberOfShots) { return true; } + if (_shots.Count == numberOfShots) { return true; } } if (!_searchPattern.TryGetOffset(out var offset)) { return false; } candidateShot += offset; @@ -51,5 +51,5 @@ internal class SearchPatternShotSelector : ShotSelector } private bool IsValidShot(Position candidate) - => candidate.IsInRange && !Target.WasTargetedAt(candidate, out _) && !_shots.Contains(candidate); + => candidate.IsInRange && !WasSelectedPreviously(candidate) && !_shots.Contains(candidate); } \ No newline at end of file diff --git a/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs b/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs index f98c7b4f..b4cf14b1 100644 --- a/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs +++ b/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs @@ -2,15 +2,16 @@ namespace Salvo.Targetting; internal abstract class ShotSelectionStrategy { - private readonly Grid _target; - protected ShotSelectionStrategy(Grid target) + private readonly ShotSelector _shotSelector; + protected ShotSelectionStrategy(ShotSelector shotSelector) { - _target = target; + _shotSelector = shotSelector; } - protected bool WasSelectedPreviously(Position position) - => _target.WasTargetedAt(position, out _); + internal abstract IEnumerable GetShots(int numberOfShots); + + protected bool WasSelectedPreviously(Position position) => _shotSelector.WasSelectedPreviously(position); protected bool WasSelectedPreviously(Position position, out int turn) - => _target.WasTargetedAt(position, out turn); + => _shotSelector.WasSelectedPreviously(position, out turn); } diff --git a/77_Salvo/csharp/Targetting/ShotSelector.cs b/77_Salvo/csharp/Targetting/ShotSelector.cs index 5aaa4753..95a5c4bd 100644 --- a/77_Salvo/csharp/Targetting/ShotSelector.cs +++ b/77_Salvo/csharp/Targetting/ShotSelector.cs @@ -2,16 +2,30 @@ namespace Salvo.Targetting; internal abstract class ShotSelector { - internal ShotSelector(Grid source, Grid target) + private readonly Grid _source; + private readonly Dictionary _previousShots = new(); + + internal ShotSelector(Grid source) { - Source = source; - Target = target; + _source = source; } - protected Grid Source { get; } - protected Grid Target { get; } + internal int NumberOfShots => _source.Ships.Sum(s => s.Shots); + internal bool CanTargetAllRemainingSquares => NumberOfShots >= 100 - _previousShots.Count; - internal int NumberOfShots => Source.Ships.Sum(s => s.Shots); + internal bool WasSelectedPreviously(Position position) => _previousShots.ContainsKey(position); - internal abstract IEnumerable GetShots(); + internal bool WasSelectedPreviously(Position position, out int turn) + => _previousShots.TryGetValue(position, out turn); + + internal IEnumerable GetShots(int turnNumber) + { + foreach (var shot in GetShots()) + { + _previousShots.Add(shot, turnNumber); + yield return shot; + } + } + + protected abstract IEnumerable GetShots(); }