Skip to content

Commit caa6459

Browse files
authored
Merge pull request #1 from amjadkofahi/pr/860
2 parents dd50341 + 02cdc61 commit caa6459

40 files changed

+819
-3
lines changed

77_Salvo/csharp/Coordinate.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace Salvo;
2+
3+
internal record struct Coordinate(int Value)
4+
{
5+
public const int MinValue = 1;
6+
public const int MaxValue = 10;
7+
8+
public static IEnumerable<Coordinate> Range => Enumerable.Range(1, 10).Select(v => new Coordinate(v));
9+
10+
public bool IsInRange => Value is >= MinValue and <= MaxValue;
11+
12+
public static Coordinate Create(float value) => new((int)value);
13+
14+
public static bool TryCreateValid(float value, out Coordinate coordinate)
15+
{
16+
coordinate = default;
17+
if (value != (int)value) { return false; }
18+
19+
var result = Create(value);
20+
21+
if (result.IsInRange)
22+
{
23+
coordinate = result;
24+
return true;
25+
}
26+
27+
return false;
28+
}
29+
30+
public Coordinate BringIntoRange(IRandom random)
31+
=> Value switch
32+
{
33+
< MinValue => new(MinValue + (int)random.NextFloat(2.5F)),
34+
> MaxValue => new(MaxValue - (int)random.NextFloat(2.5F)),
35+
_ => this
36+
};
37+
38+
public static implicit operator Coordinate(float value) => Create(value);
39+
public static implicit operator int(Coordinate coordinate) => coordinate.Value;
40+
41+
public static Coordinate operator +(Coordinate coordinate, int offset) => new(coordinate.Value + offset);
42+
public static int operator -(Coordinate a, Coordinate b) => a.Value - b.Value;
43+
44+
public override string ToString() => $" {Value} ";
45+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace Games.Common.IO;
2+
3+
internal static class IOExtensions
4+
{
5+
internal static Position ReadPosition(this IReadWrite io) => Position.Create(io.Read2Numbers(""));
6+
7+
internal static Position ReadValidPosition(this IReadWrite io)
8+
{
9+
while (true)
10+
{
11+
if (Position.TryCreateValid(io.Read2Numbers(""), out var position))
12+
{
13+
return position;
14+
}
15+
io.Write(Streams.Illegal);
16+
}
17+
}
18+
19+
internal static IEnumerable<Position> ReadPositions(this IReadWrite io, string shipName, int shipSize)
20+
{
21+
io.WriteLine(shipName);
22+
for (var i = 0; i < shipSize; i++)
23+
{
24+
yield return io.ReadPosition();
25+
}
26+
}
27+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace Games.Common.Randomness;
2+
3+
internal static class RandomExtensions
4+
{
5+
internal static (Position, Offset) NextShipPosition(this IRandom random)
6+
{
7+
var startX = random.NextCoordinate();
8+
var startY = random.NextCoordinate();
9+
var deltaY = random.NextOffset();
10+
var deltaX = random.NextOffset();
11+
return (new(startX, startY), new(deltaX, deltaY));
12+
}
13+
14+
private static Coordinate NextCoordinate(this IRandom random)
15+
=> random.Next(Coordinate.MinValue, Coordinate.MaxValue + 1);
16+
17+
private static int NextOffset(this IRandom random) => random.Next(-1, 2);
18+
19+
internal static (Position, Offset) GetRandomShipPositionInRange(this IRandom random, int shipSize)
20+
{
21+
while (true)
22+
{
23+
var (start, delta) = random.NextShipPosition();
24+
var shipSizeLessOne = shipSize - 1;
25+
var end = start + delta * shipSizeLessOne;
26+
if (delta != 0 && end.IsInRange)
27+
{
28+
return (start, delta);
29+
}
30+
}
31+
}
32+
}

77_Salvo/csharp/Fleet.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System.Collections.Immutable;
2+
using System.Diagnostics.CodeAnalysis;
3+
4+
namespace Salvo;
5+
6+
internal class Fleet
7+
{
8+
private readonly List<Ship> _ships;
9+
10+
internal Fleet(IReadWrite io)
11+
{
12+
io.WriteLine(Prompts.Coordinates);
13+
_ships = new()
14+
{
15+
new Battleship(io),
16+
new Cruiser(io),
17+
new Destroyer("A", io),
18+
new Destroyer("B", io)
19+
};
20+
}
21+
22+
internal Fleet(IRandom random)
23+
{
24+
_ships = new();
25+
while (true)
26+
{
27+
_ships.Add(new Battleship(random));
28+
if (TryPositionShip(() => new Cruiser(random)) &&
29+
TryPositionShip(() => new Destroyer("A", random)) &&
30+
TryPositionShip(() => new Destroyer("B", random)))
31+
{
32+
return;
33+
}
34+
_ships.Clear();
35+
}
36+
37+
bool TryPositionShip(Func<Ship> shipFactory)
38+
{
39+
var shipGenerationAttempts = 0;
40+
while (true)
41+
{
42+
var ship = shipFactory.Invoke();
43+
shipGenerationAttempts++;
44+
if (shipGenerationAttempts > 25) { return false; }
45+
if (_ships.Min(ship.DistanceTo) >= 3.59)
46+
{
47+
_ships.Add(ship);
48+
return true;
49+
}
50+
}
51+
}
52+
}
53+
54+
internal IEnumerable<Ship> Ships => _ships.AsEnumerable();
55+
56+
internal void ReceiveShots(IEnumerable<Position> shots, Action<Ship> reportHit)
57+
{
58+
foreach (var position in shots)
59+
{
60+
var ship = _ships.FirstOrDefault(s => s.IsHit(position));
61+
if (ship == null) { continue; }
62+
if (ship.IsDestroyed) { _ships.Remove(ship); }
63+
reportHit(ship);
64+
}
65+
}
66+
}

77_Salvo/csharp/Game.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace Salvo;
2+
3+
internal class Game
4+
{
5+
private readonly IReadWrite _io;
6+
private readonly IRandom _random;
7+
8+
public Game(IReadWrite io, IRandom random)
9+
{
10+
_io = io;
11+
_random = random;
12+
}
13+
14+
internal void Play()
15+
{
16+
_io.Write(Streams.Title);
17+
18+
var turnHandler = new TurnHandler(_io, _random);
19+
_io.WriteLine();
20+
21+
Winner? winner;
22+
do
23+
{
24+
winner = turnHandler.PlayTurn();
25+
} while (winner == null);
26+
27+
_io.Write(winner == Winner.Computer ? Streams.IWon : Streams.YouWon);
28+
}
29+
}

77_Salvo/csharp/Offset.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace Salvo;
2+
3+
internal record struct Offset(int X, int Y)
4+
{
5+
public static readonly Offset Zero = 0;
6+
7+
public static Offset operator *(Offset offset, int scale) => new(offset.X * scale, offset.Y * scale);
8+
9+
public static implicit operator Offset(int value) => new(value, value);
10+
11+
public static IEnumerable<Offset> Units
12+
{
13+
get
14+
{
15+
for (int x = -1; x <= 1; x++)
16+
{
17+
for (int y = -1; y <= 1; y++)
18+
{
19+
var offset = new Offset(x, y);
20+
if (offset != Zero) { yield return offset; }
21+
}
22+
}
23+
}
24+
}
25+
}

77_Salvo/csharp/Position.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
namespace Salvo;
2+
3+
internal record struct Position(Coordinate X, Coordinate Y)
4+
{
5+
public bool IsInRange => X.IsInRange && Y.IsInRange;
6+
public bool IsOnDiagonal => X == Y;
7+
8+
public static Position Create((float X, float Y) coordinates) => new(coordinates.X, coordinates.Y);
9+
10+
public static bool TryCreateValid((float X, float Y) coordinates, out Position position)
11+
{
12+
if (Coordinate.TryCreateValid(coordinates.X, out var x) && Coordinate.TryCreateValid(coordinates.Y, out var y))
13+
{
14+
position = new(x, y);
15+
return true;
16+
}
17+
18+
position = default;
19+
return false;
20+
}
21+
22+
public static IEnumerable<Position> All
23+
=> Coordinate.Range.SelectMany(x => Coordinate.Range.Select(y => new Position(x, y)));
24+
25+
public IEnumerable<Position> Neighbours
26+
{
27+
get
28+
{
29+
foreach (var offset in Offset.Units)
30+
{
31+
var neighbour = this + offset;
32+
if (neighbour.IsInRange) { yield return neighbour; }
33+
}
34+
}
35+
}
36+
37+
internal float DistanceTo(Position other)
38+
{
39+
var (deltaX, deltaY) = (X - other.X, Y - other.Y);
40+
return (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
41+
}
42+
43+
internal Position BringIntoRange(IRandom random)
44+
=> IsInRange ? this : new(X.BringIntoRange(random), Y.BringIntoRange(random));
45+
46+
public static Position operator +(Position position, Offset offset)
47+
=> new(position.X + offset.X, position.Y + offset.Y);
48+
49+
public static implicit operator Position(int value) => new(value, value);
50+
51+
public override string ToString() => $"{X}{Y}";
52+
}

77_Salvo/csharp/Program.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
global using System;
2+
global using Games.Common.IO;
3+
global using Games.Common.Randomness;
4+
global using Salvo;
5+
global using Salvo.Ships;
6+
global using static Salvo.Resources.Resource;
7+
8+
//new Game(new ConsoleIO(), new RandomNumberGenerator()).Play();
9+
new Game(new ConsoleIO(), new DataRandom()).Play();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Enter coordinates for...
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
I have more shots than blank squares.

0 commit comments

Comments
 (0)