From 846be86481c02fbae4460aa50a13612132f614ca Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Fri, 17 Feb 2023 07:52:45 +1100 Subject: [PATCH 01/29] Configure project --- 77_Salvo/csharp/Salvo.csproj | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/77_Salvo/csharp/Salvo.csproj b/77_Salvo/csharp/Salvo.csproj index d3fe4757..51470ec9 100644 --- a/77_Salvo/csharp/Salvo.csproj +++ b/77_Salvo/csharp/Salvo.csproj @@ -2,8 +2,16 @@ Exe net6.0 - 10 + 11 enable enable + + + + + + + + From e6425221bf3f63342d8f087de27c9190e8427906 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sun, 19 Feb 2023 19:58:24 +1100 Subject: [PATCH 02/29] Create program structure --- 77_Salvo/csharp/Game.cs | 18 +++++++++++++++++ 77_Salvo/csharp/Program.cs | 6 ++++++ 77_Salvo/csharp/Resources/Resource.cs | 29 +++++++++++++++++++++++++++ 77_Salvo/csharp/Resources/Title.txt | 5 +++++ 4 files changed, 58 insertions(+) create mode 100644 77_Salvo/csharp/Game.cs create mode 100644 77_Salvo/csharp/Program.cs create mode 100644 77_Salvo/csharp/Resources/Resource.cs create mode 100644 77_Salvo/csharp/Resources/Title.txt diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs new file mode 100644 index 00000000..e3efb88c --- /dev/null +++ b/77_Salvo/csharp/Game.cs @@ -0,0 +1,18 @@ +namespace Salvo; + +internal class Game +{ + private readonly IReadWrite _io; + private readonly IRandom _random; + + public Game(IReadWrite io, IRandom random) + { + _io = io; + _random = random; + } + + internal void Play() + { + _io.Write(Streams.Title); + } +} \ No newline at end of file diff --git a/77_Salvo/csharp/Program.cs b/77_Salvo/csharp/Program.cs new file mode 100644 index 00000000..d1fa1a27 --- /dev/null +++ b/77_Salvo/csharp/Program.cs @@ -0,0 +1,6 @@ +global using Games.Common.IO; +global using Games.Common.Randomness; +global using static Salvo.Resources.Resource; +using Salvo; + +new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); diff --git a/77_Salvo/csharp/Resources/Resource.cs b/77_Salvo/csharp/Resources/Resource.cs new file mode 100644 index 00000000..0cc55034 --- /dev/null +++ b/77_Salvo/csharp/Resources/Resource.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Salvo.Resources; + +internal static class Resource +{ + internal static class Streams + { + public static Stream Title => GetStream(); + } + + internal static class Prompts + { + } + + private static string GetPrompt([CallerMemberName] string? name = null) => GetString($"{name}Prompt"); + + private static string GetString([CallerMemberName] string? name = null) + { + using var stream = GetStream(name); + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + private static Stream GetStream([CallerMemberName] string? name = null) => + Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt") + ?? throw new Exception($"Could not find embedded resource stream '{name}'."); +} \ No newline at end of file diff --git a/77_Salvo/csharp/Resources/Title.txt b/77_Salvo/csharp/Resources/Title.txt new file mode 100644 index 00000000..6b2ba9b3 --- /dev/null +++ b/77_Salvo/csharp/Resources/Title.txt @@ -0,0 +1,5 @@ + Salvo + Creative Computing Morristown, New Jersey + + + From e5530d67c8e2f00503a5b4e2f969915d8c2a7734 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sun, 26 Feb 2023 23:40:45 +1100 Subject: [PATCH 03/29] Transliterate logic --- 77_Salvo/csharp/Game.cs | 411 +++++++++++++++++++++++++++++++++++++ 77_Salvo/csharp/Program.cs | 1 + 2 files changed, 412 insertions(+) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index e3efb88c..e7a7f818 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -1,3 +1,5 @@ +using Games.Common.Randomness; + namespace Salvo; internal class Game @@ -14,5 +16,414 @@ internal class Game internal void Play() { _io.Write(Streams.Title); + +L1040: var AA = new float[11,11]; + var BA = new float[11,11]; + var CA = new int[8]; + var DA = new int[8]; + var EA = new int[13]; + var FA = new int[13]; + var GA = new int[13]; + var HA = new float[13]; + var KA = new int[11,11]; +L1050: var Z8 = 0; +L1060: for (var W = 1; W <= 12; W++) + { +L1070: EA[W] = -1; +L1080: HA[W] = -1; + } +L1100: for (var X = 1; X <= 10; X++) + { +L1110: for (var Y = 1; Y <= 10; Y++) + { +L1120: BA[X,Y] = 0; + } + } +L1150: for (var X = 1; X <= 12; X++) + { +L1160: FA[X]=0; +L1170: GA[X]=0; + } +L1190: for (var X = 1; X <= 10; X++) + { +L1200: for (var Y = 1; Y <= 10; Y++) + { +L1210: AA[X,Y]=0; + } + } +L1240: for (var K = 4; K >= 1; K--) + { +L1250: var U6=0; +L1260: var (X, Y, V, V2) = L2910(); +L1270: int FNA(int K) => (5-K)*3-2*K/4+Math.Sign(K-1)-1; +L1280: int FNB(int K) => K+K/4-Math.Sign(K-1); +L1290: if (V+V2+V*V2 == 0) { goto L1260; } +L1300: if (Y+V*FNB(K)>10) { goto L1260; } +L1310: if (Y+V*FNB(K)<1) { goto L1260; } +L1320: if (X+V2*FNB(K)>10) { goto L1260; } +L1330: if (X+V2*FNB(K)<1) { goto L1260; } +L1340: U6=U6+1; +L1350: if (U6>25) { goto L1190; } +L1360: for (var Z = 0; Z <= FNB(K); Z++) + { +L1370: FA[Z+FNA(K)]=X+V2*Z; +L1380: GA[Z+FNA(K)]=Y+V*Z; + } +L1400: var U8=FNA(K); +L1405: if (U8>U8+FNB(K)) { goto L1460; } +L1410: for (var Z2 = U8; Z2 <= U8+FNB(K); Z2++) + { +L1415: if (U8<2) { continue; } +L1420: for (var Z3 = 1; Z3 <= U8-1; Z3++) + { +L1430: if (Math.Sqrt((FA[Z3]-FA[Z2])*(FA[Z3]-FA[Z2]) + (GA[Z3]-GA[Z2])*(GA[Z3]-GA[Z2])) < 3.59) { goto L1260; } + } + } +L1460: for (var Z = 0; Z <= FNB(K); Z++) + { +L1470: AA[FA[Z+U8],GA[Z+U8]]=.5F+Math.Sign(K-1)*(K-1.5F); + } + } +L1500: _io.WriteLine("ENTER COORDINATES FOR..."); +L1510: _io.WriteLine("BATTLESHIP"); +L1520: for (var X = 1; X <= 5; X++) + { + var (Y, Z) = _io.Read2Numbers(""); + BA[(int)Y, (int)Z] = 3; +L1540: BA[(int)Y, (int)Z] = 3; + } +L1560: _io.WriteLine("CRUISER"); +L1570: for (var X = 1; X <= 3; X++) + { + var (Y, Z) = _io.Read2Numbers(""); +L1590: BA[(int)Y, (int)Z]=2; + } +L1610: _io.WriteLine("DESTROYER"); +L1620: for (var X = 1; X <= 2; X++) + { + var (Y, Z) = _io.Read2Numbers(""); +L1640: BA[(int)Y, (int)Z]=1; + } +L1660: _io.WriteLine("DESTROYER"); +L1670: for (var X = 1; X <= 2; X++) + { + var (Y, Z) = _io.Read2Numbers(""); +L1690: BA[(int)Y, (int)Z]=.5F; + } +L1710: var JS = _io.ReadString("DO YOU WANT TO START"); +L1730: if (JS != "WHERE ARE YOUR SHIPS?") { goto L1890; } +L1740: _io.WriteLine("BATTLESHIP"); +L1750: for (var Z = 1; Z <= 5; Z++) + { +L1760: _io.WriteLine($" {FA[Z]} {GA[Z]} "); + } +L1780: _io.WriteLine("CRUISER"); +L1790: _io.WriteLine($" {FA[6]} {GA[6]} "); +L1800: _io.WriteLine($" {FA[7]} {GA[7]} "); +L1810: _io.WriteLine($" {FA[8]} {GA[8]} "); +L1820: _io.WriteLine("DESTROYER"); +L1830: _io.WriteLine($" {FA[9]} {GA[9]} "); +L1840: _io.WriteLine($" {FA[10]} {GA[10]} "); +L1850: _io.WriteLine("DESTROYER"); +L1860: _io.WriteLine($" {FA[11]} {GA[11]} "); +L1870: _io.WriteLine($" {FA[12]} {GA[12]} "); +L1880: goto L1710; +L1890: var C=0; +L1900: var KS = _io.ReadString("DO YOU WANT TO SEE MY SHOTS"); +L1920: _io.WriteLine(); +L1930: if (JS != "YES") { goto L2620; } +L1950: if (JS != "YES") { goto L1990; } +L1960: C=C+1; +L1970: _io.WriteLine(); +L1980: _io.WriteLine($"TURN {C}"); +L1990: var A=0; +L2000: for (var W = .5F; W <= 3; W += .5F) + { +L2010: for (var X = 1; X <= 10; X++) + { +L2020: for (var Y = 1; Y <= 10; Y++) + { +L2030: if (BA[X,Y] == W) { goto L2070; } + } + } +L2060: continue; +L2070: A=A+(int)(W+.5F); + } +L2090: for (var W = 1; W <= 7; W++) + { +L2100: CA[W] = 0; +L2110: DA[W] = 0; +L2120: FA[W] = 0; +L2130: GA[W] = 0; + } +L2150: var P3=0; +L2160: for (var X = 1; X <= 10; X++) + { +L2170: for (var Y = 1; Y <= 10; Y++) + { +L2180: if (AA[X,Y]>10) { continue; } +L2190: P3=P3+1; + } + } +L2220: _io.WriteLine($"YOU HAVE {A} SHOTS."); +L2230: if (P3 >= A) { goto L2260; } +L2240: _io.WriteLine("YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES."); +L2250: goto L2890; +L2260: if (A != 0) { goto L2290; } +L2270: _io.WriteLine("I HAVE WON."); +L2280: return; +L2290: for (var W = 1; W <= A; W++) + { +L2300: var (X, Y) = _io.Read2Numbers(""); +L2310: if (X != (int)X) { goto L2370; } +L2320: if (X > 10) { goto L2370; } +L2330: if (X < 1) { goto L2370; } +L2340: if (Y != (int)Y) { goto L2370; } +L2350: if (Y > 10) { goto L2370; } +L2360: if (Y >= 1) { goto L2390; } +L2370: _io.WriteLine("ILLEGAL, ENTER AGAIN."); +L2380: goto L2300; +L2390: if (AA[(int)X,(int)Y]>10) + { + _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {AA[(int)X,(int)Y]-10}"); + goto L2300; + } +L2400: CA[W]=(int)X; +L2410: DA[W]=(int)Y; + } +L2460: for (var W = 1; W <= A; W++) + { +L2470: if (AA[CA[W],DA[W]] == 3) + { + _io.WriteLine("YOU HIT MY BATTLESHIP."); + goto L2510; + } +L2480: if (AA[CA[W],DA[W]] == 2) + { + _io.WriteLine("YOU HIT MY CRUISER."); + goto L2510; + } +L2490: if (AA[CA[W],DA[W]] == 1) + { + _io.WriteLine("YOU HIT MY DESTROYER."); + goto L2510; + } +L2500: if (AA[CA[W],DA[W]] == .5F) + { + + _io.WriteLine("YOU HIT MY DESTROYER."); + goto L2510; + } +L2510: AA[CA[W],DA[W]] = 10+C; + } +L2620: A = 0; +L2630: if (JS == "YES") { goto L2670; } +L2640: C = C + 1; +L2650: _io.WriteLine(); +L2660: _io.WriteLine($"TURN {C}"); +L2670: A = 0; +L2680: for (var W = .5F; W <= 3; W += .5F) + { +L2690: for (var X = 1; X <= 10; X++) + { +L2700: for (var Y = 1; Y <= 10; Y++) + { +L2710: if (AA[X,Y] == W) { goto L2750; } + } + } +L2740: continue; +L2750: A = A + (int)(W+.5F); + } +L2770: P3=0; +L2780: for (var X = 1; X <= 10; X++) + { +L2790: for (var Y = 1; Y <= 10; Y++) + { +L2800: if (AA[X,Y]>10) { continue; } +L2810: P3=P3+1; + } + } +L2840: _io.WriteLine($"I HAVE {A} SHOTS."); +L2850: if (P3>A) { goto L2880; } +L2860: _io.WriteLine("I HAVE MORE SHOTS THAN BLANK SQUARES."); +L2870: goto L2270; +L2880: if (A != 0) { goto L2960; } +L2890: _io.WriteLine("YOU HAVE WON."); +L2900: return; + + (int, int, int, int) L2910() + { + var X = _random.Next(1, 11); + var Y = _random.Next(1, 11); + var V = _random.Next(-1, 2); + var V2 = _random.Next(-1, 2); + + return (X, Y, V, V2); + } + +L2960: for (var W = 1; W <= 12; W++) + { +L2970: if (HA[W]>0) { goto L3800; } + } +L3000: var w=0; +L3010: var R3=0; +L3020: var (x, y, v, v2) = L2910(); +L3030: //RESTORE + var index = 0; +L3040: var R2=0; +L3050: R3=R3+1; +L3060: if (R3>100) { goto L3010; } +L3070: if (x>10) { goto L3110; } +L3080: if (x>0) { goto L3120; } +L3090: x = 1 + (int)(_random.NextFloat() * 2.5F); +L3100: goto L3120; +L3110: x = 10 - (int)(_random.NextFloat() * 2.5F); +L3120: if (y>10) { goto L3160; } +L3130: if (y>0) { goto L3270; } +L3140: y=1+(int)(_random.NextFloat() * 2.5F); +L3150: goto L3270; +L3160: y=10-(int)(_random.NextFloat() * 2.5F); +L3170: goto L3270; +L3180: FA[w]=x; +L3190: GA[w]=y; +L3200: if (w==A) { goto L3380; } +L3210: if (R2==6) { goto L3030; } +L3240: //DATA 1,1,-1,1,1,-3,1,1,0,2,-1,1 + var data = new[] { 1,1,-1,1,1,-3,1,1,0,2,-1,1 }; +L3220: //READ X1,Y1 + var (X1, Y1) = (data[index++], data[index++]); +L3230: R2=R2+1; +L3250: x=x+X1; +L3260: y=y+Y1; +L3270: if (x>10) { goto L3210; } +L3280: if (x<1) { goto L3210; } +L3290: if (y>10) { goto L3210; } +L3300: if (y<1) { goto L3210; } +L3310: if (BA[x,y]>10) { goto L3210; } +L3320: for (var Q9 = 1; Q9 <= w; Q9++) + { +L3330: if (FA[Q9] != x) { continue; } +L3340: if (GA[Q9] == y) { goto L3210; } + } +L3360: w=w+1; +L3370: goto L3180; +L3380: if (KS != "YES") { goto L3420; } +L3390: for (var Z5 = 1; Z5 <= A; Z5++) + { +L3400: _io.WriteLine($" {FA[Z5]} {GA[Z5]}"); + } +L3420: for (var W = 1; W <= A; W++) + { +L3430: if (BA[FA[W],GA[W]] == 3) + { + _io.WriteLine("I HIT YOUR BATTLESHIP"); + } + else if (BA[FA[W],GA[W]] == 2) + { + _io.WriteLine("I HIT YOUR CRUISER"); + } + else if (BA[FA[W],GA[W]] == 1) + { + _io.WriteLine("I HIT YOUR DESTROYER"); + } + else if (BA[FA[W],GA[W]] == .5F) + { + _io.WriteLine("I HIT YOUR DESTROYER"); + } + else + { + BA[FA[W],GA[W]]=10+C; + continue; + } +L3570: for (var Q = 1; Q <= 12; Q++) + { +L3580: if (EA[Q] != -1) { continue; } +L3590: EA[Q]=10+C; +L3600: HA[Q]=BA[FA[W],GA[W]]; +L3610: var M3=0; +L3620: for (var M2 = 1; M2 <= 12; M2++) + { +L3630: if (HA[M2] != HA[Q]) { continue; } +L3640: M3=M3+1; + } +L3660: if (M3 != (int)(HA[Q]+.5F)+1+(int)(HA[Q]+.5F)/3) { goto L3470; } +L3670: for (var M2 = 1; M2 <= 12; M2++) + { +L3680: if (HA[M2] != HA[Q]) { continue; } +L3690: EA[M2] = -1; +L3700: HA[M2] = -1; + } +L3720: goto L3470; + } +L3740: _io.WriteLine("PROGRAM ABORT:"); +L3750: for (var Q = 1; Q <= 12; Q++) + { +L3760: _io.WriteLine($"EA[{Q}] = {EA[Q]}"); +L3770: _io.WriteLine($"HA[{Q}] = {HA[Q]}"); + } + return; +L3470: BA[FA[W],GA[W]]=10+C; + } +L3490: goto L1950; +L3800: //REM************************USINGEARRAY +L3810: for (var R = 1; R <= 10; R++) + { +L3820: for (var S = 1; S <= 10; S++) + { + KA[R,S]=0; + } + } +L3860: for (var U = 1; U <= 12; U++) + { +L3870: if (EA[U]<10) { continue; } +L3880: for (var R = 1; R <= 10; R++) + { +L3890: for (var S = 1; S <= 10; S++) + { +L3900: if (BA[R,S]<10) { goto L3930; } +L3910: KA[R,S]=-10000000; +L3920: continue; +L3930: for (var M = Math.Sign(1-R); M <= Math.Sign(10-R); M++) + { +L3940: for (var N = Math.Sign(1-S); N <= Math.Sign(10-S); N++) + { +L3950: if (N+M+N*M==0) { continue; } +L3960: if (BA[R+M,S+N] != EA[U]) { continue; } +L3970: KA[R,S]=KA[R,S]+EA[U]-S*(int)(HA[U]+.5F); + } + } + } + } + } +L4030: for (var R = 1; R <= A; R++) + { +L4040: FA[R]=R; +L4050: GA[R]=R; + } +L4070: for (var R = 1; R <= 10; R++) + { +L4080: for (var S = 1; S <= 10; S++) + { +L4090: var Q9=1; +L4100: for (var M = 1; M <= A; M++) + { +L4110: if (KA[FA[M],GA[M]]>=KA[FA[Q9],GA[Q9]]) { continue; } +L4120: Q9=M; + } +L4131: if (R>A) { goto L4140; } +L4132: if (R==S) { goto L4210; } +L4140: if (KA[R,S] Date: Sat, 4 Mar 2023 18:54:49 +1100 Subject: [PATCH 04/29] Rename variables --- 77_Salvo/csharp/Game.cs | 449 +++++++++++++++++++++------------------- 77_Salvo/salvo.bas | 2 + 2 files changed, 233 insertions(+), 218 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index e7a7f818..02cab91e 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -17,410 +17,423 @@ internal class Game { _io.Write(Streams.Title); -L1040: var AA = new float[11,11]; - var BA = new float[11,11]; - var CA = new int[8]; - var DA = new int[8]; - var EA = new int[13]; - var FA = new int[13]; - var GA = new int[13]; - var HA = new float[13]; - var KA = new int[11,11]; -L1050: var Z8 = 0; +L1040: var computerGrid = new float[11,11]; + var humanGrid = new float[11,11]; + var shotsX = new int[8]; + var shotsY = new int[8]; + var hitTurnRecord = new int[13]; + var tempX = new int[13]; + var tempY = new int[13]; + var hitShipValue = new float[13]; + var temp = new int[11,11]; L1060: for (var W = 1; W <= 12; W++) { -L1070: EA[W] = -1; -L1080: HA[W] = -1; +L1070: hitTurnRecord[W] = -1; +L1080: hitShipValue[W] = -1; } L1100: for (var X = 1; X <= 10; X++) { L1110: for (var Y = 1; Y <= 10; Y++) { -L1120: BA[X,Y] = 0; +L1120: humanGrid[X,Y] = 0; } } L1150: for (var X = 1; X <= 12; X++) { -L1160: FA[X]=0; -L1170: GA[X]=0; +L1160: tempX[X]=0; +L1170: tempY[X]=0; } L1190: for (var X = 1; X <= 10; X++) { L1200: for (var Y = 1; Y <= 10; Y++) { -L1210: AA[X,Y]=0; +L1210: computerGrid[X,Y]=0; } } L1240: for (var K = 4; K >= 1; K--) { -L1250: var U6=0; -L1260: var (X, Y, V, V2) = L2910(); -L1270: int FNA(int K) => (5-K)*3-2*K/4+Math.Sign(K-1)-1; -L1280: int FNB(int K) => K+K/4-Math.Sign(K-1); -L1290: if (V+V2+V*V2 == 0) { goto L1260; } -L1300: if (Y+V*FNB(K)>10) { goto L1260; } -L1310: if (Y+V*FNB(K)<1) { goto L1260; } -L1320: if (X+V2*FNB(K)>10) { goto L1260; } -L1330: if (X+V2*FNB(K)<1) { goto L1260; } -L1340: U6=U6+1; -L1350: if (U6>25) { goto L1190; } -L1360: for (var Z = 0; Z <= FNB(K); Z++) +L1250: var shipGenerationAttempts=0; +L1260: var (startX, startY, deltaY, deltaX) = L2910(); +L1270: int GetFirstIndex(int shipNumber) => (5-shipNumber)*3-2*(shipNumber/4)+Math.Sign(shipNumber-1)-1; +L1280: int GetShipSizeLessOne(int shipNumber) => shipNumber + shipNumber/4 - Math.Sign(shipNumber-1); +L1290: if (deltaY+deltaX+deltaY*deltaX == 0) { goto L1260; } +L1300: if (startY+deltaY*GetShipSizeLessOne(K)>10) { goto L1260; } +L1310: if (startY+deltaY*GetShipSizeLessOne(K)<1) { goto L1260; } +L1320: if (startX+deltaX*GetShipSizeLessOne(K)>10) { goto L1260; } +L1330: if (startX+deltaX*GetShipSizeLessOne(K)<1) { goto L1260; } +L1340: shipGenerationAttempts=shipGenerationAttempts+1; +L1350: if (shipGenerationAttempts>25) { goto L1190; } + // determine ship coordinates +L1360: for (var i = 0; i <= GetShipSizeLessOne(K); i++) { -L1370: FA[Z+FNA(K)]=X+V2*Z; -L1380: GA[Z+FNA(K)]=Y+V*Z; + tempX[i+GetFirstIndex(K)]=startX+deltaX*i; +L1380: tempY[i+GetFirstIndex(K)]=startY+deltaY*i; } -L1400: var U8=FNA(K); -L1405: if (U8>U8+FNB(K)) { goto L1460; } -L1410: for (var Z2 = U8; Z2 <= U8+FNB(K); Z2++) +L1400: var firstIndex=GetFirstIndex(K); +L1405: if (firstIndex>firstIndex+GetShipSizeLessOne(K)) { goto L1460; } // Can't be true + // detect proximity to previous ships + for (var i = firstIndex; i <= firstIndex+GetShipSizeLessOne(K); i++) { -L1415: if (U8<2) { continue; } -L1420: for (var Z3 = 1; Z3 <= U8-1; Z3++) +L1415: if (firstIndex<2) { continue; } // Only true for the Battleship +L1420: for (var j = 1; j <= firstIndex-1; j++) { -L1430: if (Math.Sqrt((FA[Z3]-FA[Z2])*(FA[Z3]-FA[Z2]) + (GA[Z3]-GA[Z2])*(GA[Z3]-GA[Z2])) < 3.59) { goto L1260; } +L1430: if (Math.Sqrt((tempX[j]-tempX[i])*(tempX[j]-tempX[i]) + (tempY[j]-tempY[i])*(tempY[j]-tempY[i])) < 3.59) { goto L1260; } } } -L1460: for (var Z = 0; Z <= FNB(K); Z++) + // put ship on board +L1460: for (var i = 0; i <= GetShipSizeLessOne(K); i++) { -L1470: AA[FA[Z+U8],GA[Z+U8]]=.5F+Math.Sign(K-1)*(K-1.5F); +L1470: computerGrid[tempX[i+firstIndex],tempY[i+firstIndex]]=.5F+Math.Sign(K-1)*(K-1.5F); } } L1500: _io.WriteLine("ENTER COORDINATES FOR..."); L1510: _io.WriteLine("BATTLESHIP"); -L1520: for (var X = 1; X <= 5; X++) +L1520: for (var i = 1; i <= 5; i++) { - var (Y, Z) = _io.Read2Numbers(""); - BA[(int)Y, (int)Z] = 3; -L1540: BA[(int)Y, (int)Z] = 3; + var (x, y) = _io.Read2Numbers(""); +L1540: humanGrid[(int)x, (int)y] = 3; } L1560: _io.WriteLine("CRUISER"); -L1570: for (var X = 1; X <= 3; X++) +L1570: for (var i = 1; i <= 3; i++) { - var (Y, Z) = _io.Read2Numbers(""); -L1590: BA[(int)Y, (int)Z]=2; + var (x, y) = _io.Read2Numbers(""); +L1590: humanGrid[(int)x, (int)y]=2; } L1610: _io.WriteLine("DESTROYER"); -L1620: for (var X = 1; X <= 2; X++) +L1620: for (var i = 1; i <= 2; i++) { - var (Y, Z) = _io.Read2Numbers(""); -L1640: BA[(int)Y, (int)Z]=1; + var (x, y) = _io.Read2Numbers(""); +L1640: humanGrid[(int)x, (int)y]=1; } L1660: _io.WriteLine("DESTROYER"); -L1670: for (var X = 1; X <= 2; X++) +L1670: for (var i = 1; i <= 2; i++) { - var (Y, Z) = _io.Read2Numbers(""); -L1690: BA[(int)Y, (int)Z]=.5F; + var (x, y) = _io.Read2Numbers(""); +L1690: humanGrid[(int)x, (int)y]=.5F; } -L1710: var JS = _io.ReadString("DO YOU WANT TO START"); -L1730: if (JS != "WHERE ARE YOUR SHIPS?") { goto L1890; } +L1710: var startResponse = _io.ReadString("DO YOU WANT TO START"); +L1730: if (startResponse != "WHERE ARE YOUR SHIPS?") { goto L1890; } L1740: _io.WriteLine("BATTLESHIP"); -L1750: for (var Z = 1; Z <= 5; Z++) +L1750: for (var i = 1; i <= 5; i++) { -L1760: _io.WriteLine($" {FA[Z]} {GA[Z]} "); +L1760: _io.WriteLine($" {tempX[i]} {tempY[i]} "); } L1780: _io.WriteLine("CRUISER"); -L1790: _io.WriteLine($" {FA[6]} {GA[6]} "); -L1800: _io.WriteLine($" {FA[7]} {GA[7]} "); -L1810: _io.WriteLine($" {FA[8]} {GA[8]} "); +L1790: _io.WriteLine($" {tempX[6]} {tempY[6]} "); +L1800: _io.WriteLine($" {tempX[7]} {tempY[7]} "); +L1810: _io.WriteLine($" {tempX[8]} {tempY[8]} "); L1820: _io.WriteLine("DESTROYER"); -L1830: _io.WriteLine($" {FA[9]} {GA[9]} "); -L1840: _io.WriteLine($" {FA[10]} {GA[10]} "); +L1830: _io.WriteLine($" {tempX[9]} {tempY[9]} "); +L1840: _io.WriteLine($" {tempX[10]} {tempY[10]} "); L1850: _io.WriteLine("DESTROYER"); -L1860: _io.WriteLine($" {FA[11]} {GA[11]} "); -L1870: _io.WriteLine($" {FA[12]} {GA[12]} "); +L1860: _io.WriteLine($" {tempX[11]} {tempY[11]} "); +L1870: _io.WriteLine($" {tempX[12]} {tempY[12]} "); L1880: goto L1710; -L1890: var C=0; -L1900: var KS = _io.ReadString("DO YOU WANT TO SEE MY SHOTS"); +L1890: var turnNumber=0; +L1900: var seeShotsResponse = _io.ReadString("DO YOU WANT TO SEE MY SHOTS"); L1920: _io.WriteLine(); -L1930: if (JS != "YES") { goto L2620; } -L1950: if (JS != "YES") { goto L1990; } -L1960: C=C+1; +L1930: if (startResponse != "YES") { goto L2620; } +L1950: if (startResponse != "YES") { goto L1990; } +L1960: turnNumber=turnNumber+1; L1970: _io.WriteLine(); -L1980: _io.WriteLine($"TURN {C}"); -L1990: var A=0; -L2000: for (var W = .5F; W <= 3; W += .5F) +L1980: _io.WriteLine($"TURN {turnNumber}"); +L1990: var maxShotCount=0; +L2000: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) { -L2010: for (var X = 1; X <= 10; X++) +L2010: for (var x = 1; x <= 10; x++) { -L2020: for (var Y = 1; Y <= 10; Y++) +L2020: for (var y = 1; y <= 10; y++) { -L2030: if (BA[X,Y] == W) { goto L2070; } +L2030: if (humanGrid[x,y] == shipValue) { goto L2070; } } } L2060: continue; -L2070: A=A+(int)(W+.5F); +L2070: maxShotCount=maxShotCount+(int)(shipValue+.5F); } -L2090: for (var W = 1; W <= 7; W++) +L2090: for (var i = 1; i <= 7; i++) { -L2100: CA[W] = 0; -L2110: DA[W] = 0; -L2120: FA[W] = 0; -L2130: GA[W] = 0; +L2100: shotsX[i] = 0; +L2110: shotsY[i] = 0; +L2120: tempX[i] = 0; +L2130: tempY[i] = 0; } -L2150: var P3=0; -L2160: for (var X = 1; X <= 10; X++) +L2150: var untriedSquareCount=0; +L2160: for (var x = 1; x <= 10; x++) { -L2170: for (var Y = 1; Y <= 10; Y++) +L2170: for (var y = 1; y <= 10; y++) { -L2180: if (AA[X,Y]>10) { continue; } -L2190: P3=P3+1; +L2180: if (computerGrid[x,y]>10) { continue; } +L2190: untriedSquareCount=untriedSquareCount+1; } } -L2220: _io.WriteLine($"YOU HAVE {A} SHOTS."); -L2230: if (P3 >= A) { goto L2260; } +L2220: _io.WriteLine($"YOU HAVE {maxShotCount} SHOTS."); +L2230: if (untriedSquareCount >= maxShotCount) { goto L2260; } L2240: _io.WriteLine("YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES."); L2250: goto L2890; -L2260: if (A != 0) { goto L2290; } +L2260: if (maxShotCount != 0) { goto L2290; } L2270: _io.WriteLine("I HAVE WON."); L2280: return; -L2290: for (var W = 1; W <= A; W++) +L2290: for (var i = 1; i <= maxShotCount; i++) { -L2300: var (X, Y) = _io.Read2Numbers(""); -L2310: if (X != (int)X) { goto L2370; } -L2320: if (X > 10) { goto L2370; } -L2330: if (X < 1) { goto L2370; } -L2340: if (Y != (int)Y) { goto L2370; } -L2350: if (Y > 10) { goto L2370; } -L2360: if (Y >= 1) { goto L2390; } +L2300: var (x, y) = _io.Read2Numbers(""); +L2310: if (x != (int)x) { goto L2370; } +L2320: if (x > 10) { goto L2370; } +L2330: if (x < 1) { goto L2370; } +L2340: if (y != (int)y) { goto L2370; } +L2350: if (y > 10) { goto L2370; } +L2360: if (y >= 1) { goto L2390; } L2370: _io.WriteLine("ILLEGAL, ENTER AGAIN."); L2380: goto L2300; -L2390: if (AA[(int)X,(int)Y]>10) +L2390: if (computerGrid[(int)x,(int)y]>10) { - _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {AA[(int)X,(int)Y]-10}"); + _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {computerGrid[(int)x,(int)y]-10}"); goto L2300; } -L2400: CA[W]=(int)X; -L2410: DA[W]=(int)Y; +L2400: shotsX[i]=(int)x; +L2410: shotsY[i]=(int)y; } -L2460: for (var W = 1; W <= A; W++) +L2460: for (var W = 1; W <= maxShotCount; W++) { -L2470: if (AA[CA[W],DA[W]] == 3) +L2470: if (computerGrid[shotsX[W],shotsY[W]] == 3) { _io.WriteLine("YOU HIT MY BATTLESHIP."); goto L2510; } -L2480: if (AA[CA[W],DA[W]] == 2) +L2480: if (computerGrid[shotsX[W],shotsY[W]] == 2) { _io.WriteLine("YOU HIT MY CRUISER."); goto L2510; } -L2490: if (AA[CA[W],DA[W]] == 1) +L2490: if (computerGrid[shotsX[W],shotsY[W]] == 1) { _io.WriteLine("YOU HIT MY DESTROYER."); goto L2510; } -L2500: if (AA[CA[W],DA[W]] == .5F) +L2500: if (computerGrid[shotsX[W],shotsY[W]] == .5F) { _io.WriteLine("YOU HIT MY DESTROYER."); goto L2510; } -L2510: AA[CA[W],DA[W]] = 10+C; +L2510: computerGrid[shotsX[W],shotsY[W]] = 10+turnNumber; } -L2620: A = 0; -L2630: if (JS == "YES") { goto L2670; } -L2640: C = C + 1; +L2620: maxShotCount = 0; +L2630: if (startResponse == "YES") { goto L2670; } +L2640: turnNumber = turnNumber + 1; L2650: _io.WriteLine(); -L2660: _io.WriteLine($"TURN {C}"); -L2670: A = 0; -L2680: for (var W = .5F; W <= 3; W += .5F) +L2660: _io.WriteLine($"TURN {turnNumber}"); +L2670: maxShotCount = 0; +L2680: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) { -L2690: for (var X = 1; X <= 10; X++) +L2690: for (var x = 1; x <= 10; x++) { -L2700: for (var Y = 1; Y <= 10; Y++) +L2700: for (var y = 1; y <= 10; y++) { -L2710: if (AA[X,Y] == W) { goto L2750; } +L2710: if (computerGrid[x,y] == shipValue) { goto L2750; } } } L2740: continue; -L2750: A = A + (int)(W+.5F); +L2750: maxShotCount = maxShotCount + (int)(shipValue+.5F); } -L2770: P3=0; -L2780: for (var X = 1; X <= 10; X++) +L2770: untriedSquareCount=0; +L2780: for (var x = 1; x <= 10; x++) { -L2790: for (var Y = 1; Y <= 10; Y++) +L2790: for (var y = 1; y <= 10; y++) { -L2800: if (AA[X,Y]>10) { continue; } -L2810: P3=P3+1; +L2800: if (computerGrid[x,y]>10) { continue; } +L2810: untriedSquareCount=untriedSquareCount+1; } } -L2840: _io.WriteLine($"I HAVE {A} SHOTS."); -L2850: if (P3>A) { goto L2880; } +L2840: _io.WriteLine($"I HAVE {maxShotCount} SHOTS."); +L2850: if (untriedSquareCount>maxShotCount) { goto L2880; } L2860: _io.WriteLine("I HAVE MORE SHOTS THAN BLANK SQUARES."); L2870: goto L2270; -L2880: if (A != 0) { goto L2960; } +L2880: if (maxShotCount != 0) { goto L2960; } L2890: _io.WriteLine("YOU HAVE WON."); L2900: return; (int, int, int, int) L2910() { - var X = _random.Next(1, 11); - var Y = _random.Next(1, 11); - var V = _random.Next(-1, 2); - var V2 = _random.Next(-1, 2); + var startX = _random.Next(1, 11); + var startY = _random.Next(1, 11); + var deltaY = _random.Next(-1, 2); + var deltaX = _random.Next(-1, 2); - return (X, Y, V, V2); + return (startX, startY, deltaY, deltaX); } -L2960: for (var W = 1; W <= 12; W++) +L2960: for (var i = 1; i <= 12; i++) { -L2970: if (HA[W]>0) { goto L3800; } + // if damaged ships +L2970: if (hitShipValue[i]>0) { goto L3800; } } -L3000: var w=0; -L3010: var R3=0; -L3020: var (x, y, v, v2) = L2910(); +L3000: var shotCount=0; +L3010: var shotAttempts=0; +L3020: var (shotX, shotY, _, _) = L2910(); L3030: //RESTORE var index = 0; -L3040: var R2=0; -L3050: R3=R3+1; -L3060: if (R3>100) { goto L3010; } -L3070: if (x>10) { goto L3110; } -L3080: if (x>0) { goto L3120; } -L3090: x = 1 + (int)(_random.NextFloat() * 2.5F); +L3040: var strategyNumber=0; +L3050: shotAttempts=shotAttempts+1; +L3060: if (shotAttempts>100) { goto L3010; } + // ensure shot is in range +L3070: if (shotX>10) { goto L3110; } +L3080: if (shotX>0) { goto L3120; } +L3090: shotX = 1 + (int)_random.NextFloat(2.5F); L3100: goto L3120; -L3110: x = 10 - (int)(_random.NextFloat() * 2.5F); -L3120: if (y>10) { goto L3160; } -L3130: if (y>0) { goto L3270; } -L3140: y=1+(int)(_random.NextFloat() * 2.5F); +L3110: shotX = 10 - (int)_random.NextFloat(2.5F); +L3120: if (shotY>10) { goto L3160; } +L3130: if (shotY>0) { goto L3270; } +L3140: shotY=1+(int)_random.NextFloat(2.5F); L3150: goto L3270; -L3160: y=10-(int)(_random.NextFloat() * 2.5F); +L3160: shotY=10-(int)_random.NextFloat(2.5F); L3170: goto L3270; -L3180: FA[w]=x; -L3190: GA[w]=y; -L3200: if (w==A) { goto L3380; } -L3210: if (R2==6) { goto L3030; } + // record shot +L3180: tempX[shotCount]=shotX; +L3190: tempY[shotCount]=shotY; +L3200: if (shotCount==maxShotCount) { 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[] { 1,1,-1,1,1,-3,1,1,0,2,-1,1 }; L3220: //READ X1,Y1 - var (X1, Y1) = (data[index++], data[index++]); -L3230: R2=R2+1; -L3250: x=x+X1; -L3260: y=y+Y1; -L3270: if (x>10) { goto L3210; } -L3280: if (x<1) { goto L3210; } -L3290: if (y>10) { goto L3210; } -L3300: if (y<1) { goto L3210; } -L3310: if (BA[x,y]>10) { goto L3210; } -L3320: for (var Q9 = 1; Q9 <= w; Q9++) + var (xOffset, yOffset) = (data[index++], data[index++]); +L3230: strategyNumber=strategyNumber+1; +L3250: shotX=shotX+xOffset; +L3260: shotY=shotY+yOffset; + // is the shot in range? +L3270: if (shotX>10) { goto L3210; } +L3280: if (shotX<1) { goto L3210; } +L3290: if (shotY>10) { goto L3210; } +L3300: if (shotY<1) { goto L3210; } + // have we fired here before +L3310: if (humanGrid[shotX,shotY]>10) { goto L3210; } + // have we already selected this shot? +L3320: for (var i = 1; i <= shotCount; i++) { -L3330: if (FA[Q9] != x) { continue; } -L3340: if (GA[Q9] == y) { goto L3210; } +L3330: if (tempX[i] != shotX) { continue; } +L3340: if (tempY[i] == shotY) { goto L3210; } } -L3360: w=w+1; +L3360: shotCount=shotCount+1; L3370: goto L3180; -L3380: if (KS != "YES") { goto L3420; } -L3390: for (var Z5 = 1; Z5 <= A; Z5++) + // display shots +L3380: if (seeShotsResponse != "YES") { goto L3420; } +L3390: for (var i = 1; i <= maxShotCount; i++) { -L3400: _io.WriteLine($" {FA[Z5]} {GA[Z5]}"); +L3400: _io.WriteLine($" {tempX[i]} {tempY[i]}"); } -L3420: for (var W = 1; W <= A; W++) +L3420: for (var i = 1; i <= maxShotCount; i++) { -L3430: if (BA[FA[W],GA[W]] == 3) +L3430: if (humanGrid[tempX[i],tempY[i]] == 3) { _io.WriteLine("I HIT YOUR BATTLESHIP"); } - else if (BA[FA[W],GA[W]] == 2) + else if (humanGrid[tempX[i],tempY[i]] == 2) { _io.WriteLine("I HIT YOUR CRUISER"); } - else if (BA[FA[W],GA[W]] == 1) + else if (humanGrid[tempX[i],tempY[i]] == 1) { _io.WriteLine("I HIT YOUR DESTROYER"); } - else if (BA[FA[W],GA[W]] == .5F) + else if (humanGrid[tempX[i],tempY[i]] == .5F) { _io.WriteLine("I HIT YOUR DESTROYER"); } else { - BA[FA[W],GA[W]]=10+C; + humanGrid[tempX[i],tempY[i]]=10+turnNumber; continue; } -L3570: for (var Q = 1; Q <= 12; Q++) +L3570: for (var j = 1; j <= 12; j++) { -L3580: if (EA[Q] != -1) { continue; } -L3590: EA[Q]=10+C; -L3600: HA[Q]=BA[FA[W],GA[W]]; -L3610: var M3=0; -L3620: for (var M2 = 1; M2 <= 12; M2++) + // record hit +L3580: if (hitTurnRecord[j] != -1) { continue; } +L3590: hitTurnRecord[j]=10+turnNumber; +L3600: hitShipValue[j]=humanGrid[tempX[i],tempY[i]]; + // look for past hits on same ship +L3610: var shipHits=0; +L3620: for (var k = 1; k <= 12; k++) { -L3630: if (HA[M2] != HA[Q]) { continue; } -L3640: M3=M3+1; +L3630: if (hitShipValue[k] != hitShipValue[j]) { continue; } +L3640: shipHits=shipHits+1; } -L3660: if (M3 != (int)(HA[Q]+.5F)+1+(int)(HA[Q]+.5F)/3) { goto L3470; } -L3670: for (var M2 = 1; M2 <= 12; M2++) + // if ship is not sunk +L3660: if (shipHits != (int)(hitShipValue[j]+.5F)+1+(int)(hitShipValue[j]+.5F)/3) { goto L3470; } + // otherwise, remove ship hit records +L3670: for (var k = 1; k <= 12; k++) { -L3680: if (HA[M2] != HA[Q]) { continue; } -L3690: EA[M2] = -1; -L3700: HA[M2] = -1; +L3680: if (hitShipValue[k] != hitShipValue[j]) { continue; } +L3690: hitTurnRecord[k] = -1; +L3700: hitShipValue[k] = -1; } L3720: goto L3470; } + // we shouldn't get here L3740: _io.WriteLine("PROGRAM ABORT:"); -L3750: for (var Q = 1; Q <= 12; Q++) +L3750: for (var j = 1; j <= 12; j++) { -L3760: _io.WriteLine($"EA[{Q}] = {EA[Q]}"); -L3770: _io.WriteLine($"HA[{Q}] = {HA[Q]}"); +L3760: _io.WriteLine($"{nameof(hitTurnRecord)}( {j} ) = {hitTurnRecord[j]}"); +L3770: _io.WriteLine($"{nameof(hitShipValue)}( {j} ) = {hitShipValue[j]}"); } return; -L3470: BA[FA[W],GA[W]]=10+C; +L3470: humanGrid[tempX[i],tempY[i]]=10+turnNumber; } L3490: goto L1950; L3800: //REM************************USINGEARRAY -L3810: for (var R = 1; R <= 10; R++) +L3810: for (var x = 1; x <= 10; x++) { -L3820: for (var S = 1; S <= 10; S++) +L3820: for (var y = 1; y <= 10; y++) { - KA[R,S]=0; + temp[x,y]=0; } } -L3860: for (var U = 1; U <= 12; U++) +L3860: for (var i = 1; i <= 12; i++) { -L3870: if (EA[U]<10) { continue; } -L3880: for (var R = 1; R <= 10; R++) +L3870: if (hitTurnRecord[i]<10) { continue; } +L3880: for (var x = 1; x <= 10; x++) { -L3890: for (var S = 1; S <= 10; S++) +L3890: for (var y = 1; y <= 10; y++) { -L3900: if (BA[R,S]<10) { goto L3930; } -L3910: KA[R,S]=-10000000; +L3900: if (humanGrid[x,y]<10) { goto L3930; } +L3910: temp[x,y]=-10000000; L3920: continue; -L3930: for (var M = Math.Sign(1-R); M <= Math.Sign(10-R); M++) +L3930: for (var dX = Math.Sign(1-x); dX <= Math.Sign(10-x); dX++) { -L3940: for (var N = Math.Sign(1-S); N <= Math.Sign(10-S); N++) +L3940: for (var dY = Math.Sign(1-y); dY <= Math.Sign(10-y); dY++) { -L3950: if (N+M+N*M==0) { continue; } -L3960: if (BA[R+M,S+N] != EA[U]) { continue; } -L3970: KA[R,S]=KA[R,S]+EA[U]-S*(int)(HA[U]+.5F); +L3950: if (dY+dX+dY*dX==0) { continue; } +L3960: if (humanGrid[x+dX,y+dY] != hitTurnRecord[i]) { continue; } +L3970: temp[x,y]=temp[x,y]+hitTurnRecord[i]-y*(int)(hitShipValue[i]+.5F); } } } } } -L4030: for (var R = 1; R <= A; R++) +L4030: for (var i = 1; i <= maxShotCount; i++) { -L4040: FA[R]=R; -L4050: GA[R]=R; +L4040: tempX[i]=i; +L4050: tempY[i]=i; } -L4070: for (var R = 1; R <= 10; R++) +L4070: for (var x = 1; x <= 10; x++) { -L4080: for (var S = 1; S <= 10; S++) +L4080: for (var y = 1; y <= 10; y++) { L4090: var Q9=1; -L4100: for (var M = 1; M <= A; M++) +L4100: for (var i = 1; i <= maxShotCount; i++) { -L4110: if (KA[FA[M],GA[M]]>=KA[FA[Q9],GA[Q9]]) { continue; } -L4120: Q9=M; +L4110: if (temp[tempX[i],tempY[i]]>=temp[tempX[Q9],tempY[Q9]]) { continue; } +L4120: Q9=i; } -L4131: if (R>A) { goto L4140; } -L4132: if (R==S) { goto L4210; } -L4140: if (KA[R,S]maxShotCount) { goto L4140; } +L4132: if (x==y) { goto L4210; } +L4140: if (temp[x,y] Date: Sun, 5 Mar 2023 21:03:40 +1100 Subject: [PATCH 05/29] Simplify some logic --- 77_Salvo/csharp/Game.cs | 320 ++++++++++++++++++++-------------------- 1 file changed, 159 insertions(+), 161 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 02cab91e..3c382521 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -4,6 +4,10 @@ namespace Salvo; internal class Game { + private static readonly float[] _shipValue = new[] { 0.5F, 1, 2, 3 }; + private static readonly int[] _shipFirstIndex = new[] { 11, 9, 6, 1 }; + private static readonly int[] _shipSize = new[] { 2, 2, 3, 5 }; + private readonly IReadWrite _io; private readonly IRandom _random; @@ -17,7 +21,8 @@ internal class Game { _io.Write(Streams.Title); -L1040: var computerGrid = new float[11,11]; + +L1040: var humanGrid = new float[11,11]; var shotsX = new int[8]; var shotsY = new int[8]; @@ -25,54 +30,27 @@ L1040: var computerGrid = new float[11,11]; var tempX = new int[13]; var tempY = new int[13]; var hitShipValue = new float[13]; - var temp = new int[11,11]; -L1060: for (var W = 1; W <= 12; W++) +L1060: for (var i = 1; i <= 12; i++) { -L1070: hitTurnRecord[W] = -1; -L1080: hitShipValue[W] = -1; +L1070: hitTurnRecord[i] = -1; +L1080: hitShipValue[i] = -1; } -L1100: for (var X = 1; X <= 10; X++) - { -L1110: for (var Y = 1; Y <= 10; Y++) - { -L1120: humanGrid[X,Y] = 0; - } - } -L1150: for (var X = 1; X <= 12; X++) - { -L1160: tempX[X]=0; -L1170: tempY[X]=0; - } -L1190: for (var X = 1; X <= 10; X++) - { -L1200: for (var Y = 1; Y <= 10; Y++) - { -L1210: computerGrid[X,Y]=0; - } - } -L1240: for (var K = 4; K >= 1; K--) +L1190: var computerGrid = new float[11,11]; +L1240: for (var K = 3; K >= 0; K--) { L1250: var shipGenerationAttempts=0; -L1260: var (startX, startY, deltaY, deltaX) = L2910(); -L1270: int GetFirstIndex(int shipNumber) => (5-shipNumber)*3-2*(shipNumber/4)+Math.Sign(shipNumber-1)-1; -L1280: int GetShipSizeLessOne(int shipNumber) => shipNumber + shipNumber/4 - Math.Sign(shipNumber-1); -L1290: if (deltaY+deltaX+deltaY*deltaX == 0) { goto L1260; } -L1300: if (startY+deltaY*GetShipSizeLessOne(K)>10) { goto L1260; } -L1310: if (startY+deltaY*GetShipSizeLessOne(K)<1) { goto L1260; } -L1320: if (startX+deltaX*GetShipSizeLessOne(K)>10) { goto L1260; } -L1330: if (startX+deltaX*GetShipSizeLessOne(K)<1) { goto L1260; } -L1340: shipGenerationAttempts=shipGenerationAttempts+1; +L1260: var (startX, startY, deltaY, deltaX) = GetRandomShipCoordinatesInRange(K); +L1340: shipGenerationAttempts++; L1350: if (shipGenerationAttempts>25) { goto L1190; } // determine ship coordinates -L1360: for (var i = 0; i <= GetShipSizeLessOne(K); i++) +L1360: for (var i = 0; i <= _shipSize[K] - 1; i++) { - tempX[i+GetFirstIndex(K)]=startX+deltaX*i; -L1380: tempY[i+GetFirstIndex(K)]=startY+deltaY*i; + tempX[i+_shipFirstIndex[K]]=startX+deltaX*i; +L1380: tempY[i+_shipFirstIndex[K]]=startY+deltaY*i; } -L1400: var firstIndex=GetFirstIndex(K); -L1405: if (firstIndex>firstIndex+GetShipSizeLessOne(K)) { goto L1460; } // Can't be true +L1400: var firstIndex=_shipFirstIndex[K]; // detect proximity to previous ships - for (var i = firstIndex; i <= firstIndex+GetShipSizeLessOne(K); i++) + for (var i = firstIndex; i <= firstIndex+_shipSize[K] - 1; i++) { L1415: if (firstIndex<2) { continue; } // Only true for the Battleship L1420: for (var j = 1; j <= firstIndex-1; j++) @@ -81,60 +59,63 @@ L1430: if (Math.Sqrt((tempX[j]-tempX[i])*(tempX[j]-tempX[i]) + (tem } } // put ship on board -L1460: for (var i = 0; i <= GetShipSizeLessOne(K); i++) +L1460: for (var i = firstIndex; i <= firstIndex + _shipSize[K] - 1; i++) { -L1470: computerGrid[tempX[i+firstIndex],tempY[i+firstIndex]]=.5F+Math.Sign(K-1)*(K-1.5F); + computerGrid[tempX[i], tempY[i]] = _shipValue[K]; } } L1500: _io.WriteLine("ENTER COORDINATES FOR..."); L1510: _io.WriteLine("BATTLESHIP"); L1520: for (var i = 1; i <= 5; i++) { - var (x, y) = _io.Read2Numbers(""); -L1540: humanGrid[(int)x, (int)y] = 3; + var (x, y) = _io.ReadCoordinates(); +L1540: humanGrid[x, y] = 3; } L1560: _io.WriteLine("CRUISER"); L1570: for (var i = 1; i <= 3; i++) { - var (x, y) = _io.Read2Numbers(""); -L1590: humanGrid[(int)x, (int)y]=2; + var (x, y) = _io.ReadCoordinates(); +L1590: humanGrid[x, y] = 2; } L1610: _io.WriteLine("DESTROYER"); L1620: for (var i = 1; i <= 2; i++) { - var (x, y) = _io.Read2Numbers(""); -L1640: humanGrid[(int)x, (int)y]=1; + var (x, y) = _io.ReadCoordinates(); +L1640: humanGrid[x, y] = 1; } L1660: _io.WriteLine("DESTROYER"); L1670: for (var i = 1; i <= 2; i++) { - var (x, y) = _io.Read2Numbers(""); -L1690: humanGrid[(int)x, (int)y]=.5F; + var (x, y) = _io.ReadCoordinates(); +L1690: humanGrid[x, y] = 0.5F; } L1710: var startResponse = _io.ReadString("DO YOU WANT TO START"); -L1730: if (startResponse != "WHERE ARE YOUR SHIPS?") { goto L1890; } -L1740: _io.WriteLine("BATTLESHIP"); -L1750: for (var i = 1; i <= 5; i++) +L1730: while (startResponse == "WHERE ARE YOUR SHIPS?") { -L1760: _io.WriteLine($" {tempX[i]} {tempY[i]} "); + _io.WriteLine("BATTLESHIP"); + for (var i = 1; i <= 5; i++) + { + _io.WriteLine($" {tempX[i]} {tempY[i]} "); + } + _io.WriteLine("CRUISER"); + _io.WriteLine($" {tempX[6]} {tempY[6]} "); + _io.WriteLine($" {tempX[7]} {tempY[7]} "); + _io.WriteLine($" {tempX[8]} {tempY[8]} "); + _io.WriteLine("DESTROYER"); + _io.WriteLine($" {tempX[9]} {tempY[9]} "); + _io.WriteLine($" {tempX[10]} {tempY[10]} "); + _io.WriteLine("DESTROYER"); + _io.WriteLine($" {tempX[11]} {tempY[11]} "); + _io.WriteLine($" {tempX[12]} {tempY[12]} "); + + startResponse = _io.ReadString("DO YOU WANT TO START"); } -L1780: _io.WriteLine("CRUISER"); -L1790: _io.WriteLine($" {tempX[6]} {tempY[6]} "); -L1800: _io.WriteLine($" {tempX[7]} {tempY[7]} "); -L1810: _io.WriteLine($" {tempX[8]} {tempY[8]} "); -L1820: _io.WriteLine("DESTROYER"); -L1830: _io.WriteLine($" {tempX[9]} {tempY[9]} "); -L1840: _io.WriteLine($" {tempX[10]} {tempY[10]} "); -L1850: _io.WriteLine("DESTROYER"); -L1860: _io.WriteLine($" {tempX[11]} {tempY[11]} "); -L1870: _io.WriteLine($" {tempX[12]} {tempY[12]} "); -L1880: goto L1710; L1890: var turnNumber=0; L1900: var seeShotsResponse = _io.ReadString("DO YOU WANT TO SEE MY SHOTS"); L1920: _io.WriteLine(); L1930: if (startResponse != "YES") { goto L2620; } L1950: if (startResponse != "YES") { goto L1990; } -L1960: turnNumber=turnNumber+1; +L1960: turnNumber++; L1970: _io.WriteLine(); L1980: _io.WriteLine($"TURN {turnNumber}"); L1990: var maxShotCount=0; @@ -148,78 +129,56 @@ L2030: if (humanGrid[x,y] == shipValue) { goto L2070; } } } L2060: continue; -L2070: maxShotCount=maxShotCount+(int)(shipValue+.5F); +L2070: maxShotCount+=(int)(shipValue+.5F); } L2090: for (var i = 1; i <= 7; i++) { -L2100: shotsX[i] = 0; -L2110: shotsY[i] = 0; -L2120: tempX[i] = 0; -L2130: tempY[i] = 0; +L2100: shotsX[i] = shotsY[i] = tempX[i] = tempY[i] = 0; } L2150: var untriedSquareCount=0; L2160: for (var x = 1; x <= 10; x++) { L2170: for (var y = 1; y <= 10; y++) { -L2180: if (computerGrid[x,y]>10) { continue; } -L2190: untriedSquareCount=untriedSquareCount+1; +L2180: if (computerGrid[x,y] <= 10) { untriedSquareCount++; } } } L2220: _io.WriteLine($"YOU HAVE {maxShotCount} SHOTS."); -L2230: if (untriedSquareCount >= maxShotCount) { goto L2260; } -L2240: _io.WriteLine("YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES."); -L2250: goto L2890; -L2260: if (maxShotCount != 0) { goto L2290; } -L2270: _io.WriteLine("I HAVE WON."); -L2280: return; + if (maxShotCount == 0) { goto L2270; } +L2230: if (maxShotCount > untriedSquareCount) + { + _io.WriteLine("YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES."); +L2250: goto L2890; + } L2290: for (var i = 1; i <= maxShotCount; i++) { -L2300: var (x, y) = _io.Read2Numbers(""); -L2310: if (x != (int)x) { goto L2370; } -L2320: if (x > 10) { goto L2370; } -L2330: if (x < 1) { goto L2370; } -L2340: if (y != (int)y) { goto L2370; } -L2350: if (y > 10) { goto L2370; } -L2360: if (y >= 1) { goto L2390; } -L2370: _io.WriteLine("ILLEGAL, ENTER AGAIN."); -L2380: goto L2300; -L2390: if (computerGrid[(int)x,(int)y]>10) - { - _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {computerGrid[(int)x,(int)y]-10}"); - goto L2300; + while (true) + { + var (x, y) = _io.ReadValidCoordinates(); +L2390: if (computerGrid[x,y]>10) + { + _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {computerGrid[x,y]-10}"); + continue; + } + shotsX[i]=x; + shotsY[i]=y; + break; } -L2400: shotsX[i]=(int)x; -L2410: shotsY[i]=(int)y; } L2460: for (var W = 1; W <= maxShotCount; W++) { -L2470: if (computerGrid[shotsX[W],shotsY[W]] == 3) - { - _io.WriteLine("YOU HIT MY BATTLESHIP."); - goto L2510; - } -L2480: if (computerGrid[shotsX[W],shotsY[W]] == 2) - { - _io.WriteLine("YOU HIT MY CRUISER."); - goto L2510; - } -L2490: if (computerGrid[shotsX[W],shotsY[W]] == 1) - { - _io.WriteLine("YOU HIT MY DESTROYER."); - goto L2510; - } -L2500: if (computerGrid[shotsX[W],shotsY[W]] == .5F) - { - - _io.WriteLine("YOU HIT MY DESTROYER."); - goto L2510; - } + _io.WriteLine(computerGrid[shotsX[W],shotsY[W]] switch + { + 3 => "YOU HIT MY BATTLESHIP.", + 2 => "YOU HIT MY CRUISER.", + 1 => "YOU HIT MY DESTROYER.", + .5F => "YOU HIT MY DESTROYER.", + _ => throw new InvalidOperationException($"Unexpected value {computerGrid[shotsX[W],shotsY[W]]}") + }); L2510: computerGrid[shotsX[W],shotsY[W]] = 10+turnNumber; } -L2620: maxShotCount = 0; -L2630: if (startResponse == "YES") { goto L2670; } -L2640: turnNumber = turnNumber + 1; +L2620: if (startResponse == "YES") { goto L2670; } +L2640: turnNumber++; L2650: _io.WriteLine(); L2660: _io.WriteLine($"TURN {turnNumber}"); L2670: maxShotCount = 0; @@ -233,7 +192,7 @@ L2710: if (computerGrid[x,y] == shipValue) { goto L2750; } } } L2740: continue; -L2750: maxShotCount = maxShotCount + (int)(shipValue+.5F); +L2750: maxShotCount += (int)(shipValue+.5F); } L2770: untriedSquareCount=0; L2780: for (var x = 1; x <= 10; x++) @@ -241,7 +200,7 @@ L2780: for (var x = 1; x <= 10; x++) L2790: for (var y = 1; y <= 10; y++) { L2800: if (computerGrid[x,y]>10) { continue; } -L2810: untriedSquareCount=untriedSquareCount+1; +L2810: untriedSquareCount++; } } L2840: _io.WriteLine($"I HAVE {maxShotCount} SHOTS."); @@ -251,16 +210,9 @@ L2870: goto L2270; L2880: if (maxShotCount != 0) { goto L2960; } L2890: _io.WriteLine("YOU HAVE WON."); L2900: return; +L2270: _io.WriteLine("I HAVE WON."); + return; - (int, int, int, int) L2910() - { - var startX = _random.Next(1, 11); - var startY = _random.Next(1, 11); - var deltaY = _random.Next(-1, 2); - var deltaX = _random.Next(-1, 2); - - return (startX, startY, deltaY, deltaX); - } L2960: for (var i = 1; i <= 12; i++) { @@ -269,11 +221,11 @@ L2970: if (hitShipValue[i]>0) { goto L3800; } } L3000: var shotCount=0; L3010: var shotAttempts=0; -L3020: var (shotX, shotY, _, _) = L2910(); +L3020: var (shotX, shotY, _, _) = GetRandomShipCoordinates(); L3030: //RESTORE var index = 0; L3040: var strategyNumber=0; -L3050: shotAttempts=shotAttempts+1; +L3050: shotAttempts++; L3060: if (shotAttempts>100) { goto L3010; } // ensure shot is in range L3070: if (shotX>10) { goto L3110; } @@ -296,9 +248,9 @@ L3240: //DATA 1,1,-1,1,1,-3,1,1,0,2,-1,1 var data = new[] { 1,1,-1,1,1,-3,1,1,0,2,-1,1 }; L3220: //READ X1,Y1 var (xOffset, yOffset) = (data[index++], data[index++]); -L3230: strategyNumber=strategyNumber+1; -L3250: shotX=shotX+xOffset; -L3260: shotY=shotY+yOffset; +L3230: strategyNumber++; +L3250: shotX+=xOffset; +L3260: shotY+=yOffset; // is the shot in range? L3270: if (shotX>10) { goto L3210; } L3280: if (shotX<1) { goto L3210; } @@ -309,10 +261,9 @@ L3310: if (humanGrid[shotX,shotY]>10) { goto L3210; } // have we already selected this shot? L3320: for (var i = 1; i <= shotCount; i++) { -L3330: if (tempX[i] != shotX) { continue; } -L3340: if (tempY[i] == shotY) { goto L3210; } +L3330: if (tempX[i] == shotX && tempY[i] == shotY) { goto L3210; } } -L3360: shotCount=shotCount+1; +L3360: shotCount++; L3370: goto L3180; // display shots L3380: if (seeShotsResponse != "YES") { goto L3420; } @@ -354,16 +305,17 @@ L3610: var shipHits=0; L3620: for (var k = 1; k <= 12; k++) { L3630: if (hitShipValue[k] != hitShipValue[j]) { continue; } -L3640: shipHits=shipHits+1; +L3640: shipHits++; } // if ship is not sunk L3660: if (shipHits != (int)(hitShipValue[j]+.5F)+1+(int)(hitShipValue[j]+.5F)/3) { goto L3470; } // otherwise, remove ship hit records L3670: for (var k = 1; k <= 12; k++) { -L3680: if (hitShipValue[k] != hitShipValue[j]) { continue; } -L3690: hitTurnRecord[k] = -1; -L3700: hitShipValue[k] = -1; +L3680: if (hitShipValue[k] == hitShipValue[j]) + { +L3700: hitShipValue[k] = hitTurnRecord[k] = -1; + } } L3720: goto L3470; } @@ -379,13 +331,7 @@ L3470: humanGrid[tempX[i],tempY[i]]=10+turnNumber; } L3490: goto L1950; L3800: //REM************************USINGEARRAY -L3810: for (var x = 1; x <= 10; x++) - { -L3820: for (var y = 1; y <= 10; y++) - { - temp[x,y]=0; - } - } + var temp = new int[11,11]; L3860: for (var i = 1; i <= 12; i++) { L3870: if (hitTurnRecord[i]<10) { continue; } @@ -393,18 +339,24 @@ L3880: for (var x = 1; x <= 10; x++) { L3890: for (var y = 1; y <= 10; y++) { -L3900: if (humanGrid[x,y]<10) { goto L3930; } -L3910: temp[x,y]=-10000000; -L3920: continue; -L3930: for (var dX = Math.Sign(1-x); dX <= Math.Sign(10-x); dX++) - { -L3940: for (var dY = Math.Sign(1-y); dY <= Math.Sign(10-y); dY++) +L3900: if (humanGrid[x,y]>=10) + { +L3930: for (var dX = Math.Sign(1-x); dX <= Math.Sign(10-x); dX++) { -L3950: if (dY+dX+dY*dX==0) { continue; } -L3960: if (humanGrid[x+dX,y+dY] != hitTurnRecord[i]) { continue; } -L3970: temp[x,y]=temp[x,y]+hitTurnRecord[i]-y*(int)(hitShipValue[i]+.5F); +L3940: for (var dY = Math.Sign(1-y); dY <= Math.Sign(10-y); dY++) + { +L3950: if (dX == 0 && dY ==0) { continue; } +L3960: if (humanGrid[x+dX,y+dY] == hitTurnRecord[i]) + { +L3970: temp[x,y]=temp[x,y]+hitTurnRecord[i]-y*(int)(hitShipValue[i]+.5F); + } + } } } + else + { +L3910: temp[x,y]=-10000000; + } } } } @@ -426,10 +378,10 @@ L4120: Q9=i; L4131: if (x>maxShotCount) { goto L4140; } L4132: if (x==y) { goto L4210; } L4140: if (temp[x,y]=1 && endX<=10 && endY>=1 && endY<=10) + { + return (startX, startY, deltaX, deltaY); + } + } + } +} + +internal static class IOExtensions +{ + internal static (int X, int Y) ReadCoordinates(this IReadWrite io) + { + var (x, y) = io.Read2Numbers(""); + return ((int)x, (int)y); + } + + internal static (int X, int Y) ReadValidCoordinates(this IReadWrite io) + { + while (true) + { + var (x, y) = io.Read2Numbers(""); + if (x == (int)x && x is >= 1 and <= 10 && y == (int)y && y is >= 1 and <= 10) + { + return ((int)x, (int)y); + } + io.WriteLine("ILLEGAL, ENTER AGAIN."); + } + } +} From a6d191261dc8c245f433b73e0a509ac72a2f94d6 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Mon, 6 Mar 2023 08:50:21 +1100 Subject: [PATCH 06/29] Introduce Coordinate and Offset --- 77_Salvo/csharp/Game.cs | 175 +++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 81 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 3c382521..4d4551d1 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -21,14 +21,10 @@ internal class Game { _io.Write(Streams.Title); - -L1040: - var humanGrid = new float[11,11]; - var shotsX = new int[8]; - var shotsY = new int[8]; +L1040: var humanGrid = new float[11,11]; var hitTurnRecord = new int[13]; - var tempX = new int[13]; - var tempY = new int[13]; + var shots = new Coordinates[8]; + var temp = new Coordinates[13]; var hitShipValue = new float[13]; L1060: for (var i = 1; i <= 12; i++) { @@ -39,14 +35,13 @@ L1190: var computerGrid = new float[11,11]; L1240: for (var K = 3; K >= 0; K--) { L1250: var shipGenerationAttempts=0; -L1260: var (startX, startY, deltaY, deltaX) = GetRandomShipCoordinatesInRange(K); +L1260: var (start, delta) = GetRandomShipCoordinatesInRange(K); L1340: shipGenerationAttempts++; L1350: if (shipGenerationAttempts>25) { goto L1190; } // determine ship coordinates -L1360: for (var i = 0; i <= _shipSize[K] - 1; i++) +L1360: for (var i = _shipFirstIndex[K]; i <= _shipFirstIndex[K] + _shipSize[K] - 1; i++) { - tempX[i+_shipFirstIndex[K]]=startX+deltaX*i; -L1380: tempY[i+_shipFirstIndex[K]]=startY+deltaY*i; + temp[i] = start + delta * i; } L1400: var firstIndex=_shipFirstIndex[K]; // detect proximity to previous ships @@ -55,13 +50,13 @@ L1400: var firstIndex=_shipFirstIndex[K]; L1415: if (firstIndex<2) { continue; } // Only true for the Battleship L1420: for (var j = 1; j <= firstIndex-1; j++) { -L1430: if (Math.Sqrt((tempX[j]-tempX[i])*(tempX[j]-tempX[i]) + (tempY[j]-tempY[i])*(tempY[j]-tempY[i])) < 3.59) { goto L1260; } +L1430: if (temp[j].DistanceTo(temp[i]) < 3.59) { goto L1260; } } } // put ship on board L1460: for (var i = firstIndex; i <= firstIndex + _shipSize[K] - 1; i++) { - computerGrid[tempX[i], tempY[i]] = _shipValue[K]; + computerGrid[temp[i].X, temp[i].Y] = _shipValue[K]; } } L1500: _io.WriteLine("ENTER COORDINATES FOR..."); @@ -95,18 +90,18 @@ L1730: while (startResponse == "WHERE ARE YOUR SHIPS?") _io.WriteLine("BATTLESHIP"); for (var i = 1; i <= 5; i++) { - _io.WriteLine($" {tempX[i]} {tempY[i]} "); + _io.WriteLine(temp[i]); } _io.WriteLine("CRUISER"); - _io.WriteLine($" {tempX[6]} {tempY[6]} "); - _io.WriteLine($" {tempX[7]} {tempY[7]} "); - _io.WriteLine($" {tempX[8]} {tempY[8]} "); + _io.WriteLine(temp[6]); + _io.WriteLine(temp[7]); + _io.WriteLine(temp[8]); _io.WriteLine("DESTROYER"); - _io.WriteLine($" {tempX[9]} {tempY[9]} "); - _io.WriteLine($" {tempX[10]} {tempY[10]} "); + _io.WriteLine(temp[9]); + _io.WriteLine(temp[10]); _io.WriteLine("DESTROYER"); - _io.WriteLine($" {tempX[11]} {tempY[11]} "); - _io.WriteLine($" {tempX[12]} {tempY[12]} "); + _io.WriteLine(temp[11]); + _io.WriteLine(temp[12]); startResponse = _io.ReadString("DO YOU WANT TO START"); } @@ -133,7 +128,7 @@ L2070: maxShotCount+=(int)(shipValue+.5F); } L2090: for (var i = 1; i <= 7; i++) { -L2100: shotsX[i] = shotsY[i] = tempX[i] = tempY[i] = 0; + shots[i] = temp[i] = 0; } L2150: var untriedSquareCount=0; L2160: for (var x = 1; x <= 10; x++) @@ -160,22 +155,21 @@ L2390: if (computerGrid[x,y]>10) _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {computerGrid[x,y]-10}"); continue; } - shotsX[i]=x; - shotsY[i]=y; + shots[i]= new(x, y); break; } } L2460: for (var W = 1; W <= maxShotCount; W++) { - _io.WriteLine(computerGrid[shotsX[W],shotsY[W]] switch + _io.WriteLine(computerGrid[shots[W].X,shots[W].Y] switch { 3 => "YOU HIT MY BATTLESHIP.", 2 => "YOU HIT MY CRUISER.", 1 => "YOU HIT MY DESTROYER.", .5F => "YOU HIT MY DESTROYER.", - _ => throw new InvalidOperationException($"Unexpected value {computerGrid[shotsX[W],shotsY[W]]}") + _ => throw new InvalidOperationException($"Unexpected value {computerGrid[shots[W].X,shots[W].Y]}") }); -L2510: computerGrid[shotsX[W],shotsY[W]] = 10+turnNumber; +L2510: computerGrid[shots[W].X,shots[W].Y] = 10+turnNumber; } L2620: if (startResponse == "YES") { goto L2670; } L2640: turnNumber++; @@ -221,47 +215,30 @@ L2970: if (hitShipValue[i]>0) { goto L3800; } } L3000: var shotCount=0; L3010: var shotAttempts=0; -L3020: var (shotX, shotY, _, _) = GetRandomShipCoordinates(); -L3030: //RESTORE - var index = 0; -L3040: var strategyNumber=0; +L3020: var (shot, _) = GetRandomShipCoordinates(); +L3030: var strategyNumber=0; //RESTORE L3050: shotAttempts++; L3060: if (shotAttempts>100) { goto L3010; } // ensure shot is in range -L3070: if (shotX>10) { goto L3110; } -L3080: if (shotX>0) { goto L3120; } -L3090: shotX = 1 + (int)_random.NextFloat(2.5F); -L3100: goto L3120; -L3110: shotX = 10 - (int)_random.NextFloat(2.5F); -L3120: if (shotY>10) { goto L3160; } -L3130: if (shotY>0) { goto L3270; } -L3140: shotY=1+(int)_random.NextFloat(2.5F); -L3150: goto L3270; -L3160: shotY=10-(int)_random.NextFloat(2.5F); +L3070: shot = shot.BringIntoRange(_random); L3170: goto L3270; // record shot -L3180: tempX[shotCount]=shotX; -L3190: tempY[shotCount]=shotY; +L3180: temp[shotCount]=shot; L3200: if (shotCount==maxShotCount) { 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[] { 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 (xOffset, yOffset) = (data[index++], data[index++]); -L3230: strategyNumber++; -L3250: shotX+=xOffset; -L3260: shotY+=yOffset; + var offset = data[strategyNumber++]; +L3250: shot+=offset; // is the shot in range? -L3270: if (shotX>10) { goto L3210; } -L3280: if (shotX<1) { goto L3210; } -L3290: if (shotY>10) { goto L3210; } -L3300: if (shotY<1) { goto L3210; } +L3270: if (!shot.IsInRange) { goto L3210; } // have we fired here before -L3310: if (humanGrid[shotX,shotY]>10) { goto L3210; } +L3310: if (humanGrid[shot.X,shot.Y]>10) { goto L3210; } // have we already selected this shot? L3320: for (var i = 1; i <= shotCount; i++) { -L3330: if (tempX[i] == shotX && tempY[i] == shotY) { goto L3210; } +L3330: if (temp[i] == shot) { goto L3210; } } L3360: shotCount++; L3370: goto L3180; @@ -269,29 +246,29 @@ L3370: goto L3180; L3380: if (seeShotsResponse != "YES") { goto L3420; } L3390: for (var i = 1; i <= maxShotCount; i++) { -L3400: _io.WriteLine($" {tempX[i]} {tempY[i]}"); +L3400: _io.WriteLine(temp[i]); } L3420: for (var i = 1; i <= maxShotCount; i++) { -L3430: if (humanGrid[tempX[i],tempY[i]] == 3) +L3430: if (humanGrid[temp[i].X,temp[i].Y] == 3) { _io.WriteLine("I HIT YOUR BATTLESHIP"); } - else if (humanGrid[tempX[i],tempY[i]] == 2) + else if (humanGrid[temp[i].X,temp[i].Y] == 2) { _io.WriteLine("I HIT YOUR CRUISER"); } - else if (humanGrid[tempX[i],tempY[i]] == 1) + else if (humanGrid[temp[i].X,temp[i].Y] == 1) { _io.WriteLine("I HIT YOUR DESTROYER"); } - else if (humanGrid[tempX[i],tempY[i]] == .5F) + else if (humanGrid[temp[i].X,temp[i].Y] == .5F) { _io.WriteLine("I HIT YOUR DESTROYER"); } else { - humanGrid[tempX[i],tempY[i]]=10+turnNumber; + humanGrid[temp[i].X,temp[i].Y]=10+turnNumber; continue; } L3570: for (var j = 1; j <= 12; j++) @@ -299,7 +276,7 @@ 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[tempX[i],tempY[i]]; +L3600: hitShipValue[j]=humanGrid[temp[i].X,temp[i].Y]; // look for past hits on same ship L3610: var shipHits=0; L3620: for (var k = 1; k <= 12; k++) @@ -327,11 +304,11 @@ L3760: _io.WriteLine($"{nameof(hitTurnRecord)}( {j} ) = {hitTurnRecord[ L3770: _io.WriteLine($"{nameof(hitShipValue)}( {j} ) = {hitShipValue[j]}"); } return; -L3470: humanGrid[tempX[i],tempY[i]]=10+turnNumber; +L3470: humanGrid[temp[i].X,temp[i].Y]=10+turnNumber; } L3490: goto L1950; L3800: //REM************************USINGEARRAY - var temp = new int[11,11]; + var tempGrid = new int[11,11]; L3860: for (var i = 1; i <= 12; i++) { L3870: if (hitTurnRecord[i]<10) { continue; } @@ -348,22 +325,21 @@ L3940: for (var dY = Math.Sign(1-y); dY <= Math.Sign(10-y); L3950: if (dX == 0 && dY ==0) { continue; } L3960: if (humanGrid[x+dX,y+dY] == hitTurnRecord[i]) { -L3970: temp[x,y]=temp[x,y]+hitTurnRecord[i]-y*(int)(hitShipValue[i]+.5F); +L3970: tempGrid[x,y] += hitTurnRecord[i]-y*(int)(hitShipValue[i]+.5F); } } } } else { -L3910: temp[x,y]=-10000000; +L3910: tempGrid[x,y]=-10000000; } } } } L4030: for (var i = 1; i <= maxShotCount; i++) { -L4040: tempX[i]=i; -L4050: tempY[i]=i; +L4040: temp[i]=i; } L4070: for (var x = 1; x <= 10; x++) { @@ -372,19 +348,18 @@ L4080: for (var y = 1; y <= 10; y++) L4090: var Q9=1; L4100: for (var i = 1; i <= maxShotCount; i++) { -L4110: if (temp[tempX[i],tempY[i]]>=temp[tempX[Q9],tempY[Q9]]) { continue; } +L4110: if (tempGrid[temp[i].X,temp[i].Y]>=tempGrid[temp[Q9].X,temp[Q9].Y]) { continue; } L4120: Q9=i; } L4131: if (x>maxShotCount) { goto L4140; } L4132: if (x==y) { goto L4210; } -L4140: if (temp[x,y]=1 && endX<=10 && endY>=1 && endY<=10) + var end = start + delta * shipSizeLessOne; + if (delta != 0 && end.IsInRange) { - return (startX, startY, deltaX, deltaY); + return (start, delta); } } } @@ -438,3 +412,42 @@ internal static class IOExtensions } } } + +internal record struct Coordinates(int X, int Y) +{ + internal Coordinates(float x, float y) + : this((int)x, (int)y) + { + } + + public bool IsInRange => X is >= 1 and <= 10 && Y is >= 1 and <= 10; + + internal double DistanceTo(Coordinates other) + => Math.Sqrt((X - other.X) * (X - other.Y) + (Y - other.Y) * (Y - other.Y)); + + internal Coordinates BringIntoRange(IRandom random) + => IsInRange ? this : new(BringIntoRange(X, random), BringIntoRange(Y, random)); + + private int BringIntoRange(int value, IRandom random) + => value switch + { + < 1 => 1 + (int)random.NextFloat(2.5F), + > 10 => 10 - (int)random.NextFloat(2.5F), + _ => value + }; + + public static Coordinates operator +(Coordinates coordinates, Offset offset) + => new(coordinates.X + offset.X, coordinates.Y + offset.Y); + + public static implicit operator Coordinates(int value) => new(value, value); + + public override string ToString() => $" {X} {Y} "; +} + +internal record struct Offset(int X, int Y) +{ + public static Offset operator *(Offset offset, int scale) + => new(offset.X * scale, offset.Y * scale); + + public static implicit operator Offset(int value) => new(value, value); +} From 0089593453f5a025318d049cdce27b8a3d06c663 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Tue, 7 Mar 2023 08:34:55 +1100 Subject: [PATCH 07/29] Add coordinate loops --- 77_Salvo/csharp/Game.cs | 107 ++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 37 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 4d4551d1..1bdeed5f 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -311,29 +311,22 @@ L3800: //REM************************USINGEARRAY var tempGrid = new int[11,11]; L3860: for (var i = 1; i <= 12; i++) { -L3870: if (hitTurnRecord[i]<10) { continue; } -L3880: for (var x = 1; x <= 10; x++) + if (hitTurnRecord[i]<10) { continue; } + foreach (var position in Coordinates.All) { -L3890: for (var y = 1; y <= 10; y++) - { -L3900: if (humanGrid[x,y]>=10) - { -L3930: for (var dX = Math.Sign(1-x); dX <= Math.Sign(10-x); dX++) + if (humanGrid[position.X,position.Y]>=10) + { + foreach (var neighbour in position.Neighbours) + { + if (humanGrid[neighbour.X,neighbour.Y] == hitTurnRecord[i]) { -L3940: for (var dY = Math.Sign(1-y); dY <= Math.Sign(10-y); dY++) - { -L3950: if (dX == 0 && dY ==0) { continue; } -L3960: if (humanGrid[x+dX,y+dY] == hitTurnRecord[i]) - { -L3970: tempGrid[x,y] += hitTurnRecord[i]-y*(int)(hitShipValue[i]+.5F); - } - } + tempGrid[position.X,position.Y] += hitTurnRecord[i]-position.Y*(int)(hitShipValue[i]+.5F); } } - else - { -L3910: tempGrid[x,y]=-10000000; - } + } + else + { + tempGrid[position.X,position.Y]=-10000000; } } } @@ -341,27 +334,24 @@ L4030: for (var i = 1; i <= maxShotCount; i++) { L4040: temp[i]=i; } -L4070: for (var x = 1; x <= 10; x++) + foreach (var position in Coordinates.All) { -L4080: for (var y = 1; y <= 10; y++) +L4090: var Q9=1; +L4100: for (var i = 1; i <= maxShotCount; i++) { -L4090: var Q9=1; -L4100: for (var i = 1; i <= maxShotCount; i++) - { -L4110: if (tempGrid[temp[i].X,temp[i].Y]>=tempGrid[temp[Q9].X,temp[Q9].Y]) { continue; } -L4120: Q9=i; - } -L4131: if (x>maxShotCount) { goto L4140; } -L4132: if (x==y) { goto L4210; } -L4140: if (tempGrid[x,y]=tempGrid[temp[Q9].X,temp[Q9].Y]) { continue; } +L4120: Q9=i; } +L4131: if (x>maxShotCount) { goto L4140; } +L4132: if (x==y) { goto L4210; } +L4140: if (tempGrid[x,y] X is >= 1 and <= 10 && Y is >= 1 and <= 10; + public static IEnumerable All + { + get + { + for (int x = 1; x <= 10; x++) + { + for (int y = 1; y <= 10; y++) + { + yield return new(x, y); + } + } + } + } + + public IEnumerable Neighbours + { + get + { + foreach (var offset in Offset.Units) + { + var neighbour = this + offset; + if (neighbour.IsInRange) { yield return neighbour; } + } + } + } + internal double DistanceTo(Coordinates other) => Math.Sqrt((X - other.X) * (X - other.Y) + (Y - other.Y) * (Y - other.Y)); @@ -446,8 +462,25 @@ internal record struct Coordinates(int X, int Y) internal record struct Offset(int X, int Y) { + public static readonly Offset Zero = 0; + public static Offset operator *(Offset offset, int scale) => new(offset.X * scale, offset.Y * scale); public static implicit operator Offset(int value) => new(value, value); + + public static IEnumerable Units + { + get + { + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + var offset = new Offset(x, y); + if (offset != Zero) { yield return offset; } + } + } + } + } } From ee9bfec90ee335549257618a933ea29a84271fbf Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Wed, 8 Mar 2023 08:13:00 +1100 Subject: [PATCH 08/29] Introduce Grid --- 77_Salvo/csharp/Game.cs | 170 ++++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 78 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 1bdeed5f..03a11197 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -21,7 +21,7 @@ internal class Game { _io.Write(Streams.Title); -L1040: var humanGrid = new float[11,11]; +L1040: var humanGrid = new Grid(); var hitTurnRecord = new int[13]; var shots = new Coordinates[8]; var temp = new Coordinates[13]; @@ -31,7 +31,7 @@ L1060: for (var i = 1; i <= 12; i++) L1070: hitTurnRecord[i] = -1; L1080: hitShipValue[i] = -1; } -L1190: var computerGrid = new float[11,11]; +L1190: var computerGrid = new Grid(); L1240: for (var K = 3; K >= 0; K--) { L1250: var shipGenerationAttempts=0; @@ -56,36 +56,32 @@ L1430: if (temp[j].DistanceTo(temp[i]) < 3.59) { goto L1260; } // put ship on board L1460: for (var i = firstIndex; i <= firstIndex + _shipSize[K] - 1; i++) { - computerGrid[temp[i].X, temp[i].Y] = _shipValue[K]; + computerGrid[temp[i]] = _shipValue[K]; } } -L1500: _io.WriteLine("ENTER COORDINATES FOR..."); -L1510: _io.WriteLine("BATTLESHIP"); -L1520: for (var i = 1; i <= 5; i++) + _io.WriteLine("ENTER COORDINATES FOR..."); + _io.WriteLine("BATTLESHIP"); + for (var i = 1; i <= 5; i++) { - var (x, y) = _io.ReadCoordinates(); -L1540: humanGrid[x, y] = 3; + humanGrid[_io.ReadCoordinates()] = 3; } -L1560: _io.WriteLine("CRUISER"); -L1570: for (var i = 1; i <= 3; i++) + _io.WriteLine("CRUISER"); + for (var i = 1; i <= 3; i++) { - var (x, y) = _io.ReadCoordinates(); -L1590: humanGrid[x, y] = 2; + humanGrid[_io.ReadCoordinates()] = 2; } -L1610: _io.WriteLine("DESTROYER"); -L1620: for (var i = 1; i <= 2; i++) + _io.WriteLine("DESTROYER"); + for (var i = 1; i <= 2; i++) { - var (x, y) = _io.ReadCoordinates(); -L1640: humanGrid[x, y] = 1; + humanGrid[_io.ReadCoordinates()] = 1; } -L1660: _io.WriteLine("DESTROYER"); -L1670: for (var i = 1; i <= 2; i++) + _io.WriteLine("DESTROYER"); + for (var i = 1; i <= 2; i++) { - var (x, y) = _io.ReadCoordinates(); -L1690: humanGrid[x, y] = 0.5F; + humanGrid[_io.ReadCoordinates()] = 0.5F; } -L1710: var startResponse = _io.ReadString("DO YOU WANT TO START"); -L1730: while (startResponse == "WHERE ARE YOUR SHIPS?") + var startResponse = _io.ReadString("DO YOU WANT TO START"); + while (startResponse == "WHERE ARE YOUR SHIPS?") { _io.WriteLine("BATTLESHIP"); for (var i = 1; i <= 5; i++) @@ -116,27 +112,23 @@ L1980: _io.WriteLine($"TURN {turnNumber}"); L1990: var maxShotCount=0; L2000: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) { -L2010: for (var x = 1; x <= 10; x++) + foreach (var position in Coordinates.All) { -L2020: for (var y = 1; y <= 10; y++) - { -L2030: if (humanGrid[x,y] == shipValue) { goto L2070; } + if (humanGrid[position] == shipValue) + { + maxShotCount+=(int)(shipValue+.5F); + break; } } -L2060: continue; -L2070: maxShotCount+=(int)(shipValue+.5F); } L2090: for (var i = 1; i <= 7; i++) { shots[i] = temp[i] = 0; } L2150: var untriedSquareCount=0; -L2160: for (var x = 1; x <= 10; x++) + foreach (var position in Coordinates.All) { -L2170: for (var y = 1; y <= 10; y++) - { -L2180: if (computerGrid[x,y] <= 10) { untriedSquareCount++; } - } + if (computerGrid[position.X] <= 10) { untriedSquareCount++; } } L2220: _io.WriteLine($"YOU HAVE {maxShotCount} SHOTS."); if (maxShotCount == 0) { goto L2270; } @@ -149,27 +141,27 @@ L2290: for (var i = 1; i <= maxShotCount; i++) { while (true) { - var (x, y) = _io.ReadValidCoordinates(); -L2390: if (computerGrid[x,y]>10) + var position = _io.ReadValidCoordinates(); +L2390: if (computerGrid[position]>10) { - _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {computerGrid[x,y]-10}"); + _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {computerGrid[position]-10}"); continue; } - shots[i]= new(x, y); + shots[i]= position; break; } } L2460: for (var W = 1; W <= maxShotCount; W++) { - _io.WriteLine(computerGrid[shots[W].X,shots[W].Y] switch + _io.WriteLine(computerGrid[shots[W]] switch { 3 => "YOU HIT MY BATTLESHIP.", 2 => "YOU HIT MY CRUISER.", 1 => "YOU HIT MY DESTROYER.", .5F => "YOU HIT MY DESTROYER.", - _ => throw new InvalidOperationException($"Unexpected value {computerGrid[shots[W].X,shots[W].Y]}") + _ => throw new InvalidOperationException($"Unexpected value {computerGrid[shots[W]]}") }); -L2510: computerGrid[shots[W].X,shots[W].Y] = 10+turnNumber; +L2510: computerGrid[shots[W]] = 10+turnNumber; } L2620: if (startResponse == "YES") { goto L2670; } L2640: turnNumber++; @@ -178,24 +170,19 @@ L2660: _io.WriteLine($"TURN {turnNumber}"); L2670: maxShotCount = 0; L2680: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) { -L2690: for (var x = 1; x <= 10; x++) + foreach (var position in Coordinates.All) { -L2700: for (var y = 1; y <= 10; y++) - { -L2710: if (computerGrid[x,y] == shipValue) { goto L2750; } + if (computerGrid[position] == shipValue) + { + maxShotCount += (int)(shipValue+.5F); + break; } } -L2740: continue; -L2750: maxShotCount += (int)(shipValue+.5F); } L2770: untriedSquareCount=0; -L2780: for (var x = 1; x <= 10; x++) + foreach (var position in Coordinates.All) { -L2790: for (var y = 1; y <= 10; y++) - { -L2800: if (computerGrid[x,y]>10) { continue; } -L2810: untriedSquareCount++; - } + if (computerGrid[position]<=10) { untriedSquareCount++; } } L2840: _io.WriteLine($"I HAVE {maxShotCount} SHOTS."); L2850: if (untriedSquareCount>maxShotCount) { goto L2880; } @@ -234,7 +221,7 @@ L3250: shot+=offset; // is the shot in range? L3270: if (!shot.IsInRange) { goto L3210; } // have we fired here before -L3310: if (humanGrid[shot.X,shot.Y]>10) { goto L3210; } +L3310: if (humanGrid[shot]>10) { goto L3210; } // have we already selected this shot? L3320: for (var i = 1; i <= shotCount; i++) { @@ -250,25 +237,25 @@ L3400: _io.WriteLine(temp[i]); } L3420: for (var i = 1; i <= maxShotCount; i++) { -L3430: if (humanGrid[temp[i].X,temp[i].Y] == 3) +L3430: if (humanGrid[temp[i]] == 3) { _io.WriteLine("I HIT YOUR BATTLESHIP"); } - else if (humanGrid[temp[i].X,temp[i].Y] == 2) + else if (humanGrid[temp[i]] == 2) { _io.WriteLine("I HIT YOUR CRUISER"); } - else if (humanGrid[temp[i].X,temp[i].Y] == 1) + else if (humanGrid[temp[i]] == 1) { _io.WriteLine("I HIT YOUR DESTROYER"); } - else if (humanGrid[temp[i].X,temp[i].Y] == .5F) + else if (humanGrid[temp[i]] == .5F) { _io.WriteLine("I HIT YOUR DESTROYER"); } else { - humanGrid[temp[i].X,temp[i].Y]=10+turnNumber; + humanGrid[temp[i]]=10+turnNumber; continue; } L3570: for (var j = 1; j <= 12; j++) @@ -276,7 +263,7 @@ 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].X,temp[i].Y]; +L3600: hitShipValue[j]=humanGrid[temp[i]]; // look for past hits on same ship L3610: var shipHits=0; L3620: for (var k = 1; k <= 12; k++) @@ -304,29 +291,29 @@ L3760: _io.WriteLine($"{nameof(hitTurnRecord)}( {j} ) = {hitTurnRecord[ L3770: _io.WriteLine($"{nameof(hitShipValue)}( {j} ) = {hitShipValue[j]}"); } return; -L3470: humanGrid[temp[i].X,temp[i].Y]=10+turnNumber; +L3470: humanGrid[temp[i]]=10+turnNumber; } L3490: goto L1950; L3800: //REM************************USINGEARRAY - var tempGrid = new int[11,11]; + var tempGrid = new Grid(); L3860: for (var i = 1; i <= 12; i++) { if (hitTurnRecord[i]<10) { continue; } foreach (var position in Coordinates.All) { - if (humanGrid[position.X,position.Y]>=10) + if (humanGrid[position]>=10) { foreach (var neighbour in position.Neighbours) { - if (humanGrid[neighbour.X,neighbour.Y] == hitTurnRecord[i]) + if (humanGrid[neighbour] == hitTurnRecord[i]) { - tempGrid[position.X,position.Y] += hitTurnRecord[i]-position.Y*(int)(hitShipValue[i]+.5F); + tempGrid[position] += hitTurnRecord[i]-position.Y*(int)(hitShipValue[i]+.5F); } } } else { - tempGrid[position.X,position.Y]=-10000000; + tempGrid[position]=-10000000; } } } @@ -339,22 +326,21 @@ L4040: temp[i]=i; L4090: var Q9=1; L4100: for (var i = 1; i <= maxShotCount; i++) { -L4110: if (tempGrid[temp[i].X,temp[i].Y]>=tempGrid[temp[Q9].X,temp[Q9].Y]) { continue; } +L4110: if (tempGrid[temp[i]]>=tempGrid[temp[Q9]]) { continue; } L4120: Q9=i; } -L4131: if (x>maxShotCount) { goto L4140; } -L4132: if (x==y) { goto L4210; } -L4140: if (tempGrid[x,y]maxShotCount) { goto L4140; } +L4132: if (position.IsOnDiagonal) { goto L4210; } +L4140: if (tempGrid[position] _positions[position.X - 1, position.Y - 1]; + set => _positions[position.X - 1, position.Y - 1] = value; + } +} + internal static class IOExtensions { - internal static (int X, int Y) ReadCoordinates(this IReadWrite io) + internal static Coordinates ReadCoordinates(this IReadWrite io) { var (x, y) = io.Read2Numbers(""); - return ((int)x, (int)y); + return new(x, y); } - internal static (int X, int Y) ReadValidCoordinates(this IReadWrite io) + internal static Coordinates ReadValidCoordinates(this IReadWrite io) { while (true) { var (x, y) = io.Read2Numbers(""); - if (x == (int)x && x is >= 1 and <= 10 && y == (int)y && y is >= 1 and <= 10) + if (Coordinates.TryCreateValid(x, y, out var position)) { - return ((int)x, (int)y); + return position; } io.WriteLine("ILLEGAL, ENTER AGAIN."); } @@ -411,6 +408,23 @@ internal record struct Coordinates(int X, int Y) } public bool IsInRange => X is >= 1 and <= 10 && Y is >= 1 and <= 10; + public bool IsOnDiagonal => X == Y; + + public static bool TryCreateValid(float x, float y, out Coordinates coordinates) + { + coordinates = default; + if (x != (int)x || y != (int)y) { return false; } + + var result = new Coordinates(x, y); + + if (result.IsInRange) + { + coordinates = result; + return true; + } + + return false; + } public static IEnumerable All { From a0683190347402c98ed3f910f1cf7223fafd1dd7 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Wed, 8 Mar 2023 08:17:56 +1100 Subject: [PATCH 09/29] Rename Coordinates to Position --- 77_Salvo/csharp/Game.cs | 79 ++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 03a11197..bd33c3d2 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -23,8 +23,8 @@ internal class Game L1040: var humanGrid = new Grid(); var hitTurnRecord = new int[13]; - var shots = new Coordinates[8]; - var temp = new Coordinates[13]; + var shots = new Position[8]; + var temp = new Position[13]; var hitShipValue = new float[13]; L1060: for (var i = 1; i <= 12; i++) { @@ -35,10 +35,10 @@ L1190: var computerGrid = new Grid(); L1240: for (var K = 3; K >= 0; K--) { L1250: var shipGenerationAttempts=0; -L1260: var (start, delta) = GetRandomShipCoordinatesInRange(K); +L1260: var (start, delta) = GetRandomShipPositionInRange(K); L1340: shipGenerationAttempts++; L1350: if (shipGenerationAttempts>25) { goto L1190; } - // determine ship coordinates + // determine ship position L1360: for (var i = _shipFirstIndex[K]; i <= _shipFirstIndex[K] + _shipSize[K] - 1; i++) { temp[i] = start + delta * i; @@ -59,26 +59,26 @@ L1460: for (var i = firstIndex; i <= firstIndex + _shipSize[K] - 1; i++) computerGrid[temp[i]] = _shipValue[K]; } } - _io.WriteLine("ENTER COORDINATES FOR..."); + _io.WriteLine("ENTER POSITION FOR..."); _io.WriteLine("BATTLESHIP"); for (var i = 1; i <= 5; i++) { - humanGrid[_io.ReadCoordinates()] = 3; + humanGrid[_io.ReadPosition()] = 3; } _io.WriteLine("CRUISER"); for (var i = 1; i <= 3; i++) { - humanGrid[_io.ReadCoordinates()] = 2; + humanGrid[_io.ReadPosition()] = 2; } _io.WriteLine("DESTROYER"); for (var i = 1; i <= 2; i++) { - humanGrid[_io.ReadCoordinates()] = 1; + humanGrid[_io.ReadPosition()] = 1; } _io.WriteLine("DESTROYER"); for (var i = 1; i <= 2; i++) { - humanGrid[_io.ReadCoordinates()] = 0.5F; + humanGrid[_io.ReadPosition()] = 0.5F; } var startResponse = _io.ReadString("DO YOU WANT TO START"); while (startResponse == "WHERE ARE YOUR SHIPS?") @@ -112,7 +112,7 @@ L1980: _io.WriteLine($"TURN {turnNumber}"); L1990: var maxShotCount=0; L2000: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) { - foreach (var position in Coordinates.All) + foreach (var position in Position.All) { if (humanGrid[position] == shipValue) { @@ -126,7 +126,7 @@ L2090: for (var i = 1; i <= 7; i++) shots[i] = temp[i] = 0; } L2150: var untriedSquareCount=0; - foreach (var position in Coordinates.All) + foreach (var position in Position.All) { if (computerGrid[position.X] <= 10) { untriedSquareCount++; } } @@ -141,7 +141,7 @@ L2290: for (var i = 1; i <= maxShotCount; i++) { while (true) { - var position = _io.ReadValidCoordinates(); + var position = _io.ReadValidPosition(); L2390: if (computerGrid[position]>10) { _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {computerGrid[position]-10}"); @@ -170,7 +170,7 @@ L2660: _io.WriteLine($"TURN {turnNumber}"); L2670: maxShotCount = 0; L2680: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) { - foreach (var position in Coordinates.All) + foreach (var position in Position.All) { if (computerGrid[position] == shipValue) { @@ -180,7 +180,7 @@ L2680: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) } } L2770: untriedSquareCount=0; - foreach (var position in Coordinates.All) + foreach (var position in Position.All) { if (computerGrid[position]<=10) { untriedSquareCount++; } } @@ -202,7 +202,7 @@ L2970: if (hitShipValue[i]>0) { goto L3800; } } L3000: var shotCount=0; L3010: var shotAttempts=0; -L3020: var (shot, _) = GetRandomShipCoordinates(); +L3020: var (shot, _) = GetRandomShipPosition(); L3030: var strategyNumber=0; //RESTORE L3050: shotAttempts++; L3060: if (shotAttempts>100) { goto L3010; } @@ -299,7 +299,7 @@ L3800: //REM************************USINGEARRAY L3860: for (var i = 1; i <= 12; i++) { if (hitTurnRecord[i]<10) { continue; } - foreach (var position in Coordinates.All) + foreach (var position in Position.All) { if (humanGrid[position]>=10) { @@ -321,7 +321,7 @@ L4030: for (var i = 1; i <= maxShotCount; i++) { L4040: temp[i]=i; } - foreach (var position in Coordinates.All) + foreach (var position in Position.All) { L4090: var Q9=1; L4100: for (var i = 1; i <= maxShotCount; i++) @@ -343,7 +343,7 @@ L4210: ;// NoOp - NEXT S L4230: goto L3380; } - private (Coordinates, Offset) GetRandomShipCoordinates() + private (Position, Offset) GetRandomShipPosition() { var startX = _random.Next(1, 11); var startY = _random.Next(1, 11); @@ -352,11 +352,11 @@ L4230: goto L3380; return (new(startX, startY), new(deltaX, deltaY)); } - private (Coordinates, Offset) GetRandomShipCoordinatesInRange(int shipNumber) + private (Position, Offset) GetRandomShipPositionInRange(int shipNumber) { while (true) { - var (start, delta) = GetRandomShipCoordinates(); + var (start, delta) = GetRandomShipPosition(); var shipSizeLessOne = _shipSize[shipNumber] - 1; var end = start + delta * shipSizeLessOne; if (delta != 0 && end.IsInRange) @@ -371,7 +371,7 @@ internal class Grid { private readonly float[,] _positions = new float[10, 10]; - public float this[Coordinates position] + public float this[Position position] { get => _positions[position.X - 1, position.Y - 1]; set => _positions[position.X - 1, position.Y - 1] = value; @@ -380,18 +380,18 @@ internal class Grid internal static class IOExtensions { - internal static Coordinates ReadCoordinates(this IReadWrite io) + internal static Position ReadPosition(this IReadWrite io) { var (x, y) = io.Read2Numbers(""); return new(x, y); } - internal static Coordinates ReadValidCoordinates(this IReadWrite io) + internal static Position ReadValidPosition(this IReadWrite io) { while (true) { var (x, y) = io.Read2Numbers(""); - if (Coordinates.TryCreateValid(x, y, out var position)) + if (Position.TryCreateValid(x, y, out var position)) { return position; } @@ -400,9 +400,9 @@ internal static class IOExtensions } } -internal record struct Coordinates(int X, int Y) +internal record struct Position(int X, int Y) { - internal Coordinates(float x, float y) + internal Position(float x, float y) : this((int)x, (int)y) { } @@ -410,23 +410,23 @@ internal record struct Coordinates(int X, int Y) public bool IsInRange => X is >= 1 and <= 10 && Y is >= 1 and <= 10; public bool IsOnDiagonal => X == Y; - public static bool TryCreateValid(float x, float y, out Coordinates coordinates) + public static bool TryCreateValid(float x, float y, out Position position) { - coordinates = default; + position = default; if (x != (int)x || y != (int)y) { return false; } - var result = new Coordinates(x, y); + var result = new Position(x, y); if (result.IsInRange) { - coordinates = result; + position = result; return true; } return false; } - public static IEnumerable All + public static IEnumerable All { get { @@ -440,7 +440,7 @@ internal record struct Coordinates(int X, int Y) } } - public IEnumerable Neighbours + public IEnumerable Neighbours { get { @@ -452,13 +452,13 @@ internal record struct Coordinates(int X, int Y) } } - internal double DistanceTo(Coordinates other) + internal double DistanceTo(Position other) => Math.Sqrt((X - other.X) * (X - other.Y) + (Y - other.Y) * (Y - other.Y)); - internal Coordinates BringIntoRange(IRandom random) + internal Position BringIntoRange(IRandom random) => IsInRange ? this : new(BringIntoRange(X, random), BringIntoRange(Y, random)); - private int BringIntoRange(int value, IRandom random) + private static int BringIntoRange(int value, IRandom random) => value switch { < 1 => 1 + (int)random.NextFloat(2.5F), @@ -466,10 +466,10 @@ internal record struct Coordinates(int X, int Y) _ => value }; - public static Coordinates operator +(Coordinates coordinates, Offset offset) - => new(coordinates.X + offset.X, coordinates.Y + offset.Y); + public static Position operator +(Position position, Offset offset) + => new(position.X + offset.X, position.Y + offset.Y); - public static implicit operator Coordinates(int value) => new(value, value); + public static implicit operator Position(int value) => new(value, value); public override string ToString() => $" {X} {Y} "; } @@ -478,8 +478,7 @@ internal record struct Offset(int X, int Y) { public static readonly Offset Zero = 0; - public static Offset operator *(Offset offset, int scale) - => new(offset.X * scale, offset.Y * scale); + public static Offset operator *(Offset offset, int scale) => new(offset.X * scale, offset.Y * scale); public static implicit operator Offset(int value) => new(value, value); From a936de69d1da40bc3c05b149db02f3a2342a3ed3 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Thu, 9 Mar 2023 08:17:39 +1100 Subject: [PATCH 10/29] Introduce Coordinate --- 77_Salvo/csharp/Game.cs | 104 ++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index bd33c3d2..233487d3 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -128,7 +128,7 @@ L2090: for (var i = 1; i <= 7; i++) L2150: var untriedSquareCount=0; foreach (var position in Position.All) { - if (computerGrid[position.X] <= 10) { untriedSquareCount++; } + if (computerGrid[position] <= 10) { untriedSquareCount++; } } L2220: _io.WriteLine($"YOU HAVE {maxShotCount} SHOTS."); if (maxShotCount == 0) { goto L2270; } @@ -373,25 +373,20 @@ internal class Grid public float this[Position position] { - get => _positions[position.X - 1, position.Y - 1]; - set => _positions[position.X - 1, position.Y - 1] = value; + get => _positions[position.X, position.Y]; + set => _positions[position.X, position.Y] = value; } } internal static class IOExtensions { - internal static Position ReadPosition(this IReadWrite io) - { - var (x, y) = io.Read2Numbers(""); - return new(x, y); - } + internal static Position ReadPosition(this IReadWrite io) => Position.Create(io.Read2Numbers("")); internal static Position ReadValidPosition(this IReadWrite io) { while (true) { - var (x, y) = io.Read2Numbers(""); - if (Position.TryCreateValid(x, y, out var position)) + if (Position.TryCreateValid(io.Read2Numbers(""), out var position)) { return position; } @@ -400,45 +395,27 @@ internal static class IOExtensions } } -internal record struct Position(int X, int Y) +internal record struct Position(Coordinate X, Coordinate Y) { - internal Position(float x, float y) - : this((int)x, (int)y) - { - } - - public bool IsInRange => X is >= 1 and <= 10 && Y is >= 1 and <= 10; + public bool IsInRange => X.IsInRange && Y.IsInRange; public bool IsOnDiagonal => X == Y; - public static bool TryCreateValid(float x, float y, out Position position) + public static Position Create((float X, float Y) coordinates) => new(coordinates.X, coordinates.Y); + + public static bool TryCreateValid((float X, float Y) coordinates, out Position position) { - position = default; - if (x != (int)x || y != (int)y) { return false; } - - var result = new Position(x, y); - - if (result.IsInRange) + if (Coordinate.TryCreateValid(coordinates.X, out var x) && Coordinate.TryCreateValid(coordinates.Y, out var y)) { - position = result; + position = new(x, y); return true; } + position = default; return false; } public static IEnumerable All - { - get - { - for (int x = 1; x <= 10; x++) - { - for (int y = 1; y <= 10; y++) - { - yield return new(x, y); - } - } - } - } + => Coordinate.Range.SelectMany(x => Coordinate.Range.Select(y => new Position(x, y))); public IEnumerable Neighbours { @@ -456,22 +433,55 @@ internal record struct Position(int X, int Y) => Math.Sqrt((X - other.X) * (X - other.Y) + (Y - other.Y) * (Y - other.Y)); internal Position BringIntoRange(IRandom random) - => IsInRange ? this : new(BringIntoRange(X, random), BringIntoRange(Y, random)); - - private static int BringIntoRange(int value, IRandom random) - => value switch - { - < 1 => 1 + (int)random.NextFloat(2.5F), - > 10 => 10 - (int)random.NextFloat(2.5F), - _ => value - }; + => IsInRange ? this : new(X.BringIntoRange(random), Y.BringIntoRange(random)); public static Position operator +(Position position, Offset offset) => new(position.X + offset.X, position.Y + offset.Y); public static implicit operator Position(int value) => new(value, value); - public override string ToString() => $" {X} {Y} "; + public override string ToString() => $"{X}{Y}"; +} + +internal record struct Coordinate(int Value) +{ + public static IEnumerable Range => Enumerable.Range(0, 10).Select(v => new Coordinate(v)); + + public bool IsInRange => Value is >= 0 and <= 9; + + public static Coordinate Create(float value) => new((int)value - 1); + + public static bool TryCreateValid(float value, out Coordinate coordinate) + { + coordinate = default; + if (value != (int)value) { return false; } + + var result = Create(value); + + if (result.IsInRange) + { + coordinate = result; + return true; + } + + return false; + } + + public Coordinate BringIntoRange(IRandom random) + => Value switch + { + < 0 => new(0 + (int)random.NextFloat(2.5F)), + > 9 => new(9 - (int)random.NextFloat(2.5F)), + _ => this + }; + + public static implicit operator Coordinate(float value) => new((int)value); + public static implicit operator int(Coordinate coordinate) => coordinate.Value; + + public static Coordinate operator +(Coordinate coordinate, int offset) => new(coordinate.Value + offset); + public static int operator -(Coordinate a, Coordinate b) => a.Value - b.Value; + + public override string ToString() => $" {Value + 1} "; } internal record struct Offset(int X, int Y) From 579e18d8c63e1d45c8a8749c7b0ab7a9b648892f Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Mon, 13 Mar 2023 08:33:48 +1100 Subject: [PATCH 11/29] Fix some bugs --- 77_Salvo/csharp/Game.cs | 48 +++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 233487d3..bcc16477 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -35,13 +35,13 @@ L1190: var computerGrid = new Grid(); L1240: for (var K = 3; K >= 0; K--) { L1250: var shipGenerationAttempts=0; -L1260: var (start, delta) = GetRandomShipPositionInRange(K); +L1260: var (start, delta) = _random.GetRandomShipPositionInRange(_shipSize[K]); L1340: shipGenerationAttempts++; L1350: if (shipGenerationAttempts>25) { goto L1190; } // determine ship position -L1360: for (var i = _shipFirstIndex[K]; i <= _shipFirstIndex[K] + _shipSize[K] - 1; i++) +L1360: for (var i = 0; i < _shipSize[K]; i++) { - temp[i] = start + delta * i; + temp[_shipFirstIndex[K] + i] = start + delta * i; } L1400: var firstIndex=_shipFirstIndex[K]; // detect proximity to previous ships @@ -153,14 +153,15 @@ L2390: if (computerGrid[position]>10) } L2460: for (var W = 1; W <= maxShotCount; W++) { - _io.WriteLine(computerGrid[shots[W]] switch + var hit = computerGrid[shots[W]] switch { 3 => "YOU HIT MY BATTLESHIP.", 2 => "YOU HIT MY CRUISER.", 1 => "YOU HIT MY DESTROYER.", .5F => "YOU HIT MY DESTROYER.", - _ => throw new InvalidOperationException($"Unexpected value {computerGrid[shots[W]]}") - }); + _ => null + }; + if (hit is not null) { _io.WriteLine(); } L2510: computerGrid[shots[W]] = 10+turnNumber; } L2620: if (startResponse == "YES") { goto L2670; } @@ -202,7 +203,7 @@ L2970: if (hitShipValue[i]>0) { goto L3800; } } L3000: var shotCount=0; L3010: var shotAttempts=0; -L3020: var (shot, _) = GetRandomShipPosition(); +L3020: var (shot, _) = _random.NextShipPosition(); L3030: var strategyNumber=0; //RESTORE L3050: shotAttempts++; L3060: if (shotAttempts>100) { goto L3010; } @@ -342,22 +343,30 @@ L4210: ;// NoOp - NEXT S } L4230: goto L3380; } +} - private (Position, Offset) GetRandomShipPosition() +internal static class RandomExtensions +{ + internal static (Position, Offset) NextShipPosition(this IRandom random) { - var startX = _random.Next(1, 11); - var startY = _random.Next(1, 11); - var deltaY = _random.Next(-1, 2); - var deltaX = _random.Next(-1, 2); + var startX = random.NextCoordinate(); + var startY = random.NextCoordinate(); + var deltaY = random.NextOffset(); + var deltaX = random.NextOffset(); return (new(startX, startY), new(deltaX, deltaY)); } - private (Position, Offset) GetRandomShipPositionInRange(int shipNumber) + private static Coordinate NextCoordinate(this IRandom random) + => random.Next(Coordinate.MinValue, Coordinate.MaxValue + 1); + + private static int NextOffset(this IRandom random) => random.Next(-1, 2); + + internal static (Position, Offset) GetRandomShipPositionInRange(this IRandom random, int shipSize) { while (true) { - var (start, delta) = GetRandomShipPosition(); - var shipSizeLessOne = _shipSize[shipNumber] - 1; + var (start, delta) = random.NextShipPosition(); + var shipSizeLessOne = shipSize - 1; var end = start + delta * shipSizeLessOne; if (delta != 0 && end.IsInRange) { @@ -445,9 +454,12 @@ internal record struct Position(Coordinate X, Coordinate Y) internal record struct Coordinate(int Value) { + public const int MinValue = 0; + public const int MaxValue = 9; + public static IEnumerable Range => Enumerable.Range(0, 10).Select(v => new Coordinate(v)); - public bool IsInRange => Value is >= 0 and <= 9; + public bool IsInRange => Value is >= MinValue and <= MaxValue; public static Coordinate Create(float value) => new((int)value - 1); @@ -470,8 +482,8 @@ internal record struct Coordinate(int Value) public Coordinate BringIntoRange(IRandom random) => Value switch { - < 0 => new(0 + (int)random.NextFloat(2.5F)), - > 9 => new(9 - (int)random.NextFloat(2.5F)), + < MinValue => new(MinValue + (int)random.NextFloat(2.5F)), + > MaxValue => new(MaxValue - (int)random.NextFloat(2.5F)), _ => this }; From f5b2fe6e8a293ca066d65c6f4103ce0448c2faf1 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Mon, 27 Mar 2023 08:10:43 +1100 Subject: [PATCH 12/29] Add Ship type --- 77_Salvo/csharp/Game.cs | 211 +++++++++++++++++++++++++++------------- 1 file changed, 141 insertions(+), 70 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index bcc16477..ddf9aa13 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -1,3 +1,4 @@ +using System.Collections.Immutable; using Games.Common.Randomness; namespace Salvo; @@ -21,84 +22,26 @@ internal class Game { _io.Write(Streams.Title); -L1040: var humanGrid = new Grid(); var hitTurnRecord = new int[13]; var shots = new Position[8]; var temp = new Position[13]; var hitShipValue = new float[13]; -L1060: for (var i = 1; i <= 12; i++) + + for (var i = 1; i <= 12; i++) { -L1070: hitTurnRecord[i] = -1; -L1080: hitShipValue[i] = -1; - } -L1190: var computerGrid = new Grid(); -L1240: for (var K = 3; K >= 0; K--) - { -L1250: var shipGenerationAttempts=0; -L1260: var (start, delta) = _random.GetRandomShipPositionInRange(_shipSize[K]); -L1340: shipGenerationAttempts++; -L1350: if (shipGenerationAttempts>25) { goto L1190; } - // determine ship position -L1360: for (var i = 0; i < _shipSize[K]; i++) - { - temp[_shipFirstIndex[K] + i] = start + delta * i; - } -L1400: var firstIndex=_shipFirstIndex[K]; - // detect proximity to previous ships - for (var i = firstIndex; i <= firstIndex+_shipSize[K] - 1; i++) - { -L1415: if (firstIndex<2) { continue; } // Only true for the Battleship -L1420: for (var j = 1; j <= firstIndex-1; j++) - { -L1430: if (temp[j].DistanceTo(temp[i]) < 3.59) { goto L1260; } - } - } - // put ship on board -L1460: for (var i = firstIndex; i <= firstIndex + _shipSize[K] - 1; i++) - { - computerGrid[temp[i]] = _shipValue[K]; - } - } - _io.WriteLine("ENTER POSITION FOR..."); - _io.WriteLine("BATTLESHIP"); - for (var i = 1; i <= 5; i++) - { - humanGrid[_io.ReadPosition()] = 3; - } - _io.WriteLine("CRUISER"); - for (var i = 1; i <= 3; i++) - { - humanGrid[_io.ReadPosition()] = 2; - } - _io.WriteLine("DESTROYER"); - for (var i = 1; i <= 2; i++) - { - humanGrid[_io.ReadPosition()] = 1; - } - _io.WriteLine("DESTROYER"); - for (var i = 1; i <= 2; i++) - { - humanGrid[_io.ReadPosition()] = 0.5F; + hitTurnRecord[i] = -1; + hitShipValue[i] = -1; } + var computerGrid = new Grid().PositionShips(_random); + var humanGrid = new Grid().PositionShips(_io); + var startResponse = _io.ReadString("DO YOU WANT TO START"); while (startResponse == "WHERE ARE YOUR SHIPS?") { - _io.WriteLine("BATTLESHIP"); - for (var i = 1; i <= 5; i++) + foreach (var ship in computerGrid.Ships) { - _io.WriteLine(temp[i]); + _io.WriteLine(ship); } - _io.WriteLine("CRUISER"); - _io.WriteLine(temp[6]); - _io.WriteLine(temp[7]); - _io.WriteLine(temp[8]); - _io.WriteLine("DESTROYER"); - _io.WriteLine(temp[9]); - _io.WriteLine(temp[10]); - _io.WriteLine("DESTROYER"); - _io.WriteLine(temp[11]); - _io.WriteLine(temp[12]); - startResponse = _io.ReadString("DO YOU WANT TO START"); } L1890: var turnNumber=0; @@ -109,7 +52,7 @@ L1950: if (startResponse != "YES") { goto L1990; } L1960: turnNumber++; L1970: _io.WriteLine(); L1980: _io.WriteLine($"TURN {turnNumber}"); -L1990: var maxShotCount=0; +L1990: var maxShotCount = 0; L2000: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) { foreach (var position in Position.All) @@ -345,6 +288,73 @@ L4230: goto L3380; } } +internal abstract class Ship +{ + private readonly List _positions = new(); + + internal virtual string Name => GetType().Name; + internal abstract int Shots { get; } + internal abstract int Size { get; } + internal abstract float Value { get; } + internal IEnumerable Positions => _positions; + internal bool IsAfloat => _positions.Count > 0; + + internal void SetPositionInRange(IRandom random) + { + var (start, delta) = random.GetRandomShipPositionInRange(Size); + for (var i = 0; i < Size; i++) + { + _positions[i] = start + delta * i; + } + } + + internal void GetPosition(IReadWrite io, Grid grid) + { + io.WriteLine(Name); + for (var i = 0; i < Size; i++) + { + var position = io.ReadPosition(); + _positions[i] = position; + grid[position] = Value; + } + } + + internal bool IsHit(Position position) => _positions.Remove(position); + + internal float DistanceTo(Ship other) + => _positions.SelectMany(a => other._positions.Select(b => a.DistanceTo(b))).Min(); + + public override string ToString() + => string.Join(Environment.NewLine, _positions.Select(p => p.ToString()).Prepend(Name)); +} + +internal sealed class Battleship : Ship +{ + internal override int Shots => 3; + internal override int Size => 5; + internal override float Value => 3; +} + +internal sealed class Cruiser : Ship +{ + internal override int Shots => 2; + internal override int Size => 3; + internal override float Value => 2; +} + +internal sealed class Destroyer : Ship +{ + internal Destroyer(string nameIndex) + { + Name = $"{base.Name}<{nameIndex}>"; + } + + internal override string Name { get; } + internal override int Shots => 1; + internal override int Size => 2; + internal override float Value => Name.EndsWith("") ? 1 : 0.5F; +} + internal static class RandomExtensions { internal static (Position, Offset) NextShipPosition(this IRandom random) @@ -378,6 +388,67 @@ internal static class RandomExtensions internal class Grid { + private readonly List _ships = new() + { + new Battleship(), + new Cruiser(), + new Destroyer("A"), + new Destroyer("B") + }; + + internal IEnumerable Ships => _ships.AsEnumerable(); + + internal Grid PositionShips(IRandom random) + { + while (true) + { + var allPositioned = false; + for (var i = 0; i < _ships.Count; i++) + { + if (!TryPositionShip(i)) { break; } + if (i == _ships.Count - 1) { allPositioned = true; } + } + if (allPositioned) { break; } + } + + foreach (var ship in _ships) + { + foreach (var position in ship.Positions) + { + this[position] = ship.Value; + } + } + + return this; + + bool TryPositionShip(int i) + { + while (true) + { + var shipGenerationAttempts = 0; + _ships[i].SetPositionInRange(random); + if (i == 0) { return true; } + shipGenerationAttempts++; + if (shipGenerationAttempts > 25) { return false; } + for (var j = 0; j < i; j++) + { + if (_ships[i].DistanceTo(_ships[j]) >= 3.59) { return true; } + } + } + } + } + + internal Grid PositionShips(IReadWrite io) + { + io.WriteLine("ENTER POSITION FOR..."); + foreach (var ship in _ships) + { + ship.GetPosition(io, this); + } + + return this; + } + private readonly float[,] _positions = new float[10, 10]; public float this[Position position] @@ -438,8 +509,8 @@ internal record struct Position(Coordinate X, Coordinate Y) } } - internal double DistanceTo(Position other) - => Math.Sqrt((X - other.X) * (X - other.Y) + (Y - other.Y) * (Y - other.Y)); + internal float DistanceTo(Position other) + => (float)Math.Sqrt((X - other.X) * (X - other.Y) + (Y - other.Y) * (Y - other.Y)); internal Position BringIntoRange(IRandom random) => IsInRange ? this : new(X.BringIntoRange(random), Y.BringIntoRange(random)); From b5d573f075f280233c772ffefa3fb0bbbb033ec0 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Tue, 4 Apr 2023 17:29:50 +1000 Subject: [PATCH 13/29] More use of grid --- 77_Salvo/csharp/Game.cs | 116 ++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 70 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index ddf9aa13..14504304 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -5,10 +5,6 @@ namespace Salvo; internal class Game { - private static readonly float[] _shipValue = new[] { 0.5F, 1, 2, 3 }; - private static readonly int[] _shipFirstIndex = new[] { 11, 9, 6, 1 }; - private static readonly int[] _shipSize = new[] { 2, 2, 3, 5 }; - private readonly IReadWrite _io; private readonly IRandom _random; @@ -34,7 +30,6 @@ internal class Game } var computerGrid = new Grid().PositionShips(_random); var humanGrid = new Grid().PositionShips(_io); - var startResponse = _io.ReadString("DO YOU WANT TO START"); while (startResponse == "WHERE ARE YOUR SHIPS?") { @@ -52,30 +47,14 @@ L1950: if (startResponse != "YES") { goto L1990; } L1960: turnNumber++; L1970: _io.WriteLine(); L1980: _io.WriteLine($"TURN {turnNumber}"); -L1990: var maxShotCount = 0; -L2000: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) - { - foreach (var position in Position.All) - { - if (humanGrid[position] == shipValue) - { - maxShotCount+=(int)(shipValue+.5F); - break; - } - } - } +L1990: var maxShotCount = humanGrid.Ships.Sum(s => s.Shots); L2090: for (var i = 1; i <= 7; i++) { shots[i] = temp[i] = 0; } -L2150: var untriedSquareCount=0; - foreach (var position in Position.All) - { - if (computerGrid[position] <= 10) { untriedSquareCount++; } - } L2220: _io.WriteLine($"YOU HAVE {maxShotCount} SHOTS."); if (maxShotCount == 0) { goto L2270; } -L2230: if (maxShotCount > untriedSquareCount) +L2230: if (maxShotCount > computerGrid.UntriedSquareCount) { _io.WriteLine("YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES."); L2250: goto L2890; @@ -111,32 +90,15 @@ L2620: if (startResponse == "YES") { goto L2670; } L2640: turnNumber++; L2650: _io.WriteLine(); L2660: _io.WriteLine($"TURN {turnNumber}"); -L2670: maxShotCount = 0; -L2680: for (var shipValue = .5F; shipValue <= 3; shipValue += .5F) - { - foreach (var position in Position.All) - { - if (computerGrid[position] == shipValue) - { - maxShotCount += (int)(shipValue+.5F); - break; - } - } - } -L2770: untriedSquareCount=0; - foreach (var position in Position.All) - { - if (computerGrid[position]<=10) { untriedSquareCount++; } - } +L2670: maxShotCount = computerGrid.Ships.Sum(s => s.Shots); L2840: _io.WriteLine($"I HAVE {maxShotCount} SHOTS."); -L2850: if (untriedSquareCount>maxShotCount) { goto L2880; } +L2850: if (humanGrid.UntriedSquareCount > maxShotCount) { goto L2880; } L2860: _io.WriteLine("I HAVE MORE SHOTS THAN BLANK SQUARES."); -L2870: goto L2270; +L2270: _io.WriteLine("I HAVE WON."); + return; L2880: if (maxShotCount != 0) { goto L2960; } L2890: _io.WriteLine("YOU HAVE WON."); L2900: return; -L2270: _io.WriteLine("I HAVE WON."); - return; L2960: for (var i = 1; i <= 12; i++) @@ -304,20 +266,11 @@ internal abstract class Ship var (start, delta) = random.GetRandomShipPositionInRange(Size); for (var i = 0; i < Size; i++) { - _positions[i] = start + delta * i; + _positions.Add(start + delta * i); } } - internal void GetPosition(IReadWrite io, Grid grid) - { - io.WriteLine(Name); - for (var i = 0; i < Size; i++) - { - var position = io.ReadPosition(); - _positions[i] = position; - grid[position] = Value; - } - } + internal void AddPosition(Position position) => _positions.Add(position); internal bool IsHit(Position position) => _positions.Remove(position); @@ -395,7 +348,21 @@ internal class Grid new Destroyer("A"), new Destroyer("B") }; + private readonly Dictionary _shots = new(); + public float this[Position position] + { + get => _shots.TryGetValue(position, out var value) + ? value + 10 + : _ships.FirstOrDefault(s => s.Positions.Contains(position))?.Value ?? 0; + set + { + _ = _ships.FirstOrDefault(s => s.IsHit(position)); + _shots[position] = (int)value - 10; + } + } + + internal int UntriedSquareCount => 100 - _shots.Count; internal IEnumerable Ships => _ships.AsEnumerable(); internal Grid PositionShips(IRandom random) @@ -439,23 +406,10 @@ internal class Grid } internal Grid PositionShips(IReadWrite io) - { - io.WriteLine("ENTER POSITION FOR..."); - foreach (var ship in _ships) - { - ship.GetPosition(io, this); - } - + { + io.ReadShipPositions(this); return this; } - - private readonly float[,] _positions = new float[10, 10]; - - public float this[Position position] - { - get => _positions[position.X, position.Y]; - set => _positions[position.X, position.Y] = value; - } } internal static class IOExtensions @@ -473,6 +427,28 @@ internal static class IOExtensions io.WriteLine("ILLEGAL, ENTER AGAIN."); } } + + + internal static void ReadShipPositions(this IReadWrite io, Grid grid) + { + io.WriteLine("ENTER POSITION FOR..."); + foreach (var ship in grid.Ships) + { + io.ReadPosition(ship, grid); + } + } + + internal static void ReadPosition(this IReadWrite io, Ship ship, Grid grid) + { + io.WriteLine(ship.Name); + for (var i = 0; i < ship.Size; i++) + { + var position = io.ReadPosition(); + ship.AddPosition(position); + grid[position] = ship.Value; + } + } + } internal record struct Position(Coordinate X, Coordinate Y) From 69240e50a0b175ad1d46b75f5f00fd226fa2c927 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Wed, 12 Apr 2023 07:49:10 +1000 Subject: [PATCH 14/29] More improvments to Ships and Grid --- 77_Salvo/csharp/Game.cs | 189 ++++++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 84 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 14504304..312796db 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -28,8 +28,8 @@ internal class Game hitTurnRecord[i] = -1; hitShipValue[i] = -1; } - var computerGrid = new Grid().PositionShips(_random); - var humanGrid = new Grid().PositionShips(_io); + var computerGrid = new Grid(_random); + var humanGrid = new Grid(_io); var startResponse = _io.ReadString("DO YOU WANT TO START"); while (startResponse == "WHERE ARE YOUR SHIPS?") { @@ -254,15 +254,16 @@ internal abstract class Ship { private readonly List _positions = new(); - internal virtual string Name => GetType().Name; - internal abstract int Shots { get; } - internal abstract int Size { get; } - internal abstract float Value { get; } - internal IEnumerable Positions => _positions; - internal bool IsAfloat => _positions.Count > 0; - - internal void SetPositionInRange(IRandom random) + protected Ship(IReadWrite io, string? nameSuffix = null) { + Name = GetType().Name + nameSuffix; + _positions = io.ReadPositions(Name, Size).ToList(); + } + + protected Ship(IRandom random, string? nameSuffix = null) + { + Name = GetType().Name + nameSuffix; + var (start, delta) = random.GetRandomShipPositionInRange(Size); for (var i = 0; i < Size; i++) { @@ -270,7 +271,12 @@ internal abstract class Ship } } - internal void AddPosition(Position position) => _positions.Add(position); + internal string Name { get; } + internal abstract int Shots { get; } + internal abstract int Size { get; } + internal abstract float Value { get; } + internal IEnumerable Positions => _positions; + internal bool IsAfloat => _positions.Count > 0; internal bool IsHit(Position position) => _positions.Remove(position); @@ -283,6 +289,16 @@ internal abstract class Ship internal sealed class Battleship : Ship { + internal Battleship(IReadWrite io) + : base(io) + { + } + + internal Battleship(IRandom random) + : base(random) + { + } + internal override int Shots => 3; internal override int Size => 5; internal override float Value => 3; @@ -290,6 +306,16 @@ internal sealed class Battleship : Ship internal sealed class Cruiser : Ship { + internal Cruiser(IReadWrite io) + : base(io) + { + } + + internal Cruiser(IRandom random) + : base(random) + { + } + internal override int Shots => 2; internal override int Size => 3; internal override float Value => 2; @@ -297,12 +323,16 @@ internal sealed class Cruiser : Ship internal sealed class Destroyer : Ship { - internal Destroyer(string nameIndex) + internal Destroyer(string nameIndex, IReadWrite io) + : base(io, $"<{nameIndex}>") + { + } + + internal Destroyer(string nameIndex, IRandom random) + : base(random, $"<{nameIndex}>") { - Name = $"{base.Name}<{nameIndex}>"; } - internal override string Name { get; } internal override int Shots => 1; internal override int Size => 2; internal override float Value => Name.EndsWith("") ? 1 : 0.5F; @@ -341,15 +371,65 @@ internal static class RandomExtensions internal class Grid { - private readonly List _ships = new() - { - new Battleship(), - new Cruiser(), - new Destroyer("A"), - new Destroyer("B") - }; + private readonly List _ships; private readonly Dictionary _shots = new(); + internal Grid() + { + _ships = new(); + } + + internal Grid(IReadWrite io) + { + io.WriteLine("ENTER POSITION FOR..."); + _ships = new() + { + new Battleship(io), + new Cruiser(io), + new Destroyer("A", io), + new Destroyer("B", io) + }; + } + + internal Grid(IRandom random) + { + _ships = new(); + while (true) + { + _ships.Add(new Battleship(random)); + if (TryPositionShip(() => new Cruiser(random)) && + TryPositionShip(() => new Destroyer("A", random)) && + TryPositionShip(() => new Destroyer("B", random))) + { + break; + } + _ships.Clear(); + } + + foreach (var ship in _ships) + { + foreach (var position in ship.Positions) + { + this[position] = ship.Value; + } + } + + bool TryPositionShip(Func shipFactory) + { + while (true) + { + var shipGenerationAttempts = 0; + var ship = shipFactory.Invoke(); + shipGenerationAttempts++; + if (shipGenerationAttempts > 25) { return false; } + foreach (var previousShip in _ships) + { + if (ship.DistanceTo(previousShip) >= 3.59) { return true; } + } + } + } + } + public float this[Position position] { get => _shots.TryGetValue(position, out var value) @@ -364,52 +444,6 @@ internal class Grid internal int UntriedSquareCount => 100 - _shots.Count; internal IEnumerable Ships => _ships.AsEnumerable(); - - internal Grid PositionShips(IRandom random) - { - while (true) - { - var allPositioned = false; - for (var i = 0; i < _ships.Count; i++) - { - if (!TryPositionShip(i)) { break; } - if (i == _ships.Count - 1) { allPositioned = true; } - } - if (allPositioned) { break; } - } - - foreach (var ship in _ships) - { - foreach (var position in ship.Positions) - { - this[position] = ship.Value; - } - } - - return this; - - bool TryPositionShip(int i) - { - while (true) - { - var shipGenerationAttempts = 0; - _ships[i].SetPositionInRange(random); - if (i == 0) { return true; } - shipGenerationAttempts++; - if (shipGenerationAttempts > 25) { return false; } - for (var j = 0; j < i; j++) - { - if (_ships[i].DistanceTo(_ships[j]) >= 3.59) { return true; } - } - } - } - } - - internal Grid PositionShips(IReadWrite io) - { - io.ReadShipPositions(this); - return this; - } } internal static class IOExtensions @@ -428,27 +462,14 @@ internal static class IOExtensions } } - - internal static void ReadShipPositions(this IReadWrite io, Grid grid) + internal static IEnumerable ReadPositions(this IReadWrite io, string shipName, int shipSize) { - io.WriteLine("ENTER POSITION FOR..."); - foreach (var ship in grid.Ships) + io.WriteLine(shipName); + for (var i = 0; i < shipSize; i++) { - io.ReadPosition(ship, grid); + yield return io.ReadPosition(); } } - - internal static void ReadPosition(this IReadWrite io, Ship ship, Grid grid) - { - io.WriteLine(ship.Name); - for (var i = 0; i < ship.Size; i++) - { - var position = io.ReadPosition(); - ship.AddPosition(position); - grid[position] = ship.Value; - } - } - } internal record struct Position(Coordinate X, Coordinate Y) From b2de1b09e14bc16df3c6f36d896cc0555b75cfda Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sat, 22 Apr 2023 07:56:35 +1000 Subject: [PATCH 15/29] CSHARP-77 Add HumnShotSelctor and some fixes --- 77_Salvo/csharp/Game.cs | 143 +++++++++++++++++++++++++++------------- 77_Salvo/salvo.bas | 2 - 2 files changed, 98 insertions(+), 47 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 312796db..491cebdb 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -19,7 +19,6 @@ internal class Game _io.Write(Streams.Title); var hitTurnRecord = new int[13]; - var shots = new Position[8]; var temp = new Position[13]; var hitShipValue = new float[13]; @@ -30,6 +29,7 @@ internal class Game } var computerGrid = new Grid(_random); var humanGrid = new Grid(_io); + var humanShotSelector = new HumanShotSelector(humanGrid, computerGrid); var startResponse = _io.ReadString("DO YOU WANT TO START"); while (startResponse == "WHERE ARE YOUR SHIPS?") { @@ -47,11 +47,7 @@ L1950: if (startResponse != "YES") { goto L1990; } L1960: turnNumber++; L1970: _io.WriteLine(); L1980: _io.WriteLine($"TURN {turnNumber}"); -L1990: var maxShotCount = humanGrid.Ships.Sum(s => s.Shots); -L2090: for (var i = 1; i <= 7; i++) - { - shots[i] = temp[i] = 0; - } +L1990: var maxShotCount = humanShotSelector.GetShotCount(); L2220: _io.WriteLine($"YOU HAVE {maxShotCount} SHOTS."); if (maxShotCount == 0) { goto L2270; } L2230: if (maxShotCount > computerGrid.UntriedSquareCount) @@ -59,33 +55,13 @@ L2230: if (maxShotCount > computerGrid.UntriedSquareCount) _io.WriteLine("YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES."); L2250: goto L2890; } -L2290: for (var i = 1; i <= maxShotCount; i++) + foreach (var shot1 in humanShotSelector.GetShots(_io)) { - while (true) + if (computerGrid.IsHit(shot1, turnNumber, out var shipName)) { - var position = _io.ReadValidPosition(); -L2390: if (computerGrid[position]>10) - { - _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {computerGrid[position]-10}"); - continue; - } - shots[i]= position; - break; + _io.WriteLine($"YOU HIT MY {shipName}."); } } -L2460: for (var W = 1; W <= maxShotCount; W++) - { - var hit = computerGrid[shots[W]] switch - { - 3 => "YOU HIT MY BATTLESHIP.", - 2 => "YOU HIT MY CRUISER.", - 1 => "YOU HIT MY DESTROYER.", - .5F => "YOU HIT MY DESTROYER.", - _ => null - }; - if (hit is not null) { _io.WriteLine(); } -L2510: computerGrid[shots[W]] = 10+turnNumber; - } L2620: if (startResponse == "YES") { goto L2670; } L2640: turnNumber++; L2650: _io.WriteLine(); @@ -100,7 +76,6 @@ L2880: if (maxShotCount != 0) { goto L2960; } L2890: _io.WriteLine("YOU HAVE WON."); L2900: return; - L2960: for (var i = 1; i <= 12; i++) { // if damaged ships @@ -250,6 +225,72 @@ L4230: goto L3380; } } +internal abstract class ShotSelector +{ + internal ShotSelector(Grid source, Grid target) + { + Source = source; + Target = target; + } + + protected Grid Source { get; } + protected Grid Target { get; } + + public int GetShotCount() => Source.Ships.Sum(s => s.Shots); +} + +internal abstract class ComputerShotSelector : ShotSelector +{ + private readonly bool _displayShots; + + internal ComputerShotSelector(Grid source, Grid target, bool displayShots) + : base(source, target) + { + _displayShots = displayShots; + } + + private void DisplayShots(IEnumerable shots, IReadWrite io) + { + if (_displayShots) + { + foreach (var shot in shots) + { + io.WriteLine(shot); + } + } + } +} + +internal class HumanShotSelector : ShotSelector +{ + public HumanShotSelector(Grid source, Grid target) + : base(source, target) + { + } + + public IEnumerable GetShots(IReadWrite io) + { + var shots = new Position[GetShotCount()]; + + for (var i = 0; i < shots.Length; i++) + { + while (true) + { + var position = io.ReadValidPosition(); + if (Target.WasTargetedAt(position, out var turnTargeted)) + { + io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {turnTargeted}"); + continue; + } + shots[i] = position; + break; + } + } + + return shots; + } +} + internal abstract class Ship { private readonly List _positions = new(); @@ -276,7 +317,7 @@ internal abstract class Ship internal abstract int Size { get; } internal abstract float Value { get; } internal IEnumerable Positions => _positions; - internal bool IsAfloat => _positions.Count > 0; + internal bool IsDestroyed => _positions.Count == 0; internal bool IsHit(Position position) => _positions.Remove(position); @@ -381,7 +422,7 @@ internal class Grid internal Grid(IReadWrite io) { - io.WriteLine("ENTER POSITION FOR..."); + io.WriteLine("ENTER COORDINATES FOR..."); _ships = new() { new Battleship(io), @@ -401,30 +442,23 @@ internal class Grid TryPositionShip(() => new Destroyer("A", random)) && TryPositionShip(() => new Destroyer("B", random))) { - break; + return; } _ships.Clear(); } - foreach (var ship in _ships) - { - foreach (var position in ship.Positions) - { - this[position] = ship.Value; - } - } - bool TryPositionShip(Func shipFactory) { + var shipGenerationAttempts = 0; while (true) { - var shipGenerationAttempts = 0; var ship = shipFactory.Invoke(); shipGenerationAttempts++; if (shipGenerationAttempts > 25) { return false; } - foreach (var previousShip in _ships) + if (_ships.Min(ship.DistanceTo) >= 3.59) { - if (ship.DistanceTo(previousShip) >= 3.59) { return true; } + _ships.Add(ship); + return true; } } } @@ -444,6 +478,22 @@ 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, out string? shipName) + { + shipName = null; + _shots[position] = turnNumber; + + var ship = _ships.FirstOrDefault(s => s.IsHit(position)); + if (ship == null) { return false; } + + if (ship.IsDestroyed) { _ships.Remove(ship); } + + return true; + } } internal static class IOExtensions @@ -507,7 +557,10 @@ internal record struct Position(Coordinate X, Coordinate Y) } internal float DistanceTo(Position other) - => (float)Math.Sqrt((X - other.X) * (X - other.Y) + (Y - other.Y) * (Y - other.Y)); + { + var (deltaX, deltaY) = (X - other.X, Y - other.Y); + return (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); + } internal Position BringIntoRange(IRandom random) => IsInRange ? this : new(X.BringIntoRange(random), Y.BringIntoRange(random)); diff --git a/77_Salvo/salvo.bas b/77_Salvo/salvo.bas index 71036081..6a85d715 100644 --- a/77_Salvo/salvo.bas +++ b/77_Salvo/salvo.bas @@ -43,8 +43,6 @@ 1410 FOR Z2= U8 TO U8+FNB(K) 1415 IF U8<2 THEN 1450 1420 FOR Z3=1 TO U8-1 -1421 D = SQR((F(Z3)-F(Z2))^2 + (G(Z3)-G(Z2))^2) -1422 PRINT "(";F(Z3);G(Z3);")","(";F(Z2);G(Z2);")",D 1430 IF SQR((F(Z3)-F(Z2))^2 + (G(Z3)-G(Z2))^2) < 3.59 THEN 1260 1440 NEXT Z3 1450 NEXT Z2 From 3e4fb4fe237bbea7232232551a4b36960c418f03 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sat, 22 Apr 2023 08:09:50 +1000 Subject: [PATCH 16/29] Split classes to files --- 77_Salvo/csharp/Coordinate.cs | 45 ++ 77_Salvo/csharp/Extensions/IOExtensions.cs | 27 ++ .../csharp/Extensions/RandomExtensions.cs | 32 ++ 77_Salvo/csharp/Game.cs | 419 +----------------- 77_Salvo/csharp/Grid.cs | 89 ++++ 77_Salvo/csharp/Offset.cs | 25 ++ 77_Salvo/csharp/Position.cs | 52 +++ 77_Salvo/csharp/Program.cs | 3 +- 77_Salvo/csharp/Ships/Battleship.cs | 18 + 77_Salvo/csharp/Ships/Cruiser.cs | 18 + 77_Salvo/csharp/Ships/Destroyer.cs | 18 + 77_Salvo/csharp/Ships/Ship.cs | 38 ++ .../csharp/Targetting/ComputerShotSelector.cs | 23 + .../csharp/Targetting/HumanShotSelector.cs | 31 ++ 77_Salvo/csharp/Targetting/ShotSelector.cs | 15 + 15 files changed, 434 insertions(+), 419 deletions(-) create mode 100644 77_Salvo/csharp/Coordinate.cs create mode 100644 77_Salvo/csharp/Extensions/IOExtensions.cs create mode 100644 77_Salvo/csharp/Extensions/RandomExtensions.cs create mode 100644 77_Salvo/csharp/Grid.cs create mode 100644 77_Salvo/csharp/Offset.cs create mode 100644 77_Salvo/csharp/Position.cs create mode 100644 77_Salvo/csharp/Ships/Battleship.cs create mode 100644 77_Salvo/csharp/Ships/Cruiser.cs create mode 100644 77_Salvo/csharp/Ships/Destroyer.cs create mode 100644 77_Salvo/csharp/Ships/Ship.cs create mode 100644 77_Salvo/csharp/Targetting/ComputerShotSelector.cs create mode 100644 77_Salvo/csharp/Targetting/HumanShotSelector.cs create mode 100644 77_Salvo/csharp/Targetting/ShotSelector.cs diff --git a/77_Salvo/csharp/Coordinate.cs b/77_Salvo/csharp/Coordinate.cs new file mode 100644 index 00000000..45d149c1 --- /dev/null +++ b/77_Salvo/csharp/Coordinate.cs @@ -0,0 +1,45 @@ +namespace Salvo; + +internal record struct Coordinate(int Value) +{ + public const int MinValue = 0; + public const int MaxValue = 9; + + public static IEnumerable Range => Enumerable.Range(0, 10).Select(v => new Coordinate(v)); + + public bool IsInRange => Value is >= MinValue and <= MaxValue; + + public static Coordinate Create(float value) => new((int)value - 1); + + public static bool TryCreateValid(float value, out Coordinate coordinate) + { + coordinate = default; + if (value != (int)value) { return false; } + + var result = Create(value); + + if (result.IsInRange) + { + coordinate = result; + return true; + } + + return false; + } + + public Coordinate BringIntoRange(IRandom random) + => Value switch + { + < MinValue => new(MinValue + (int)random.NextFloat(2.5F)), + > MaxValue => new(MaxValue - (int)random.NextFloat(2.5F)), + _ => this + }; + + public static implicit operator Coordinate(float value) => new((int)value); + public static implicit operator int(Coordinate coordinate) => coordinate.Value; + + public static Coordinate operator +(Coordinate coordinate, int offset) => new(coordinate.Value + offset); + public static int operator -(Coordinate a, Coordinate b) => a.Value - b.Value; + + public override string ToString() => $" {Value + 1} "; +} diff --git a/77_Salvo/csharp/Extensions/IOExtensions.cs b/77_Salvo/csharp/Extensions/IOExtensions.cs new file mode 100644 index 00000000..b9eaa2c4 --- /dev/null +++ b/77_Salvo/csharp/Extensions/IOExtensions.cs @@ -0,0 +1,27 @@ +namespace Games.Common.IO; + +internal static class IOExtensions +{ + internal static Position ReadPosition(this IReadWrite io) => Position.Create(io.Read2Numbers("")); + + internal static Position ReadValidPosition(this IReadWrite io) + { + while (true) + { + if (Position.TryCreateValid(io.Read2Numbers(""), out var position)) + { + return position; + } + io.WriteLine("ILLEGAL, ENTER AGAIN."); + } + } + + internal static IEnumerable ReadPositions(this IReadWrite io, string shipName, int shipSize) + { + io.WriteLine(shipName); + for (var i = 0; i < shipSize; i++) + { + yield return io.ReadPosition(); + } + } +} diff --git a/77_Salvo/csharp/Extensions/RandomExtensions.cs b/77_Salvo/csharp/Extensions/RandomExtensions.cs new file mode 100644 index 00000000..e83ab265 --- /dev/null +++ b/77_Salvo/csharp/Extensions/RandomExtensions.cs @@ -0,0 +1,32 @@ +namespace Games.Common.Randomness; + +internal static class RandomExtensions +{ + internal static (Position, Offset) NextShipPosition(this IRandom random) + { + var startX = random.NextCoordinate(); + var startY = random.NextCoordinate(); + var deltaY = random.NextOffset(); + var deltaX = random.NextOffset(); + return (new(startX, startY), new(deltaX, deltaY)); + } + + private static Coordinate NextCoordinate(this IRandom random) + => random.Next(Coordinate.MinValue, Coordinate.MaxValue + 1); + + private static int NextOffset(this IRandom random) => random.Next(-1, 2); + + internal static (Position, Offset) GetRandomShipPositionInRange(this IRandom random, int shipSize) + { + while (true) + { + var (start, delta) = random.NextShipPosition(); + var shipSizeLessOne = shipSize - 1; + var end = start + delta * shipSizeLessOne; + if (delta != 0 && end.IsInRange) + { + return (start, delta); + } + } + } +} diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 491cebdb..2afac1bd 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using Games.Common.Randomness; +using Salvo.Targetting; namespace Salvo; @@ -224,419 +223,3 @@ L4210: ;// NoOp - NEXT S L4230: goto L3380; } } - -internal abstract class ShotSelector -{ - internal ShotSelector(Grid source, Grid target) - { - Source = source; - Target = target; - } - - protected Grid Source { get; } - protected Grid Target { get; } - - public int GetShotCount() => Source.Ships.Sum(s => s.Shots); -} - -internal abstract class ComputerShotSelector : ShotSelector -{ - private readonly bool _displayShots; - - internal ComputerShotSelector(Grid source, Grid target, bool displayShots) - : base(source, target) - { - _displayShots = displayShots; - } - - private void DisplayShots(IEnumerable shots, IReadWrite io) - { - if (_displayShots) - { - foreach (var shot in shots) - { - io.WriteLine(shot); - } - } - } -} - -internal class HumanShotSelector : ShotSelector -{ - public HumanShotSelector(Grid source, Grid target) - : base(source, target) - { - } - - public IEnumerable GetShots(IReadWrite io) - { - var shots = new Position[GetShotCount()]; - - for (var i = 0; i < shots.Length; i++) - { - while (true) - { - var position = io.ReadValidPosition(); - if (Target.WasTargetedAt(position, out var turnTargeted)) - { - io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {turnTargeted}"); - continue; - } - shots[i] = position; - break; - } - } - - return shots; - } -} - -internal abstract class Ship -{ - private readonly List _positions = new(); - - protected Ship(IReadWrite io, string? nameSuffix = null) - { - Name = GetType().Name + nameSuffix; - _positions = io.ReadPositions(Name, Size).ToList(); - } - - protected Ship(IRandom random, string? nameSuffix = null) - { - Name = GetType().Name + nameSuffix; - - var (start, delta) = random.GetRandomShipPositionInRange(Size); - for (var i = 0; i < Size; i++) - { - _positions.Add(start + delta * i); - } - } - - internal string Name { get; } - internal abstract int Shots { get; } - internal abstract int Size { get; } - internal abstract float Value { get; } - internal IEnumerable Positions => _positions; - internal bool IsDestroyed => _positions.Count == 0; - - internal bool IsHit(Position position) => _positions.Remove(position); - - internal float DistanceTo(Ship other) - => _positions.SelectMany(a => other._positions.Select(b => a.DistanceTo(b))).Min(); - - public override string ToString() - => string.Join(Environment.NewLine, _positions.Select(p => p.ToString()).Prepend(Name)); -} - -internal sealed class Battleship : Ship -{ - internal Battleship(IReadWrite io) - : base(io) - { - } - - internal Battleship(IRandom random) - : base(random) - { - } - - internal override int Shots => 3; - internal override int Size => 5; - internal override float Value => 3; -} - -internal sealed class Cruiser : Ship -{ - internal Cruiser(IReadWrite io) - : base(io) - { - } - - internal Cruiser(IRandom random) - : base(random) - { - } - - internal override int Shots => 2; - internal override int Size => 3; - internal override float Value => 2; -} - -internal sealed class Destroyer : Ship -{ - internal Destroyer(string nameIndex, IReadWrite io) - : base(io, $"<{nameIndex}>") - { - } - - internal Destroyer(string nameIndex, IRandom random) - : base(random, $"<{nameIndex}>") - { - } - - internal override int Shots => 1; - internal override int Size => 2; - internal override float Value => Name.EndsWith("") ? 1 : 0.5F; -} - -internal static class RandomExtensions -{ - internal static (Position, Offset) NextShipPosition(this IRandom random) - { - var startX = random.NextCoordinate(); - var startY = random.NextCoordinate(); - var deltaY = random.NextOffset(); - var deltaX = random.NextOffset(); - return (new(startX, startY), new(deltaX, deltaY)); - } - - private static Coordinate NextCoordinate(this IRandom random) - => random.Next(Coordinate.MinValue, Coordinate.MaxValue + 1); - - private static int NextOffset(this IRandom random) => random.Next(-1, 2); - - internal static (Position, Offset) GetRandomShipPositionInRange(this IRandom random, int shipSize) - { - while (true) - { - var (start, delta) = random.NextShipPosition(); - var shipSizeLessOne = shipSize - 1; - var end = start + delta * shipSizeLessOne; - if (delta != 0 && end.IsInRange) - { - return (start, delta); - } - } - } -} - -internal class Grid -{ - private readonly List _ships; - private readonly Dictionary _shots = new(); - - internal Grid() - { - _ships = new(); - } - - internal Grid(IReadWrite io) - { - io.WriteLine("ENTER COORDINATES FOR..."); - _ships = new() - { - new Battleship(io), - new Cruiser(io), - new Destroyer("A", io), - new Destroyer("B", io) - }; - } - - internal Grid(IRandom random) - { - _ships = new(); - while (true) - { - _ships.Add(new Battleship(random)); - if (TryPositionShip(() => new Cruiser(random)) && - TryPositionShip(() => new Destroyer("A", random)) && - TryPositionShip(() => new Destroyer("B", random))) - { - return; - } - _ships.Clear(); - } - - bool TryPositionShip(Func shipFactory) - { - var shipGenerationAttempts = 0; - while (true) - { - var ship = shipFactory.Invoke(); - shipGenerationAttempts++; - if (shipGenerationAttempts > 25) { return false; } - if (_ships.Min(ship.DistanceTo) >= 3.59) - { - _ships.Add(ship); - return true; - } - } - } - } - - public float this[Position position] - { - get => _shots.TryGetValue(position, out var value) - ? value + 10 - : _ships.FirstOrDefault(s => s.Positions.Contains(position))?.Value ?? 0; - set - { - _ = _ships.FirstOrDefault(s => s.IsHit(position)); - _shots[position] = (int)value - 10; - } - } - - 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, out string? shipName) - { - shipName = null; - _shots[position] = turnNumber; - - var ship = _ships.FirstOrDefault(s => s.IsHit(position)); - if (ship == null) { return false; } - - if (ship.IsDestroyed) { _ships.Remove(ship); } - - return true; - } -} - -internal static class IOExtensions -{ - internal static Position ReadPosition(this IReadWrite io) => Position.Create(io.Read2Numbers("")); - - internal static Position ReadValidPosition(this IReadWrite io) - { - while (true) - { - if (Position.TryCreateValid(io.Read2Numbers(""), out var position)) - { - return position; - } - io.WriteLine("ILLEGAL, ENTER AGAIN."); - } - } - - internal static IEnumerable ReadPositions(this IReadWrite io, string shipName, int shipSize) - { - io.WriteLine(shipName); - for (var i = 0; i < shipSize; i++) - { - yield return io.ReadPosition(); - } - } -} - -internal record struct Position(Coordinate X, Coordinate Y) -{ - public bool IsInRange => X.IsInRange && Y.IsInRange; - public bool IsOnDiagonal => X == Y; - - public static Position Create((float X, float Y) coordinates) => new(coordinates.X, coordinates.Y); - - public static bool TryCreateValid((float X, float Y) coordinates, out Position position) - { - if (Coordinate.TryCreateValid(coordinates.X, out var x) && Coordinate.TryCreateValid(coordinates.Y, out var y)) - { - position = new(x, y); - return true; - } - - position = default; - return false; - } - - public static IEnumerable All - => Coordinate.Range.SelectMany(x => Coordinate.Range.Select(y => new Position(x, y))); - - public IEnumerable Neighbours - { - get - { - foreach (var offset in Offset.Units) - { - var neighbour = this + offset; - if (neighbour.IsInRange) { yield return neighbour; } - } - } - } - - internal float DistanceTo(Position other) - { - var (deltaX, deltaY) = (X - other.X, Y - other.Y); - return (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); - } - - internal Position BringIntoRange(IRandom random) - => IsInRange ? this : new(X.BringIntoRange(random), Y.BringIntoRange(random)); - - public static Position operator +(Position position, Offset offset) - => new(position.X + offset.X, position.Y + offset.Y); - - public static implicit operator Position(int value) => new(value, value); - - public override string ToString() => $"{X}{Y}"; -} - -internal record struct Coordinate(int Value) -{ - public const int MinValue = 0; - public const int MaxValue = 9; - - public static IEnumerable Range => Enumerable.Range(0, 10).Select(v => new Coordinate(v)); - - public bool IsInRange => Value is >= MinValue and <= MaxValue; - - public static Coordinate Create(float value) => new((int)value - 1); - - public static bool TryCreateValid(float value, out Coordinate coordinate) - { - coordinate = default; - if (value != (int)value) { return false; } - - var result = Create(value); - - if (result.IsInRange) - { - coordinate = result; - return true; - } - - return false; - } - - public Coordinate BringIntoRange(IRandom random) - => Value switch - { - < MinValue => new(MinValue + (int)random.NextFloat(2.5F)), - > MaxValue => new(MaxValue - (int)random.NextFloat(2.5F)), - _ => this - }; - - public static implicit operator Coordinate(float value) => new((int)value); - public static implicit operator int(Coordinate coordinate) => coordinate.Value; - - public static Coordinate operator +(Coordinate coordinate, int offset) => new(coordinate.Value + offset); - public static int operator -(Coordinate a, Coordinate b) => a.Value - b.Value; - - public override string ToString() => $" {Value + 1} "; -} - -internal record struct Offset(int X, int Y) -{ - public static readonly Offset Zero = 0; - - public static Offset operator *(Offset offset, int scale) => new(offset.X * scale, offset.Y * scale); - - public static implicit operator Offset(int value) => new(value, value); - - public static IEnumerable Units - { - get - { - for (int x = -1; x <= 1; x++) - { - for (int y = -1; y <= 1; y++) - { - var offset = new Offset(x, y); - if (offset != Zero) { yield return offset; } - } - } - } - } -} diff --git a/77_Salvo/csharp/Grid.cs b/77_Salvo/csharp/Grid.cs new file mode 100644 index 00000000..c3d04dff --- /dev/null +++ b/77_Salvo/csharp/Grid.cs @@ -0,0 +1,89 @@ +using System.Collections.Immutable; + +namespace Salvo; + +internal class Grid +{ + private readonly List _ships; + private readonly Dictionary _shots = new(); + + internal Grid() + { + _ships = new(); + } + + internal Grid(IReadWrite io) + { + io.WriteLine("ENTER COORDINATES FOR..."); + _ships = new() + { + new Battleship(io), + new Cruiser(io), + new Destroyer("A", io), + new Destroyer("B", io) + }; + } + + internal Grid(IRandom random) + { + _ships = new(); + while (true) + { + _ships.Add(new Battleship(random)); + if (TryPositionShip(() => new Cruiser(random)) && + TryPositionShip(() => new Destroyer("A", random)) && + TryPositionShip(() => new Destroyer("B", random))) + { + return; + } + _ships.Clear(); + } + + bool TryPositionShip(Func shipFactory) + { + var shipGenerationAttempts = 0; + while (true) + { + var ship = shipFactory.Invoke(); + shipGenerationAttempts++; + if (shipGenerationAttempts > 25) { return false; } + if (_ships.Min(ship.DistanceTo) >= 3.59) + { + _ships.Add(ship); + return true; + } + } + } + } + + public float this[Position position] + { + get => _shots.TryGetValue(position, out var value) + ? value + 10 + : _ships.FirstOrDefault(s => s.Positions.Contains(position))?.Value ?? 0; + set + { + _ = _ships.FirstOrDefault(s => s.IsHit(position)); + _shots[position] = (int)value - 10; + } + } + + 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, out string? shipName) + { + shipName = null; + _shots[position] = turnNumber; + + var ship = _ships.FirstOrDefault(s => s.IsHit(position)); + if (ship == null) { return false; } + + if (ship.IsDestroyed) { _ships.Remove(ship); } + + return true; + } +} diff --git a/77_Salvo/csharp/Offset.cs b/77_Salvo/csharp/Offset.cs new file mode 100644 index 00000000..3c873f10 --- /dev/null +++ b/77_Salvo/csharp/Offset.cs @@ -0,0 +1,25 @@ +namespace Salvo; + +internal record struct Offset(int X, int Y) +{ + public static readonly Offset Zero = 0; + + public static Offset operator *(Offset offset, int scale) => new(offset.X * scale, offset.Y * scale); + + public static implicit operator Offset(int value) => new(value, value); + + public static IEnumerable Units + { + get + { + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + var offset = new Offset(x, y); + if (offset != Zero) { yield return offset; } + } + } + } + } +} diff --git a/77_Salvo/csharp/Position.cs b/77_Salvo/csharp/Position.cs new file mode 100644 index 00000000..3bafe217 --- /dev/null +++ b/77_Salvo/csharp/Position.cs @@ -0,0 +1,52 @@ +namespace Salvo; + +internal record struct Position(Coordinate X, Coordinate Y) +{ + public bool IsInRange => X.IsInRange && Y.IsInRange; + public bool IsOnDiagonal => X == Y; + + public static Position Create((float X, float Y) coordinates) => new(coordinates.X, coordinates.Y); + + public static bool TryCreateValid((float X, float Y) coordinates, out Position position) + { + if (Coordinate.TryCreateValid(coordinates.X, out var x) && Coordinate.TryCreateValid(coordinates.Y, out var y)) + { + position = new(x, y); + return true; + } + + position = default; + return false; + } + + public static IEnumerable All + => Coordinate.Range.SelectMany(x => Coordinate.Range.Select(y => new Position(x, y))); + + public IEnumerable Neighbours + { + get + { + foreach (var offset in Offset.Units) + { + var neighbour = this + offset; + if (neighbour.IsInRange) { yield return neighbour; } + } + } + } + + internal float DistanceTo(Position other) + { + var (deltaX, deltaY) = (X - other.X, Y - other.Y); + return (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); + } + + internal Position BringIntoRange(IRandom random) + => IsInRange ? this : new(X.BringIntoRange(random), Y.BringIntoRange(random)); + + public static Position operator +(Position position, Offset offset) + => new(position.X + offset.X, position.Y + offset.Y); + + public static implicit operator Position(int value) => new(value, value); + + public override string ToString() => $"{X}{Y}"; +} diff --git a/77_Salvo/csharp/Program.cs b/77_Salvo/csharp/Program.cs index b9d6a84c..6878fd38 100644 --- a/77_Salvo/csharp/Program.cs +++ b/77_Salvo/csharp/Program.cs @@ -1,7 +1,8 @@ global using System; global using Games.Common.IO; global using Games.Common.Randomness; +global using Salvo; +global using Salvo.Ships; global using static Salvo.Resources.Resource; -using Salvo; new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); diff --git a/77_Salvo/csharp/Ships/Battleship.cs b/77_Salvo/csharp/Ships/Battleship.cs new file mode 100644 index 00000000..1e57e59d --- /dev/null +++ b/77_Salvo/csharp/Ships/Battleship.cs @@ -0,0 +1,18 @@ +namespace Salvo.Ships; + +internal sealed class Battleship : Ship +{ + internal Battleship(IReadWrite io) + : base(io) + { + } + + internal Battleship(IRandom random) + : base(random) + { + } + + internal override int Shots => 3; + internal override int Size => 5; + internal override float Value => 3; +} diff --git a/77_Salvo/csharp/Ships/Cruiser.cs b/77_Salvo/csharp/Ships/Cruiser.cs new file mode 100644 index 00000000..33f30e38 --- /dev/null +++ b/77_Salvo/csharp/Ships/Cruiser.cs @@ -0,0 +1,18 @@ +namespace Salvo.Ships; + +internal sealed class Cruiser : Ship +{ + internal Cruiser(IReadWrite io) + : base(io) + { + } + + internal Cruiser(IRandom random) + : base(random) + { + } + + internal override int Shots => 2; + internal override int Size => 3; + internal override float Value => 2; +} diff --git a/77_Salvo/csharp/Ships/Destroyer.cs b/77_Salvo/csharp/Ships/Destroyer.cs new file mode 100644 index 00000000..0e2a023b --- /dev/null +++ b/77_Salvo/csharp/Ships/Destroyer.cs @@ -0,0 +1,18 @@ +namespace Salvo.Ships; + +internal sealed class Destroyer : Ship +{ + internal Destroyer(string nameIndex, IReadWrite io) + : base(io, $"<{nameIndex}>") + { + } + + internal Destroyer(string nameIndex, IRandom random) + : base(random, $"<{nameIndex}>") + { + } + + internal override int Shots => 1; + internal override int Size => 2; + internal override float Value => Name.EndsWith("") ? 1 : 0.5F; +} diff --git a/77_Salvo/csharp/Ships/Ship.cs b/77_Salvo/csharp/Ships/Ship.cs new file mode 100644 index 00000000..58f311c2 --- /dev/null +++ b/77_Salvo/csharp/Ships/Ship.cs @@ -0,0 +1,38 @@ +namespace Salvo.Ships; + +internal abstract class Ship +{ + private readonly List _positions = new(); + + protected Ship(IReadWrite io, string? nameSuffix = null) + { + Name = GetType().Name + nameSuffix; + _positions = io.ReadPositions(Name, Size).ToList(); + } + + protected Ship(IRandom random, string? nameSuffix = null) + { + Name = GetType().Name + nameSuffix; + + var (start, delta) = random.GetRandomShipPositionInRange(Size); + for (var i = 0; i < Size; i++) + { + _positions.Add(start + delta * i); + } + } + + internal string Name { get; } + internal abstract int Shots { get; } + internal abstract int Size { get; } + internal abstract float Value { get; } + internal IEnumerable Positions => _positions; + internal bool IsDestroyed => _positions.Count == 0; + + internal bool IsHit(Position position) => _positions.Remove(position); + + internal float DistanceTo(Ship other) + => _positions.SelectMany(a => other._positions.Select(b => a.DistanceTo(b))).Min(); + + public override string ToString() + => string.Join(Environment.NewLine, _positions.Select(p => p.ToString()).Prepend(Name)); +} diff --git a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs new file mode 100644 index 00000000..1cf2a98a --- /dev/null +++ b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs @@ -0,0 +1,23 @@ +namespace Salvo.Targetting; + +internal abstract class ComputerShotSelector : ShotSelector +{ + private readonly bool _displayShots; + + internal ComputerShotSelector(Grid source, Grid target, bool displayShots) + : base(source, target) + { + _displayShots = displayShots; + } + + private void DisplayShots(IEnumerable shots, IReadWrite io) + { + if (_displayShots) + { + foreach (var shot in shots) + { + io.WriteLine(shot); + } + } + } +} diff --git a/77_Salvo/csharp/Targetting/HumanShotSelector.cs b/77_Salvo/csharp/Targetting/HumanShotSelector.cs new file mode 100644 index 00000000..6a50330a --- /dev/null +++ b/77_Salvo/csharp/Targetting/HumanShotSelector.cs @@ -0,0 +1,31 @@ +namespace Salvo.Targetting; + +internal class HumanShotSelector : ShotSelector +{ + public HumanShotSelector(Grid source, Grid target) + : base(source, target) + { + } + + public IEnumerable GetShots(IReadWrite io) + { + var shots = new Position[GetShotCount()]; + + for (var i = 0; i < shots.Length; i++) + { + while (true) + { + var position = io.ReadValidPosition(); + if (Target.WasTargetedAt(position, out var turnTargeted)) + { + io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {turnTargeted}"); + continue; + } + shots[i] = position; + break; + } + } + + return shots; + } +} diff --git a/77_Salvo/csharp/Targetting/ShotSelector.cs b/77_Salvo/csharp/Targetting/ShotSelector.cs new file mode 100644 index 00000000..fae30f31 --- /dev/null +++ b/77_Salvo/csharp/Targetting/ShotSelector.cs @@ -0,0 +1,15 @@ +namespace Salvo.Targetting; + +internal abstract class ShotSelector +{ + internal ShotSelector(Grid source, Grid target) + { + Source = source; + Target = target; + } + + protected Grid Source { get; } + protected Grid Target { get; } + + public int GetShotCount() => Source.Ships.Sum(s => s.Shots); +} From d21b41d3a724cca452d65e0c99a168f5c931b6b2 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sat, 22 Apr 2023 18:33:03 +1000 Subject: [PATCH 17/29] Add string resources --- 77_Salvo/csharp/Extensions/IOExtensions.cs | 2 +- 77_Salvo/csharp/Game.cs | 54 +++++++++---------- 77_Salvo/csharp/Grid.cs | 6 ++- 77_Salvo/csharp/Resources/Coordinates.txt | 1 + .../Resources/IHaveMoreShotsThanSquares.txt | 1 + 77_Salvo/csharp/Resources/IHaveShots.txt | 1 + 77_Salvo/csharp/Resources/IHit.txt | 1 + 77_Salvo/csharp/Resources/IWon.txt | 1 + 77_Salvo/csharp/Resources/Illegal.txt | 1 + 77_Salvo/csharp/Resources/Resource.cs | 21 +++++++- 77_Salvo/csharp/Resources/SeeShots.txt | 1 + 77_Salvo/csharp/Resources/ShotBefore.txt | 1 + 77_Salvo/csharp/Resources/Start.txt | 1 + 77_Salvo/csharp/Resources/Turn.txt | 2 + .../csharp/Resources/WhereAreYourShips.txt | 1 + .../Resources/YouHaveMoreShotsThanSquares.txt | 1 + 77_Salvo/csharp/Resources/YouHaveShots.txt | 1 + 77_Salvo/csharp/Resources/YouHit.txt | 1 + 77_Salvo/csharp/Resources/YouWon.txt | 1 + 19 files changed, 67 insertions(+), 32 deletions(-) create mode 100644 77_Salvo/csharp/Resources/Coordinates.txt create mode 100644 77_Salvo/csharp/Resources/IHaveMoreShotsThanSquares.txt create mode 100644 77_Salvo/csharp/Resources/IHaveShots.txt create mode 100644 77_Salvo/csharp/Resources/IHit.txt create mode 100644 77_Salvo/csharp/Resources/IWon.txt create mode 100644 77_Salvo/csharp/Resources/Illegal.txt create mode 100644 77_Salvo/csharp/Resources/SeeShots.txt create mode 100644 77_Salvo/csharp/Resources/ShotBefore.txt create mode 100644 77_Salvo/csharp/Resources/Start.txt create mode 100644 77_Salvo/csharp/Resources/Turn.txt create mode 100644 77_Salvo/csharp/Resources/WhereAreYourShips.txt create mode 100644 77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt create mode 100644 77_Salvo/csharp/Resources/YouHaveShots.txt create mode 100644 77_Salvo/csharp/Resources/YouHit.txt create mode 100644 77_Salvo/csharp/Resources/YouWon.txt diff --git a/77_Salvo/csharp/Extensions/IOExtensions.cs b/77_Salvo/csharp/Extensions/IOExtensions.cs index b9eaa2c4..6d021deb 100644 --- a/77_Salvo/csharp/Extensions/IOExtensions.cs +++ b/77_Salvo/csharp/Extensions/IOExtensions.cs @@ -12,7 +12,7 @@ internal static class IOExtensions { return position; } - io.WriteLine("ILLEGAL, ENTER AGAIN."); + io.Write(Streams.Illegal); } } diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 2afac1bd..2f3ebe8e 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -29,50 +29,48 @@ internal class Game var computerGrid = new Grid(_random); var humanGrid = new Grid(_io); var humanShotSelector = new HumanShotSelector(humanGrid, computerGrid); - var startResponse = _io.ReadString("DO YOU WANT TO START"); - while (startResponse == "WHERE ARE YOUR SHIPS?") + var startResponse = _io.ReadString(Prompts.Start); + while (startResponse == Strings.WhereAreYourShips) { foreach (var ship in computerGrid.Ships) { _io.WriteLine(ship); } - startResponse = _io.ReadString("DO YOU WANT TO START"); + startResponse = _io.ReadString(Prompts.Start); } L1890: var turnNumber=0; -L1900: var seeShotsResponse = _io.ReadString("DO YOU WANT TO SEE MY SHOTS"); +L1900: var seeShotsResponse = _io.ReadString(Prompts.SeeShots); L1920: _io.WriteLine(); L1930: if (startResponse != "YES") { goto L2620; } L1950: if (startResponse != "YES") { goto L1990; } L1960: turnNumber++; -L1970: _io.WriteLine(); -L1980: _io.WriteLine($"TURN {turnNumber}"); -L1990: var maxShotCount = humanShotSelector.GetShotCount(); -L2220: _io.WriteLine($"YOU HAVE {maxShotCount} SHOTS."); - if (maxShotCount == 0) { goto L2270; } -L2230: if (maxShotCount > computerGrid.UntriedSquareCount) +L1980: _io.Write(Strings.Turn(turnNumber)); +L1990: var numberOfShots = humanShotSelector.GetShotCount(); +L2220: _io.Write(Strings.YouHaveShots(numberOfShots)); + if (numberOfShots == 0) { goto L2270; } +L2230: if (numberOfShots > computerGrid.UntriedSquareCount) { - _io.WriteLine("YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES."); + _io.WriteLine(Streams.YouHaveMoreShotsThanSquares); L2250: goto L2890; } foreach (var shot1 in humanShotSelector.GetShots(_io)) { if (computerGrid.IsHit(shot1, turnNumber, out var shipName)) { - _io.WriteLine($"YOU HIT MY {shipName}."); + _io.Write(Strings.YouHit(shipName)); } } L2620: if (startResponse == "YES") { goto L2670; } L2640: turnNumber++; -L2650: _io.WriteLine(); -L2660: _io.WriteLine($"TURN {turnNumber}"); -L2670: maxShotCount = computerGrid.Ships.Sum(s => s.Shots); -L2840: _io.WriteLine($"I HAVE {maxShotCount} SHOTS."); -L2850: if (humanGrid.UntriedSquareCount > maxShotCount) { goto L2880; } -L2860: _io.WriteLine("I HAVE MORE SHOTS THAN BLANK SQUARES."); -L2270: _io.WriteLine("I HAVE WON."); +L2660: _io.Write(Strings.Turn(turnNumber)); +L2670: numberOfShots = computerGrid.Ships.Sum(s => s.Shots); +L2840: _io.Write(Strings.IHaveShots(numberOfShots)); +L2850: if (humanGrid.UntriedSquareCount > numberOfShots) { goto L2880; } +L2860: _io.Write(Streams.IHaveMoreShotsThanSquares); +L2270: _io.Write(Streams.IWon); return; -L2880: if (maxShotCount != 0) { goto L2960; } -L2890: _io.WriteLine("YOU HAVE WON."); +L2880: if (numberOfShots != 0) { goto L2960; } +L2890: _io.Write(Streams.YouWon); L2900: return; L2960: for (var i = 1; i <= 12; i++) @@ -91,7 +89,7 @@ L3070: shot = shot.BringIntoRange(_random); L3170: goto L3270; // record shot L3180: temp[shotCount]=shot; -L3200: if (shotCount==maxShotCount) { goto L3380; } +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) }; @@ -111,11 +109,11 @@ L3360: shotCount++; L3370: goto L3180; // display shots L3380: if (seeShotsResponse != "YES") { goto L3420; } -L3390: for (var i = 1; i <= maxShotCount; i++) +L3390: for (var i = 1; i <= numberOfShots; i++) { L3400: _io.WriteLine(temp[i]); } -L3420: for (var i = 1; i <= maxShotCount; i++) +L3420: for (var i = 1; i <= numberOfShots; i++) { L3430: if (humanGrid[temp[i]] == 3) { @@ -197,22 +195,22 @@ L3860: for (var i = 1; i <= 12; i++) } } } -L4030: for (var i = 1; i <= maxShotCount; i++) +L4030: for (var i = 1; i <= numberOfShots; i++) { L4040: temp[i]=i; } foreach (var position in Position.All) { L4090: var Q9=1; -L4100: for (var i = 1; i <= maxShotCount; i++) +L4100: for (var i = 1; i <= numberOfShots; i++) { L4110: if (tempGrid[temp[i]]>=tempGrid[temp[Q9]]) { continue; } L4120: Q9=i; } -L4131: if (position.X>maxShotCount) { goto L4140; } +L4131: if (position.X>numberOfShots) { goto L4140; } L4132: if (position.IsOnDiagonal) { goto L4210; } L4140: if (tempGrid[position] _shots.TryGetValue(position, out turnTargeted); - internal bool IsHit(Position position, int turnNumber, out string? shipName) + internal bool IsHit(Position position, int turnNumber, [NotNullWhen(true)] out string? shipName) { shipName = null; _shots[position] = turnNumber; var ship = _ships.FirstOrDefault(s => s.IsHit(position)); if (ship == null) { return false; } + shipName = ship.Name; if (ship.IsDestroyed) { _ships.Remove(ship); } diff --git a/77_Salvo/csharp/Resources/Coordinates.txt b/77_Salvo/csharp/Resources/Coordinates.txt new file mode 100644 index 00000000..387d7a6b --- /dev/null +++ b/77_Salvo/csharp/Resources/Coordinates.txt @@ -0,0 +1 @@ +Enter coordinates for... diff --git a/77_Salvo/csharp/Resources/IHaveMoreShotsThanSquares.txt b/77_Salvo/csharp/Resources/IHaveMoreShotsThanSquares.txt new file mode 100644 index 00000000..a6f37110 --- /dev/null +++ b/77_Salvo/csharp/Resources/IHaveMoreShotsThanSquares.txt @@ -0,0 +1 @@ +I have more shots than blank squares. diff --git a/77_Salvo/csharp/Resources/IHaveShots.txt b/77_Salvo/csharp/Resources/IHaveShots.txt new file mode 100644 index 00000000..9157e1a7 --- /dev/null +++ b/77_Salvo/csharp/Resources/IHaveShots.txt @@ -0,0 +1 @@ +I have {0} shots. diff --git a/77_Salvo/csharp/Resources/IHit.txt b/77_Salvo/csharp/Resources/IHit.txt new file mode 100644 index 00000000..a3f3dccf --- /dev/null +++ b/77_Salvo/csharp/Resources/IHit.txt @@ -0,0 +1 @@ +I hot your {0} diff --git a/77_Salvo/csharp/Resources/IWon.txt b/77_Salvo/csharp/Resources/IWon.txt new file mode 100644 index 00000000..b3f50634 --- /dev/null +++ b/77_Salvo/csharp/Resources/IWon.txt @@ -0,0 +1 @@ +I have won. diff --git a/77_Salvo/csharp/Resources/Illegal.txt b/77_Salvo/csharp/Resources/Illegal.txt new file mode 100644 index 00000000..6a71787a --- /dev/null +++ b/77_Salvo/csharp/Resources/Illegal.txt @@ -0,0 +1 @@ +Illegal, enter again. diff --git a/77_Salvo/csharp/Resources/Resource.cs b/77_Salvo/csharp/Resources/Resource.cs index 0cc55034..c3a0d8ca 100644 --- a/77_Salvo/csharp/Resources/Resource.cs +++ b/77_Salvo/csharp/Resources/Resource.cs @@ -8,13 +8,32 @@ internal static class Resource internal static class Streams { public static Stream Title => GetStream(); + public static Stream YouHaveMoreShotsThanSquares => GetStream(); + public static Stream YouWon => GetStream(); + public static Stream IHaveMoreShotsThanSquares => GetStream(); + public static Stream IWon => GetStream(); + public static Stream Illegal => GetStream(); + } + + internal static class Strings + { + public static string WhereAreYourShips => GetString(); + 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 ShotBefore(int turnNumber) => Format(turnNumber); + public static string Turn(int number) => Format(number); } internal static class Prompts { + public static string Coordinates => GetString(); + public static string Start => GetString(); + public static string SeeShots => GetString(); } - private static string GetPrompt([CallerMemberName] string? name = null) => GetString($"{name}Prompt"); + private static string Format(T value, [CallerMemberName] string? name = null) + => string.Format(GetString(name), value); private static string GetString([CallerMemberName] string? name = null) { diff --git a/77_Salvo/csharp/Resources/SeeShots.txt b/77_Salvo/csharp/Resources/SeeShots.txt new file mode 100644 index 00000000..7f608b0b --- /dev/null +++ b/77_Salvo/csharp/Resources/SeeShots.txt @@ -0,0 +1 @@ +Do you want to see my shots \ No newline at end of file diff --git a/77_Salvo/csharp/Resources/ShotBefore.txt b/77_Salvo/csharp/Resources/ShotBefore.txt new file mode 100644 index 00000000..18e90fcb --- /dev/null +++ b/77_Salvo/csharp/Resources/ShotBefore.txt @@ -0,0 +1 @@ +You shot there before on turn {0} diff --git a/77_Salvo/csharp/Resources/Start.txt b/77_Salvo/csharp/Resources/Start.txt new file mode 100644 index 00000000..283a0fbb --- /dev/null +++ b/77_Salvo/csharp/Resources/Start.txt @@ -0,0 +1 @@ +Do you want to start \ No newline at end of file diff --git a/77_Salvo/csharp/Resources/Turn.txt b/77_Salvo/csharp/Resources/Turn.txt new file mode 100644 index 00000000..14bf316e --- /dev/null +++ b/77_Salvo/csharp/Resources/Turn.txt @@ -0,0 +1,2 @@ + +Turn {0} diff --git a/77_Salvo/csharp/Resources/WhereAreYourShips.txt b/77_Salvo/csharp/Resources/WhereAreYourShips.txt new file mode 100644 index 00000000..797c1144 --- /dev/null +++ b/77_Salvo/csharp/Resources/WhereAreYourShips.txt @@ -0,0 +1 @@ +Where are your ships? diff --git a/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt b/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt new file mode 100644 index 00000000..37c06812 --- /dev/null +++ b/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt @@ -0,0 +1 @@ +You have more shots that there are blank squares. diff --git a/77_Salvo/csharp/Resources/YouHaveShots.txt b/77_Salvo/csharp/Resources/YouHaveShots.txt new file mode 100644 index 00000000..43abeda8 --- /dev/null +++ b/77_Salvo/csharp/Resources/YouHaveShots.txt @@ -0,0 +1 @@ +You have {0} shots. diff --git a/77_Salvo/csharp/Resources/YouHit.txt b/77_Salvo/csharp/Resources/YouHit.txt new file mode 100644 index 00000000..b5b5d761 --- /dev/null +++ b/77_Salvo/csharp/Resources/YouHit.txt @@ -0,0 +1 @@ +You hit my {0}. diff --git a/77_Salvo/csharp/Resources/YouWon.txt b/77_Salvo/csharp/Resources/YouWon.txt new file mode 100644 index 00000000..1f71343b --- /dev/null +++ b/77_Salvo/csharp/Resources/YouWon.txt @@ -0,0 +1 @@ +You have won. From aa6a06cdc8400789d549b09e67fdb700df1309de Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sun, 23 Apr 2023 17:55:48 +1000 Subject: [PATCH 18/29] 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(); } From 405c50e0e7e1594ab9fc01dd434f1c0a077a61d9 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Wed, 10 May 2023 07:54:36 +1000 Subject: [PATCH 19/29] Fix computer targetting errors --- 77_Salvo/csharp/Coordinate.cs | 12 +-- 77_Salvo/csharp/Game.cs | 78 +++++++++++-------- 77_Salvo/csharp/Grid.cs | 6 +- 77_Salvo/csharp/Program.cs | 3 +- 77_Salvo/csharp/Resources/IHit.txt | 2 +- .../csharp/Resources/WhereAreYourShips.txt | 2 +- .../csharp/Targetting/ComputerShotSelector.cs | 5 ++ .../csharp/Targetting/HumanShotSelector.cs | 2 +- .../Targetting/SearchPatternShotSelector.cs | 5 +- 9 files changed, 67 insertions(+), 48 deletions(-) diff --git a/77_Salvo/csharp/Coordinate.cs b/77_Salvo/csharp/Coordinate.cs index 45d149c1..4cd2c433 100644 --- a/77_Salvo/csharp/Coordinate.cs +++ b/77_Salvo/csharp/Coordinate.cs @@ -2,14 +2,14 @@ namespace Salvo; internal record struct Coordinate(int Value) { - public const int MinValue = 0; - public const int MaxValue = 9; + public const int MinValue = 1; + public const int MaxValue = 10; - public static IEnumerable Range => Enumerable.Range(0, 10).Select(v => new Coordinate(v)); + public static IEnumerable Range => Enumerable.Range(1, 10).Select(v => new Coordinate(v)); public bool IsInRange => Value is >= MinValue and <= MaxValue; - public static Coordinate Create(float value) => new((int)value - 1); + public static Coordinate Create(float value) => new((int)value); public static bool TryCreateValid(float value, out Coordinate coordinate) { @@ -35,11 +35,11 @@ internal record struct Coordinate(int Value) _ => this }; - public static implicit operator Coordinate(float value) => new((int)value); + public static implicit operator Coordinate(float value) => Create(value); public static implicit operator int(Coordinate coordinate) => coordinate.Value; public static Coordinate operator +(Coordinate coordinate, int offset) => new(coordinate.Value + offset); public static int operator -(Coordinate a, Coordinate b) => a.Value - b.Value; - public override string ToString() => $" {Value + 1} "; + public override string ToString() => $" {Value} "; } diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 616f0cbc..c9d537d5 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -56,9 +56,9 @@ L2250: goto L2890; } foreach (var shot1 in humanShotSelector.GetShots()) { - if (computerGrid.IsHit(shot1, turnNumber, out var shipName)) + if (computerGrid.IsHit(shot1, turnNumber, out var ship)) { - _io.Write(Strings.YouHit(shipName)); + _io.Write(Strings.YouHit(ship.Name)); } } L2620: if (startResponse == "YES") { goto L2670; } @@ -81,7 +81,7 @@ L2970: if (hitShipValue[i]>0) { goto L3800; } } temp = computerShotSelector.GetShots().ToArray(); // display shots -L3380: if (seeShotsResponse == "yes") +L3380: if (seeShotsResponse == "YES") { foreach (var shot in temp) { @@ -90,17 +90,17 @@ L3380: if (seeShotsResponse == "yes") } foreach (var shot in temp) { - if (!humanGrid.IsHit(shot, turnNumber, out var shipName)) + if (!humanGrid.IsHit(shot, turnNumber, out var ship)) { continue; } - _io.WriteLine(Strings.IHit(shipName)); + _io.Write(Strings.IHit(ship.Name)); 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[shot]; +L3600: hitShipValue[j]=ship.Value; // look for past hits on same ship L3610: var shipHits=0; L3620: for (var k = 1; k <= 12; k++) @@ -132,51 +132,65 @@ L3470: humanGrid[shot]=10+turnNumber; } L3490: goto L1950; L3800: //REM************************USINGEARRAY - var tempGrid = new Grid(); + var tempGrid = Position.All.ToDictionary(x => x, _ => 0); L3860: for (var i = 1; i <= 12; i++) { if (hitTurnRecord[i]<10) { continue; } foreach (var position in Position.All) { - if (humanGrid[position]>=10) + if (humanGrid.WasTargetedAt(position, out _)) { - foreach (var neighbour in position.Neighbours) - { - if (humanGrid[neighbour] == hitTurnRecord[i]) - { - tempGrid[position] += hitTurnRecord[i]-position.Y*(int)(hitShipValue[i]+.5F); - } - } - } - else - { tempGrid[position]=-10000000; + continue; + } + + foreach (var neighbour in position.Neighbours) + { + if (humanGrid.WasTargetedAt(neighbour, out var turn) && turn == (hitTurnRecord[i] - 10)) + { + tempGrid[position] += hitTurnRecord[i]-position.Y*(int)(hitShipValue[i]+.5F); + } } } } -L4030: for (var i = 1; i <= numberOfShots; i++) +L4030: for (var i = 0; i < numberOfShots; i++) { -L4040: temp[i]=i; +L4040: temp[i]=i+1; } foreach (var position in Position.All) { -L4090: var Q9=1; -L4100: for (var i = 1; i <= numberOfShots; i++) +L4090: var Q9=0; +L4100: for (var i = 0; i < numberOfShots; i++) { -L4110: if (tempGrid[temp[i]]>=tempGrid[temp[Q9]]) { continue; } -L4120: Q9=i; +L4110: if (tempGrid[temp[i]] < tempGrid[temp[Q9]]) + { +L4120: Q9 = i; + } } -L4131: if (position.X>numberOfShots) { goto L4140; } -L4132: if (position.IsOnDiagonal) { goto L4210; } -L4140: if (tempGrid[position] _values = + new(File.ReadAllLines("data.txt").Select(l => float.Parse(l) / 1000000)); + private float _previous; + + public float NextFloat() => _previous = _values.Dequeue(); + + public float PreviousFloat() => _previous; + + public void Reseed(int seed) + { + throw new NotImplementedException(); + } +} + diff --git a/77_Salvo/csharp/Grid.cs b/77_Salvo/csharp/Grid.cs index 85e3f387..43dd61b4 100644 --- a/77_Salvo/csharp/Grid.cs +++ b/77_Salvo/csharp/Grid.cs @@ -75,14 +75,12 @@ internal class Grid internal bool WasTargetedAt(Position position, out int turnTargeted) => _shots.TryGetValue(position, out turnTargeted); - internal bool IsHit(Position position, int turnNumber, [NotNullWhen(true)] out string? shipName) + internal bool IsHit(Position position, int turnNumber, [NotNullWhen(true)] out Ship? ship) { - shipName = null; _shots[position] = turnNumber; - var ship = _ships.FirstOrDefault(s => s.IsHit(position)); + ship = _ships.FirstOrDefault(s => s.IsHit(position)); if (ship == null) { return false; } - shipName = ship.Name; if (ship.IsDestroyed) { _ships.Remove(ship); } diff --git a/77_Salvo/csharp/Program.cs b/77_Salvo/csharp/Program.cs index 6878fd38..5dde4d05 100644 --- a/77_Salvo/csharp/Program.cs +++ b/77_Salvo/csharp/Program.cs @@ -5,4 +5,5 @@ global using Salvo; global using Salvo.Ships; global using static Salvo.Resources.Resource; -new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); +//new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); +new Game(new ConsoleIO(), new DataRandom()).Play(); diff --git a/77_Salvo/csharp/Resources/IHit.txt b/77_Salvo/csharp/Resources/IHit.txt index a3f3dccf..3b43216c 100644 --- a/77_Salvo/csharp/Resources/IHit.txt +++ b/77_Salvo/csharp/Resources/IHit.txt @@ -1 +1 @@ -I hot your {0} +I hit your {0} diff --git a/77_Salvo/csharp/Resources/WhereAreYourShips.txt b/77_Salvo/csharp/Resources/WhereAreYourShips.txt index 797c1144..e8391a46 100644 --- a/77_Salvo/csharp/Resources/WhereAreYourShips.txt +++ b/77_Salvo/csharp/Resources/WhereAreYourShips.txt @@ -1 +1 @@ -Where are your ships? +Where are your ships? \ No newline at end of file diff --git a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs index c8f57d20..c69c36d2 100644 --- a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs +++ b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs @@ -10,6 +10,11 @@ internal class ComputerShotSelector : ShotSelector _displayShots = displayShots; } + internal override IEnumerable GetShots() + { + throw new NotImplementedException(); + } + private void DisplayShots(IEnumerable shots, IReadWrite io) { if (_displayShots) diff --git a/77_Salvo/csharp/Targetting/HumanShotSelector.cs b/77_Salvo/csharp/Targetting/HumanShotSelector.cs index 40812926..b4a0db13 100644 --- a/77_Salvo/csharp/Targetting/HumanShotSelector.cs +++ b/77_Salvo/csharp/Targetting/HumanShotSelector.cs @@ -12,7 +12,7 @@ internal class HumanShotSelector : ShotSelector internal override IEnumerable GetShots() { - var shots = new Position[NumberOfShots()]; + var shots = new Position[NumberOfShots]; for (var i = 0; i < shots.Length; i++) { diff --git a/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs b/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs index 4e3fcafb..329a0a98 100644 --- a/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs +++ b/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs @@ -15,6 +15,7 @@ internal class SearchPatternShotSelector : ShotSelector internal override IEnumerable GetShots() { + _shots.Clear(); while(_shots.Count < NumberOfShots) { var (seed, _) = _random.NextShipPosition(); @@ -31,11 +32,11 @@ internal class SearchPatternShotSelector : ShotSelector _searchPattern.Reset(); if (attemptsLeft-- == 0) { return; } candidateShot = candidateShot.BringIntoRange(_random); - if (FindValidShots(candidateShot)) { return; } + if (FindValidShots(ref candidateShot)) { return; } } } - private bool FindValidShots(Position candidateShot) + private bool FindValidShots(ref Position candidateShot) { while (true) { From 6e01ee5b6a3258c5abb244433176ea0e8b98ab0c Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Tue, 16 May 2023 08:05:39 +1000 Subject: [PATCH 20/29] Simplify hit recording --- 77_Salvo/csharp/Game.cs | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index c9d537d5..978701cb 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -95,42 +95,28 @@ L3380: if (seeShotsResponse == "YES") continue; } _io.Write(Strings.IHit(ship.Name)); -L3570: for (var j = 1; j <= 12; j++) + for (var j = 1; j <= 12; j++) { - // record hit -L3580: if (hitTurnRecord[j] != -1) { continue; } -L3590: hitTurnRecord[j]=10+turnNumber; -L3600: hitShipValue[j]=ship.Value; - // look for past hits on same ship -L3610: var shipHits=0; -L3620: for (var k = 1; k <= 12; k++) + if (hitTurnRecord[j] == -1) { -L3630: if (hitShipValue[k] != hitShipValue[j]) { continue; } -L3640: shipHits++; + hitTurnRecord[j]=10+turnNumber; + hitShipValue[j]=ship.Value; + break; } - // if ship is not sunk -L3660: if (shipHits != (int)(hitShipValue[j]+.5F)+1+(int)(hitShipValue[j]+.5F)/3) { goto L3470; } - // otherwise, remove ship hit records -L3670: for (var k = 1; k <= 12; k++) + } + if (ship.IsDestroyed) + { + for (var k = 1; k <= 12; k++) { -L3680: if (hitShipValue[k] == hitShipValue[j]) + if (hitShipValue[k] == ship.Value) { -L3700: hitShipValue[k] = hitTurnRecord[k] = -1; + hitShipValue[k] = hitTurnRecord[k] = -1; } } -L3720: goto L3470; } - // we shouldn't get here -L3740: _io.WriteLine("PROGRAM ABORT:"); -L3750: for (var j = 1; j <= 12; j++) - { -L3760: _io.WriteLine($"{nameof(hitTurnRecord)}( {j} ) = {hitTurnRecord[j]}"); -L3770: _io.WriteLine($"{nameof(hitShipValue)}( {j} ) = {hitShipValue[j]}"); - } - return; -L3470: humanGrid[shot]=10+turnNumber; + humanGrid[shot]=10+turnNumber; } -L3490: goto L1950; + goto L1950; L3800: //REM************************USINGEARRAY var tempGrid = Position.All.ToDictionary(x => x, _ => 0); L3860: for (var i = 1; i <= 12; i++) From 30360f278446d0ab2e7f99ec3216fe8a3d3d0d4e Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Tue, 16 May 2023 17:45:48 +1000 Subject: [PATCH 21/29] Improve computer shot tracking/selection --- 77_Salvo/csharp/Game.cs | 43 +++++++---------------------- 77_Salvo/csharp/Grid.cs | 12 -------- 77_Salvo/csharp/Ships/Battleship.cs | 1 - 77_Salvo/csharp/Ships/Cruiser.cs | 1 - 77_Salvo/csharp/Ships/Destroyer.cs | 1 - 77_Salvo/csharp/Ships/Ship.cs | 3 +- 6 files changed, 11 insertions(+), 50 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 978701cb..33d750df 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -17,15 +17,9 @@ internal class Game { _io.Write(Streams.Title); - var hitTurnRecord = new int[13]; + var hitRecords = new List<(int Turn, Ship Ship)>(); var temp = new Position[13]; - var hitShipValue = new float[13]; - for (var i = 1; i <= 12; i++) - { - hitTurnRecord[i] = -1; - hitShipValue[i] = -1; - } var computerGrid = new Grid(_random); var humanGrid = new Grid(_io); var humanShotSelector = new HumanShotSelector(humanGrid, computerGrid, _io); @@ -74,11 +68,7 @@ L2880: if (numberOfShots != 0) { goto L2960; } L2890: _io.Write(Streams.YouWon); L2900: return; -L2960: for (var i = 1; i <= 12; i++) - { - // if damaged ships -L2970: if (hitShipValue[i]>0) { goto L3800; } - } +L2960: if (humanGrid.Ships.Any(s => s.IsDamaged)) { goto L3800; } temp = computerShotSelector.GetShots().ToArray(); // display shots L3380: if (seeShotsResponse == "YES") @@ -95,33 +85,20 @@ L3380: if (seeShotsResponse == "YES") continue; } _io.Write(Strings.IHit(ship.Name)); - for (var j = 1; j <= 12; j++) - { - if (hitTurnRecord[j] == -1) - { - hitTurnRecord[j]=10+turnNumber; - hitShipValue[j]=ship.Value; - break; - } - } if (ship.IsDestroyed) { - for (var k = 1; k <= 12; k++) - { - if (hitShipValue[k] == ship.Value) - { - hitShipValue[k] = hitTurnRecord[k] = -1; - } - } + hitRecords = hitRecords.Where(hr => hr.Ship != ship).ToList(); + } + else + { + hitRecords.Add((turnNumber, ship)); } - humanGrid[shot]=10+turnNumber; } goto L1950; L3800: //REM************************USINGEARRAY var tempGrid = Position.All.ToDictionary(x => x, _ => 0); -L3860: for (var i = 1; i <= 12; i++) +L3860: foreach (var (hitTurn, ship) in hitRecords) { - if (hitTurnRecord[i]<10) { continue; } foreach (var position in Position.All) { if (humanGrid.WasTargetedAt(position, out _)) @@ -132,9 +109,9 @@ L3860: for (var i = 1; i <= 12; i++) foreach (var neighbour in position.Neighbours) { - if (humanGrid.WasTargetedAt(neighbour, out var turn) && turn == (hitTurnRecord[i] - 10)) + if (humanGrid.WasTargetedAt(neighbour, out var turn) && turn == hitTurn) { - tempGrid[position] += hitTurnRecord[i]-position.Y*(int)(hitShipValue[i]+.5F); + tempGrid[position] += hitTurn + 10 - position.Y * ship.Shots; } } } diff --git a/77_Salvo/csharp/Grid.cs b/77_Salvo/csharp/Grid.cs index 43dd61b4..0c84d688 100644 --- a/77_Salvo/csharp/Grid.cs +++ b/77_Salvo/csharp/Grid.cs @@ -57,18 +57,6 @@ internal class Grid } } - public float this[Position position] - { - get => _shots.TryGetValue(position, out var value) - ? value + 10 - : _ships.FirstOrDefault(s => s.Positions.Contains(position))?.Value ?? 0; - set - { - _ = _ships.FirstOrDefault(s => s.IsHit(position)); - _shots[position] = (int)value - 10; - } - } - internal int UntriedSquareCount => 100 - _shots.Count; internal IEnumerable Ships => _ships.AsEnumerable(); diff --git a/77_Salvo/csharp/Ships/Battleship.cs b/77_Salvo/csharp/Ships/Battleship.cs index 1e57e59d..4f864eb6 100644 --- a/77_Salvo/csharp/Ships/Battleship.cs +++ b/77_Salvo/csharp/Ships/Battleship.cs @@ -14,5 +14,4 @@ internal sealed class Battleship : Ship internal override int Shots => 3; internal override int Size => 5; - internal override float Value => 3; } diff --git a/77_Salvo/csharp/Ships/Cruiser.cs b/77_Salvo/csharp/Ships/Cruiser.cs index 33f30e38..d004f24c 100644 --- a/77_Salvo/csharp/Ships/Cruiser.cs +++ b/77_Salvo/csharp/Ships/Cruiser.cs @@ -14,5 +14,4 @@ internal sealed class Cruiser : Ship internal override int Shots => 2; internal override int Size => 3; - internal override float Value => 2; } diff --git a/77_Salvo/csharp/Ships/Destroyer.cs b/77_Salvo/csharp/Ships/Destroyer.cs index 0e2a023b..6523395d 100644 --- a/77_Salvo/csharp/Ships/Destroyer.cs +++ b/77_Salvo/csharp/Ships/Destroyer.cs @@ -14,5 +14,4 @@ internal sealed class Destroyer : Ship internal override int Shots => 1; internal override int Size => 2; - internal override float Value => Name.EndsWith("") ? 1 : 0.5F; } diff --git a/77_Salvo/csharp/Ships/Ship.cs b/77_Salvo/csharp/Ships/Ship.cs index 58f311c2..ee204d3f 100644 --- a/77_Salvo/csharp/Ships/Ship.cs +++ b/77_Salvo/csharp/Ships/Ship.cs @@ -24,8 +24,7 @@ internal abstract class Ship internal string Name { get; } internal abstract int Shots { get; } internal abstract int Size { get; } - internal abstract float Value { get; } - internal IEnumerable Positions => _positions; + internal bool IsDamaged => _positions.Count > 0 && _positions.Count < Size; internal bool IsDestroyed => _positions.Count == 0; internal bool IsHit(Position position) => _positions.Remove(position); From e27b5ec6e5018a051e9c1a85c91fd83875da8817 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Thu, 18 May 2023 19:27:56 +1000 Subject: [PATCH 22/29] Move shot selection into ComputerShotSelector --- 77_Salvo/csharp/Game.cs | 66 ++--------------- .../csharp/Targetting/ComputerShotSelector.cs | 21 ++---- .../KnownHitsShotSelectionStrategy.cs | 71 +++++++++++++++++++ .../Targetting/ShotSelectionStrategy.cs | 16 +++++ 4 files changed, 101 insertions(+), 73 deletions(-) create mode 100644 77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs create mode 100644 77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 33d750df..95ccef23 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -17,13 +17,13 @@ internal class Game { _io.Write(Streams.Title); - var hitRecords = new List<(int Turn, Ship Ship)>(); + var damagedShips = new List<(int Turn, Ship Ship)>(); var temp = new Position[13]; var computerGrid = new Grid(_random); var humanGrid = new Grid(_io); var humanShotSelector = new HumanShotSelector(humanGrid, computerGrid, _io); - var computerShotSelector = new SearchPatternShotSelector(computerGrid, humanGrid, _random); + var computerShotSelector = new ComputerShotSelector(computerGrid, humanGrid, _random); var startResponse = _io.ReadString(Prompts.Start); while (startResponse == Strings.WhereAreYourShips) { @@ -64,12 +64,11 @@ L2850: if (humanGrid.UntriedSquareCount > numberOfShots) { 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: if (humanGrid.Ships.Any(s => s.IsDamaged)) { goto L3800; } - temp = computerShotSelector.GetShots().ToArray(); +L2960: temp = computerShotSelector.GetShots().ToArray(); // display shots L3380: if (seeShotsResponse == "YES") { @@ -80,64 +79,13 @@ L3380: if (seeShotsResponse == "YES") } foreach (var shot in temp) { - if (!humanGrid.IsHit(shot, turnNumber, out var ship)) + if (humanGrid.IsHit(shot, turnNumber, out var ship)) { - continue; - } - _io.Write(Strings.IHit(ship.Name)); - if (ship.IsDestroyed) - { - hitRecords = hitRecords.Where(hr => hr.Ship != ship).ToList(); - } - else - { - hitRecords.Add((turnNumber, ship)); + _io.Write(Strings.IHit(ship.Name)); + computerShotSelector.RecordHit(ship, turnNumber); } } goto L1950; -L3800: //REM************************USINGEARRAY - var tempGrid = Position.All.ToDictionary(x => x, _ => 0); -L3860: foreach (var (hitTurn, ship) in hitRecords) - { - foreach (var position in Position.All) - { - if (humanGrid.WasTargetedAt(position, out _)) - { - tempGrid[position]=-10000000; - continue; - } - - foreach (var neighbour in position.Neighbours) - { - if (humanGrid.WasTargetedAt(neighbour, out var turn) && turn == hitTurn) - { - tempGrid[position] += hitTurn + 10 - position.Y * ship.Shots; - } - } - } - } -L4030: for (var i = 0; i < numberOfShots; i++) - { -L4040: temp[i]=i+1; - } - foreach (var position in Position.All) - { -L4090: var Q9=0; -L4100: for (var i = 0; i < numberOfShots; i++) - { -L4110: if (tempGrid[temp[i]] < tempGrid[temp[Q9]]) - { -L4120: Q9 = i; - } - } -L4131: if (position.X <= numberOfShots && position.IsOnDiagonal) { continue; } -L4140: if (tempGrid[position] GetShots() { - throw new NotImplementedException(); + return _knownHitsStrategy.GetShots(NumberOfShots) ?? _searchPatternShotSelector.GetShots(); } - private void DisplayShots(IEnumerable shots, IReadWrite io) - { - if (_displayShots) - { - foreach (var shot in shots) - { - io.WriteLine(shot); - } - } - } + internal void RecordHit(Ship ship, int turn) => _knownHitsStrategy.RecordHit(ship, turn); } diff --git a/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs b/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs new file mode 100644 index 00000000..917b232e --- /dev/null +++ b/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs @@ -0,0 +1,71 @@ +namespace Salvo.Targetting; + +internal class KnownHitsShotSelectionStrategy : ShotSelectionStrategy +{ + private readonly List<(int Turn, Ship Ship)> _damagedShips = new(); + + internal KnownHitsShotSelectionStrategy(Grid target) + : base(target) + { + } + + internal IEnumerable? GetShots(int numberOfShots) + { + if (!_damagedShips.Any()) { return null; } + + var tempGrid = Position.All.ToDictionary(x => x, _ => 0); + var shots = Enumerable.Range(1, numberOfShots).Select(x => new Position(x, x)).ToArray(); + + foreach (var (hitTurn, ship) in _damagedShips) + { + foreach (var position in Position.All) + { + if (WasSelectedPreviously(position)) + { + tempGrid[position]=-10000000; + continue; + } + + foreach (var neighbour in position.Neighbours) + { + if (WasSelectedPreviously(neighbour, out var turn) && turn == hitTurn) + { + tempGrid[position] += hitTurn + 10 - position.Y * ship.Shots; + } + } + } + } + + foreach (var position in Position.All) + { + var Q9=0; + for (var i = 0; i < numberOfShots; i++) + { + if (tempGrid[shots[i]] < tempGrid[shots[Q9]]) + { + Q9 = i; + } + } + if (position.X <= numberOfShots && position.IsOnDiagonal) { continue; } + if (tempGrid[position] x.Ship == ship); + } + else + { + _damagedShips.Add((turn, ship)); + } + } +} diff --git a/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs b/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs new file mode 100644 index 00000000..f98c7b4f --- /dev/null +++ b/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs @@ -0,0 +1,16 @@ +namespace Salvo.Targetting; + +internal abstract class ShotSelectionStrategy +{ + private readonly Grid _target; + protected ShotSelectionStrategy(Grid target) + { + _target = target; + } + + protected bool WasSelectedPreviously(Position position) + => _target.WasTargetedAt(position, out _); + + protected bool WasSelectedPreviously(Position position, out int turn) + => _target.WasTargetedAt(position, out turn); +} From e02a4ca5da107752ccbe61d0ff4e58197a303868 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Sat, 20 May 2023 17:12:48 +1000 Subject: [PATCH 23/29] 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(); } From 1d3fd94ae6a80ab75c5082d7974533660677ef77 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Mon, 22 May 2023 08:42:22 +1000 Subject: [PATCH 24/29] Simplify shot selection and evaluation --- 77_Salvo/csharp/{Grid.cs => Fleet.cs} | 21 ++++++----- 77_Salvo/csharp/Game.cs | 37 +++++-------------- .../csharp/Targetting/ComputerShotSelector.cs | 16 +++++++- .../csharp/Targetting/HumanShotSelector.cs | 2 +- 77_Salvo/csharp/Targetting/ShotSelector.cs | 4 +- 5 files changed, 38 insertions(+), 42 deletions(-) rename 77_Salvo/csharp/{Grid.cs => Fleet.cs} (75%) diff --git a/77_Salvo/csharp/Grid.cs b/77_Salvo/csharp/Fleet.cs similarity index 75% rename from 77_Salvo/csharp/Grid.cs rename to 77_Salvo/csharp/Fleet.cs index f91ae43a..5f267225 100644 --- a/77_Salvo/csharp/Grid.cs +++ b/77_Salvo/csharp/Fleet.cs @@ -3,11 +3,11 @@ using System.Diagnostics.CodeAnalysis; namespace Salvo; -internal class Grid +internal class Fleet { private readonly List _ships; - internal Grid(IReadWrite io) + internal Fleet(IReadWrite io) { io.WriteLine(Prompts.Coordinates); _ships = new() @@ -19,7 +19,7 @@ internal class Grid }; } - internal Grid(IRandom random) + internal Fleet(IRandom random) { _ships = new(); while (true) @@ -53,13 +53,14 @@ internal class Grid internal IEnumerable Ships => _ships.AsEnumerable(); - internal bool IsHit(Position position, [NotNullWhen(true)] out Ship? ship) + internal void ReceiveShots(IEnumerable shots, Action reportHit) { - ship = _ships.FirstOrDefault(s => s.IsHit(position)); - if (ship == null) { return false; } - - if (ship.IsDestroyed) { _ships.Remove(ship); } - - return true; + foreach (var position in shots) + { + var ship = _ships.FirstOrDefault(s => s.IsHit(position)); + if (ship == null) { continue; } + if (ship.IsDestroyed) { _ships.Remove(ship); } + reportHit(ship); + } } } diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 6c0add12..5004cacb 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -20,21 +20,20 @@ internal class Game var damagedShips = new List<(int Turn, Ship Ship)>(); var temp = new Position[13]; - var computerGrid = new Grid(_random); - var humanGrid = new Grid(_io); - var humanShotSelector = new HumanShotSelector(humanGrid, _io); - var computerShotSelector = new ComputerShotSelector(computerGrid, _random); + var computerFleet = new Fleet(_random); + var humanFleet = new Fleet(_io); var startResponse = _io.ReadString(Prompts.Start); while (startResponse == Strings.WhereAreYourShips) { - foreach (var ship in computerGrid.Ships) + foreach (var ship in computerFleet.Ships) { _io.WriteLine(ship); } startResponse = _io.ReadString(Prompts.Start); } L1890: var turnNumber=0; -L1900: var seeShotsResponse = _io.ReadString(Prompts.SeeShots); + var humanShotSelector = new HumanShotSelector(humanFleet, _io); + var computerShotSelector = new ComputerShotSelector(computerFleet, _random, _io); L1920: _io.WriteLine(); L1930: if (startResponse != "YES") { goto L2620; } L1950: if (startResponse != "YES") { goto L1990; } @@ -48,13 +47,7 @@ L2230: if (humanShotSelector.CanTargetAllRemainingSquares) _io.WriteLine(Streams.YouHaveMoreShotsThanSquares); L2250: goto L2890; } - foreach (var shot1 in humanShotSelector.GetShots(turnNumber)) - { - if (computerGrid.IsHit(shot1, out var ship)) - { - _io.Write(Strings.YouHit(ship.Name)); - } - } + computerFleet.ReceiveShots(humanShotSelector.GetShots(turnNumber), ship => _io.Write(Strings.YouHit(ship.Name))); L2620: if (startResponse == "YES") { goto L2670; } L2640: turnNumber++; L2660: _io.Write(Strings.Turn(turnNumber)); @@ -68,23 +61,13 @@ L2880: if (numberOfShots > 0) { goto L2960; } L2890: _io.Write(Streams.YouWon); L2900: return; -L2960: temp = computerShotSelector.GetShots(turnNumber).ToArray(); - // display shots -L3380: if (seeShotsResponse == "YES") - { - foreach (var shot in temp) - { - _io.WriteLine(shot); - } - } - foreach (var shot in temp) - { - if (humanGrid.IsHit(shot, out var ship)) +L2960: humanFleet.ReceiveShots( + computerShotSelector.GetShots(turnNumber), + ship => { _io.Write(Strings.IHit(ship.Name)); computerShotSelector.RecordHit(ship, turnNumber); - } - } + }); goto L1950; } } diff --git a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs index 5a5214dd..721f50c5 100644 --- a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs +++ b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs @@ -4,15 +4,27 @@ internal class ComputerShotSelector : ShotSelector { private readonly KnownHitsShotSelectionStrategy _knownHitsStrategy; private readonly SearchPatternShotSelectionStrategy _searchPatternStrategy; + private readonly IReadWrite _io; + private readonly bool _showShots; - internal ComputerShotSelector(Grid source, IRandom random) + internal ComputerShotSelector(Fleet source, IRandom random, IReadWrite io) : base(source) { _knownHitsStrategy = new KnownHitsShotSelectionStrategy(this); _searchPatternStrategy = new SearchPatternShotSelectionStrategy(this, random); + _io = io; + _showShots = io.ReadString(Prompts.SeeShots).Equals("yes", StringComparison.InvariantCultureIgnoreCase); } - protected override IEnumerable GetShots() => GetSelectionStrategy().GetShots(NumberOfShots); + protected override IEnumerable GetShots() + { + var shots = GetSelectionStrategy().GetShots(NumberOfShots).ToArray(); + if (_showShots) + { + _io.WriteLine(string.Join(Environment.NewLine, shots)); + } + return shots; + } internal void RecordHit(Ship ship, int turn) => _knownHitsStrategy.RecordHit(ship, turn); diff --git a/77_Salvo/csharp/Targetting/HumanShotSelector.cs b/77_Salvo/csharp/Targetting/HumanShotSelector.cs index f3e5fba2..ea49bf12 100644 --- a/77_Salvo/csharp/Targetting/HumanShotSelector.cs +++ b/77_Salvo/csharp/Targetting/HumanShotSelector.cs @@ -4,7 +4,7 @@ internal class HumanShotSelector : ShotSelector { private readonly IReadWrite _io; - internal HumanShotSelector(Grid source, IReadWrite io) + internal HumanShotSelector(Fleet source, IReadWrite io) : base(source) { _io = io; diff --git a/77_Salvo/csharp/Targetting/ShotSelector.cs b/77_Salvo/csharp/Targetting/ShotSelector.cs index 95a5c4bd..695dbc42 100644 --- a/77_Salvo/csharp/Targetting/ShotSelector.cs +++ b/77_Salvo/csharp/Targetting/ShotSelector.cs @@ -2,10 +2,10 @@ namespace Salvo.Targetting; internal abstract class ShotSelector { - private readonly Grid _source; + private readonly Fleet _source; private readonly Dictionary _previousShots = new(); - internal ShotSelector(Grid source) + internal ShotSelector(Fleet source) { _source = source; } From 461b4e2b64dd94754212c8e0b7f0992349c34b48 Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Mon, 22 May 2023 08:50:27 +1000 Subject: [PATCH 25/29] Simplify game start seelection --- 77_Salvo/csharp/Game.cs | 50 +++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index 5004cacb..dd1a49b9 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -17,28 +17,19 @@ internal class Game { _io.Write(Streams.Title); - var damagedShips = new List<(int Turn, Ship Ship)>(); - var temp = new Position[13]; - var computerFleet = new Fleet(_random); var humanFleet = new Fleet(_io); - var startResponse = _io.ReadString(Prompts.Start); - while (startResponse == Strings.WhereAreYourShips) - { - foreach (var ship in computerFleet.Ships) - { - _io.WriteLine(ship); - } - startResponse = _io.ReadString(Prompts.Start); - } + var humanStarts = AskWhoStarts(computerFleet); L1890: var turnNumber=0; var humanShotSelector = new HumanShotSelector(humanFleet, _io); var computerShotSelector = new ComputerShotSelector(computerFleet, _random, _io); L1920: _io.WriteLine(); -L1930: if (startResponse != "YES") { goto L2620; } -L1950: if (startResponse != "YES") { goto L1990; } -L1960: turnNumber++; -L1980: _io.Write(Strings.Turn(turnNumber)); +L1930: if (!humanStarts) { goto L2620; } +L1950: if (humanStarts) + { +L1960: turnNumber++; +L1980: _io.Write(Strings.Turn(turnNumber)); + } L1990: var numberOfShots = humanShotSelector.NumberOfShots; L2220: _io.Write(Strings.YouHaveShots(numberOfShots)); if (numberOfShots == 0) { goto L2270; } @@ -48,9 +39,11 @@ L2230: if (humanShotSelector.CanTargetAllRemainingSquares) L2250: goto L2890; } computerFleet.ReceiveShots(humanShotSelector.GetShots(turnNumber), ship => _io.Write(Strings.YouHit(ship.Name))); -L2620: if (startResponse == "YES") { goto L2670; } -L2640: turnNumber++; -L2660: _io.Write(Strings.Turn(turnNumber)); +L2620: if (!humanStarts) + { +L2640: turnNumber++; +L2660: _io.Write(Strings.Turn(turnNumber)); + } L2670: numberOfShots = computerShotSelector.NumberOfShots; L2840: _io.Write(Strings.IHaveShots(numberOfShots)); L2850: if (!computerShotSelector.CanTargetAllRemainingSquares) { goto L2880; } @@ -70,6 +63,25 @@ L2960: humanFleet.ReceiveShots( }); goto L1950; } + + private bool AskWhoStarts(Fleet computerFleet) + { + while (true) + { + var startResponse = _io.ReadString(Prompts.Start); + if (startResponse.Equals(Strings.WhereAreYourShips, StringComparison.InvariantCultureIgnoreCase)) + { + foreach (var ship in computerFleet.Ships) + { + _io.WriteLine(ship); + } + } + else + { + return startResponse.Equals("yes", StringComparison.InvariantCultureIgnoreCase); + } + } + } } internal class DataRandom : IRandom From d1be351045e2b3e0288935252d6a42492053b8be Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Tue, 23 May 2023 07:55:41 +1000 Subject: [PATCH 26/29] Simplify game loop --- 77_Salvo/csharp/Game.cs | 11 ++++++++++- .../csharp/Resources/YouHaveMoreShotsThanSquares.txt | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index dd1a49b9..ff43c268 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -38,7 +38,9 @@ L2230: if (humanShotSelector.CanTargetAllRemainingSquares) _io.WriteLine(Streams.YouHaveMoreShotsThanSquares); L2250: goto L2890; } - computerFleet.ReceiveShots(humanShotSelector.GetShots(turnNumber), ship => _io.Write(Strings.YouHit(ship.Name))); + computerFleet.ReceiveShots( + humanShotSelector.GetShots(turnNumber), + ship => _io.Write(Strings.YouHit(ship.Name))); L2620: if (!humanStarts) { L2640: turnNumber++; @@ -84,6 +86,13 @@ L2960: humanFleet.ReceiveShots( } } +internal enum Winner +{ + None, + Human, + Computer +} + internal class DataRandom : IRandom { private readonly Queue _values = diff --git a/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt b/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt index 37c06812..04d58f92 100644 --- a/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt +++ b/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt @@ -1 +1 @@ -You have more shots that there are blank squares. +You have more shots than there are blank squares. From 2e52c6e1878a4d63e20c590c6d494b6ac06009bb Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Wed, 24 May 2023 20:58:37 +1000 Subject: [PATCH 27/29] Add TurnHandler --- 77_Salvo/csharp/Game.cs | 98 +++------------------------------- 77_Salvo/csharp/TurnHandler.cs | 92 +++++++++++++++++++++++++++++++ 77_Salvo/csharp/Winner.cs | 7 +++ 3 files changed, 107 insertions(+), 90 deletions(-) create mode 100644 77_Salvo/csharp/TurnHandler.cs create mode 100644 77_Salvo/csharp/Winner.cs diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs index ff43c268..a0223d8b 100644 --- a/77_Salvo/csharp/Game.cs +++ b/77_Salvo/csharp/Game.cs @@ -1,5 +1,3 @@ -using Salvo.Targetting; - namespace Salvo; internal class Game @@ -17,95 +15,15 @@ internal class Game { _io.Write(Streams.Title); - var computerFleet = new Fleet(_random); - var humanFleet = new Fleet(_io); - var humanStarts = AskWhoStarts(computerFleet); -L1890: var turnNumber=0; - var humanShotSelector = new HumanShotSelector(humanFleet, _io); - var computerShotSelector = new ComputerShotSelector(computerFleet, _random, _io); -L1920: _io.WriteLine(); -L1930: if (!humanStarts) { goto L2620; } -L1950: if (humanStarts) - { -L1960: turnNumber++; -L1980: _io.Write(Strings.Turn(turnNumber)); - } -L1990: var numberOfShots = humanShotSelector.NumberOfShots; -L2220: _io.Write(Strings.YouHaveShots(numberOfShots)); - if (numberOfShots == 0) { goto L2270; } -L2230: if (humanShotSelector.CanTargetAllRemainingSquares) - { - _io.WriteLine(Streams.YouHaveMoreShotsThanSquares); -L2250: goto L2890; - } - computerFleet.ReceiveShots( - humanShotSelector.GetShots(turnNumber), - ship => _io.Write(Strings.YouHit(ship.Name))); -L2620: if (!humanStarts) - { -L2640: turnNumber++; -L2660: _io.Write(Strings.Turn(turnNumber)); - } -L2670: numberOfShots = computerShotSelector.NumberOfShots; -L2840: _io.Write(Strings.IHaveShots(numberOfShots)); -L2850: if (!computerShotSelector.CanTargetAllRemainingSquares) { goto L2880; } -L2860: _io.Write(Streams.IHaveMoreShotsThanSquares); -L2270: _io.Write(Streams.IWon); - return; -L2880: if (numberOfShots > 0) { goto L2960; } -L2890: _io.Write(Streams.YouWon); -L2900: return; + var turnHandler = new TurnHandler(_io, _random); + _io.WriteLine(); -L2960: humanFleet.ReceiveShots( - computerShotSelector.GetShots(turnNumber), - ship => - { - _io.Write(Strings.IHit(ship.Name)); - computerShotSelector.RecordHit(ship, turnNumber); - }); - goto L1950; - } - - private bool AskWhoStarts(Fleet computerFleet) - { - while (true) + Winner? winner; + do { - var startResponse = _io.ReadString(Prompts.Start); - if (startResponse.Equals(Strings.WhereAreYourShips, StringComparison.InvariantCultureIgnoreCase)) - { - foreach (var ship in computerFleet.Ships) - { - _io.WriteLine(ship); - } - } - else - { - return startResponse.Equals("yes", StringComparison.InvariantCultureIgnoreCase); - } - } + winner = turnHandler.PlayTurn(); + } while (winner == null); + + _io.Write(winner == Winner.Computer ? Streams.IWon : Streams.YouWon); } } - -internal enum Winner -{ - None, - Human, - Computer -} - -internal class DataRandom : IRandom -{ - private readonly Queue _values = - new(File.ReadAllLines("data.txt").Select(l => float.Parse(l) / 1000000)); - private float _previous; - - public float NextFloat() => _previous = _values.Dequeue(); - - public float PreviousFloat() => _previous; - - public void Reseed(int seed) - { - throw new NotImplementedException(); - } -} - diff --git a/77_Salvo/csharp/TurnHandler.cs b/77_Salvo/csharp/TurnHandler.cs new file mode 100644 index 00000000..afde9613 --- /dev/null +++ b/77_Salvo/csharp/TurnHandler.cs @@ -0,0 +1,92 @@ +using Salvo.Targetting; + +namespace Salvo; + +internal class TurnHandler +{ + private readonly IReadWrite _io; + private readonly Fleet _humanFleet; + private readonly Fleet _computerFleet; + private readonly bool _humanStarts; + private readonly HumanShotSelector _humanShotSelector; + private readonly ComputerShotSelector _computerShotSelector; + private readonly Func _turnAction; + private int _turnNumber; + + public TurnHandler(IReadWrite io, IRandom random) + { + _io = io; + _computerFleet = new Fleet(random); + _humanFleet = new Fleet(io); + _turnAction = AskWhoStarts() + ? () => PlayHumanTurn() ?? PlayComputerTurn() + : () => PlayComputerTurn() ?? PlayHumanTurn(); + _humanShotSelector = new HumanShotSelector(_humanFleet, io); + _computerShotSelector = new ComputerShotSelector(_computerFleet, random, io); + } + + public Winner? PlayTurn() + { + _io.Write(Strings.Turn(++_turnNumber)); + return _turnAction.Invoke(); + } + + private bool AskWhoStarts() + { + while (true) + { + var startResponse = _io.ReadString(Prompts.Start); + if (startResponse.Equals(Strings.WhereAreYourShips, StringComparison.InvariantCultureIgnoreCase)) + { + foreach (var ship in _computerFleet.Ships) + { + _io.WriteLine(ship); + } + } + else + { + return startResponse.Equals("yes", StringComparison.InvariantCultureIgnoreCase); + } + } + } + + private Winner? PlayComputerTurn() + { + var numberOfShots = _computerShotSelector.NumberOfShots; + _io.Write(Strings.IHaveShots(numberOfShots)); + if (numberOfShots == 0) { return Winner.Human; } + if (_computerShotSelector.CanTargetAllRemainingSquares) + { + _io.Write(Streams.IHaveMoreShotsThanSquares); + return Winner.Computer; + } + + _humanFleet.ReceiveShots( + _computerShotSelector.GetShots(_turnNumber), + ship => + { + _io.Write(Strings.IHit(ship.Name)); + _computerShotSelector.RecordHit(ship, _turnNumber); + }); + + return null; + } + + private Winner? PlayHumanTurn() + { + var numberOfShots = _humanShotSelector.NumberOfShots; + _io.Write(Strings.YouHaveShots(numberOfShots)); + if (numberOfShots == 0) { return Winner.Computer; } + if (_humanShotSelector.CanTargetAllRemainingSquares) + { + _io.WriteLine(Streams.YouHaveMoreShotsThanSquares); + return Winner.Human; + } + + _computerFleet.ReceiveShots( + _humanShotSelector.GetShots(_turnNumber), + ship => _io.Write(Strings.YouHit(ship.Name))); + + return null; + } +} diff --git a/77_Salvo/csharp/Winner.cs b/77_Salvo/csharp/Winner.cs new file mode 100644 index 00000000..a5a73eb4 --- /dev/null +++ b/77_Salvo/csharp/Winner.cs @@ -0,0 +1,7 @@ +namespace Salvo; + +internal enum Winner +{ + Human, + Computer +} From 282f5600184bf031a1da7b3b1a67e70654702daf Mon Sep 17 00:00:00 2001 From: amjadkofahi <119040835+amjadkofahi@users.noreply.github.com> Date: Thu, 25 May 2023 01:52:51 +0300 Subject: [PATCH 28/29] Update README.md --- 84_Super_Star_Trek/javascript/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/84_Super_Star_Trek/javascript/README.md b/84_Super_Star_Trek/javascript/README.md index 49b7ece6..1ddcc1d6 100644 --- a/84_Super_Star_Trek/javascript/README.md +++ b/84_Super_Star_Trek/javascript/README.md @@ -1,3 +1,6 @@ -Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) +Original source downloaded [from Vintage Basic][def] -Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) +Conversion to [**J**avaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) + + +[def]: http://www.vintage-basic.net/games.html \ No newline at end of file From 02cdc6102ab21be751a4121b49ed5b980c885e70 Mon Sep 17 00:00:00 2001 From: amjadkofahi <119040835+amjadkofahi@users.noreply.github.com> Date: Thu, 25 May 2023 01:54:55 +0300 Subject: [PATCH 29/29] Update README.md --- 84_Super_Star_Trek/javascript/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/84_Super_Star_Trek/javascript/README.md b/84_Super_Star_Trek/javascript/README.md index 1ddcc1d6..06f53f12 100644 --- a/84_Super_Star_Trek/javascript/README.md +++ b/84_Super_Star_Trek/javascript/README.md @@ -1,6 +1,6 @@ Original source downloaded [from Vintage Basic][def] -Conversion to [**J**avaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) +Conversion to [****J****avaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) [def]: http://www.vintage-basic.net/games.html \ No newline at end of file