diff --git a/56_Life_for_Two/csharp/Board.cs b/56_Life_for_Two/csharp/Board.cs index 1da68626..a2c8e397 100644 --- a/56_Life_for_Two/csharp/Board.cs +++ b/56_Life_for_Two/csharp/Board.cs @@ -1,40 +1,27 @@ -using System.Collections.Immutable; - namespace LifeforTwo; internal class Board { - private const int Empty = 0x0000; - private const int Player1 = 0x0100; - private const int Player2 = 0x1000; - private const int PieceMask = Player1 | Player2; - private const int NeighbourValueOffset = 8; - - private readonly ImmutableHashSet _willBePlayer1 = - new[] { 0x0003, 0x0102, 0x0103, 0x0120, 0x0130, 0x0121, 0x0112, 0x0111, 0x0012 }.ToImmutableHashSet(); - private readonly ImmutableHashSet _willBePlayer2 = - new[] { 0x0021, 0x0030, 0x1020, 0x1030, 0x1011, 0x1021, 0x1003, 0x1002, 0x1012 }.ToImmutableHashSet(); - - private readonly int[,] _cells = new int[7,7]; + private readonly Piece[,] _cells = new Piece[7,7]; private readonly Dictionary _cellCounts = new(); - public int this[Coordinates coordinates] + public Piece this[Coordinates coordinates] { get => _cells[coordinates.X, coordinates.Y]; set => _cells[coordinates.X, coordinates.Y] = value; } - public int this[int x, int y] + public Piece this[int x, int y] { get => _cells[x, y]; set => _cells[x, y] = value; } - public int Player1Count => _cellCounts[Player1]; - public int Player2Count => _cellCounts[Player2]; + public int Player1Count => _cellCounts[Piece.Player1]; + public int Player2Count => _cellCounts[Piece.Player2]; - internal bool IsEmptyAt(Coordinates coordinates) => (this[coordinates] & PieceMask) == Empty; + internal bool IsEmptyAt(Coordinates coordinates) => this[coordinates].IsEmpty; public string? Result => (Player1Count, Player2Count) switch @@ -45,49 +32,41 @@ internal class Board _ => null }; - internal void ClearCell(Coordinates coordinates) => this[coordinates] = Empty; + internal void ClearCell(Coordinates coordinates) => this[coordinates] = Piece.NewEmpty(); - internal void AddPlayer1Piece(Coordinates coordinates) => this[coordinates] = Player1; + internal void AddPlayer1Piece(Coordinates coordinates) => this[coordinates] = Piece.NewPlayer1(); - internal void AddPlayer2Piece(Coordinates coordinates) => this[coordinates] = Player2; + internal void AddPlayer2Piece(Coordinates coordinates) => this[coordinates] = Piece.NewPlayer2(); public void CalculateNextGeneration() { - _cellCounts[Empty] = _cellCounts[Player1] = _cellCounts[Player2] = 0; + _cellCounts[Piece.None] = _cellCounts[Piece.Player1] = _cellCounts[Piece.Player2] = 0; for (var x = 1; x <= 5; x++) { for (var y = 1; y <= 5; y++) { - var currentValue = this[x, y]; - var newValue = currentValue switch - { - _ when _willBePlayer1.Contains(currentValue) => Player1, - _ when _willBePlayer2.Contains(currentValue) => Player2, - _ => Empty - }; - - this[x, y] = newValue; - _cellCounts[newValue]++; + this[x, y] = this[x, y].GetNext(); + _cellCounts[this[x, y].Value]++; } } CountNeighbours(); } - private void CountNeighbours() + public void CountNeighbours() { for (var x = 1; x <= 5; x++) { for (var y = 1; y <= 5; y++) { var coordinates = new Coordinates(x, y); - var neighbourValue = (this[coordinates] & PieceMask) >> NeighbourValueOffset; - if (neighbourValue > 0) + var piece = this[coordinates]; + if (!piece.IsEmpty) { foreach (var neighbour in coordinates.GetNeighbors()) { - this[neighbour] += neighbourValue; + this[neighbour] = this[neighbour].AddNeighbour(piece); } } } @@ -107,13 +86,10 @@ internal class Board } private string GetDisplay(int x, int y) => - (x, y, this[x, y] & PieceMask) switch + (x, y) switch { - (0 or 6, _, _) => $" {y % 6} ", - (_, 0 or 6, _) => $" {x % 6} ", - (_, _, Empty) => " ", - (_, _, Player1) => " * ", - (_, _, Player2) => " # ", - _ => throw new InvalidOperationException($"Unexpected cell value at ({x}, {y}): {this[x, y]}") + (0 or 6, _) => $" {y % 6} ", + (_, 0 or 6) => $" {x % 6} ", + _ => $" {this[x, y]} " }; -} \ No newline at end of file +} diff --git a/56_Life_for_Two/csharp/Game.cs b/56_Life_for_Two/csharp/Game.cs index e8fb05aa..c4656d43 100644 --- a/56_Life_for_Two/csharp/Game.cs +++ b/56_Life_for_Two/csharp/Game.cs @@ -13,17 +13,16 @@ internal class Game { _io.Write(Streams.Title); - for (var _player = 1; _player <= 2; _player++) + for (var player = 1; player <= 2; player++) { - var P1 = _player == 2 ? 0x30 : 0x03; - _io.WriteLine(Formats.InitialPieces, _player); + _io.WriteLine(Formats.InitialPieces, player); for (var i = 1; i <= 3; i++) { - _board[_io.ReadCoordinates(_board)] = P1; + _board[_io.ReadCoordinates(_board)] = player == 1 ? Piece.NewPlayer1() : Piece.NewPlayer2(); } } - _board.CalculateNextGeneration(); + _board.CountNeighbours(); _board.Display(_io); while(true) diff --git a/56_Life_for_Two/csharp/Piece.cs b/56_Life_for_Two/csharp/Piece.cs new file mode 100644 index 00000000..545de669 --- /dev/null +++ b/56_Life_for_Two/csharp/Piece.cs @@ -0,0 +1,50 @@ +using System.Collections.Immutable; + +namespace LifeforTwo; + +public struct Piece +{ + public const int None = 0x0000; + public const int Player1 = 0x0100; + public const int Player2 = 0x1000; + private const int PieceMask = Player1 | Player2; + private const int NeighbourValueOffset = 8; + + private static readonly ImmutableHashSet _willBePlayer1 = + new[] { 0x0003, 0x0102, 0x0103, 0x0120, 0x0130, 0x0121, 0x0112, 0x0111, 0x0012 }.ToImmutableHashSet(); + private static readonly ImmutableHashSet _willBePlayer2 = + new[] { 0x0021, 0x0030, 0x1020, 0x1030, 0x1011, 0x1021, 0x1003, 0x1002, 0x1012 }.ToImmutableHashSet(); + + private int _value; + + private Piece(int value) => _value = value; + + public int Value => _value; + public bool IsEmpty => (_value & PieceMask) == None; + + public static Piece NewEmpty() => new(None); + public static Piece NewPlayer1() => new(Player1); + public static Piece NewPlayer2() => new(Player2); + + public Piece AddNeighbour(Piece neighbour) + { + _value += neighbour.Value >> NeighbourValueOffset; + return this; + } + + public Piece GetNext() => new( + _value switch + { + _ when _willBePlayer1.Contains(_value) => Player1, + _ when _willBePlayer2.Contains(_value) => Player2, + _ => None + }); + + public override string ToString() => + (_value & PieceMask) switch + { + Player1 => "*", + Player2 => "#", + _ => " " + }; +} \ No newline at end of file