Files
InverseOfLife/src/Board.cs
2025-01-23 21:58:27 +00:00

161 lines
4.4 KiB
C#

using System.Diagnostics.CodeAnalysis;
namespace InverseOfLife;
public class Board
{
[SetsRequiredMembers]
public Board(int w, int h, bool qx = false, bool qy = false, bool useTracer = false)
{
Width = w;
Height = h;
QuotientX = qx;
QuotientY = qy;
Lives = new();
if (useTracer)
Tracer = new CellTracer();
}
public required int Width { get; init; }
public required int Height { get; init; }
public required bool QuotientX { get; init; }
public required bool QuotientY { get; init; }
public HashSet<(int, int)> Lives { get; internal set; }
public CellTracer Tracer { get; init; }
public void Toggle(int x, int y, bool initialConfig=false)
{
bool added = Lives.Add((x, y));
if (!added)
{
Lives.Remove((x, y));
if (initialConfig && Tracer is not null)
Tracer.Contribution.Remove((x, y));
}
else if (initialConfig && Tracer is not null)
{
Tracer.Contribution[(x, y)] = new Dictionary<(int, int), double>
{
{ (x, y), 1d }
};
}
}
public void Toggle((int x, int y) cell, bool initialConfig = false) => Toggle(cell.x, cell.y, initialConfig);
public void Evaluate(int steps)
{
for(int i = 0; i< steps; i++)
Evaluate();
}
public void Evaluate()
{
Dictionary<(int, int), int> neighbourCount = new();
foreach ((int x, int y) in Lives)
{
if (!neighbourCount.ContainsKey((x, y)))
neighbourCount[(x, y)] = 0;
Populate(x, y, neighbourCount);
}
foreach ((int x, int y) in neighbourCount.Keys)
{
if (Lives.Contains((x, y)))
{
if (neighbourCount[(x, y)] != 2 && neighbourCount[(x, y)] != 3)
{
Lives.Remove((x, y));
if (Tracer is not null)
Tracer.DeferredRemove((x, y));
}
}
else
{
if (neighbourCount[(x, y)] == 3)
{
Lives.Add((x, y));
if (Tracer is not null)
Tracer.DeferredAdd((x, y));
}
}
}
if (Tracer is not null)
Tracer.BatchProcess();
}
public void Populate(int x, int y, Dictionary<(int, int), int> count)
{
for (int dx = -1; dx <= 1; dx++)
{
for (int dy = -1; dy <= 1; dy++)
{
if (dx == 0 && dy == 0)
continue;
int nx = QuotientX ? (x + dx + Width) % Width : x + dx;
int ny = QuotientY ? (y + dy + Height) % Height : y + dy;
if(nx >= Width || nx < 0)
continue;
if(ny >= Height || ny < 0)
continue;
if (!count.ContainsKey((nx, ny)))
count[(nx, ny)] = 0;
count[(nx, ny)] += 1;
if (Tracer is not null)
{
if (!Tracer.Parents.ContainsKey((nx, ny)))
Tracer.Parents[(nx, ny)] = new HashSet<(int, int)>();
Tracer.Parents[(nx, ny)].Add((x, y));
}
}
}
}
public override string ToString()
{
var builder = new System.Text.StringBuilder();
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
builder.Append(Lives.Contains((x, y)) ? 'o' : ' ');
builder.AppendLine();
}
return builder.ToString();
}
public HashSet<(int, int)>[] Frames(int steps)
{
HashSet<(int, int)>[] res = new HashSet<(int, int)>[steps];
for (int i = 0; i < steps; i++)
{
Evaluate();
res[i] = CopyLives();
}
return res;
}
private HashSet<(int, int)> CopyLives()
{
HashSet<(int, int)> res = new();
foreach ((int, int) cell in Lives)
res.Add(cell);
return res;
}
public void Play(int generations, int delay = 200)
{
for (int i = 0; i < generations; i++)
{
Console.Clear();
Console.WriteLine(ToString());
Evaluate();
Thread.Sleep(delay);
}
}
}