Initial commit
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
bin/
|
||||
obj/
|
||||
.vs/
|
||||
*.user
|
||||
*.suo
|
||||
packages/
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.33027.164
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameOfLife", "GameOfLife\GameOfLife.csproj", "{0BEA14A8-8E11-4A18-BD7D-628D3F1561B0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GolConsole", "GolConsole\GolConsole.csproj", "{8E6D5F09-E4A5-496D-8DCC-36F4780F4CCD}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CD87109A-F147-479C-B9C6-6FCCE53453B2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameOfLifeTests", "GameOfLifeTests\GameOfLifeTests.csproj", "{B45B230E-ED95-4524-9B54-0578E689E015}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0BEA14A8-8E11-4A18-BD7D-628D3F1561B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0BEA14A8-8E11-4A18-BD7D-628D3F1561B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0BEA14A8-8E11-4A18-BD7D-628D3F1561B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0BEA14A8-8E11-4A18-BD7D-628D3F1561B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8E6D5F09-E4A5-496D-8DCC-36F4780F4CCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8E6D5F09-E4A5-496D-8DCC-36F4780F4CCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8E6D5F09-E4A5-496D-8DCC-36F4780F4CCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8E6D5F09-E4A5-496D-8DCC-36F4780F4CCD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B45B230E-ED95-4524-9B54-0578E689E015}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B45B230E-ED95-4524-9B54-0578E689E015}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B45B230E-ED95-4524-9B54-0578E689E015}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B45B230E-ED95-4524-9B54-0578E689E015}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E38811A1-A6A9-421C-A493-CE02CE3E0DDD}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -0,0 +1,101 @@
|
||||
Name,Apgcode
|
||||
Block,xs4_33
|
||||
Blinker,xp2_7
|
||||
Beehive,xs6_696
|
||||
Glider,xq4_153
|
||||
Loaf,xs7_2596
|
||||
Boat,xs5_253
|
||||
Ship,xs6_356
|
||||
Tub,xs4_252
|
||||
Pond,xs8_6996
|
||||
Long boat,xs7_25ac
|
||||
Toad,xp2_7e
|
||||
Ship-tie,xs12_g8o653z11
|
||||
Beacon,xp2_318c
|
||||
Barge,xs6_25a4
|
||||
Half-bakery,xs14_g88m952z121
|
||||
Mango,xs8_69ic
|
||||
Eater 1,xs7_178c
|
||||
Lightweight spaceship,xq4_6frc
|
||||
Long barge,xs8_25ak8
|
||||
Aircraft carrier,xs6_39c
|
||||
Pulsar,xp3_co9nas0san9oczgoldlo0oldlogz1047210127401
|
||||
Paperclip,xs14_69bqic
|
||||
Middleweight spaceship,xq4_27dee6
|
||||
Long ship,xs8_35ac
|
||||
Integral sign,xs9_31ego
|
||||
Shillelagh,xs8_3pm
|
||||
Boat-tie,xs10_g8o652z01
|
||||
Snake,xs6_bd
|
||||
Big S,xs14_g88b96z123
|
||||
Bipond,xs16_g88m996z1221
|
||||
Trans-boat with tail,xs9_178ko
|
||||
Boat tie ship,xs11_g8o652z11
|
||||
Hat,xs9_4aar
|
||||
Very long ship,xs10_35ako
|
||||
Heavyweight spaceship,xq4_27deee6
|
||||
Very long boat,xs9_25ako
|
||||
Tub with tail,xs8_178k8
|
||||
Mirrored table,xs12_raar
|
||||
Dead spark coil,xs18_rhe0ehr
|
||||
Canoe,xs8_312ko
|
||||
Beehive on dock,xs16_j1u0696z11
|
||||
Cis-mirrored bun,xs14_6970796
|
||||
Moose antlers,xs15_354cgc453
|
||||
Block on table,xs10_32qr
|
||||
Block on dock,xs14_j1u066z11
|
||||
Scorpion,xs16_69egmiczx1
|
||||
Beehive with tail,xs10_178kk8
|
||||
Twin hat,xs17_2ege1ege2
|
||||
Loop,xs10_69ar
|
||||
Long snake,xs7_3lo
|
||||
Fourteener,xs14_69bo8a6
|
||||
Pentadecathlon,xp15_4r4z4r4
|
||||
Cis-mirrored bookend,xs14_39e0e93
|
||||
Cis-boat with tail,xs9_178kc
|
||||
Cis-rotated bookend,xs14_6is079c
|
||||
Elevener,xs11_g0s453z11
|
||||
Mirrored dock,xs20_3lkkl3z32w23
|
||||
Block on cap,xs12_330f96
|
||||
Trans-loaf with tail,xs11_ggm952z1
|
||||
Cis-shillelagh,xs10_358gkc
|
||||
Trans-mirrored bun,xs14_69e0eic
|
||||
Clock,xp2_2a54
|
||||
Trans-block on long bookend,xs12_330fho
|
||||
Block-laying switch engine,yl144_1_16_afb5f3db909e60548f086e22ee3353ac
|
||||
Prodigal,xs10_g0s252z11
|
||||
Broken snake,xs10_0drz32
|
||||
Trans-bookend and bun,xs14_39e0eic
|
||||
Eater with nine,xs12_178c453
|
||||
Block on cover,xs12_178br
|
||||
Cis-boat on dock,xs15_j1u06a4z11
|
||||
Cis-block on long bookend,xs12_3hu066
|
||||
Very long snake,xs8_31248c
|
||||
Boat with long tail,xs10_3215ac
|
||||
Long shillelagh,xs9_312453
|
||||
Beehive at loaf,xs13_g88m96z121
|
||||
Trans-bun and wing,xs15_259e0eic
|
||||
Long integral,xs10_3542ac
|
||||
Tub with long tail,xs9_25a84c
|
||||
Cis-bookend and bun,xs14_39e0e96
|
||||
Hook with tail,xs8_32qk
|
||||
Loaf siamese loaf,xs11_69lic
|
||||
Long canoe,xs9_g0g853z11
|
||||
Eleven loop,xs11_178jd
|
||||
Trans-loaf on table,xs13_4a960ui
|
||||
Cis-loaf with tail,xs11_178kic
|
||||
Symmetric scorpion,xs16_69bob96
|
||||
Claw with tail,xs10_1784ko
|
||||
Bee hat,xs15_3lkm96z01
|
||||
Cis-mirrored dove,xs18_69is0si96
|
||||
Trans-rotated bun,xs14_g8o0e96z121
|
||||
Glider-producing switch engine,yl384_1_59_7aeb1999980c43b4945fb7fcdb023326
|
||||
Cis-mirrored wing,xs16_259e0e952
|
||||
Trans-snake on bun,xs13_69e0mq
|
||||
Boat tie eater tail,xs12_256o8a6
|
||||
Snorkel loop,xs12_2egm93
|
||||
Beehive on table,xs12_6960ui
|
||||
Cis-boat on table,xs11_2530f9
|
||||
Trans-barge with tail,xs10_ggka52z1
|
||||
Trans-boat on dock,xs15_3lk453z121
|
||||
Beehive on cap,xs14_6960uic
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{0BEA14A8-8E11-4A18-BD7D-628D3F1561B0}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GameOfLife</RootNamespace>
|
||||
<AssemblyName>GameOfLife</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Entities\Cell.cs" />
|
||||
<Compile Include="Entities\Class1.cs" />
|
||||
<Compile Include="PatternLibrary.cs" />
|
||||
<Compile Include="Entities\PatternType.cs" />
|
||||
<Compile Include="IO\ApgcodeDecoder.cs" />
|
||||
<Compile Include="IO\ApgcodePatternMetaData.cs" />
|
||||
<Compile Include="IO\RlePatternMetaData.cs" />
|
||||
<Compile Include="Neighborhood.cs" />
|
||||
<Compile Include="Search\Node.cs" />
|
||||
<Compile Include="Util.cs" />
|
||||
<Compile Include="LifeBase.cs" />
|
||||
<Compile Include="LifeArray.cs" />
|
||||
<Compile Include="LifeHashSet.cs" />
|
||||
<Compile Include="ILife.cs" />
|
||||
<Compile Include="Entities\Pattern.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="IO\PatternMetadata.cs" />
|
||||
<Compile Include="IO\RleDecoder.cs" />
|
||||
<Compile Include="Entities\Projections.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<EmbeddedResource Include="CommonPatterns.csv" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
public abstract class LifeBase : ILife
|
||||
{
|
||||
public int Generation { get; protected set; }
|
||||
public int Population { get; protected set; }
|
||||
public abstract bool IsCellAlive(Cell cell);
|
||||
public virtual bool IsCellAlive(short x, short y) => IsCellAlive(new Cell(x,y));
|
||||
public bool AreAllCellsAlive(IEnumerable<Cell> cells) => cells.All(IsCellAlive);
|
||||
public bool AreAllCellsDead(IEnumerable<Cell> cells) => cells.All(c => !IsCellAlive(c));
|
||||
|
||||
public bool ToggleCell(Cell cell)
|
||||
{
|
||||
var r = ToggleCell_Internal(cell);
|
||||
Population += r ? 1 : -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
protected abstract bool ToggleCell_Internal(Cell cell);
|
||||
|
||||
public void IncrementGeneration()
|
||||
{
|
||||
Population = IncrementGeneration_Internal();
|
||||
Generation++;
|
||||
}
|
||||
|
||||
protected abstract int IncrementGeneration_Internal();
|
||||
|
||||
public abstract IEnumerable<Cell> LivingCells { get; }
|
||||
|
||||
public static readonly short[,] NeighborOffsets =
|
||||
{
|
||||
{ -1, -1 }, { -1, 0 }, { -1, 1 },
|
||||
{ 0, -1 }, { 0, 1 },
|
||||
{ 1, -1 }, { 1, 0 }, { 1, 1 },
|
||||
};
|
||||
|
||||
public byte GetNeighborCount(short x, short y)
|
||||
{
|
||||
byte numberOfNeighbors = 0;
|
||||
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
numberOfNeighbors +=
|
||||
IsCellAlive((short)(x + NeighborOffsets[i,0]), (short)(y + NeighborOffsets[i,1]))
|
||||
? (byte)1
|
||||
: (byte)0;
|
||||
}
|
||||
|
||||
return numberOfNeighbors;
|
||||
}
|
||||
|
||||
protected bool ShouldLive(short x, short y)
|
||||
{
|
||||
var numberOfNeighbors = GetNeighborCount(x, y);
|
||||
|
||||
var isAlive = IsCellAlive(x, y);
|
||||
|
||||
return GameRule(isAlive, numberOfNeighbors);
|
||||
}
|
||||
|
||||
protected bool GameRule(bool isAlive, int neighborCount)
|
||||
{
|
||||
switch (isAlive)
|
||||
{
|
||||
case false
|
||||
when neighborCount == (byte)3:
|
||||
case true
|
||||
when neighborCount == (byte)2
|
||||
|| neighborCount == (byte)3:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public Tuple<Cell, short>[] GetNeighborField() => GetNeighborField(LivingCells);
|
||||
|
||||
public static Tuple<Cell, short>[] GetNeighborField(IEnumerable<Cell> world)
|
||||
{
|
||||
var neighborCount = new Dictionary<Cell, short>();
|
||||
|
||||
foreach (var cell in world)
|
||||
{
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
var xOffset = LifeBase.NeighborOffsets[i, 0];
|
||||
var yOffset = LifeBase.NeighborOffsets[i, 1];
|
||||
var neighbor =
|
||||
new Cell(
|
||||
(cell.Item1 + xOffset),
|
||||
(cell.Item2 + yOffset));
|
||||
if (!neighborCount.ContainsKey(neighbor))
|
||||
neighborCount[neighbor] = 1;
|
||||
else
|
||||
neighborCount[neighbor] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return neighborCount.Select(pair => Tuple.Create(pair.Key, pair.Value)).ToArray();
|
||||
}
|
||||
|
||||
protected static Tuple<Cell, short>[] GetNeighborField_Parallel(IEnumerable<Cell> world)
|
||||
{
|
||||
//var neighborCount = new Dictionary<Tuple<int, int>, int>();
|
||||
var neighbors = new List<Cell>();
|
||||
|
||||
Parallel.ForEach(
|
||||
world,
|
||||
() => new List<Cell>(),
|
||||
(cell, loop, localNeighbors) =>
|
||||
{
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
|
||||
var neighbor =
|
||||
new Cell(
|
||||
(cell.Item1 + LifeBase.NeighborOffsets[i, 0]),
|
||||
(cell.Item2 + LifeBase.NeighborOffsets[i, 1]));
|
||||
localNeighbors.Add(neighbor);
|
||||
}
|
||||
|
||||
return localNeighbors;
|
||||
},
|
||||
finalNeighbors =>
|
||||
{
|
||||
lock(neighbors)
|
||||
neighbors.AddRange(finalNeighbors);
|
||||
});
|
||||
|
||||
var neighborCount =
|
||||
neighbors
|
||||
.GroupBy(n => n)
|
||||
.Select(n => Tuple.Create(n.Key, (short) n.Count()))
|
||||
.ToArray();
|
||||
|
||||
return neighborCount;
|
||||
}
|
||||
|
||||
public IEnumerable<Pattern> FindPattern(Pattern pattern)
|
||||
{
|
||||
var variations = new Variations(pattern);
|
||||
var livingCells = LivingCells.ToList();
|
||||
foreach (var patternVar in variations)
|
||||
{
|
||||
var foundsCells = new List<Cell>();
|
||||
foreach (var livingCell in livingCells)
|
||||
{
|
||||
var patternProjection
|
||||
= patternVar.Select(c => livingCell + c).ToArray();
|
||||
var patternMatch = AreAllCellsAlive(patternProjection);
|
||||
if (!patternMatch)
|
||||
continue;
|
||||
|
||||
// check if surrounding cells are not alive
|
||||
var boundaryProjection
|
||||
= variations.BoundaryCells[patternVar].Select(c => livingCell + c).ToArray();
|
||||
var boundaryMatch = AreAllCellsDead(boundaryProjection);
|
||||
|
||||
if (boundaryMatch)
|
||||
yield return patternProjection.ToPattern();
|
||||
|
||||
foundsCells.AddRange(patternProjection);
|
||||
|
||||
}
|
||||
|
||||
livingCells.RemoveAll(c => foundsCells.Contains(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
public interface ILife
|
||||
{
|
||||
int Generation { get; }
|
||||
int Population { get; }
|
||||
bool IsCellAlive(Cell cell);
|
||||
bool AllAlive(IEnumerable<Cell> cells);
|
||||
bool AllDead(IEnumerable<Cell> cells);
|
||||
bool ToggleCell(Cell cell);
|
||||
void IncrementGeneration();
|
||||
IEnumerable<Cell> LivingCells { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife.IO
|
||||
{
|
||||
// https://conwaylife.com/wiki/Apgcode
|
||||
public class ApgcodeDecoder
|
||||
{
|
||||
private static readonly Regex PrefixRegex = new Regex(@"(?<header>.+(?=_))?_?(?<code>.+)");
|
||||
private static readonly Regex ExpansionRegex = new Regex(@"y(\w+)");
|
||||
public Pattern Pattern { get; }
|
||||
public PatternMetadata Metadata { get; }
|
||||
|
||||
public ApgcodeDecoder(string apgcode, string name = null)
|
||||
{
|
||||
var match = PrefixRegex.Match(apgcode);
|
||||
if (!match.Success)
|
||||
throw new ArgumentException("Invalid apgcode");
|
||||
var header = match.Groups["header"].Value;
|
||||
var code = match.Groups["code"].Value;
|
||||
if (!string.IsNullOrEmpty(header))
|
||||
Metadata = new ApgcodePatternMetaData(header,name);
|
||||
|
||||
var expanded = ExpandAbbreviations(code);
|
||||
Pattern = DecodeCells(expanded).ToPattern();
|
||||
}
|
||||
|
||||
private IEnumerable<Cell> DecodeCells(string expanded)
|
||||
{
|
||||
var rowNum = 0;
|
||||
foreach (var strip in expanded.Split('z'))
|
||||
{
|
||||
var colBits = strip.Select(ConvertToUint).ToArray();
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var colNum = 0;
|
||||
foreach (var colBit in colBits)
|
||||
{
|
||||
if ((colBit >> i & 0b_1) == 1)
|
||||
yield return new Cell(colNum, rowNum);
|
||||
colNum++;
|
||||
}
|
||||
|
||||
rowNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string ExpandAbbreviations(string apgcode)
|
||||
{
|
||||
apgcode = apgcode.Replace("w", "00");
|
||||
apgcode = apgcode.Replace("x", "000");
|
||||
apgcode = ExpansionRegex.Replace(apgcode, m =>
|
||||
new string('0', 4 + Convert.ToInt32(ConvertToUint(m.Groups[1].Value[0]))));
|
||||
return apgcode;
|
||||
}
|
||||
|
||||
public static uint ConvertToUint(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return (uint)(c - '0');
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return (uint)(c - 'a' + 10);
|
||||
throw new Exception("Cannot convert char " + c.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife.IO
|
||||
{
|
||||
public class ApgcodePatternMetaData : PatternMetadata
|
||||
{
|
||||
private static readonly Regex HeaderRegex = new Regex(@"(?<prefix>\w{2})(?<suffix>.*)");
|
||||
public ApgcodePatternMetaData(string header, string name=null)
|
||||
{
|
||||
if (name != null)
|
||||
Name = name;
|
||||
var match = HeaderRegex.Match(header);
|
||||
Type = Prefix(match.Groups["prefix"].Value);
|
||||
Comments = new [] { match.Groups["suffix"].Value };
|
||||
}
|
||||
|
||||
private PatternType Prefix(string prefix)
|
||||
{
|
||||
switch (prefix)
|
||||
{
|
||||
case "xs": return PatternType.StillLife;
|
||||
case "xp": return PatternType.Oscillator;
|
||||
case "xq": return PatternType.Spaceship;
|
||||
case "yl": return PatternType.Periodic;
|
||||
case "methuselah" : return PatternType.Methuselah;
|
||||
case "messless" : return PatternType.Diehard;
|
||||
case "megasized" : return PatternType.Megasized;
|
||||
default:
|
||||
throw new ArgumentException($"Unknown prefix: {prefix}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife.IO
|
||||
{
|
||||
public abstract class PatternMetadata
|
||||
{
|
||||
public string Name { protected set; get; }
|
||||
public PatternType Type { get; protected set; }
|
||||
public string Rules { get; protected set; }
|
||||
public string[] Comments { get; protected set; }
|
||||
public int Width { get; protected set; }
|
||||
public int Height { get; protected set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife.IO
|
||||
{
|
||||
public class RleDecoder
|
||||
{
|
||||
public Pattern Pattern { get; }
|
||||
public PatternMetadata Metadata { get; }
|
||||
|
||||
public RleDecoder(string filename) : this(new FileInfo(filename)) { }
|
||||
|
||||
public RleDecoder(FileSystemInfo fi) : this(GetLines(fi)) { }
|
||||
|
||||
public RleDecoder(IEnumerable<string> encodedRle)
|
||||
{
|
||||
var groups =
|
||||
(from line in encodedRle
|
||||
let trimmed = line.Trim()
|
||||
where
|
||||
!string.IsNullOrEmpty(trimmed)
|
||||
group line by line.StartsWith("#") into g
|
||||
select g
|
||||
).ToArray();
|
||||
|
||||
var comments = groups.First(g => g.Key);
|
||||
var nonComments = groups.First(g => !g.Key).ToArray();
|
||||
|
||||
Metadata = new RlePatternMetaData(nonComments.First(), comments);
|
||||
var rleData = string.Join("", nonComments.Skip(1));
|
||||
Pattern = Pattern.Extract(ExpandRle(rleData).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns RLE encoded string into decoded expansion, breaking lines into an enumeration
|
||||
/// </summary>
|
||||
private static IEnumerable<string> ExpandRle(string rleData)
|
||||
{
|
||||
if (!rleData.Contains("!"))
|
||||
throw new ArgumentException("RLE pattern did not contain terminating character '!'");
|
||||
|
||||
var encodedRle
|
||||
= rleData.Substring(0, rleData.IndexOf("!", StringComparison.Ordinal));
|
||||
|
||||
var rleDecodingRegex = new Regex(@"(?<count>\d*)(?<char>[a-z$])");
|
||||
|
||||
var matches = rleDecodingRegex.Matches(encodedRle);
|
||||
var decoded = string.Empty;
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
var countStr = match.Groups["count"].Value;
|
||||
var count = string.IsNullOrEmpty(countStr) ? 1 : int.Parse(countStr);
|
||||
var c = match.Groups["char"].Value.First();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '$':
|
||||
yield return decoded;
|
||||
decoded = string.Empty;
|
||||
break;
|
||||
default:
|
||||
decoded += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
yield return decoded;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetLines(FileSystemInfo fi)
|
||||
{
|
||||
if (!fi.Exists)
|
||||
throw new FileNotFoundException(fi.FullName);
|
||||
return File.ReadAllLines(fi.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GameOfLife.IO
|
||||
{
|
||||
public class RlePatternMetaData : PatternMetadata
|
||||
{
|
||||
public RlePatternMetaData(string line, IEnumerable<string> comments)
|
||||
{
|
||||
Comments = comments.ToArray();
|
||||
|
||||
var mdLine = Regex.Replace(line, @"\s+", "");
|
||||
var props = mdLine.Split(',');
|
||||
if (props.Length < 2)
|
||||
throw new ArgumentException("Line does not contain two properties");
|
||||
|
||||
var mp = new Regex(@"(\w+)=(.*)");
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var capture = mp.Match(prop).Groups;
|
||||
var name = capture[1].Value;
|
||||
var val = capture[2].Value;
|
||||
switch (name)
|
||||
{
|
||||
case "x":
|
||||
Width = int.Parse(val);
|
||||
break;
|
||||
case "y":
|
||||
Height = int.Parse(val);
|
||||
break;
|
||||
// TODO: decode the rules
|
||||
case "rules":
|
||||
Rules = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
public class LifeArray : LifeBase
|
||||
{
|
||||
protected bool[,] World;
|
||||
private bool[,] _nextGeneration;
|
||||
|
||||
//private Task _processTask;
|
||||
public int SizeX { get; private set; }
|
||||
public int SizeY { get; private set; }
|
||||
|
||||
public override bool IsCellAlive(Cell cell)
|
||||
{
|
||||
return IsCellAlive(cell.Item1, cell.Item2);
|
||||
}
|
||||
|
||||
public override bool IsCellAlive(short x, short y)
|
||||
{
|
||||
// first check boundaries
|
||||
if (x < 0 || x >= SizeX || y < 0 || y >= SizeY)
|
||||
return false;
|
||||
return IsCellAlive_NoBoundaryCheck(x, y);
|
||||
}
|
||||
|
||||
private bool IsCellAlive_NoBoundaryCheck(short x, short y) => World[x, y];
|
||||
|
||||
public LifeArray(Pattern pattern)
|
||||
: this(pattern.GetBoundaryMax().Item1, pattern.GetBoundaryMax().Item2)
|
||||
{
|
||||
foreach (var cell in pattern)
|
||||
{
|
||||
ToggleCell(cell);
|
||||
}
|
||||
}
|
||||
|
||||
public LifeArray(Cell sizes) : this (sizes.Item1, sizes.Item2) { }
|
||||
|
||||
public LifeArray(short sizeX, short sizeY)
|
||||
{
|
||||
if (sizeX <= 0 || sizeY <= 0) throw new ArgumentOutOfRangeException("sizeX", "Size must be greater than zero");
|
||||
SizeX = sizeX;
|
||||
SizeY = sizeY;
|
||||
World = new bool[sizeX, sizeY];
|
||||
_nextGeneration = new bool[sizeX, sizeY];
|
||||
}
|
||||
|
||||
protected override bool ToggleCell_Internal(Cell cell)
|
||||
{
|
||||
return World[cell.Item1, cell.Item2] = !World[cell.Item1, cell.Item2];
|
||||
}
|
||||
|
||||
protected override int IncrementGeneration_Internal()
|
||||
{
|
||||
for (short x = 0; x < SizeX; x++)
|
||||
for (short y = 0; y < SizeY; y++)
|
||||
{
|
||||
var shouldLive = ShouldLive(x, y);
|
||||
_nextGeneration[x, y] = shouldLive;
|
||||
}
|
||||
|
||||
// now flip the back buffer so we can start processing on the next generation
|
||||
var flip = _nextGeneration;
|
||||
_nextGeneration = World;
|
||||
World = flip;
|
||||
|
||||
return Population;
|
||||
}
|
||||
|
||||
public override IEnumerable<Cell> LivingCells
|
||||
{
|
||||
get
|
||||
{
|
||||
for (short y=0; y<SizeY;y++)
|
||||
for (short x =0; x<SizeX;x++)
|
||||
if (IsCellAlive_NoBoundaryCheck(x, y))
|
||||
yield return new Cell(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
public abstract class LifeBase : ILife
|
||||
{
|
||||
public int Generation { get; protected set; }
|
||||
public int Population { get; protected set; }
|
||||
public abstract bool IsCellAlive(Cell cell);
|
||||
public virtual bool IsCellAlive(short x, short y) => IsCellAlive(new Cell(x,y));
|
||||
public bool AllAlive(IEnumerable<Cell> cells) => cells.All(IsCellAlive);
|
||||
public bool AllDead(IEnumerable<Cell> cells) => cells.All(c => !IsCellAlive(c));
|
||||
|
||||
private IReadOnlyList<Tuple<Cell, short>> _neighborhoodField;
|
||||
|
||||
public IReadOnlyList<Tuple<Cell, short>> NeighborhoodField =>
|
||||
// cache values this expensive operation
|
||||
_neighborhoodField ?? (_neighborhoodField = Neighborhood.GetNeighborField(LivingCells).ToList());
|
||||
|
||||
private Pattern _livingCellPattern;
|
||||
|
||||
public Pattern LivingCellPattern =>
|
||||
_livingCellPattern ?? (_livingCellPattern = LivingCells.ToPattern());
|
||||
|
||||
public bool ToggleCell(Cell cell)
|
||||
{
|
||||
var alive = ToggleCell_Internal(cell);
|
||||
Population += alive ? 1 : -1;
|
||||
return alive;
|
||||
}
|
||||
|
||||
protected abstract bool ToggleCell_Internal(Cell cell);
|
||||
|
||||
public void IncrementGeneration()
|
||||
{
|
||||
_neighborhoodField = null;
|
||||
_livingCellPattern = null;
|
||||
Population = IncrementGeneration_Internal();
|
||||
Generation++;
|
||||
}
|
||||
|
||||
protected abstract int IncrementGeneration_Internal();
|
||||
|
||||
public abstract IEnumerable<Cell> LivingCells { get; }
|
||||
|
||||
public static readonly short[,] NeighborOffsets =
|
||||
{
|
||||
{ -1, -1 }, { -1, 0 }, { -1, 1 },
|
||||
{ 0, -1 }, { 0, 1 },
|
||||
{ 1, -1 }, { 1, 0 }, { 1, 1 },
|
||||
};
|
||||
|
||||
public byte GetNeighborCount(short x, short y)
|
||||
{
|
||||
byte numberOfNeighbors = 0;
|
||||
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
numberOfNeighbors +=
|
||||
IsCellAlive((short)(x + NeighborOffsets[i,0]), (short)(y + NeighborOffsets[i,1]))
|
||||
? (byte)1
|
||||
: (byte)0;
|
||||
}
|
||||
|
||||
return numberOfNeighbors;
|
||||
}
|
||||
|
||||
protected bool ShouldLive(short x, short y)
|
||||
{
|
||||
var numberOfNeighbors = GetNeighborCount(x, y);
|
||||
|
||||
var isAlive = IsCellAlive(x, y);
|
||||
|
||||
return LifeRule(isAlive, numberOfNeighbors);
|
||||
}
|
||||
|
||||
protected bool LifeRule(bool isAlive, int neighborCount)
|
||||
{
|
||||
switch (isAlive)
|
||||
{
|
||||
case false
|
||||
when neighborCount == 3:
|
||||
case true
|
||||
when neighborCount == 2
|
||||
|| neighborCount == 3:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
public class LifeHashSet : LifeBase
|
||||
{
|
||||
protected HashSet<Cell> World;
|
||||
|
||||
public LifeHashSet()
|
||||
{
|
||||
World = new HashSet<Cell>();
|
||||
}
|
||||
|
||||
public LifeHashSet(IEnumerable<Cell> cells) : this()
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
World.Add(cell);
|
||||
}
|
||||
|
||||
public override bool IsCellAlive(Cell cell) => World.Contains(cell);
|
||||
|
||||
protected override bool ToggleCell_Internal(Cell cell)
|
||||
{
|
||||
if (IsCellAlive(cell))
|
||||
{
|
||||
World.Remove(cell);
|
||||
return false;
|
||||
}
|
||||
|
||||
World.Add(cell);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override int IncrementGeneration_Internal()
|
||||
{
|
||||
World = GetNextGeneration_Parallel(NeighborhoodField);
|
||||
return World.Count;
|
||||
}
|
||||
|
||||
private HashSet<Cell> GetNextGeneration_Serial(IEnumerable<Tuple<Cell, short>> field)
|
||||
{
|
||||
return
|
||||
new HashSet<Cell>(
|
||||
field.Where(
|
||||
cellNeighborCount =>
|
||||
LifeRule(World.Contains(cellNeighborCount.Item1), cellNeighborCount.Item2))
|
||||
.Select(c => c.Item1));
|
||||
}
|
||||
|
||||
private HashSet<Cell> GetNextGeneration_Parallel(IEnumerable<Tuple<Cell, short>> field)
|
||||
{
|
||||
var nextGeneration = new HashSet<Cell>();
|
||||
|
||||
Parallel.ForEach(
|
||||
field,
|
||||
() => new HashSet<Cell>(),
|
||||
(cellNeighborCount, loop, localNextGen) =>
|
||||
{
|
||||
var cell = cellNeighborCount.Item1;
|
||||
var neighborCount = cellNeighborCount.Item2;
|
||||
|
||||
if (LifeRule(World.Contains(cell), neighborCount))
|
||||
localNextGen.Add(cell);
|
||||
|
||||
return localNextGen;
|
||||
},
|
||||
globalNextGen =>
|
||||
{
|
||||
lock (nextGeneration)
|
||||
foreach (var cell in globalNextGen)
|
||||
nextGeneration.Add(cell);
|
||||
}
|
||||
);
|
||||
return nextGeneration;
|
||||
}
|
||||
|
||||
public override IEnumerable<Cell> LivingCells => World;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
public static class Neighborhood
|
||||
{
|
||||
public static Tuple<Cell, short>[] GetNeighborField(IEnumerable<Cell> world)
|
||||
{
|
||||
var neighbors = new List<Cell>();
|
||||
|
||||
Parallel.ForEach(
|
||||
world,
|
||||
() => new List<Cell>(),
|
||||
(cell, loop, localNeighbors) =>
|
||||
{
|
||||
localNeighbors.AddRange(cell.NeighborCells);
|
||||
return localNeighbors;
|
||||
},
|
||||
globalNeighbors =>
|
||||
{
|
||||
lock(neighbors)
|
||||
neighbors.AddRange(globalNeighbors);
|
||||
});
|
||||
|
||||
var neighborCount =
|
||||
neighbors
|
||||
.GroupBy(n => n)
|
||||
.Select(n => Tuple.Create(n.Key, (short)n.Count()))
|
||||
.ToArray();
|
||||
|
||||
return neighborCount;
|
||||
}
|
||||
|
||||
public static Tuple<Cell, short>[] GetNeighborField_Serial(IEnumerable<Cell> world)
|
||||
{
|
||||
var neighborCount = new Dictionary<Cell, short>();
|
||||
|
||||
foreach (var cell in world)
|
||||
{
|
||||
foreach (var neighbor in cell.NeighborCells)
|
||||
{
|
||||
if (!neighborCount.ContainsKey(neighbor))
|
||||
neighborCount[neighbor] = 1;
|
||||
else
|
||||
neighborCount[neighbor] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return neighborCount.Select(pair => Tuple.Create(pair.Key, pair.Value)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using GameOfLife.Entities;
|
||||
using GameOfLife.IO;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
public class PatternLibrary
|
||||
{
|
||||
public const int PatternLoadLimit = 100;
|
||||
public const int OscillationMaxPeriod = 50;
|
||||
|
||||
public IReadOnlyList<Tuple<PatternMetadata, Projections>> PatternProjections;
|
||||
public IReadOnlyList<Tuple<PatternMetadata, Pattern>> Patterns;
|
||||
public IReadOnlyDictionary<Pattern, PatternMetadata> PatternToMetadata;
|
||||
|
||||
public PatternLibrary()
|
||||
{
|
||||
Patterns = ExtractPatterns(GetPatternFileLines()).AsReadOnly();
|
||||
PatternProjections = ExtractOscillationsAndProjections(Patterns);
|
||||
PatternToMetadata = Patterns.ToDictionary(t => t.Item2, t => t.Item1);
|
||||
}
|
||||
|
||||
private static List<Tuple<PatternMetadata, Projections>> ExtractOscillationsAndProjections(IEnumerable<Tuple<PatternMetadata, Pattern>> patterns)
|
||||
{
|
||||
var results =
|
||||
from pattern in patterns.AsParallel()
|
||||
let life = new LifeHashSet(pattern.Item2)
|
||||
let oscillation = life.FindOscillation(OscillationMaxPeriod)
|
||||
let variations = new Projections(oscillation)
|
||||
select Tuple.Create(pattern.Item1, variations);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
|
||||
private static List<Tuple<PatternMetadata, Pattern>> ExtractPatterns(IEnumerable<string> fileLines)
|
||||
{
|
||||
// first line is the header
|
||||
var pattern =
|
||||
from line in fileLines.Skip(1).Take(PatternLoadLimit)
|
||||
select line.Split(',')
|
||||
into split
|
||||
let name = split[0].Trim()
|
||||
let apgCode = split[1].Trim()
|
||||
select new ApgcodeDecoder(apgCode, name)
|
||||
into decoder
|
||||
select Tuple.Create(decoder.Metadata, decoder.Pattern.Normalize());
|
||||
|
||||
|
||||
var extractPatterns = pattern.ToList();
|
||||
|
||||
// ensure this patterns are all unique
|
||||
var duplicates =
|
||||
string.Join(", ",
|
||||
extractPatterns
|
||||
.GroupBy(tuple => tuple)
|
||||
.Where(tuples => tuples.Count() > 1)
|
||||
.Select(t => t.Key.Item1.Name));
|
||||
if (duplicates.Length > 0)
|
||||
throw new Exception($"Duplicate patterns: " + duplicates);
|
||||
|
||||
return extractPatterns;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetPatternFileLines()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
//Getting names of all embedded resources
|
||||
var allResourceNames = assembly.GetManifestResourceNames();
|
||||
|
||||
var resourceName = allResourceNames.First(n => n.Contains("CommonPatterns.csv"));
|
||||
var fileLines = new List<string>();
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceName))
|
||||
if (stream != null)
|
||||
using (var tx = new StreamReader(stream))
|
||||
{
|
||||
while (!tx.EndOfStream)
|
||||
fileLines.Add(tx.ReadLine());
|
||||
}
|
||||
|
||||
return fileLines;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GameOfLife")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GameOfLife")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("0bea14a8-8e11-4a18-bd7d-628d3f1561b0")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Threading.Tasks;
|
||||
using GameOfLife.Entities;
|
||||
using GameOfLife.IO;
|
||||
|
||||
namespace GameOfLife.Search
|
||||
{
|
||||
public class LibrarySearcher
|
||||
{
|
||||
private readonly PatternLibrary _patternLibrary;
|
||||
private Dictionary<Cell, List<Pattern>> _cellPatterns;
|
||||
|
||||
public LibrarySearcher(PatternLibrary patternLibrary)
|
||||
{
|
||||
_patternLibrary = patternLibrary;
|
||||
|
||||
var cellPatterns =
|
||||
from patternProjections in _patternLibrary.PatternProjections
|
||||
from projection in patternProjections.Item2
|
||||
from cell in projection
|
||||
group projection by cell into cellGroups
|
||||
select cellGroups;
|
||||
|
||||
_cellPatterns = cellPatterns.ToDictionary(t => t.Key, t => t.ToList());
|
||||
}
|
||||
|
||||
//public IEnumerable<Tuple<PatternMetadata, Pattern[]>> MatchLibraryPatternsLookup(IEnumerable<Cell> cells)
|
||||
//{
|
||||
// var pattern = cells.ToPattern();
|
||||
//}
|
||||
|
||||
public IEnumerable<Tuple<PatternMetadata, Pattern[]>> MatchLibraryPatterns(IEnumerable<Cell> cells)
|
||||
{
|
||||
var inputCells = cells.ToPattern();
|
||||
|
||||
var matches = new ConcurrentBag<Tuple<PatternMetadata, Pattern[]>>();
|
||||
|
||||
Parallel.ForEach(_patternLibrary.PatternProjections, tuple =>
|
||||
{
|
||||
var patternMetadata = tuple.Item1;
|
||||
var projections = tuple.Item2;
|
||||
|
||||
var foundPatterns = inputCells.FindPatterns_Serial(projections).ToArray();
|
||||
if (foundPatterns.Any())
|
||||
matches.Add(Tuple.Create(patternMetadata, foundPatterns.ToArray()));
|
||||
}
|
||||
);
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
public static class Util
|
||||
{
|
||||
public static Pattern ToPattern(this IEnumerable<Cell> cells)
|
||||
{
|
||||
return new Pattern(cells.ToList());
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/a/63820524
|
||||
public static IEnumerable<string> SplitByLine(this string str)
|
||||
{
|
||||
return Regex
|
||||
.Split(str, @"((\r)+)?(\n)+((\r)+)?")
|
||||
.Select(i => i.Trim())
|
||||
.Where(i => !string.IsNullOrEmpty(i));
|
||||
}
|
||||
|
||||
public static IEnumerable<Pattern> FindOscillation(this ILife gol, int maxPeriod = 50)
|
||||
{
|
||||
var list = new List<Pattern>();
|
||||
|
||||
while (gol.Generation < maxPeriod)
|
||||
{
|
||||
var currentPattern = gol.LivingCells.ToPattern().Normalize();
|
||||
if (list.Contains(currentPattern))
|
||||
return list;
|
||||
|
||||
list.Add(currentPattern);
|
||||
gol.IncrementGeneration();
|
||||
}
|
||||
|
||||
return list.Take(1); // didn't find any oscillations, return the first pattern
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#Beehive
|
||||
x = 4, y = 3, rule = B3/S23
|
||||
b2o$o2bo$b2o!
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using GameOfLife;
|
||||
using GameOfLife.Entities;
|
||||
using GameOfLife.IO;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GameOfLifeTests
|
||||
{
|
||||
public class ApgcodeDecoderTests
|
||||
{
|
||||
[Test]
|
||||
public void ConvertToUintTest()
|
||||
{
|
||||
Assert.AreEqual(0, ApgcodeDecoder.ConvertToUint('0'));
|
||||
Assert.AreEqual(9, ApgcodeDecoder.ConvertToUint('9'));
|
||||
Assert.AreEqual(10, ApgcodeDecoder.ConvertToUint('a'));
|
||||
Assert.AreEqual(11, ApgcodeDecoder.ConvertToUint('b'));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpandAbbreviationsTests()
|
||||
{
|
||||
Assert.AreEqual("00", ApgcodeDecoder.ExpandAbbreviations("w"));
|
||||
Assert.AreEqual("000", ApgcodeDecoder.ExpandAbbreviations("x"));
|
||||
Assert.AreEqual("00000", ApgcodeDecoder.ExpandAbbreviations("wx"));
|
||||
Assert.AreEqual("0000", ApgcodeDecoder.ExpandAbbreviations("y0"));
|
||||
Assert.AreEqual("00000", ApgcodeDecoder.ExpandAbbreviations("y1"));
|
||||
Assert.AreEqual(new string('0', 39), ApgcodeDecoder.ExpandAbbreviations("yz"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Headerless_DecodeTests()
|
||||
{
|
||||
var apgcodeDecoder = new ApgcodeDecoder("0ca178b96z69d1d96");
|
||||
|
||||
foreach (var line in apgcodeDecoder.Pattern.ToGrid())
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
}
|
||||
[Test]
|
||||
public void Xs_DecodeTests()
|
||||
{
|
||||
var apgcodeDecoder = new ApgcodeDecoder("xs4_33");
|
||||
Assert.AreEqual(4, apgcodeDecoder.Pattern.Count);
|
||||
Assert.AreEqual(PatternType.StillLife, apgcodeDecoder.Metadata.Type);
|
||||
Assert.AreEqual("4", apgcodeDecoder.Metadata.Comments.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using GameOfLife;
|
||||
using GameOfLife.Entities;
|
||||
|
||||
namespace GameOfLifeTests
|
||||
{
|
||||
public class CellTests
|
||||
{
|
||||
private readonly PatternTestData _patternTestData = new PatternTestData();
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equals_True_Test()
|
||||
{
|
||||
Assert.True(Util.Equals(_patternTestData.CornerPattern, _patternTestData.CornerCopy));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equals_False_Test()
|
||||
{
|
||||
Assert.False(Util.Equals(_patternTestData.CornerPattern, _patternTestData.SquarePattern));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Rotate_Test()
|
||||
{
|
||||
var rotate = _patternTestData.CornerPattern.Rotate();
|
||||
Console.WriteLine(_patternTestData.CornerPattern);
|
||||
Console.WriteLine(rotate);
|
||||
Assert.Contains(-_patternTestData.CornerPattern[0].Item2, rotate.Select(r => r.Item1).ToList());
|
||||
Assert.Contains(_patternTestData.CornerPattern[0].Item1, rotate.Select(r => r.Item2).ToList());
|
||||
Assert.Contains(-_patternTestData.CornerPattern[0].Item2, rotate.Select(r => r.Item1).ToList());
|
||||
Assert.Contains(-_patternTestData.CornerPattern[0].Item2, rotate.Select(r => r.Item1).ToList());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Rotate_Circle_Test()
|
||||
{
|
||||
var rotate = _patternTestData.CornerPattern.Rotate(4);
|
||||
Assert.AreEqual(_patternTestData.CornerPattern, rotate);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFirstPoint_Test()
|
||||
{
|
||||
var fp = _patternTestData.CornerPattern.GetFirstRowFirstCell();
|
||||
Assert.True(fp.Item1 == 2);
|
||||
Assert.True(fp.Item2 == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Subtract_Test()
|
||||
{
|
||||
var fp = _patternTestData.CornerPattern[0] - new Cell(1,2);
|
||||
Assert.True(fp.Item1 == 1);
|
||||
Assert.True(fp.Item2 == -1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Hash_Test()
|
||||
{
|
||||
var h1 = new Cell(1, 2).GetHashCode();
|
||||
var h2 = new Cell(2, 3).GetHashCode();
|
||||
var h3 = new Cell(1, 2).GetHashCode();
|
||||
|
||||
Assert.AreEqual(h1, h3);
|
||||
Assert.AreNotEqual(h1, h2);
|
||||
}
|
||||
}
|
||||
|
||||
public class PatternTests
|
||||
{
|
||||
[Test]
|
||||
public void GetHashCode_Tests()
|
||||
{
|
||||
var p1 = new Pattern(new List<Cell>() { new Cell(1, 2) }).GetHashCode();
|
||||
var p2 = new Pattern(new List<Cell>() { new Cell(2, 3) }).GetHashCode();
|
||||
var p3 = new Pattern(new List<Cell>() { new Cell(1, 2) }).GetHashCode();
|
||||
var p4 = new Pattern(new List<Cell>() { new Cell(1, 2), new Cell(2,3) }).GetHashCode();
|
||||
var p5 = new Pattern(new List<Cell>() { new Cell(1, 2), new Cell(2,3) }).GetHashCode();
|
||||
var p6 = new Pattern(new List<Cell>() { new Cell(2, 3), new Cell(1, 2) }).GetHashCode();
|
||||
|
||||
Assert.AreEqual(p1, p3);
|
||||
Assert.AreNotEqual(p1, p2);
|
||||
Assert.AreNotEqual(p1, p4);
|
||||
Assert.AreEqual(p4, p5);
|
||||
Assert.AreEqual(p5, p6);
|
||||
}//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net452</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GameOfLife\GameOfLife.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GameOfLife;
|
||||
using GameOfLife.Entities;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GameOfLifeTests
|
||||
{
|
||||
public class PatternLibraryTests
|
||||
{
|
||||
[Test]
|
||||
public void PrintLibrary()
|
||||
{
|
||||
var patternLibrary = new PatternLibrary();
|
||||
//Assert.AreEqual(15, patternLibrary.PatternProjections.Count);
|
||||
foreach (var p in patternLibrary.PatternProjections)
|
||||
{
|
||||
Console.WriteLine($"{p.Item1.Name}, {p.Item1.Type} {string.Join(" ", p.Item1.Comments)}");
|
||||
foreach (var v in p.Item2)
|
||||
{
|
||||
Console.WriteLine(v.First());
|
||||
PatternTestData.ConsoleWriteGrid(v.Offset(new Cell(2,0)));
|
||||
Console.WriteLine();
|
||||
}
|
||||
Console.WriteLine("* * * * ");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchLibraryPatterns_SelfTest()
|
||||
{
|
||||
var patternLibrary = new PatternLibrary();
|
||||
|
||||
foreach (var pattern in patternLibrary.PatternProjections)
|
||||
{
|
||||
var matches = patternLibrary.MatchLibraryPatterns(pattern.Item2[0]);
|
||||
// pattern metadata is the same
|
||||
Assert.AreEqual(matches.Count(m => m.Item1 == pattern.Item1), 1);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultTest()
|
||||
{
|
||||
|
||||
var singleDigits = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }.Select(i=> i.ToString()[0]);
|
||||
|
||||
string ReplaceAtIndex(string s, char c, int i)
|
||||
{
|
||||
var array = s.ToCharArray();
|
||||
array[i] = c;
|
||||
return new string(array);
|
||||
}
|
||||
|
||||
IEnumerable<int> EnumUnderscoreWithNumbers(string s)
|
||||
{
|
||||
var currIndex = s.IndexOf('_');
|
||||
if (currIndex == -1) return new[] { int.Parse(s) };
|
||||
var skip = currIndex == 0 ? 1 : 0;
|
||||
return singleDigits.Skip(skip).SelectMany<char, int>(d => EnumUnderscoreWithNumbers(ReplaceAtIndex(s, d, currIndex)));
|
||||
}
|
||||
|
||||
int PartialProduct(int m1, int m2, int digit)
|
||||
{
|
||||
var m2s = m2.ToString();
|
||||
var c = int.Parse(m2s[m2s.Length - digit].ToString());
|
||||
return m1 * c * (int)Math.Pow(10, digit - 1);
|
||||
}
|
||||
Console.WriteLine(PartialProduct(131, 73, 2));
|
||||
|
||||
|
||||
string multplicand = "_3_";
|
||||
string multiplier = "__";
|
||||
string partialProd1 = "3__9";
|
||||
string partialProd2 = "___";
|
||||
string product = "9__9";
|
||||
|
||||
var r =
|
||||
from mcnd in EnumUnderscoreWithNumbers(multplicand)
|
||||
from mplr in EnumUnderscoreWithNumbers(multiplier)
|
||||
from prdc in EnumUnderscoreWithNumbers(product)
|
||||
from prt in EnumUnderscoreWithNumbers(partialProd1)
|
||||
where PartialProduct(mcnd, mplr, 1) == prt && mcnd * mplr == prdc
|
||||
select new { mcnd, mplr, prdc };
|
||||
|
||||
foreach (var result in r)
|
||||
{
|
||||
Console.WriteLine($"{result.mcnd} * {result.mplr} = {result.prdc}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using GameOfLife;
|
||||
using GameOfLife.Entities;
|
||||
using GameOfLife.IO;
|
||||
|
||||
namespace GameOfLifeTests
|
||||
{
|
||||
public class PatternTestData
|
||||
{
|
||||
public readonly string C0Text = "....." + Environment.NewLine +
|
||||
"..o.." + Environment.NewLine +
|
||||
"..oo." + Environment.NewLine +
|
||||
".....";
|
||||
|
||||
public Pattern CornerPattern;
|
||||
public Pattern CornerCopy;
|
||||
|
||||
public readonly string C2SquareText = "....." + Environment.NewLine +
|
||||
".oo.." + Environment.NewLine +
|
||||
".oo.." + Environment.NewLine +
|
||||
".....";
|
||||
|
||||
public Pattern SquarePattern;
|
||||
|
||||
public readonly string Glider = "...o." + Environment.NewLine +
|
||||
".o.o." + Environment.NewLine +
|
||||
"..oo." + Environment.NewLine +
|
||||
".....";
|
||||
|
||||
public Pattern GliderPattern;
|
||||
|
||||
public readonly string GliderApgcode = "xq4_153";
|
||||
|
||||
public Pattern GliderApgPattern;
|
||||
|
||||
public PatternTestData()
|
||||
{
|
||||
CornerPattern = Pattern.Extract(C0Text);
|
||||
CornerCopy = Pattern.Extract(C0Text);
|
||||
SquarePattern = Pattern.Extract(C2SquareText);
|
||||
GliderPattern = Pattern.Extract(Glider);
|
||||
var apgcodeDecoder = new ApgcodeDecoder(GliderApgcode);
|
||||
GliderApgPattern = apgcodeDecoder.Pattern;
|
||||
}
|
||||
|
||||
public static void ConsoleWrite(Pattern pattern)
|
||||
{
|
||||
foreach (var cell in pattern)
|
||||
{
|
||||
Console.WriteLine(cell);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ConsoleWriteGrid(Pattern pattern)
|
||||
{
|
||||
var enumerable = pattern.ToGrid();
|
||||
|
||||
foreach (var e in enumerable)
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using GameOfLife;
|
||||
using GameOfLife.Entities;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GameOfLifeTests
|
||||
{
|
||||
public class VariationsTests
|
||||
{
|
||||
private readonly PatternTestData _patternTestData = new PatternTestData();
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CornerTest()
|
||||
{
|
||||
var pattern = new Projections(_patternTestData.CornerPattern);
|
||||
Assert.AreEqual(4, pattern.Count);
|
||||
|
||||
var offsetCell = new Cell(2,2);
|
||||
foreach (var variation in pattern)
|
||||
{
|
||||
PatternTestData.ConsoleWrite(variation);
|
||||
PatternTestData.ConsoleWriteGrid(variation.Offset(offsetCell));
|
||||
Console.WriteLine();
|
||||
PatternTestData.ConsoleWriteGrid(variation.GetBoundary.Offset(offsetCell));
|
||||
Console.WriteLine("-----");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SquareTest()
|
||||
{
|
||||
var pattern = new Pattern(_patternTestData.SquarePattern);
|
||||
PatternTestData.ConsoleWrite(pattern);
|
||||
Assert.AreEqual(4, pattern.Count);
|
||||
|
||||
foreach (var variation in new Projections(pattern))
|
||||
{
|
||||
PatternTestData.ConsoleWriteGrid(variation);
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GliderTest()
|
||||
{
|
||||
PatternTestData.ConsoleWrite(_patternTestData.GliderPattern);
|
||||
var variations = new Projections(_patternTestData.GliderPattern);
|
||||
|
||||
foreach (var variation in variations)
|
||||
{
|
||||
PatternTestData.ConsoleWriteGrid(variation.Offset(new Cell(2,0)));
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindPattern_Corner_Tests()
|
||||
{
|
||||
var gol = new LifeArray(_patternTestData.CornerPattern);
|
||||
|
||||
var results = gol.LivingCells.ToPattern().FindPattern(_patternTestData.CornerPattern).ToArray();
|
||||
Assert.AreEqual(1, results.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindPattern_Corner_Square_Tests()
|
||||
{
|
||||
var gol = new LifeArray(_patternTestData.CornerPattern);
|
||||
|
||||
var results = gol.LivingCells.ToPattern().FindPattern(_patternTestData.SquarePattern).ToArray();
|
||||
Assert.AreEqual(0, results.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindPattern_Corner_Rotated_Tests()
|
||||
{
|
||||
var gol = new LifeHashSet(_patternTestData.CornerPattern.Rotate(2).Normalize());
|
||||
|
||||
var results = gol.LivingCells.ToPattern().FindPattern(_patternTestData.CornerPattern).ToArray();
|
||||
Assert.AreEqual(1, results.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindPattern_TwoCorners_Tests()
|
||||
{
|
||||
var offsetCorner = _patternTestData.CornerPattern.Offset(new Cell(3,0));
|
||||
var pattern = _patternTestData.CornerPattern + offsetCorner;
|
||||
//PatternTestData.ConsoleWriteGrid(pattern, 10);
|
||||
|
||||
var gol = new LifeHashSet(pattern);
|
||||
|
||||
var results = gol.LivingCellPattern.FindPattern(_patternTestData.CornerPattern).ToArray();
|
||||
Assert.AreEqual(2, results.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindPattern_Overlapping_Empty_Space_Tests()
|
||||
{
|
||||
var offsetCorner = _patternTestData.CornerPattern.Offset(new Cell(2, 0));
|
||||
var pattern = _patternTestData.CornerPattern + offsetCorner;
|
||||
|
||||
var gol = new LifeHashSet(pattern);
|
||||
|
||||
var results = gol.LivingCells.ToPattern().FindPattern(_patternTestData.CornerPattern).ToArray();
|
||||
Assert.AreEqual(0, results.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindPattern_Corner_and_Square_Tests()
|
||||
{
|
||||
var offsetCorner = _patternTestData.SquarePattern.Offset(new Cell(4, 0));
|
||||
var pattern = _patternTestData.CornerPattern + offsetCorner;
|
||||
PatternTestData.ConsoleWriteGrid(pattern);
|
||||
|
||||
var gol = new LifeHashSet(pattern);
|
||||
|
||||
var cornerResults = gol.LivingCells.ToPattern().FindPattern(_patternTestData.CornerPattern).ToArray();
|
||||
Assert.AreEqual(1, cornerResults.Length);
|
||||
|
||||
var squareResults = gol.LivingCells.ToPattern().FindPattern(_patternTestData.SquarePattern).ToArray();
|
||||
Assert.AreEqual(1, squareResults.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FindOscillations_Tests()
|
||||
{
|
||||
var oscillations= new LifeHashSet(_patternTestData.GliderApgPattern).FindOscillation();
|
||||
Assert.Greater(oscillations.Count(), 1);
|
||||
Console.WriteLine(oscillations.Count());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{8E6D5F09-E4A5-496D-8DCC-36F4780F4CCD}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>GolConsole</RootNamespace>
|
||||
<AssemblyName>GolConsole</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<Content Include="Patterns\ark1.rle">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Patterns\ark2.rle">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Patterns\blom.rle">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Patterns\glider-stream-crystal.rle">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Patterns\glider.rle">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Patterns\honey-farm-hassler-147.rle">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GameOfLife\GameOfLife.csproj">
|
||||
<Project>{0BEA14A8-8E11-4A18-BD7D-628D3F1561B0}</Project>
|
||||
<Name>GameOfLife</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
# ark1 -- 16 cells, stabilizes at 736692 gens, found by Nick Gotts.
|
||||
x = 32, y = 29, rule = B3/S23
|
||||
27bo$28bo$29bo$28bo$27bo$29b3o20$oo$bbo$bbo$3b4o!
|
||||
@@ -0,0 +1,3 @@
|
||||
# ark2 -- 19 cells, stabilizes at 8120878 gens, found by Nick Gotts.
|
||||
x = 53, y = 44, rule = B3/S23
|
||||
50b3o28$12bo$12bo$13boo$15bo$15bo$15bo$15bo6$oo$bbo$bbo$3b4o!
|
||||
@@ -0,0 +1,5 @@
|
||||
#C Blom. Runs for 23314 gens. Initial pop = 13. Final pop = 2740.
|
||||
#C Found by Dean Hickerson, 7 July 2002
|
||||
#C See also http://www.radicaleye.com/DRH/methuselahs.html
|
||||
x = 12, y = 5, rule = B3/S23
|
||||
o10bo$b4o6bo$2b2o7bo$10bo$8bobo!
|
||||
@@ -0,0 +1,66 @@
|
||||
#C High-period oscillator built by David Dauthier, showing
|
||||
#C a common crystal forming on a glider stream. Several adjustable
|
||||
#C high-period guns have been constructed that make use of two
|
||||
#C identical copies of this repeatable growth-and-decay reaction.
|
||||
#C It is also possible to support a single crystal of adjustable
|
||||
#C length, and extract a glider from either end of the reaction.
|
||||
#C See, e.g., p08832.lif in Jason Summers' "guns2j" collection.
|
||||
x = 252, y = 221, rule = B3/S23
|
||||
90b2o68b2o$90b2o16b2o32b2o16b2o$108b2o32b2o4$89b3o8b2o48b2o8b3o$89b3o
|
||||
8b2o48b2o8b3o$88bo3bo66bo3bo$108b3o30b3o$27b2o58b2o3b2o6bo6bo3bo28bo3b
|
||||
o6bo6b2o3b2o58b2o$29bo69bobo48bobo69bo$16b2o12bo67bo3bo3bo5bo26bo5bo3b
|
||||
o3bo67bo12b2o$16b2o4bo7bo8b2o57b5o3b2o3b2o26b2o3b2o3b5o57b2o8bo7bo4b2o
|
||||
$13b2o5b2o8bo8b2o56b2o3b2o44b2o3b2o56b2o8bo8b2o5b2o$5b2o5b3o5bo2b2o4bo
|
||||
57b3o8b5o46b5o8b3o57bo4b2o2bo5b3o5b2o$5b2o6b2o6b5ob2o62b2o6b3o7bo32bo
|
||||
7b3o6b2o62b2ob5o6b2o6b2o$16b2o4bo68b2o7bo7bob2o28b2obo7bo7b2o68bo4b2o$
|
||||
16b2o74b2o14bo34bo14b2o74b2o$90bobo15bo3bo26bo3bo15bobo$90b2o8bo8bo2bo
|
||||
26bo2bo8bo8b2o$18b2o79bobo7b5o24b5o7bobo79b2o$16bo3bo78bo9b5o24b5o9bo
|
||||
78bo3bo$10b2o3bo5bo63b2o3b2o16b2o3b2o22b2o3b2o16b2o3b2o63bo5bo3b2o$10b
|
||||
2o2b2obo3bo2bobo58b2o3b2o17b5o24b5o17b2o3b2o58bobo2bo3bob2o2b2o$15bo5b
|
||||
o3bo71b2o4bo6b3o26b3o6bo4b2o71bo3bo5bo$16bo3bo8b2o56b3o7b2o12bo28bo12b
|
||||
2o7b3o56b2o8bo3bo$18b2o9b2o56b3o72b3o56b2o9b2o$88bo74bo3$13bo8b2o204b
|
||||
2o8bo$11bobo6bo2bo204bo2bo6bobo$4b2o4bobo7bo3bo2b2o194b2o2bo3bo7bobo4b
|
||||
2o$4b2o3bo2bo7bo2b2o2b3o57b2o74b2o57b3o2b2o2bo7bo2bo3b2o$10bobo16b2obo
|
||||
12bo41b2o21b2o28b2o21b2o41bo12bob2o16bobo$11bobo4b3o8bo2bo5b2o6bo63b2o
|
||||
28b2o63bo6b2o5bo2bo8b3o4bobo$13bo15b2obo5b2o4b3o158b3o4b2o5bob2o15bo$
|
||||
27b3o192b3o$27b2o194b2o$87bobo72bobo$87b2o74b2o$88bo74bo18$65b2o118b2o
|
||||
$64b3o118b3o$64b2obo116bob2o$65b3o116b3o$66bo118bo13$23b2o202b2o$23b3o
|
||||
200b3o$9bo15b2obo5b2o4b3o166b3o4b2o5bob2o15bo$7bobo4b3o8bo2bo5b2o6bo
|
||||
166bo6b2o5bo2bo8b3o4bobo$6bobo16b2obo12bo168bo12bob2o16bobo$2o3bo2bo7b
|
||||
o2b2o2b3o200b3o2b2o2bo7bo2bo3b2o$2o4bobo7bo3bo2b2o202b2o2bo3bo7bobo4b
|
||||
2o$7bobo6bo2bo212bo2bo6bobo$9bo8b2o212b2o8bo4$14b2o9b2o198b2o9b2o$12bo
|
||||
3bo8b2o198b2o8bo3bo$11bo5bo3bo208bo3bo5bo$6b2o2b2obo3bo2bobo206bobo2bo
|
||||
3bob2o2b2o$6b2o3bo5bo216bo5bo3b2o$12bo3bo218bo3bo$14b2o220b2o3$12b2o
|
||||
224b2o$12b2o4bo214bo4b2o$b2o6b2o6b5ob2o202b2ob5o6b2o6b2o$b2o5b3o5bo2b
|
||||
2o4bo200bo4b2o2bo5b3o5b2o$9b2o5b2o8bo8b2o178b2o8bo8b2o5b2o$12b2o4bo7bo
|
||||
8b2o178b2o8bo7bo4b2o$12b2o12bo198bo12b2o$25bo40b2o116b2o40bo$23b2o41b
|
||||
3o114b3o41b2o$65bob2o114b2obo$65b3o116b3o$66bo118bo2$66bo118bo$65b3o
|
||||
116b3o$65bob2o114b2obo$23b2o41b3o114b3o41b2o$25bo40b2o116b2o40bo$12b2o
|
||||
12bo198bo12b2o$12b2o4bo7bo8b2o178b2o8bo7bo4b2o$9b2o5b2o8bo8b2o178b2o8b
|
||||
o8b2o5b2o$b2o5b3o5bo2b2o4bo200bo4b2o2bo5b3o5b2o$b2o6b2o6b5ob2o202b2ob
|
||||
5o6b2o6b2o$12b2o4bo214bo4b2o$12b2o224b2o3$14b2o220b2o$12bo3bo218bo3bo$
|
||||
6b2o3bo5bo216bo5bo3b2o$6b2o2b2obo3bo2bobo206bobo2bo3bob2o2b2o$11bo5bo
|
||||
3bo208bo3bo5bo$12bo3bo8b2o198b2o8bo3bo$14b2o9b2o198b2o9b2o4$9bo8b2o
|
||||
212b2o8bo$7bobo6bo2bo212bo2bo6bobo$2o4bobo7bo3bo2b2o202b2o2bo3bo7bobo
|
||||
4b2o$2o3bo2bo7bo2b2o2b3o200b3o2b2o2bo7bo2bo3b2o$6bobo16b2obo12bo168bo
|
||||
12bob2o16bobo$7bobo4b3o8bo2bo5b2o6bo166bo6b2o5bo2bo8b3o4bobo$9bo15b2ob
|
||||
o5b2o4b3o166b3o4b2o5bob2o15bo$23b3o200b3o$23b2o202b2o13$66bo118bo$65b
|
||||
3o116b3o$64b2obo116bob2o$64b3o118b3o$65b2o118b2o18$88bo74bo$87b2o74b2o
|
||||
$87bobo72bobo$27b2o194b2o$27b3o192b3o$13bo15b2obo5b2o4b3o158b3o4b2o5bo
|
||||
b2o15bo$11bobo4b3o8bo2bo5b2o6bo63b2o28b2o63bo6b2o5bo2bo8b3o4bobo$10bob
|
||||
o16b2obo12bo41b2o21b2o28b2o21b2o41bo12bob2o16bobo$4b2o3bo2bo7bo2b2o2b
|
||||
3o57b2o74b2o57b3o2b2o2bo7bo2bo3b2o$4b2o4bobo7bo3bo2b2o194b2o2bo3bo7bob
|
||||
o4b2o$11bobo6bo2bo204bo2bo6bobo$13bo8b2o204b2o8bo3$88bo74bo$18b2o9b2o
|
||||
56b3o72b3o56b2o9b2o$16bo3bo8b2o56b3o7b2o12bo28bo12b2o7b3o56b2o8bo3bo$
|
||||
15bo5bo3bo71b2o4bo6b3o26b3o6bo4b2o71bo3bo5bo$10b2o2b2obo3bo2bobo58b2o
|
||||
3b2o17b5o24b5o17b2o3b2o58bobo2bo3bob2o2b2o$10b2o3bo5bo63b2o3b2o16b2o3b
|
||||
2o22b2o3b2o16b2o3b2o63bo5bo3b2o$16bo3bo78bo9b5o24b5o9bo78bo3bo$18b2o
|
||||
79bobo7b5o24b5o7bobo79b2o$90b2o8bo8bo2bo26bo2bo8bo8b2o$90bobo15bo3bo
|
||||
26bo3bo15bobo$16b2o74b2o14bo34bo14b2o74b2o$16b2o4bo68b2o7bo7bob2o28b2o
|
||||
bo7bo7b2o68bo4b2o$5b2o6b2o6b5ob2o62b2o6b3o7bo32bo7b3o6b2o62b2ob5o6b2o
|
||||
6b2o$5b2o5b3o5bo2b2o4bo57b3o8b5o46b5o8b3o57bo4b2o2bo5b3o5b2o$13b2o5b2o
|
||||
8bo8b2o56b2o3b2o44b2o3b2o56b2o8bo8b2o5b2o$16b2o4bo7bo8b2o57b5o3b2o3b2o
|
||||
26b2o3b2o3b5o57b2o8bo7bo4b2o$16b2o12bo67bo3bo3bo5bo26bo5bo3bo3bo67bo
|
||||
12b2o$29bo69bobo48bobo69bo$27b2o58b2o3b2o6bo6bo3bo28bo3bo6bo6b2o3b2o
|
||||
58b2o$108b3o30b3o$88bo3bo66bo3bo$89b3o8b2o48b2o8b3o$89b3o8b2o48b2o8b3o
|
||||
4$108b2o32b2o$90b2o16b2o32b2o16b2o$90b2o68b2o!
|
||||
@@ -0,0 +1,3 @@
|
||||
#C This is a glider.
|
||||
x = 3, y = 3
|
||||
bo$2bo$3o!
|
||||
@@ -0,0 +1,12 @@
|
||||
#CXRLE Pos=-28,-26
|
||||
x = 42, y = 49, rule = B3/S23
|
||||
13b2o12b2o$9b2o3bo12bo3b2o$10bo3bobo4bo3bobo3bo$10bob2obobob3o2bobob2o
|
||||
bo$11bobobob2o3b3obobobo$20b2o$14bob4o2b4obo$9b2o3bobo2bo2bo2bobo3b2o$
|
||||
9b2o2bo4bo4bo4bo2b2o$14bob2o6b2obo3$2b2o34b2o$2bobo32bobo$3bo34bo3$bo
|
||||
12b3o8b3o12bo$obo10bo3bo6bo3bo10bobo$2o10bo5bo4bo5bo10b2o$12bo5bo4bo5b
|
||||
o$12bo5bo4bo5bo$13bo3bo6bo3bo$14b3o8b3o2$14b3o8b3o$13bo3bo6bo3bo$12bo
|
||||
5bo4bo5bo$12bo5bo4bo5bo$2o10bo5bo4bo5bo10b2o$obo10bo3bo6bo3bo10bobo$bo
|
||||
12b3o8b3o12bo3$3bo34bo$2bobo32bobo$2b2o34b2o3$14bob2o6b2obo$9b2o2bo4bo
|
||||
4bo4bo2b2o$9b2o3bobo2bo2bo2bobo3b2o$14bob4o2b4obo$20b2o$11bobobob2o3b
|
||||
3obobobo$10bob2obobob3o2bobob2obo$10bo3bobo4bo3bobo3bo$9b2o3bo12bo3b2o
|
||||
$13b2o12b2o!
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using GameOfLife.Entities;
|
||||
using GameOfLife.IO;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace GameOfLife
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
//var rleParser = new RleDecoder(@"PatternProjections/honey-farm-hassler-147.rle");
|
||||
//var rleParser = new RleDecoder(@"PatternProjections/glider-stream-crystal.rle");
|
||||
//var rleParser = new RleDecoder(@"PatternProjections/glider.rle");
|
||||
var rleParser = new RleDecoder(@"PatternProjections/blom.rle");
|
||||
|
||||
var pattern = rleParser.Pattern;
|
||||
|
||||
var paddingX = 3;
|
||||
var paddingY = 3;
|
||||
|
||||
var life = new LifeHashSet(rleParser.Pattern);
|
||||
var patternLibrary = new PatternLibrary();
|
||||
|
||||
var timer = new Stopwatch();
|
||||
timer.Start();
|
||||
do
|
||||
{
|
||||
life.IncrementGeneration();
|
||||
|
||||
Console.WriteLine($"{life.Generation} + {life.Population}");
|
||||
foreach (var p in patternLibrary.MatchLibraryPatterns(life.LivingCells))
|
||||
{
|
||||
Console.WriteLine($"\t\t\tFound {p.Item1.Name} {p.Item2.Length}");
|
||||
}
|
||||
|
||||
|
||||
//Console.Clear();
|
||||
//OutputBoard(sim);
|
||||
//Console.ReadKey();
|
||||
|
||||
//Thread.Sleep(100);
|
||||
} while (life.Population > 0 && life.Generation < 3000);
|
||||
timer.Stop();
|
||||
Console.WriteLine($"Final: Generation:{life.Generation}, Pop:{life.Population}, Time:{timer.ElapsedMilliseconds}");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
private static void OutputBoard(LifeBase sim)
|
||||
{
|
||||
var neighborField = Neighborhood.GetNeighborField(sim.LivingCells);
|
||||
|
||||
for (short x = 0; x < 40 /*sim.SizeX*/; x++)
|
||||
{
|
||||
for (short y = 0; y < 120 /*sim.SizeY*/; y++)
|
||||
{
|
||||
var cell = new Cell(x, y);
|
||||
|
||||
var neighbors = neighborField.FirstOrDefault(n => n.Item1.Equals(cell));
|
||||
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.Write(" ");
|
||||
var isCellAlive = sim.IsCellAlive(cell);
|
||||
|
||||
if (neighbors != null)
|
||||
switch (neighbors.Item2)
|
||||
{
|
||||
case 0:
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
break;
|
||||
case 1 when isCellAlive:
|
||||
Console.BackgroundColor = ConsoleColor.DarkMagenta;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
break;
|
||||
case 1:
|
||||
Console.BackgroundColor = ConsoleColor.DarkGreen;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
break;
|
||||
|
||||
case 2 when isCellAlive:
|
||||
Console.BackgroundColor = ConsoleColor.DarkGray;
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
break;
|
||||
case 2:
|
||||
Console.BackgroundColor = ConsoleColor.DarkGreen;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
break;
|
||||
|
||||
|
||||
case 3 when isCellAlive:
|
||||
Console.BackgroundColor = ConsoleColor.DarkGray;
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
break;
|
||||
case 3:
|
||||
Console.BackgroundColor = ConsoleColor.Green;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.BackgroundColor = ConsoleColor.DarkRed;
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
break;
|
||||
}
|
||||
|
||||
Console.Write(isCellAlive ? "O" : ".");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GolConsole")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GolConsole")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2022")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("8e6d5f09-e4a5-496d-8dcc-36f4780f4ccd")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
Reference in New Issue
Block a user