1. Simulation is 5 times faster (now 1 gun needs ~1 second to validate);
2. Parameters generation is separated from validation;
3. Guns are generated in random order to produce more uniform load.
Code: Select all
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication145
{
class Pattern : IEquatable<Pattern>
{
public readonly int Width;
public readonly int Height;
public readonly int WordWidth;
public readonly int WordCount;
ulong[] cells;
public Pattern(int width, int height)
{
Width = width;
Height = height;
WordWidth = (Width + 15) / 16;
WordCount = WordWidth * Height;
cells = new ulong[WordCount];
}
public Pattern(int width, int height, Pattern source, int offsetX, int offsetY)
: this(width, height)
{
for (int y = 0; y < Height; y++)
for (int x = 0; x < Width; x++)
this[x, y] = source[x + offsetX, y + offsetY];
}
public bool this[int x, int y]
{
get
{
int div = x / 16;
int shift = x % 16 * 4;
return ((cells[div + y * WordWidth] >> shift) & 0xFUL) == 1;
}
set
{
int div = x / 16;
int shift = x % 16 * 4;
cells[div + y * WordWidth] &= ~(0xFUL << shift);
cells[div + y * WordWidth] |= (value ? 1UL : 0UL) << shift;
}
}
private ulong[] ShiftLeft(ulong[] src)
{
ulong[] result = new ulong[WordCount];
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < WordWidth; x++)
{
ulong w = src[x + y * WordWidth];
result[x + y * WordWidth] |= w >> 4;
if (x != 0)
result[x - 1 + y * WordWidth] |= w << 60;
else
{
int shift = ((Width + 15) % 16) * 4;
result[WordWidth - 1 + y * WordWidth] |= (w & 0xFUL) << shift;
}
}
}
return result;
}
private ulong[] ShiftRight(ulong[] src)
{
ulong[] result = new ulong[WordCount];
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < WordWidth; x++)
{
ulong w = src[x + y * WordWidth];
result[x + y * WordWidth] |= w << 4;
if (x != WordWidth - 1)
result[x + 1 + y * WordWidth] |= w >> 60;
else
{
int shift1 = 64 - (Width % 16) * 4;
ulong mask = 0xFFFFFFFFFFFFFFFFUL >> shift1;
result[x + y * WordWidth] &= mask;
int shift2 = ((Width - 1) % 16) * 4;
result[y * WordWidth] |= w >> shift2;
}
}
}
return result;
}
public Pattern Advance()
{
var result = new Pattern(Width, Height);
var n1 = ShiftLeft(cells);
var n2 = ShiftRight(cells);
for (int i = 0; i < cells.Length; i++)
{
int im1 = i - WordWidth < 0 ? i - WordWidth + WordCount : i - WordWidth;
int ip1 = i + WordWidth >= WordCount ? i + WordWidth - WordCount : i + WordWidth;
ulong center = cells[i];
ulong neighbours = n1[i] + n2[i] + n1[im1] +
cells[im1] + n2[im1] + n1[ip1] + cells[ip1] + n2[ip1];
neighbours &= 0x7777777777777777UL;
ulong born = neighbours | (center << 3);
born ^= ~0x3333333333333333UL;
born &= (born >> 2);
born &= (born >> 1);
born &= 0x1111111111111111UL;
ulong stay = ((neighbours & ~0x1111111111111111UL) >> 1) | (center << 2);
stay ^= ~0x5555555555555555UL;
stay &= stay >> 2;
stay &= stay >> 1;
stay &= 0x1111111111111111UL;
result.cells[i] = born | stay;
}
return result;
}
public void Clear(int startX, int startY, int countX, int countY)
{
for (int y = 0; y < countY; y++)
for (int x = 0; x < countX; x++)
this[x + startX, y + startY] = false;
}
public bool Search(Pattern pattern, int startX, int startY, int countX, int countY)
{
for (int y1 = 0; y1 < countY; y1++)
{
for (int x1 = 0; x1 < countX; x1++)
{
bool found = true;
for (int y2 = 0; y2 < pattern.Height; y2++)
{
for (int x2 = 0; x2 < pattern.Width; x2++)
{
if (this[startX + x1 + x2, startY + y1 + y2] != pattern[x2, y2])
{
found = false;
break;
}
}
if (!found)
break;
}
if (found)
return true;
}
}
return false;
}
public Pattern Normalize(ref int period)
{
Pattern gliderTop1 = ReadRLE("x = 3, y = 3", "3o$2bo$bo!");
Pattern gliderTop2 = ReadRLE("x = 3, y = 3", "3o$o$bo!");
Pattern gliderBottom1 = ReadRLE("x = 3, y = 3", "bo$2bo$3o!");
Pattern gliderBottom2 = ReadRLE("x = 3, y = 3", "bo$o$3o!");
Pattern gliderLeft1 = ReadRLE("x = 3, y = 3", "2o$obo$o!");
Pattern gliderLeft2 = ReadRLE("x = 3, y = 3", "o$obo$2o!");
Pattern gliderRight1 = ReadRLE("x = 3, y = 3", "2bo$obo$b2o!");
Pattern gliderRight2 = ReadRLE("x = 3, y = 3", "b2o$obo$2bo!");
Pattern expanded = new Pattern(Width + 6, Height + 6);
expanded.Stamp(this, 3, 3);
int lastPeriod = int.MinValue;
int gliderTick = int.MaxValue;
var latestGenerations = new List<Pattern>();
for (int tick = 0; tick < 40000; tick++)
{
bool gliderFound =
expanded.Search(gliderTop1, 0, 0, Width + 4, 1) ||
expanded.Search(gliderTop2, 0, 0, Width + 4, 1) ||
expanded.Search(gliderBottom1, 0, Height + 3, Width + 4, 1) ||
expanded.Search(gliderBottom2, 0, Height + 3, Width + 4, 1) ||
expanded.Search(gliderLeft1, 0, 0, 1, Height + 4) ||
expanded.Search(gliderLeft2, 0, 0, 1, Height + 4) ||
expanded.Search(gliderRight1, Width + 3, 0, 1, Height + 4) ||
expanded.Search(gliderRight2, Width + 3, 0, 1, Height + 4);
if (gliderFound)
{
expanded.Clear(0, 0, expanded.Width, 3);
expanded.Clear(0, Height + 3, expanded.Width, 3);
expanded.Clear(0, 0, 3, expanded.Height);
expanded.Clear(Width + 3, 0, 3, expanded.Height);
period = tick - gliderTick;
if (period == lastPeriod)
return new Pattern(Width, Height, latestGenerations[0], 3, 3);
else
{
gliderTick = tick;
lastPeriod = period;
}
}
latestGenerations.Add(expanded);
if (latestGenerations.Count > 12)
latestGenerations.RemoveAt(0);
expanded = expanded.Advance();
}
return null;
}
public void Stamp(Pattern stamp, int xo, int yo)
{
for (int y = 0; y < stamp.Height; y++)
for (int x = 0; x < stamp.Width; x++)
this[x + xo, y + yo] |= stamp[x, y];
}
public void WriteRLE(string fileName, string comment = null)
{
var sb = new StringBuilder();
if (comment != null)
sb.AppendLine($"#C {comment}");
sb.AppendLine($"x = {Width}, y = {Height}, rule = B3/S23");
var tags = new List<char>();
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
tags.Add(this[x, y] ? 'o' : 'b');
tags.Add(y == Height - 1 ? '!' : '$');
}
int lineLength = 0;
for (int i = 0; i < tags.Count; i++)
{
int runCount = 1;
while (i + 1 < tags.Count && tags[i] == tags[i + 1])
{
i++;
runCount++;
}
string run = runCount == 1 ? $"{tags[i]}" : $"{runCount}{tags[i]}";
if (lineLength + run.Length > 70)
{
sb.AppendLine();
lineLength = 0;
}
sb.Append(run);
lineLength += run.Length;
}
File.WriteAllText(fileName, sb.ToString());
}
public static Pattern ReadRLE(params string[] lines)
{
int x = 0;
int y = 0;
string scount = "";
Pattern pattern = null;
foreach (var line in lines)
{
if (line.StartsWith("#"))
continue;
if (line.StartsWith("x"))
{
var match = Regex.Match(line, "^x = ([0-9]+), y = ([0-9]+)");
if (!match.Success)
throw new Exception();
int width = int.Parse(match.Groups[1].Value);
int height = int.Parse(match.Groups[2].Value);
pattern = new Pattern(width, height);
continue;
}
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
if (c >= '0' && c <= '9')
{
scount += c;
}
else
{
if (c == '$')
{
x = 0;
int count = 1;
if (scount != "")
count = int.Parse(scount);
y += count;
scount = "";
}
else if (c == '!')
break;
else if (c == 'o' || c == 'b')
{
int count = 1;
if (scount != "")
count = int.Parse(scount);
for (int k = 0; k < count; k++)
{
pattern[x, y] = c == 'o';
x++;
pattern.WrapCoordinates(ref x, ref y);
}
scount = "";
}
}
}
}
return pattern;
}
public void ReadRLE(string fileName)
{
ReadRLE(File.ReadAllLines(fileName));
}
public override int GetHashCode()
{
int shift = 0;
ulong hash = (ulong)(Width << 16 | Height);
for (int i = 0; i < cells.Length; i++)
{
hash = hash ^ cells[i] << shift;
shift = (shift + 1) & 3;
}
return (int)(hash ^ hash >> 32);
}
public bool Equals(Pattern other)
{
if (Width != other.Width || Height != other.Height)
return false;
for (int i = 0; i < cells.Length; i++)
if (cells[i] != other.cells[i])
return false;
return true;
}
void WrapCoordinates(ref int x, ref int y)
{
if (x < 0)
x += Width;
else if (x >= Width)
x -= Width;
if (y < 0)
y += Height;
else if (y >= Height)
y -= Width;
}
}
class Component
{
public int X1;
public int Y1;
public int X2;
public int Y2;
public Pattern Pattern;
public int X
{
get { return X1 + X2; }
}
public int Y
{
get { return Y1 + Y2; }
}
public Component(int x, int y, Pattern pattern)
{
X1 = x;
Y1 = y;
Pattern = pattern;
}
}
class Params
{
public int Slide1;
public int Slide2;
public int Mul1;
public int Mul2;
public int Mod;
}
static class ShuffleClass
{
private static Random rng = new Random(1234321);
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
class Program
{
public static bool IsPrime(int number)
{
if (number <= 1) return false;
if (number == 2) return true;
if (number % 2 == 0) return false;
var boundary = (int)Math.Floor(Math.Sqrt(number));
for (int i = 3; i <= boundary; i += 2)
if (number % i == 0)
return false;
return true;
}
static Dictionary<int, int> ParseCosts(string costsS)
{
var costs = new Dictionary<int, int>();
if (costsS != null)
{
var lines = costsS.Split('\n');
for (int i = 1; i < lines.Length - 1; i++)
{
var spl = lines[i].Split(',');
int period = int.Parse(spl[0].Trim('"').Split('_')[1]);
int area = int.Parse(spl[1].Trim('"'));
costs.Add(period, area);
}
}
return costs;
}
static Pattern Combine(params Component[] components)
{
int minX = int.MaxValue;
int minY = int.MaxValue;
foreach (var component in components)
{
if (component == null)
continue;
minX = Math.Min(minX, component.X);
minY = Math.Min(minY, component.Y);
}
int width = 0;
int height = 0;
foreach (var component in components)
{
if (component == null)
continue;
width = Math.Max(width,
component.X + component.Pattern.Width - minX);
height = Math.Max(height,
component.Y + component.Pattern.Height - minY);
}
Pattern pattern = new Pattern(width, height);
foreach (var component in components)
{
if (component == null)
continue;
pattern.Stamp(component.Pattern,
component.X - minX, component.Y - minY);
}
return pattern;
}
Program()
{
Component part1 = new Component(0, 0, Pattern.ReadRLE(
"x = 67, y = 92, rule = B3/S23",
"$39b2o$39bo$10b2o29bo$10bo2bo8b2o16b2o$12b2o8bo15bo3b2o$8b4o11bo14b4o",
"2bo$8bo3b4o4b3o19b2o$10bobo2bo4bo19b2o$9b2o30bo$8bo2bo29bobo$6bo2bobo",
"30b2o$6b2obob2o$9bo$9bob2o$8b2obo$11bo$11b2o9$12b2o$13bo$10b3o$10bo6$",
"3b2o$4bo$4bobo15b2o$5b2o15b2o10$3b2o$2bo2bo2b2o$3b2o2bobo3b3o7b2o$5b2o",
"7bo8bobo$5bo8b3o8bo$2b2obo2bo16b2o$2bob2obobo$6bobo$3b2o2bo$b3ob2o$o$b",
"3ob2o$3bob2o2$13b2o$13b2o7b2o$22bo$20bobo$20b2o2$43bo11b2o$42bobo10b2o",
"$2o28bo3bo3b2o2bobo$2o26b3o2bobo2bo2b2ob2o$27bo5bobo3bobo$27b2o5bob4o",
"2bob2o$36bo3bobob2o$35bo3bobo$16bo17bo3bobo$15bobo16b2o3bo$15bobo$16bo",
"$17b3o$19bo27b2o$47b2o$62b2o$61bo2bo$62b2obo$15b2o48bo$15b2o48b2o$50b",
"2o$51bo$48b3o$48bo!"
));
Component part2 = new Component(50, 71, Pattern.ReadRLE(
"x = 41, y = 49, rule = B3/S23",
"31bo$29b3o$28bo$28b2o3$36b2o$37bo$37bob2o$29b2o4b3o2bo$29b2o3bo3b2o$",
"34b4o$20b2o15bo$19bobo12b3o$19bo13bo$18b2o14b5o$38bo$36bo$36b2o8$3bo$",
"3b3o$6bo$5b2o7$15b2o$8b2o5bobo$8b2o7bo$17b2o2$4bo$3bobob2o$3bobobobo$",
"2obobobobo2bo$o2bo2b2ob4o$2b2o4bo$8bobo$9b2o!"
));
Component[] parts3 = new Component[]
{
new Component(71, 96, Pattern.ReadRLE(
"x = 19, y = 23, rule = B3/S23",
"3bo$3b3o$6bo$5b2o7$15b2o$8b2o5bobo$8b2o7bo$17b2o2$4bo$3bobob2o$3bobobo",
"bo$2obobobobo2bo$o2bo2b2ob4o$2b2o4bo$8bobo$9b2o!"
)),
new Component(73, 96, Pattern.ReadRLE(
"x = 21, y = 23, rule = B3/S23",
"bo$b3o$4bo$3b2o5$14b2ob2o$14b2obo2bo$19b2o$6b2o$6b2o4bo2bo$12b4o$18bo$",
"2bo11b5o$bobob2o7bo$bobobobo8bo$2obobobo2bo4b2o$bo2b2ob4o$bo4bo$2b3obo",
"2b2o$4b2o3b2o!"
)),
new Component(72, 96, Pattern.ReadRLE(
"x = 18, y = 25, rule = B3/S23",
"2bo$2b3o$5bo$4b2o7$14b2o$7b2o5bobo$7b2o7bo$16b2o5$4b2ob2o$2o3bob2o2b2o",
"$o2bobo6bo$2b2ob7o$4bo$4bob4o$5b2o2bo!"
)),
new Component(72, 96, Pattern.ReadRLE(
"x = 22, y = 25, rule = B3/S23",
"2bo$2b3o$5bo$4b2o5$15b2ob2o$15b2obo2bo$20b2o$7b2o$7b2o4bo2bo$13b4o$19b",
"o$15b5o$15bo$17bo$4b2ob2o7b2o$2o3bob2o2b2o$o2bobo6bo$2b2ob7o$4bo$4bob",
"4o$5b2o2bo!"
))
};
Component[] parts4 = new Component[]
{
new Component(93, 66, Pattern.ReadRLE(
"x = 23, y = 19, rule = B3/S23",
"13bo$11b3o$10bo$10b2o3$18b2o$19bo$19bob2o$11b2o4b3o2bo$11b2o3bo3b2o$",
"16b4o$2b2o15bo$bobo12b3o$bo13bo$2o14b5o$20bo$18bo$18b2o!"
)),
new Component(93, 62, Pattern.ReadRLE(
"x = 23, y = 21, rule = B3/S23",
"9b2o$10bo$8bo5b2o$8b2o5bo$15bob2o$8b2o2b2obo2bo$8b2o3bob2o$13bo$12b2o",
"2$18b2ob2o$19bob2o$19bo$11b2o4b3o$11b2o3bo3b2o$16b4o2bo$2b2o15bob2o$bo",
"bo12b3o2bo$bo13bo5bo$2o14b5o$18bo!"
)),
new Component(93, 66, Pattern.ReadRLE(
"x = 25, y = 18, rule = B3/S23",
"13bo$11b3o$10bo$10b2o2$19b2o$19bobo$21bo$21bob2o$11b2o5b2obobo$11b2o5b",
"2obobo$21bob2o$2b2o14b4o2bo$bobo14bo3b2o$bo18b2o$2o19bo$19bo$19b2o!"
)),
new Component(93, 62, Pattern.ReadRLE(
"x = 25, y = 22, rule = B3/S23",
"9b2o$10bo$8bo5b2o$8b2o5bo$15bob2o$8b2o2b2obo2bo$8b2o3bob2o$13bo$12b2o$",
"19b2o$19bobo$21bo$21bob2o$11b2o5b2obobo$11b2o5b2obobo$21bob2o$2b2o14b",
"4o2bo$bobo14bo3b2o$bo18b2o$2o19bo$19bo$19b2o!"
))
};
Component[] parts5 = new Component[]
{
new Component(45, 0, Pattern.ReadRLE(
"x = 81, y = 61, rule = B3/S23",
"45b2o3b2o$45b2o2bob3o$17bo31bo4bo$16bobo26b4ob2o2bo$16bobo7b2o17bo2bob",
"obob2o$14b3ob2o6b2o20bobobobo$13bo19b2o14b2obobo$14b3ob2o13b2o18bo$16b",
"ob2o$39b2o$40bo7b2o$40bobo5b2o$29bo11b2o$28bobo$27bo2bo$28b2o9b2o30bo$",
"39b2o28b3o$68bo$68b2o$17b2o32b2o$17b2o32bo$52b3o21b2ob2o$54bo22bob2o$",
"15bo61bo$13b3o53b2o4b3o$12bo56b2o3bo3b2o$12b2o60b4o2bo$60b2o15bob2o$",
"59bobo12b3o2bo$47bo11bo13bo5bo$31b2o14b3o8b2o14b5o$31bo18bo25bo$32b3o",
"14b2o$2b2o30bo$bobo5b2o$bo7b2o$2o$27bo$14bo12b3o$10b2obobo14bo$9bobobo",
"bo13b2o$6bo2bobobob2o$6b4ob2o2bo47b2o$10bo4bo2b2ob2o40b2o$6b2o2bob3o3b",
"2obo$6b2o3b2o8bo$21b3o4b2o11b2o$19b2o3bo3b2o11b2o9b2o$18bo2b4o26bo2bo$",
"18b2obo15b2o12bobo$19bo2b3o12bobo12bo$19bo5bo13bo$20b5o14b2o$22bo$62b",
"2obo$47b2o13b2ob3o$47b2o19bo$54b2o6b2ob3o$54b2o7bobo$63bobo$64bo!"
)),
new Component(45, -8, Pattern.ReadRLE(
"x = 91, y = 65, rule = B3/S23",
"23bo$22bobo$22bobo7b2o$20b3ob2o6b2o$19bo19b2o$20b3ob2o13b2o$22bob2o34b",
"o$59bobo$59bob3o$56b2obo4bo$35bo20bobobobobo$34bobo21bobob2o4b2o$33bo",
"2bo13b2o5bobo8bo$34b2o9b2o3b2o6bo7bobo$45b2o19b2o3$23b2o30b2o$15b2o6b",
"2o29bo2bo$15bo3b2o34b2o24bo$11b2o4bo2bo58b3o$6b2o3b2o3b4o58bo$ob2obobo",
"70b2o$2obobo10b2o$4b2o10bobo$17b2o44b2o21b2ob2o$50b2o10bobo22bob2o$46b",
"2obobo10b2o23bo$37b2o7bob2obobo25b2o4b3o$37bo14b2o3b2o3b4o13b2o3bo3b2o",
"$38b3o16b2o4bo2bo17b4o2bo$9b2o29bo20bo3b2o3b2o15bob2o$8bo2bo49b2o6bobo",
"12b3o2bo$9b2o46bo11bo13bo5bo$57b3o8b2o14b5o$60bo25bo$20b2o37b2o$4b2o6b",
"o7bobo$4b2o5bobo8bo$12bobob2o4b2o$10bobobobobo$10b2obo4bo$13bob3o$13bo",
"bo$14bo2$73b2o$73b2o$30bo$30b3o$33bo17b2o$32b2o17b2o9b2o$61bo2bo$61bob",
"o$21b2ob2o36bo$21b2obo$24bo$24b3o4b2o$22b2o3bo3b2o39b2obo$21bo2b4o29b",
"2o13b2ob3o$21b2obo15b2o15b2o19bo$22bo2b3o12bobo21b2o6b2ob3o$22bo5bo13b",
"o21b2o7bobo$23b5o14b2o29bobo$25bo48bo!"
)),
null,
new Component(41, 10, Pattern.ReadRLE(
"x = 62, y = 54, rule = B3/S23",
"31bo$30bobo$30bob3o$27b2obo4bo$27bobobobobo$29bobob2o4b2o$21b2o5bobo8b",
"o$21b2o6bo7bobo$37b2o$15bo$13b3o$12bo13b2o$12b2o11bo2bo$26b2o24bo$50b",
"3o$49bo$49b2o3$2b2o30b2o21b2ob2o$bobo5b2o10b2o10bobo22bob2o$bo7b2o6b2o",
"bobo10b2o23bo$2o15bob2obobo25b2o4b3o$23b2o3b2o3b4o13b2o3bo3b2o$14bo13b",
"2o4bo2bo17b4o2bo$10b2obobo16bo3b2o3b2o15bob2o$9bobobobo16b2o6bobo12b3o",
"2bo$6bo2bobobob2o23bo13bo5bo$6b4ob2o2bo23b2o14b5o$10bo4bo41bo$6b2o2bob",
"3o36b2o$6b2o3b2o38bo$52bo$51b2o$36b2o12bo$36b2o12b3o$53bo$52b2o$42bo$",
"41bobo$33b2o6bobo$33bo2bo5bo10b2o$34b2obo15b2o$31b3o2bo$30bo3b2o$31b2o",
"22b2o$32bob2o14b2obo2bo$32bo2bo13bobob2o$33b2o14b2o2bo$53bobo$37b2o15b",
"2o$38bo$35b3o$35bo!"
)),
new Component(41, 10, Pattern.ReadRLE(
"x = 63, y = 46, rule = B3/S23",
"31bo$30bobo$30bob3o$27b2obo4bo$27bobobobobo$29bobob2o4b2o$21b2o5bobo8b",
"o$21b2o6bo7bobo$37b2o$15bo$13b3o$12bo13b2o$12b2o11bo2bo$26b2o$53bo$51b",
"3o$50bo$50b2o2$2b2o30b2o$bobo5b2o10b2o10bobo22b2ob2o$bo7b2o6b2obobo10b",
"2o24bob2o$2o15bob2obobo34bo$23b2o3b2o3b4o14b2o4b3o$14bo13b2o4bo2bo13b",
"2o3bo3b2o$10b2obobo16bo3b2o18b4o2bo$9bobobobo16b2o8b2o15bob2o$6bo2bobo",
"bob2o24bobo12b3o2bo$6b4ob2o2bo25bo13bo5bo$10bo4bo17bo6b2o14b5o$6b2o2bo",
"b3o18b3o22bo$6b2o3b2o23bo$35b2o3$24b2ob2o$24b2obo$27bo$27b3o4b2o$25b2o",
"3bo3b2o$24bo2b4o$24b2obo15b2o$25bo2b3o12bobo$25bo5bo13bo$26b5o14b2o$",
"28bo!"
)),
new Component(35, -4, Pattern.ReadRLE(
"x = 71, y = 70, rule = B3/S23",
"39bo$38bobo$38bob3o$35b2obo4bo$35bobobobobo$37bobob2o4b2o$29b2o5bobo8b",
"o$29b2o6bo7bobo$45b2o3$34b2o$33bo2bo$34b2o4$15bo$13b3o$12bo29b2o$12b2o",
"15b2o10bobo$25b2obobo10b2o$25bob2obobo$31b2o3b2o3b4o3b2o$36b2o4bo2bo3b",
"o$40bo3b2o2bo$40b2o6b2o$2b2o46bo12b2o$bobo5b2o37b3o12b2o$bo7b2o36bo$2o",
"45b2o$58bo$14bo42bobo$10b2obobo41bobo6b2o$9bobobobo30b2o10bo5bo2bo$6bo",
"2bobobob2o29b2o15bob2o$6b4ob2o2bo48bo2b3o$10bo4bo49b2o3bo$6b2o2bob3o",
"29b2o22b2o$6b2o3b2o31bo2bob2o14b2obo$46b2obobo13bo2bo$47bo2b2o14b2o$",
"45bobo$45b2o15b2o$62bo$63b3o$59b2o4bo$59bo$60bo$59b2o$44b2o12bo$44b2o",
"12b3o$61bo$60b2o$50bo$49bobo$41b2o6bobo$41bo2bo5bo10b2o$42b2obo15b2o$",
"39b3o2bo$38bo3b2o$39b2o22b2o$40bob2o14b2obo2bo$40bo2bo13bobob2o$41b2o",
"14b2o2bo$61bobo$45b2o15b2o$46bo$43b3o$43bo!"
)),
new Component(43, -3, Pattern.ReadRLE(
"x = 88, y = 65, rule = B3/S23",
"20bo$19bobo$19bobo7b2o$17b3ob2o6b2o$16bo19b2o$17b3ob2o13b2o$19bob2o34b",
"o$56bobo$56bob3o$53b2obo4bo$32bo20bobobobobo$31bobo21bobob2o4b2o$30bo",
"2bo13b2o5bobo8bo$31b2o9b2o3b2o6bo7bobo$42b2o19b2o3$20b2o30b2o$20b2o29b",
"o2bo$52b2o24bo$76b3o$75bo$75b2o2$15bo$13b3o44b2o21b2ob2o$12bo34b2o10bo",
"bo22bob2o$12b2o29b2obobo10b2o23bo$34b2o7bob2obobo25b2o4b3o$34bo14b2o3b",
"2o3b4o13b2o3bo3b2o$35b3o16b2o4bo2bo17b4o2bo$37bo20bo3b2o3b2o15bob2o$",
"58b2o6bobo12b3o2bo$54bo11bo13bo5bo$2b2o50b3o8b2o14b5o$bobo5b2o46bo25bo",
"$bo7b2o45b2o$2o2$14bo$10b2obobo$9bobobobo$6bo2bobobob2o$6b4ob2o2bo16bo",
"$10bo4bo16b3o$6b2o2bob3o20bo$6b2o3b2o21b2o34b2o$70b2o2$23b2ob2o$23b2ob",
"o21b2o$26bo21b2o9b2o$26b3o4b2o23bo2bo$24b2o3bo3b2o23bobo$23bo2b4o29bo$",
"23b2obo15b2o$24bo2b3o12bobo$24bo5bo13bo$25b5o14b2o23b2obo$27bo26b2o13b",
"2ob3o$54b2o19bo$61b2o6b2ob3o$61b2o7bobo$70bobo$71bo!"
)),
new Component(45, -10, Pattern.ReadRLE(
"x = 93, y = 76, rule = B3/S23",
"25bo$24bobo$24bobo7b2o$22b3ob2o6b2o$21bo19b2o$22b3ob2o13b2o$24bob2o34b",
"o$61bobo$61bob3o$58b2obo4bo$37bo20bobobobobo$36bobo21bobob2o4b2o$35bo",
"2bo13b2o5bobo8bo$36b2o9b2o3b2o6bo7bobo$47b2o19b2o3$25b2o30b2o$25b2o29b",
"o2bo$57b2o24bo$15b2o64b3o$15bo3b2o59bo$11b2o4bo2bo59b2o$6b2o3b2o3b4o$o",
"b2obobo$2obobo10b2o47b2o21b2ob2o$4b2o10bobo33b2o10bobo22bob2o$17b2o29b",
"2obobo10b2o23bo$39b2o7bob2obobo25b2o4b3o$39bo14b2o3b2o3b4o13b2o3bo3b2o",
"$40b3o16b2o4bo2bo17b4o2bo$42bo20bo3b2o3b2o15bob2o$63b2o6bobo12b3o2bo$",
"9b2o48bo11bo13bo5bo$8bo2bo47b3o8b2o14b5o$9b2o51bo25bo$61b2o2$20b2o$4b",
"2o6bo7bobo$4b2o5bobo8bo$12bobob2o4b2o$10bobobobobo$10b2obo4bo$13bob3o$",
"13bobo$14bo60b2o$75b2o3$53b2o$53b2o9b2o$49b2o12bo2bo$49bo13bobo$50bo",
"13bo$49b2o$34b2o12bo$34b2o12b3o$51bo22b2obo$50b2o7b2o13b2ob3o$40bo18b",
"2o19bo$39bobo24b2o6b2ob3o$31b2o6bobo24b2o7bobo$31bo2bo5bo10b2o22bobo$",
"32b2obo15b2o23bo$29b3o2bo$28bo3b2o$29b2o22b2o$30bob2o14b2obo2bo$30bo2b",
"o13bobob2o$31b2o14b2o2bo$51bobo$35b2o15b2o$36bo$33b3o$33bo!"
)),
};
Console.Write("Preparing parameters...");
var costsS = new WebClient().DownloadString(
"https://catagolue.hatsya.com/textcensus/b3s23/synthesis-costs/gun");
var costs = ParseCosts(costsS);
var paramsList = new List<Params>();
for (int mul1 = 1; mul1 <= 4; mul1++)
{
for (int mul2 = 1; mul2 <= 4; mul2++)
{
int mul = mul1 * mul2;
if (mul1 > mul2 || mul == 1 || mul == 16)
continue;
for (int mod = 0; mod < 4; mod++)
{
for (int slide1 = 0; slide1 < 20; slide1++)
{
for (int slide2a = 0; slide2a < mul; slide2a++)
{
paramsList.Add(new Params
{
Mul1 = mul1,
Mul2 = mul2,
Mod = mod + (mul % 2) * 4,
Slide1 = slide1,
Slide2 = slide1 + slide2a
});
}
}
}
}
}
paramsList.Shuffle();
Console.WriteLine(" Done");
var newGuns = new SortedDictionary<int, Pattern>();
var newGunsCosts = new Dictionary<int, int>();
int failed = 0;
for (int i = 0; i < paramsList.Count; i++)
{
Params p = paramsList[i];
Component part3 = parts3[p.Mul1 - 1];
Component part4 = parts4[p.Mul2 - 1];
Component part5 = parts5[p.Mod];
part2.X2 = p.Slide1;
part2.Y2 = p.Slide1;
part3.X2 = p.Slide2;
part3.Y2 = p.Slide2;
part4.X2 = p.Slide2;
part4.Y2 = p.Slide2;
Pattern gun = Combine(part1, part2, part3, part4, part5);
Console.Write("\r" + new string(' ', 50) + "\r");
Console.Write($"Current: {i,5}, Failed: {failed,5}, Total: {paramsList.Count}");
int period = 0;
Pattern normalizedGun = gun.Normalize(ref period);
if (normalizedGun == null)
{
failed++;
gun.WriteRLE($"gun_x_{p.Mul1}_{p.Mul2}_{p.Mod}_{p.Slide1}_{p.Slide2}.rle");
continue;
}
int area = normalizedGun.Width * normalizedGun.Height;
if (newGuns.ContainsKey(period) && area >= newGunsCosts[period])
continue;
newGuns[period] = normalizedGun;
newGunsCosts[period] = area;
}
Console.WriteLine();
string logName = "report.txt";
File.Delete(logName);
foreach (var kv in newGuns)
{
int period = kv.Key;
if (!IsPrime(period) || period > 9999)
continue;
Pattern gun = kv.Value;
int area = newGunsCosts[period];
string costS = " ";
if (costs.ContainsKey(period))
{
costS = $"{costs[period],6}";
if (costs[period] <= area)
continue;
}
string reportLine = $"gun_{period,-4}: {costS} -> {area,6}";
Console.WriteLine(reportLine);
File.AppendAllLines(logName, new string[] { reportLine });
gun.WriteRLE($"gun_{period}.rle");
}
}
static void Main(string[] args)
{
new Program();
}
}
}
I executed few more passes of this algorithm, added several new guns to cut the spikes from minimum (blue) line and here is how area chart looks now: