From aa6a06cdc8400789d549b09e67fdb700df1309de Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sun, 23 Apr 2023 17:55:48 +1000 Subject: [PATCH] Add SearchPatternShotSelector --- 77_Salvo/csharp/Game.cs | 77 +++++-------------- 77_Salvo/csharp/Resources/Resource.cs | 1 + .../csharp/Targetting/ComputerShotSelector.cs | 2 +- .../csharp/Targetting/HumanShotSelector.cs | 13 ++-- 77_Salvo/csharp/Targetting/SearchPattern.cs | 22 ++++++ .../Targetting/SearchPatternShotSelector.cs | 54 +++++++++++++ 77_Salvo/csharp/Targetting/ShotSelector.cs | 4 +- 7 files changed, 107 insertions(+), 66 deletions(-) create mode 100644 77_Salvo/csharp/Targetting/SearchPattern.cs create mode 100644 77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 2f3ebe8e..616f0cbc 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -28,7 +28,8 @@ internal class Game } var computerGrid = new Grid(_random); var humanGrid = new Grid(_io); - var humanShotSelector = new HumanShotSelector(humanGrid, computerGrid); + var humanShotSelector = new HumanShotSelector(humanGrid, computerGrid, _io); + var computerShotSelector = new SearchPatternShotSelector(computerGrid, humanGrid, _random); var startResponse = _io.ReadString(Prompts.Start); while (startResponse == Strings.WhereAreYourShips) { @@ -45,7 +46,7 @@ L1930: if (startResponse != "YES") { goto L2620; } L1950: if (startResponse != "YES") { goto L1990; } L1960: turnNumber++; L1980: _io.Write(Strings.Turn(turnNumber)); -L1990: var numberOfShots = humanShotSelector.GetShotCount(); +L1990: var numberOfShots = humanShotSelector.NumberOfShots; L2220: _io.Write(Strings.YouHaveShots(numberOfShots)); if (numberOfShots == 0) { goto L2270; } L2230: if (numberOfShots > computerGrid.UntriedSquareCount) @@ -53,7 +54,7 @@ L2230: if (numberOfShots > computerGrid.UntriedSquareCount) _io.WriteLine(Streams.YouHaveMoreShotsThanSquares); L2250: goto L2890; } - foreach (var shot1 in humanShotSelector.GetShots(_io)) + foreach (var shot1 in humanShotSelector.GetShots()) { if (computerGrid.IsHit(shot1, turnNumber, out var shipName)) { @@ -63,7 +64,7 @@ L2250: goto L2890; L2620: if (startResponse == "YES") { goto L2670; } L2640: turnNumber++; L2660: _io.Write(Strings.Turn(turnNumber)); -L2670: numberOfShots = computerGrid.Ships.Sum(s => s.Shots); +L2670: numberOfShots = computerShotSelector.NumberOfShots; L2840: _io.Write(Strings.IHaveShots(numberOfShots)); L2850: if (humanGrid.UntriedSquareCount > numberOfShots) { goto L2880; } L2860: _io.Write(Streams.IHaveMoreShotsThanSquares); @@ -78,70 +79,28 @@ L2960: for (var i = 1; i <= 12; i++) // if damaged ships L2970: if (hitShipValue[i]>0) { goto L3800; } } -L3000: var shotCount=0; -L3010: var shotAttempts=0; -L3020: var (shot, _) = _random.NextShipPosition(); -L3030: var strategyNumber=0; //RESTORE -L3050: shotAttempts++; -L3060: if (shotAttempts>100) { goto L3010; } - // ensure shot is in range -L3070: shot = shot.BringIntoRange(_random); -L3170: goto L3270; - // record shot -L3180: temp[shotCount]=shot; -L3200: if (shotCount==numberOfShots) { goto L3380; } -L3210: if (strategyNumber==6) { goto L3030; } -L3240: //DATA 1,1,-1,1,1,-3,1,1,0,2,-1,1 - var data = new Offset[] { new(1,1),new(-1,1),new(1,-3),new(1,1),new(0,2),new(-1,1) }; -L3220: //READ X1,Y1 - var offset = data[strategyNumber++]; -L3250: shot+=offset; - // is the shot in range? -L3270: if (!shot.IsInRange) { goto L3210; } - // have we fired here before -L3310: if (humanGrid[shot]>10) { goto L3210; } - // have we already selected this shot? -L3320: for (var i = 1; i <= shotCount; i++) - { -L3330: if (temp[i] == shot) { goto L3210; } - } -L3360: shotCount++; -L3370: goto L3180; + temp = computerShotSelector.GetShots().ToArray(); // display shots -L3380: if (seeShotsResponse != "YES") { goto L3420; } -L3390: for (var i = 1; i <= numberOfShots; i++) +L3380: if (seeShotsResponse == "yes") { -L3400: _io.WriteLine(temp[i]); - } -L3420: for (var i = 1; i <= numberOfShots; i++) - { -L3430: if (humanGrid[temp[i]] == 3) - { - _io.WriteLine("I HIT YOUR BATTLESHIP"); - } - else if (humanGrid[temp[i]] == 2) - { - _io.WriteLine("I HIT YOUR CRUISER"); - } - else if (humanGrid[temp[i]] == 1) - { - _io.WriteLine("I HIT YOUR DESTROYER"); - } - else if (humanGrid[temp[i]] == .5F) - { - _io.WriteLine("I HIT YOUR DESTROYER"); - } - else + foreach (var shot in temp) { - humanGrid[temp[i]]=10+turnNumber; + _io.WriteLine(shot); + } + } + foreach (var shot in temp) + { + if (!humanGrid.IsHit(shot, turnNumber, out var shipName)) + { continue; } + _io.WriteLine(Strings.IHit(shipName)); L3570: for (var j = 1; j <= 12; j++) { // record hit L3580: if (hitTurnRecord[j] != -1) { continue; } L3590: hitTurnRecord[j]=10+turnNumber; -L3600: hitShipValue[j]=humanGrid[temp[i]]; +L3600: hitShipValue[j]=humanGrid[shot]; // look for past hits on same ship L3610: var shipHits=0; L3620: for (var k = 1; k <= 12; k++) @@ -169,7 +128,7 @@ L3760: _io.WriteLine($"{nameof(hitTurnRecord)}( {j} ) = {hitTurnRecord[ L3770: _io.WriteLine($"{nameof(hitShipValue)}( {j} ) = {hitShipValue[j]}"); } return; -L3470: humanGrid[temp[i]]=10+turnNumber; +L3470: humanGrid[shot]=10+turnNumber; } L3490: goto L1950; L3800: //REM************************USINGEARRAY diff --git a/77_Salvo/csharp/Resources/Resource.cs b/77_Salvo/csharp/Resources/Resource.cs index c3a0d8ca..8be52fdb 100644 --- a/77_Salvo/csharp/Resources/Resource.cs +++ b/77_Salvo/csharp/Resources/Resource.cs @@ -21,6 +21,7 @@ internal static class Resource public static string YouHaveShots(int number) => Format(number); public static string IHaveShots(int number) => Format(number); public static string YouHit(string shipName) => Format(shipName); + public static string IHit(string shipName) => Format(shipName); public static string ShotBefore(int turnNumber) => Format(turnNumber); public static string Turn(int number) => Format(number); } diff --git a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs index 1cf2a98a..c8f57d20 100644 --- a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs +++ b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs @@ -1,6 +1,6 @@ namespace Salvo.Targetting; -internal abstract class ComputerShotSelector : ShotSelector +internal class ComputerShotSelector : ShotSelector { private readonly bool _displayShots; diff --git a/77_Salvo/csharp/Targetting/HumanShotSelector.cs b/77_Salvo/csharp/Targetting/HumanShotSelector.cs index 6a50330a..40812926 100644 --- a/77_Salvo/csharp/Targetting/HumanShotSelector.cs +++ b/77_Salvo/csharp/Targetting/HumanShotSelector.cs @@ -2,23 +2,26 @@ namespace Salvo.Targetting; internal class HumanShotSelector : ShotSelector { - public HumanShotSelector(Grid source, Grid target) + private readonly IReadWrite _io; + + internal HumanShotSelector(Grid source, Grid target, IReadWrite io) : base(source, target) { + _io = io; } - public IEnumerable GetShots(IReadWrite io) + internal override IEnumerable GetShots() { - var shots = new Position[GetShotCount()]; + var shots = new Position[NumberOfShots()]; for (var i = 0; i < shots.Length; i++) { while (true) { - var position = io.ReadValidPosition(); + var position = _io.ReadValidPosition(); if (Target.WasTargetedAt(position, out var turnTargeted)) { - io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {turnTargeted}"); + _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {turnTargeted}"); continue; } shots[i] = position; diff --git a/77_Salvo/csharp/Targetting/SearchPattern.cs b/77_Salvo/csharp/Targetting/SearchPattern.cs new file mode 100644 index 00000000..ac2071c8 --- /dev/null +++ b/77_Salvo/csharp/Targetting/SearchPattern.cs @@ -0,0 +1,22 @@ +using System.Collections.Immutable; + +namespace Salvo.Targetting; + +internal class SearchPattern +{ + private static readonly ImmutableArray _offsets = + ImmutableArray.Create(new(1, 1), new(-1, 1), new(1, -3), new(1, 1), new(0, 2), new(-1, 1)); + + private int _nextIndex; + + internal bool TryGetOffset(out Offset offset) + { + offset = default; + if (_nextIndex >= _offsets.Length) { return false; } + + offset = _offsets[_nextIndex++]; + return true; + } + + internal void Reset() => _nextIndex = 0; +} \ No newline at end of file diff --git a/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs b/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs new file mode 100644 index 00000000..4e3fcafb --- /dev/null +++ b/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs @@ -0,0 +1,54 @@ +namespace Salvo.Targetting; + +internal class SearchPatternShotSelector : ShotSelector +{ + 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) + { + _random = random; + } + + internal override IEnumerable GetShots() + { + while(_shots.Count < NumberOfShots) + { + var (seed, _) = _random.NextShipPosition(); + SearchFrom(seed); + } + return _shots; + } + + private void SearchFrom(Position candidateShot) + { + var attemptsLeft = MaxSearchPatternAttempts; + while (true) + { + _searchPattern.Reset(); + if (attemptsLeft-- == 0) { return; } + candidateShot = candidateShot.BringIntoRange(_random); + if (FindValidShots(candidateShot)) { return; } + } + } + + private bool FindValidShots(Position candidateShot) + { + while (true) + { + if (IsValidShot(candidateShot)) + { + _shots.Add(candidateShot); + if (_shots.Count == NumberOfShots) { return true; } + } + if (!_searchPattern.TryGetOffset(out var offset)) { return false; } + candidateShot += offset; + } + } + + private bool IsValidShot(Position candidate) + => candidate.IsInRange && !Target.WasTargetedAt(candidate, out _) && !_shots.Contains(candidate); +} \ No newline at end of file diff --git a/77_Salvo/csharp/Targetting/ShotSelector.cs b/77_Salvo/csharp/Targetting/ShotSelector.cs index fae30f31..5aaa4753 100644 --- a/77_Salvo/csharp/Targetting/ShotSelector.cs +++ b/77_Salvo/csharp/Targetting/ShotSelector.cs @@ -11,5 +11,7 @@ internal abstract class ShotSelector protected Grid Source { get; } protected Grid Target { get; } - public int GetShotCount() => Source.Ships.Sum(s => s.Shots); + internal int NumberOfShots => Source.Ships.Sum(s => s.Shots); + + internal abstract IEnumerable GetShots(); }