Files
2026-05-07 03:23:56 +00:00

70 lines
2.4 KiB
C#

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());
}
}
}