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(@"(?
.+(?=_))?_?(?.+)"); 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 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()); } } }