Initial commit
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GameOfLife.Entities
|
||||
{
|
||||
public class Cell : Tuple<short, short>
|
||||
{
|
||||
public Cell(short x, short y) : base(x, y)
|
||||
{
|
||||
}
|
||||
public Cell(short x, int y) : base(x, (short)y) { }
|
||||
public Cell(int x, short y) : base((short)x, y) { }
|
||||
public Cell(int x, int y) : base((short)x, (short)y) { }
|
||||
|
||||
public static Cell operator +(Cell cell1, Cell cell2)
|
||||
=> new Cell(cell1.Item1 + cell2.Item1, cell1.Item2 + cell2.Item2);
|
||||
|
||||
public static Cell operator -(Cell cell1, Cell cell2)
|
||||
=> new Cell(cell1.Item1 - cell2.Item1, cell1.Item2 - cell2.Item2);
|
||||
|
||||
public Cell Negate()
|
||||
=> new Cell(-Item1, -Item2);
|
||||
|
||||
public IEnumerable<Cell> NeighborCells => new []
|
||||
{
|
||||
new Cell(Item1 - 1, Item2 - 1), new Cell(Item1 - 1, Item2), new Cell(Item1 - 1, Item2 + 1),
|
||||
new Cell(Item1, Item2 - 1), new Cell(Item1, Item2 + 1),
|
||||
new Cell(Item1 + 1, Item2 - 1), new Cell(Item1 + 1, Item2), new Cell(Item1 + 1, Item2 + 1),
|
||||
};
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{Item1}, {Item2}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace GameOfLife.Entities
|
||||
{
|
||||
public class Matcher
|
||||
{
|
||||
private Cell Key;
|
||||
private Cell Left;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameOfLife.Entities
|
||||
{
|
||||
public class Pattern : ReadOnlyCollection<Cell>
|
||||
{
|
||||
public Pattern(IEnumerable<Cell> pattern) : base(pattern.OrderBy(c => c.Item2).ThenBy(c => c.Item1).ToList())
|
||||
{
|
||||
// https://stackoverflow.com/a/263416/99492
|
||||
unchecked
|
||||
{
|
||||
_hashCodeCache =
|
||||
this //.OrderBy(c => c)
|
||||
.Aggregate(23, (hash, c)
|
||||
=> hash * 37 + c.GetHashCode());
|
||||
}
|
||||
}
|
||||
|
||||
public Cell GetBoundaryMin() =>
|
||||
new Cell(
|
||||
this.Min(c => c.Item1) - 1,
|
||||
this.Min(c => c.Item2) - 1);
|
||||
|
||||
public Cell GetBoundaryMax() =>
|
||||
new Cell(
|
||||
this.Max(c => c.Item1) + 1,
|
||||
this.Max(c => c.Item2) + 1);
|
||||
|
||||
public Cell GetFirstRowFirstCell() =>
|
||||
this.OrderBy(c => c.Item2).ThenBy(c => c.Item1).First();
|
||||
|
||||
public bool ContainsAll(IEnumerable<Cell> cells) => cells.All(Contains);
|
||||
public bool ContainsNone(IEnumerable<Cell> cells) => cells.All(c => !Contains(c));
|
||||
|
||||
public short GetMinY() =>
|
||||
this.Min(c => c.Item2);
|
||||
|
||||
// All neighboring cells
|
||||
public Pattern GetBoundary =>
|
||||
this.SelectMany(cell => cell.NeighborCells).Distinct().ToPattern() - this;
|
||||
|
||||
public Pattern Rotate() =>
|
||||
/* Rotate 90deg clockwise
|
||||
* ..... ..... .oo.. ...o.
|
||||
* ..o.. -> .oo.. -> ..o.. -> ..oo.
|
||||
* ..oo. .o... ..... .....
|
||||
* ..... ..... ..... .....
|
||||
* [0,0] [0,0] [0,0] [0,0]
|
||||
* [0,1] [-1,0] [0,-1] [1,0]
|
||||
* [1,1] [-1,1] [-1,-1] [1,-1]
|
||||
*/
|
||||
this
|
||||
.Select(cell
|
||||
=> new Cell(-cell.Item2, cell.Item1))
|
||||
.ToPattern();
|
||||
|
||||
public Pattern Rotate(int count)
|
||||
{
|
||||
var p = new Pattern(this);
|
||||
for (var i = 0; i < count; i++)
|
||||
p = p.Rotate();
|
||||
return p.ToPattern();
|
||||
}
|
||||
|
||||
public Pattern ReflectX() =>
|
||||
/* ..... ..... ..oo.
|
||||
* ..o.. ..o.. ..o..
|
||||
* ..oo. .oo.. .....
|
||||
* ..... ..... .....
|
||||
* [0,0] [0,0] [0,0]
|
||||
* [0,1] [0,1] [0,-1]
|
||||
* [1,1] [-1,1] [1,-1]
|
||||
*/
|
||||
this
|
||||
.Select(cell
|
||||
=> new Cell((short)-cell.Item1, cell.Item2))
|
||||
.ToPattern();
|
||||
|
||||
public Pattern ReflectY() =>
|
||||
this
|
||||
.Select(cell
|
||||
=> new Cell((short)cell.Item1, (short)-cell.Item2))
|
||||
.ToPattern();
|
||||
|
||||
// move pattern to deterministic position
|
||||
public Pattern Normalize() =>
|
||||
this.Select(c => c - GetFirstRowFirstCell()).ToPattern();
|
||||
|
||||
public Pattern Offset(Cell offsetCell) =>
|
||||
this.Select(c => c + offsetCell).ToPattern();
|
||||
|
||||
// Subtract cells from another pattern
|
||||
public static Pattern operator -(Pattern p1, Pattern p2)
|
||||
=> p1.Where(c => !p2.Contains(c)).ToPattern();
|
||||
|
||||
// Adds cells from another pattern
|
||||
public static Pattern operator +(Pattern p1, Pattern p2)
|
||||
=> p1.Concat(p2).Distinct().ToPattern();
|
||||
|
||||
/// <summary>
|
||||
/// Extracts coordinates from text encoded pattern, treating 'char c' as cells
|
||||
/// </summary>
|
||||
public static Pattern Extract(string text, char c = 'o')
|
||||
=> Extract(text.SplitByLine(), c);
|
||||
|
||||
public static Pattern Extract(IEnumerable<string> text, char c = 'o')
|
||||
=> ExtractCells(text, c).ToPattern();
|
||||
|
||||
private static IEnumerable<Cell> ExtractCells(IEnumerable<string> text, char c = 'o')
|
||||
{
|
||||
short y = 0;
|
||||
foreach (var line in text)
|
||||
{
|
||||
for (short x = 0; x < line.Length; x++)
|
||||
if (line[x] == c)
|
||||
yield return new Cell(x, y);
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> ToGrid(char live = 'O', char dead = '.')
|
||||
{
|
||||
var boundaryMin = GetBoundaryMin();
|
||||
var boundaryMax = GetBoundaryMax();
|
||||
|
||||
for (var y = boundaryMin.Item2; y < boundaryMax.Item2; y++)
|
||||
{
|
||||
var s = "";
|
||||
for (var x = boundaryMin.Item1; x < boundaryMax.Item1; x++)
|
||||
s += (Contains(new Cell(x, y)) ? live : dead) + " ";
|
||||
yield return s;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is Pattern pattern))
|
||||
return false;
|
||||
|
||||
return this.All(pattern.Contains) && pattern.All(this.Contains);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => _hashCodeCache;
|
||||
|
||||
private readonly int _hashCodeCache;
|
||||
|
||||
public IEnumerable<Pattern> FindPatterns(Projections projections)
|
||||
{
|
||||
var livingCells = new ConcurrentBag<Cell>(this);
|
||||
var foundPatterns = new ConcurrentBag<Pattern>();
|
||||
|
||||
Parallel.ForEach(projections,
|
||||
projection =>
|
||||
{
|
||||
foreach (var livingCell in livingCells)
|
||||
{
|
||||
var patternProjection
|
||||
= projection.Select(c => livingCell + c).ToArray();
|
||||
var patternMatch = ContainsAll(patternProjection);
|
||||
if (!patternMatch)
|
||||
continue;
|
||||
|
||||
// check if surrounding cells are not alive
|
||||
var boundaryProjection
|
||||
= projections.BoundaryCells[projection].Select(c => livingCell + c).ToArray();
|
||||
var boundaryMatch = ContainsNone(boundaryProjection);
|
||||
|
||||
if (boundaryMatch)
|
||||
foundPatterns.Add(patternProjection.ToPattern());
|
||||
|
||||
// shorten search time by removing these cells
|
||||
// from examination for other patterns
|
||||
foreach (var foundsCell in patternProjection)
|
||||
{
|
||||
// ReSharper disable once NotAccessedVariable
|
||||
var cell = new Cell(foundsCell.Item1, foundsCell.Item2);
|
||||
livingCells.TryTake(out cell);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return foundPatterns;
|
||||
}
|
||||
|
||||
public IEnumerable<Pattern> FindPatterns_Serial(Projections projections)
|
||||
{
|
||||
var cells = new List<Cell>(this);
|
||||
var foundPatterns = new ConcurrentBag<Pattern>();
|
||||
|
||||
foreach (var projection in projections)
|
||||
{
|
||||
foreach (var livingCell in cells)
|
||||
{
|
||||
var patternProjection
|
||||
= projection.Select(c => livingCell + c).ToArray();
|
||||
var patternMatch = ContainsAll(patternProjection);
|
||||
if (!patternMatch)
|
||||
continue;
|
||||
|
||||
// check if surrounding cells are dead
|
||||
var boundaryProjection
|
||||
= projections.BoundaryCells[projection].Select(c => livingCell + c).ToArray();
|
||||
var boundaryMatch = ContainsNone(boundaryProjection);
|
||||
|
||||
if (boundaryMatch)
|
||||
foundPatterns.Add(patternProjection.ToPattern());
|
||||
|
||||
// shorten search time by removing these cells
|
||||
// from examination for other patterns
|
||||
//foreach (var foundsCell in patternProjection)
|
||||
//{
|
||||
// // ReSharper disable once NotAccessedVariable
|
||||
// //var cell = new Cell(foundsCell.Item1, foundsCell.Item2);
|
||||
// cells.Remove(foundsCell);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
return foundPatterns;
|
||||
}
|
||||
|
||||
public IEnumerable<Pattern> FindPattern(Pattern pattern)
|
||||
{
|
||||
return FindPatterns(new Projections(pattern));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join(", ", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace GameOfLife.Entities
|
||||
{
|
||||
public enum PatternType
|
||||
{
|
||||
StillLife,
|
||||
Oscillator,
|
||||
Spaceship,
|
||||
Periodic,
|
||||
Methuselah,
|
||||
Diehard,
|
||||
Megasized,
|
||||
//Oversized,
|
||||
//Chaotic
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace GameOfLife.Entities
|
||||
{
|
||||
public class Projections : ReadOnlyCollection<Pattern>
|
||||
{
|
||||
public Dictionary<Pattern, Pattern> BoundaryCells { get; }
|
||||
|
||||
public Projections(IEnumerable<Pattern> pattern)
|
||||
: base(pattern.SelectMany(GeneratePatterns).Distinct().ToList())
|
||||
{
|
||||
BoundaryCells = this.ToDictionary(p => p, p => p.GetBoundary);
|
||||
}
|
||||
|
||||
public Projections(Pattern pattern) : this(new[] { pattern }) { }
|
||||
|
||||
private static List<Pattern> GeneratePatterns(Pattern pattern) =>
|
||||
new[]
|
||||
{
|
||||
pattern.Normalize(),
|
||||
pattern.Rotate(1).Normalize(),
|
||||
pattern.Rotate(2).Normalize(),
|
||||
pattern.Rotate(3).Normalize(),
|
||||
pattern.ReflectX().Normalize(),
|
||||
pattern.ReflectY().Normalize(),
|
||||
pattern.ReflectX().Rotate().Normalize(),
|
||||
pattern.ReflectY().Rotate().Normalize()
|
||||
}.Distinct().ToList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user