private static void WriteMazeInManual(HexamazeInfo maze) { const double hexWidth = 72; foreach (var path in new[] { @"D:\c\KTANE\HTML\Hexamaze.html", @"D:\c\KTANE\Hexamaze\Manual\Hexamaze.html" }) { // Create the color chart (top-left of the manual page) File.WriteAllText(path, Regex.Replace(File.ReadAllText(path), @"(?<=<!--%%-->).*(?=<!--%%%-->)", options: RegexOptions.Singleline, replacement: $@" <svg class='legend' viewBox='-325 -375 650 750'> {"red,yellow,green,cyan,blue,pink".Split(',').Select((color, i) => $"<g class='label' transform='rotate({330 + 60 * i})'><text text-anchor='middle' y='-280'>{color}</text><path d='M-124.7-150v-200M124.7-150v-200' /></g>").JoinString()} <polygon class='outline' points='{Hex.LargeHexagonOutline(4, hexWidth).Select(p => $"{p.X},{p.Y}").JoinString(" ")}' /> {Hex.LargeHexagon(4).Select(h => h.GetCenter(hexWidth)).Select(p => $"<circle class='dot' cx='{p.X}' cy='{p.Y}' r='{hexWidth / 12}' />").JoinString()} </svg>")); // Create the main maze in the manual page File.WriteAllText(path, Regex.Replace(File.ReadAllText(path), @"(?<=<!--##-->).*(?=<!--###-->)", maze.CreateSvg(), RegexOptions.Singleline)); } }
private static bool areMarkingsUnique(HexamazeInfo maze, bool saveFiles = false) { var ambig = 1; var size = maze.Size; var smallSize = maze.SubmazeSize; var unique = new Dictionary <string, List <Tuple <Hex, int> > >(); foreach (var centerHex in Hex.LargeHexagon(size - smallSize + 1)) { for (int rotation = 0; rotation < 6; rotation++) { var markingsStr = Hex.LargeHexagon(smallSize).Select(h => h.Rotate(rotation) + centerHex).Select(h => maze.Markings.Get(h, Marking.None).Rotate(-rotation)).ToMarkingString(); List <Tuple <Hex, int> > uniqs; if (unique.TryGetValue(markingsStr, out uniqs) && uniqs.Count > 0) { if (saveFiles) { var fills1 = Tuple.Create(Hex.LargeHexagon(smallSize).Select(h => h.Rotate(uniqs[0].Item2) + uniqs[0].Item1), "fed"); var fills2 = Tuple.Create(Hex.LargeHexagon(smallSize).Select(h => h.Rotate(rotation) + centerHex), "def"); ambig++; File.WriteAllText($@"D:\c\KTANE\HTML\Hexamaze{ambig}.html", Regex.Replace(File.ReadAllText(@"D:\c\KTANE\HTML\Hexamaze.html"), @"\A(.*<!--##-->\s*).*?(?=\s*<!--###-->)", options: RegexOptions.Singleline, evaluator: m => m.Groups[1].Value + maze.CreateSvg(new[] { fills1, fills2 }))); } else { return(false); } } unique.AddSafe(markingsStr, Tuple.Create(centerHex, rotation)); } } if (!saveFiles) { return(true); } foreach (var nonUnique in unique.Where(k => k.Value.Count > 1)) { Console.WriteLine($"{nonUnique.Key} = {nonUnique.Value.Select(tup => $"{tup.Item1}/{(6 - tup.Item2) % 6}").JoinString(", ")}"); } return(unique.All(kvp => kvp.Value.Count <= 1)); }
public static HexamazeInfo GenerateMarkings(HexamazeInfo origMaze) { var obj = new object(); var lowestMarkings = int.MaxValue; HexamazeInfo bestMaze = null; var seed = 146; // 19 markings with initial hexagon in the center //Enumerable.Range(0, 300).ParallelForEach(4, seed => //{ var rnd = new Random(seed); var maze = origMaze.Clone(); var size = maze.Size; var smallSize = maze.SubmazeSize; maze.Markings = new Dictionary <Hex, Marking>(); // List Circle and Hexagon twice so that triangles don’t completely dominate the distribution var allowedMarkings = new[] { Marking.Circle, Marking.Circle, Marking.Hexagon, Marking.Hexagon, Marking.TriangleDown, Marking.TriangleLeft, Marking.TriangleRight, Marking.TriangleUp }; // Put a hexagon in the center maze.Markings.Add(new Hex(0, 0), Marking.Hexagon); // Step 1: Put random markings in until there are no more ambiguities while (!areMarkingsUnique(maze)) { var availableHexes = Hex.LargeHexagon(size).Where(h => !maze.Markings.ContainsKey(h) && !h.Neighbors.SelectMany(n => n.Neighbors).Any(maze.Markings.ContainsKey)).ToArray(); if (availableHexes.Length == 0) { goto impossiburu; } var randomHex = availableHexes[rnd.Next(availableHexes.Length)]; maze.Markings[randomHex] = allowedMarkings.PickRandom(rnd); } // Step 2: Find markings to remove again var removableMarkings = maze.Markings.ToList(); while (removableMarkings.Count > 0) { var tryRemoveIndex = rnd.Next(removableMarkings.Count); var tryRemove = removableMarkings[tryRemoveIndex]; removableMarkings.RemoveAt(tryRemoveIndex); maze.Markings.Remove(tryRemove.Key); if (!areMarkingsUnique(maze)) { // No longer unique — put it back in maze.Markings.Add(tryRemove.Key, tryRemove.Value); } } lock (obj) { var msg = "{0/White} = {1/Green}{2/Magenta}".Color(null).Fmt(seed, maze.Markings.Count, maze.Markings.Count < lowestMarkings ? " — NEW BEST" : maze.Markings.Count == lowestMarkings ? " — TIED" : ""); if (maze.Markings.Count <= lowestMarkings) { lowestMarkings = maze.Markings.Count; bestMaze = maze; WriteMazeInManual(bestMaze); } ConsoleUtil.WriteLine(msg); } impossiburu :; //}); return(bestMaze); }