Initial commit

This commit is contained in:
2026-05-07 03:23:56 +00:00
commit 5e8575f42a
42 changed files with 2330 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
bin/
obj/
.vs/
*.user
*.suo
packages/
+39
View File
@@ -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
+6
View File
@@ -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>
+101
View File
@@ -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
1 Name Apgcode
2 Block xs4_33
3 Blinker xp2_7
4 Beehive xs6_696
5 Glider xq4_153
6 Loaf xs7_2596
7 Boat xs5_253
8 Ship xs6_356
9 Tub xs4_252
10 Pond xs8_6996
11 Long boat xs7_25ac
12 Toad xp2_7e
13 Ship-tie xs12_g8o653z11
14 Beacon xp2_318c
15 Barge xs6_25a4
16 Half-bakery xs14_g88m952z121
17 Mango xs8_69ic
18 Eater 1 xs7_178c
19 Lightweight spaceship xq4_6frc
20 Long barge xs8_25ak8
21 Aircraft carrier xs6_39c
22 Pulsar xp3_co9nas0san9oczgoldlo0oldlogz1047210127401
23 Paperclip xs14_69bqic
24 Middleweight spaceship xq4_27dee6
25 Long ship xs8_35ac
26 Integral sign xs9_31ego
27 Shillelagh xs8_3pm
28 Boat-tie xs10_g8o652z01
29 Snake xs6_bd
30 Big S xs14_g88b96z123
31 Bipond xs16_g88m996z1221
32 Trans-boat with tail xs9_178ko
33 Boat tie ship xs11_g8o652z11
34 Hat xs9_4aar
35 Very long ship xs10_35ako
36 Heavyweight spaceship xq4_27deee6
37 Very long boat xs9_25ako
38 Tub with tail xs8_178k8
39 Mirrored table xs12_raar
40 Dead spark coil xs18_rhe0ehr
41 Canoe xs8_312ko
42 Beehive on dock xs16_j1u0696z11
43 Cis-mirrored bun xs14_6970796
44 Moose antlers xs15_354cgc453
45 Block on table xs10_32qr
46 Block on dock xs14_j1u066z11
47 Scorpion xs16_69egmiczx1
48 Beehive with tail xs10_178kk8
49 Twin hat xs17_2ege1ege2
50 Loop xs10_69ar
51 Long snake xs7_3lo
52 Fourteener xs14_69bo8a6
53 Pentadecathlon xp15_4r4z4r4
54 Cis-mirrored bookend xs14_39e0e93
55 Cis-boat with tail xs9_178kc
56 Cis-rotated bookend xs14_6is079c
57 Elevener xs11_g0s453z11
58 Mirrored dock xs20_3lkkl3z32w23
59 Block on cap xs12_330f96
60 Trans-loaf with tail xs11_ggm952z1
61 Cis-shillelagh xs10_358gkc
62 Trans-mirrored bun xs14_69e0eic
63 Clock xp2_2a54
64 Trans-block on long bookend xs12_330fho
65 Block-laying switch engine yl144_1_16_afb5f3db909e60548f086e22ee3353ac
66 Prodigal xs10_g0s252z11
67 Broken snake xs10_0drz32
68 Trans-bookend and bun xs14_39e0eic
69 Eater with nine xs12_178c453
70 Block on cover xs12_178br
71 Cis-boat on dock xs15_j1u06a4z11
72 Cis-block on long bookend xs12_3hu066
73 Very long snake xs8_31248c
74 Boat with long tail xs10_3215ac
75 Long shillelagh xs9_312453
76 Beehive at loaf xs13_g88m96z121
77 Trans-bun and wing xs15_259e0eic
78 Long integral xs10_3542ac
79 Tub with long tail xs9_25a84c
80 Cis-bookend and bun xs14_39e0e96
81 Hook with tail xs8_32qk
82 Loaf siamese loaf xs11_69lic
83 Long canoe xs9_g0g853z11
84 Eleven loop xs11_178jd
85 Trans-loaf on table xs13_4a960ui
86 Cis-loaf with tail xs11_178kic
87 Symmetric scorpion xs16_69bob96
88 Claw with tail xs10_1784ko
89 Bee hat xs15_3lkm96z01
90 Cis-mirrored dove xs18_69is0si96
91 Trans-rotated bun xs14_g8o0e96z121
92 Glider-producing switch engine yl384_1_59_7aeb1999980c43b4945fb7fcdb023326
93 Cis-mirrored wing xs16_259e0e952
94 Trans-snake on bun xs13_69e0mq
95 Boat tie eater tail xs12_256o8a6
96 Snorkel loop xs12_2egm93
97 Beehive on table xs12_6960ui
98 Cis-boat on table xs11_2530f9
99 Trans-barge with tail xs10_ggka52z1
100 Trans-boat on dock xs15_3lk453z121
101 Beehive on cap xs14_6960uic
+36
View File
@@ -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}]";
}
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace GameOfLife.Entities
{
public class Matcher
{
private Cell Key;
private Cell Left;
}
}
+236
View File
@@ -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);
}
}
}
+15
View File
@@ -0,0 +1,15 @@
namespace GameOfLife.Entities
{
public enum PatternType
{
StillLife,
Oscillator,
Spaceship,
Periodic,
Methuselah,
Diehard,
Megasized,
//Oversized,
//Chaotic
}
}
+32
View File
@@ -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();
}
}
+82
View File
@@ -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>
+176
View File
@@ -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));
}
}
}
}
+17
View File
@@ -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; }
}
}
+70
View File
@@ -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());
}
}
}
+35
View File
@@ -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}");
}
}
}
}
+14
View File
@@ -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; }
}
}
+82
View File
@@ -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);
}
}
}
+41
View File
@@ -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;
}
}
}
}
}
+84
View File
@@ -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);
}
}
}
}
+96
View File
@@ -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;
}
}
}
}
+83
View File
@@ -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;
}
}
+56
View File
@@ -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();
}
}
}
+89
View File
@@ -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;
}
}
}
+36
View File
@@ -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")]
+56
View File
@@ -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;
}
}
}
+41
View File
@@ -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
}
}
}
+3
View File
@@ -0,0 +1,3 @@
#Beehive
x = 4, y = 3, rule = B3/S23
b2o$o2bo$b2o!
+51
View File
@@ -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());
}
}
}
+97
View File
@@ -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);
}//
}
}
+19
View File
@@ -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>
+94
View File
@@ -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}");
}
}
}
}
+62
View File
@@ -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);
}
}
}
+141
View File
@@ -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());
}
}
}
+6
View File
@@ -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>
+77
View File
@@ -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>
+3
View File
@@ -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!
+3
View File
@@ -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!
+5
View File
@@ -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!
+3
View File
@@ -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!
+115
View File
@@ -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();
}
}
}
}
+36
View File
@@ -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")]