161 lines
4.4 KiB
C#
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);
|
|
}
|
|
}
|
|
} |