public Map(string serialNumber, string[] litIndicators, string[] unlitIndicators, int moduleId, MonoRandom rnd) { var mazes = new List <MapNode[, ]>(); string rowPhrase, colPhrase; Debug.LogFormat("[3D Maze #{0}] Using rule seed: {1}", moduleId, rnd.Seed); if (rnd.Seed == 1) { string[] labels; string[] n_walls; string[] w_walls; for (var mapIndex = 0; mapIndex < 10; mapIndex++) { var rawMapData = new MapNode[WIDTH, WIDTH]; switch (mapIndex) { // ABC case 0: labels = new string[] { " A ", " *A B", "A B C ", " C * B", " A ", " B C B ", "* C ", " A C " }; n_walls = new string[] { "11000110", "00001000", "01011100", "00100011", "10011001", "00000100", "00110010", "11000001" }; w_walls = new string[] { "10101001", "10100100", "00010001", "01010110", "01010001", "01100100", "01001101", "00101100" }; break; // ABD case 1: labels = new string[] { "A B A*", " D ", " D B", " A B ", " * A ", "D A ", " B D ", " D * B" }; n_walls = new string[] { "10100011", "01100100", "00011010", "00100000", "11100110", "00010100", "10100001", "00011000" }; w_walls = new string[] { "10000010", "11000100", "00010001", "01000100", "10010001", "00010100", "01000101", "00010100" }; break; // ABH case 2: labels = new string[] { "B A H", "* H ", "B B ", " * HA", " A H ", " A B ", " B * ", "A H " }; n_walls = new string[] { "11101011", "01011000", "10001101", "01010000", "00001000", "00110100", "00101110", "01100000" }; w_walls = new string[] { "10010000", "01000001", "00110011", "11010101", "11010010", "11100000", "00010101", "00000001" }; break; // ACD case 3: labels = new string[] { "D ", " C D* C", " * C ", " A ", "D C D ", " A * A", " A D ", "A C " }; n_walls = new string[] { "10111011", "01100110", "00000111", "01000000", "11101110", "01100100", "00010110", "01101100" }; w_walls = new string[] { "00001000", "11001100", "01110010", "10011001", "10001001", "01100101", "01010000", "10000010" }; break; // ACH case 4: labels = new string[] { "H C A ", "* H ", " *C", " A H ", "C H C A ", " * A", " C H ", " A " }; n_walls = new string[] { "00111100", "11010000", "01100110", "00000000", "01000100", "00000001", "11100010", "01011011" }; w_walls = new string[] { "10000110", "10010001", "01010101", "10000001", "11000000", "01010101", "10010000", "01000010" }; break; // ADH case 5: labels = new string[] { "D D * ", " H A", " *H A ", "A D ", " HD ", "* H A", "D ", " A H " }; n_walls = new string[] { "01110101", "00001110", "00000111", "01001100", "00110101", "11100000", "01100000", "11001000" }; w_walls = new string[] { "10100100", "11110000", "11011000", "01110000", "10000101", "10001110", "00011111", "10000101" }; break; // BCD case 6: labels = new string[] { " B ", "C D * ", " * B C ", " C B ", " C D", "B D ", " C * D ", "D B " }; n_walls = new string[] { "01011110", "01101011", "01100100", "10000111", "10110011", "10011001", "01100010", "10111001" }; w_walls = new string[] { "10010000", "00001010", "11101000", "00001000", "00100010", "00010001", "00000110", "00100001" }; break; // BCH case 7: labels = new string[] { "C H ", " C H", " * B ", "B H* ", " H B C", " * ", " B C ", " C H B" }; n_walls = new string[] { "10110011", "01010111", "10101100", "00000110", "01111110", "00100010", "10010100", "00000110" }; w_walls = new string[] { "10000100", "01001000", "01111001", "10001000", "10001000", "01101101", "01101001", "00010010" }; break; // BDH case 8: labels = new string[] { " D B H", " * D ", " H * B", "D B ", " D H", " B ", " H H*", "D B " }; n_walls = new string[] { "00100001", "00010001", "11011000", "11011011", "00010011", "10000000", "01101100", "01001111" }; w_walls = new string[] { "01101000", "11010111", "00000110", "00100000", "00110100", "10001110", "11000000", "00011010" }; break; // CDH default: labels = new string[] { " H D ", " C* ", " H D", "H D ", " C ", "C D C H", "*D H * ", " C" }; n_walls = new string[] { "01011010", "00100100", "00000000", "01000010", "01000010", "01100110", "01011010", "10100101" }; w_walls = new string[] { "11001001", "11110111", "00100010", "00011100", "10011100", "10001000", "11000001", "00101010" }; break; } for (int y = 0; y < WIDTH; y++) { for (int x = 0; x < WIDTH; x++) { rawMapData[x, y] = new MapNode(labels[y][x], n_walls[y][x] == '1', w_walls[y][x] == '1'); } } mazes.Add(rawMapData); } rowPhrase = "MAZEGAMER"; colPhrase = "HELPIMLOST"; } else { // RULE SEEDED CODE starts here var mazeLabels = "ABC,ABD,ABH,ACD,ACH,ADH,BCD,BCH,BDH,CDH".Split(','); foreach (var labels in mazeLabels) { var mapData = new MapNode[WIDTH, WIDTH]; for (var x = 0; x < WIDTH; x++) { for (var y = 0; y < WIDTH; y++) { mapData[x, y] = new MapNode(' ', true, true); } } var todo = Enumerable.Range(0, WIDTH * WIDTH).Select(i => new Coordinate(i % WIDTH, i / WIDTH)).ToList(); var visited = new List <Coordinate>(); var done = new List <Coordinate>(); // Choose a random square to start from var startIx = rnd.Next(0, todo.Count); visited.Add(todo[startIx]); todo.RemoveAt(startIx); // Generate a maze while (todo.Count > 0) { var cellIx = rnd.Next(0, visited.Count); var cell = visited[cellIx]; var validWalls = Enumerable.Range(0, 4).Select(dir => new { Dir = dir, Cell = Neighbor(cell, dir) }).Where(c => todo.Contains(c.Cell)).ToArray(); if (validWalls.Length == 0) { visited.RemoveAt(cellIx); done.Add(cell); continue; } var wallIx = rnd.Next(0, validWalls.Length); var wall = validWalls[wallIx]; switch (wall.Dir) { case NORTH: mapData[cell.X, cell.Y].n_wall = false; break; case WEST: mapData[cell.X, cell.Y].w_wall = false; break; case SOUTH: mapData[wall.Cell.X, wall.Cell.Y].n_wall = false; break; default: mapData[wall.Cell.X, wall.Cell.Y].w_wall = false; break; } todo.Remove(wall.Cell); visited.Add(wall.Cell); } // Remove 20–35% of the remaining walls to make the maze more spacious var remainingWalls = new List <int>(); for (var x = 0; x < WIDTH; x++) { for (var y = 0; y < WIDTH; y++) { if (mapData[x, y].n_wall) { remainingWalls.Add((WIDTH * y + x) * 2); } if (mapData[x, y].w_wall) { remainingWalls.Add((WIDTH * y + x) * 2 + 1); } } } var percentage = (rnd.NextDouble() * .15) + .2; var removeWalls = (int)(remainingWalls.Count * percentage); while (removeWalls > 0) { var wallIx = rnd.Next(0, remainingWalls.Count); var wall = remainingWalls[wallIx]; remainingWalls.RemoveAt(wallIx); var x = (wall / 2) % WIDTH; var y = (wall / 2) / WIDTH; if (wall % 2 == 1) { // Make sure not to remove a wall that would leave an entire row devoid of walls if (Enumerable.Range(0, WIDTH).Count(xx => mapData[xx, y].w_wall) == 1) { continue; } mapData[x, y].w_wall = false; } else { // Make sure not to remove a wall that would leave an entire column devoid of walls if (Enumerable.Range(0, WIDTH).Count(yy => mapData[x, yy].n_wall) == 1) { continue; } mapData[x, y].n_wall = false; } removeWalls--; } // Select 33 random cells for the labels (letters and cardinals) done.AddRange(visited); rnd.ShuffleFisherYates(done); // Add the labels (5 of each letter) for (var letterIx = 0; letterIx < labels.Length; letterIx++) { for (var i = 0; i < 5; i++) { mapData[done[5 * letterIx + i].X, done[5 * letterIx + i].Y].label = labels[letterIx]; } } // Add the 3 asterisks for (var i = 0; i < 3; i++) { mapData[done[30 + i].X, done[30 + i].Y].label = '*'; } mazes.Add(mapData); } var phrases = new List <string> { "MAZE GAMER", "MAZE TRAVELER", "MAZE CORRIDOR", "MAZE SOJOURNER", "MAZE VOYAGER", "HELP IM LOST", "WINDING MAZE", "TWISTY PASSAGES", "ADVENTURING", "FIND THE EXIT", "WHERES THE EXIT", "GO TO THE EXIT", "FIND THE WALL", "UPON DISCOVERY", "ON A JOURNEY", "SOS WHERE AM I", "SHOW ME AROUND", "I NEED ASSISTANCE", "LABYRINTHIAN", "PATH FINDER", "CARDINAL DIRECTION", "WHAT IS THIS PLACE", "GO IN CIRCLES", "DEAD END", "TURN AROUND" }; var ix = rnd.Next(0, phrases.Count); rowPhrase = phrases[ix]; phrases.RemoveAt(ix); colPhrase = phrases[rnd.Next(0, phrases.Count)]; Debug.LogFormat("<3D Maze #{0}> Row phrase: {1}", moduleId, rowPhrase); Debug.LogFormat("<3D Maze #{0}> Column phrase: {1}", moduleId, colPhrase); } // Calculate the goal position from the edgework var firstDigit = serialNumber.Where(ch => ch >= '0' && ch <= '9').First(); var lastDigit = serialNumber.Where(ch => ch >= '0' && ch <= '9').Last(); var rowMsg = firstDigit.ToString(); var numUnlit = 0; foreach (string unlit in unlitIndicators) { // MAZE GAMER if (unlit.Intersect(rowPhrase).Any()) { numUnlit++; rowMsg += " + " + unlit; } } if (numUnlit == 0) { rowMsg = "no unlit indicators"; } var colMsg = lastDigit.ToString(); var numLit = 0; foreach (string lit in litIndicators) { // HELP I'M LOST if (lit.Intersect(colPhrase).Any()) { numLit++; colMsg += " + " + lit; } } if (numLit == 0) { colMsg = "no lit indicators"; } sol_x = (lastDigit + numLit) % WIDTH; sol_y = (firstDigit + numUnlit) % WIDTH; mapData = mazes[Random.Range(0, 10)]; end_dir = Random.Range(0, 4); var decoy_dir = Random.Range(0, 3); if (decoy_dir >= end_dir) { decoy_dir++; } Debug.LogFormat("[3D Maze #{0}] Selected map: {1}", moduleId, join("", Enumerable.Range(0, WIDTH * WIDTH).Select(ix => mapData[ix % WIDTH, ix / WIDTH].label).Where("ABCDH".Contains).Distinct().OrderBy(c => c))); Debug.LogFormat("[3D Maze #{0}] Column: {1} ({2}), Row: {3} ({4}), Cardinal: {5}", moduleId, sol_x, colMsg, sol_y, rowMsg, new[] { "North", "East", "South", "West" }[end_dir]); // Replace the asterisks in the map with the true cardinal direction for (var x = 0; x < WIDTH; x++) { for (var y = 0; y < WIDTH; y++) { if (mapData[x, y].label == '*') { mapData[x, y].label = dirToChar(end_dir); } } } // Add three decoy cardinal directions for (int i = 0; i < 3; i++) { int x = Random.Range(0, WIDTH); int y = Random.Range(0, WIDTH); while (mapData[x, y].label != ' ') { x = Random.Range(0, WIDTH); y = Random.Range(0, WIDTH); } mapData[x, y].label = dirToChar(decoy_dir); } pl_x = Random.Range(0, WIDTH); pl_y = Random.Range(0, WIDTH); pl_dir = Random.Range(0, 4); while (!getEdge(sol_x, sol_y, end_dir, 0, 0, end_dir)) { sol_x = bound(sol_x + xMod(1, 0, end_dir), WIDTH); sol_y = bound(sol_y + yMod(1, 0, end_dir), WIDTH); } win_x1 = sol_x; win_y1 = sol_y; win_x2 = bound(sol_x + xMod(1, 0, end_dir), WIDTH); win_y2 = bound(sol_y + yMod(1, 0, end_dir), WIDTH); }
private static Dictionary <string, string> GetRulesInternal(int ruleSeed) { if (ruleSeed == 1) { return new Dictionary <string, string>(StringComparer.CurrentCultureIgnoreCase) { { "shell", "3.505" }, { "halls", "3.515" }, { "slick", "3.522" }, { "trick", "3.532" }, { "boxes", "3.535" }, { "leaks", "3.542" }, { "strobe", "3.545" }, { "bistro", "3.552" }, { "flick", "3.555" }, { "bombs", "3.565" }, { "break", "3.572" }, { "brick", "3.575" }, { "steak", "3.582" }, { "sting", "3.592" }, { "vector", "3.595" }, { "beats", "3.600" } } } ; var random = new MonoRandom(ruleSeed); var frequencies = random.Shuffle((string[])MorseCodeSolver.frequencies.Clone()).Take(FrequenciesUsed).ToArray(); var words = new string[FrequenciesUsed]; Array.Copy(random.Shuffle((string[])MorseCodeSolver.words.Clone()), words, FrequenciesUsed / 2); int i = FrequenciesUsed / 2; for (int j = 0; j < FrequenciesUsed / 2; ++j) { var s = words[j].Substring(1); var toAdd = (from w in MorseCodeSolver.words where !words.Contains(w) && !w.EndsWith(s) select(word: w, pref: random.NextDouble(), dist: CyclingLevenshteinDistance(w, words[j]))).ToArray(); Array.Sort(toAdd, (a, b) => { var r = a.dist - b.dist; return(r != 0 ? r : a.pref.CompareTo(b.pref)); }); var numberToAdd = Math.Min(random.Next(1, 4), Math.Min(FrequenciesUsed - i, toAdd.Length)); for (int k = 0; k < numberToAdd; ++k) { words[i] = toAdd[k].word; ++i; } } var dictionary = new Dictionary <string, string>(StringComparer.CurrentCultureIgnoreCase); for (i = 0; i < FrequenciesUsed; ++i) { dictionary.Add(words[i], frequencies[i]); } return(dictionary); }