diff --git a/.gitignore b/.gitignore index eed4580..a0e76a3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ obj/ *.user output/ +source/old/ diff --git a/README.md b/README.md index 3998d65..6c62158 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,7 @@ Sources of examples: 1. [SokobanLevel1](models/SokobanLevel1.xml) seems to be the first level from Hiroyuki Imabayashi's Sokoban puzzle. [SokobanLevel2](models/SokobanLevel2.xml) is the [level 452](https://www.sokobanonline.com/play/web-archive/razorflame/ionic-catalysts-xi/58022_ionic-catalysts-xi-452) from Ionic Catalysts XI set. 1. [RainbowGrowth](models/RainbowGrowth.xml) was [proposed](https://github.com/mxgmn/MarkovJunior/discussions/25) by [mure](https://github.com/mure). 1. [MultiHeadedWalk](models/MultiHeadedWalk.xml), [MultiHeadedDungeon](models/MultiHeadedDungeon.xml) and [MultiHeadedWalkDungeon](models/MultiHeadedWalkDungeon.xml) are [based](https://github.com/mxgmn/MarkovJunior/discussions/38) on the idea by [Ilya Kudritsky](https://github.com/Inferdy). +1. [Island](models/Island.xml) model is by [Guillaume Fiette](https://github.com/woldendans/MJ-simple-island). Voxel scenes were rendered in [MagicaVoxel](https://ephtracy.github.io/) by [ephtracy](https://github.com/ephtracy). Special thanks to [Brian Bucklew](https://github.com/unormal) for demonstrating the power of Dijkstra fields to me in roguelike level generation and [Kevin Chapelier](https://github.com/kchapelier) for a number of good suggestions. The font used in GUI is [Tamzen](https://github.com/sunaku/tamzen-font). diff --git a/models.xml b/models.xml index b383cd8..6335562 100644 --- a/models.xml +++ b/models.xml @@ -7,6 +7,16 @@ + + + + + + + + + + diff --git a/models/Island.xml b/models/Island.xml new file mode 100644 index 0000000..3060c53 --- /dev/null +++ b/models/Island.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/models/MultiHeadedWalk.xml b/models/MultiHeadedWalk.xml index c8c3c61..96c481d 100644 --- a/models/MultiHeadedWalk.xml +++ b/models/MultiHeadedWalk.xml @@ -1,7 +1,7 @@ - + - + diff --git a/resources/palette.xml b/resources/palette.xml index 5c57b31..16e679c 100644 --- a/resources/palette.xml +++ b/resources/palette.xml @@ -1,56 +1,56 @@  - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/source/Map.cs b/source/Map.cs index 6b05fb2..93eb705 100644 --- a/source/Map.cs +++ b/source/Map.cs @@ -38,7 +38,7 @@ override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid) (NY, DY) = readScale(scales[1]); (NZ, DZ) = readScale(scales[2]); - newgrid = Grid.Load(xelem, NX * grid.MX / DX, NY * grid.MY / DY, NZ * grid.MZ / DZ); + newgrid = Grid.Load(xelem, grid.MX * NX / DX, grid.MY * NY / DY, grid.MZ * NZ / DZ); if (newgrid == null) return false; if (!base.Load(xelem, parentSymmetry, newgrid)) return false; diff --git a/source/Program.cs b/source/Program.cs index ef951e8..957d760 100644 --- a/source/Program.cs +++ b/source/Program.cs @@ -54,17 +54,20 @@ static void Main() int gui = xmodel.Get("gui", 0); if (gif) amount = 1; + Dictionary customPalette = new(palette); + foreach (var x in xmodel.Elements("color")) customPalette[x.Get("symbol")] = (255 << 24) + Convert.ToInt32(x.Get("value"), 16); + for (int k = 0; k < amount; k++) { int seed = seeds != null && k < seeds.Length ? seeds[k] : meta.Next(); foreach ((byte[] result, char[] legend, int FX, int FY, int FZ) in interpreter.Run(seed, steps, gif)) { - int[] colors = legend.Select(ch => palette[ch]).ToArray(); + int[] colors = legend.Select(ch => customPalette[ch]).ToArray(); string outputname = gif ? $"output/{interpreter.counter}" : $"output/{name}_{seed}"; if (FZ == 1 || iso) { var (bitmap, WIDTH, HEIGHT) = Graphics.Render(result, FX, FY, FZ, colors, pixelsize, gui); - if (gui > 0) GUI.Draw(name, interpreter.root, interpreter.current, bitmap, WIDTH, HEIGHT, palette); + if (gui > 0) GUI.Draw(name, interpreter.root, interpreter.current, bitmap, WIDTH, HEIGHT, customPalette); Graphics.SaveBitmap(bitmap, WIDTH, HEIGHT, outputname + ".png"); } else VoxHelper.SaveVox(result, (byte)FX, (byte)FY, (byte)FZ, colors, outputname + ".vox"); diff --git a/source/RuleNode.cs b/source/RuleNode.cs index 51e63e0..4641bf6 100644 --- a/source/RuleNode.cs +++ b/source/RuleNode.cs @@ -66,7 +66,16 @@ override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid) if (xfields.Any()) { fields = new Field[grid.C]; - foreach (XElement xfield in xfields) fields[grid.values[xfield.Get("for")]] = new Field(xfield, grid); + foreach (XElement xfield in xfields) + { + char c = xfield.Get("for"); + if (grid.values.TryGetValue(c, out byte value)) fields[value] = new Field(xfield, grid); + else + { + Interpreter.WriteLine($"unknown field value {c} at line {xfield.LineNumber()}"); + return false; + } + } potentials = AH.Array2D(grid.C, grid.state.Length, 0); } diff --git a/syntax.md b/syntax.md index 2926ddd..32b3552 100644 --- a/syntax.md +++ b/syntax.md @@ -58,7 +58,7 @@ If search is set false, the interpreter can be made to follow the goal more stri 1. Integer `limit` attribute sets the maximum number of states searched. By default, the number of states is not limited. 2. Floating point `depthCoefficient` attribute [interpolates](https://github.com/mxgmn/MarkovJunior/blob/4e64162f00203f5b5753af100af0dab8d72ce805/source/Search.cs#L269) between breadth-first search and depth-first search. -See examples of inference use in [MultiSokoban9](models/MultiSokoban9.xml), [SokobanLevel1](models/SokobanLevel1.xml), [StairsPath](models/StairsPath.xml), [KnightPatrol](models/KnightPatrol.xml), [CrossCountry](models/CrossCountry.xml), [RegularPath](models/RegularPath.xml), [DiagonalPath](models/DiagonalPath.xml), [EuclideanPath](models/EuclideanPath.xml), [BishopParity](models/BishopParity.xml), [SnellLaw](models/SnellLaw.xml), [SequentialSokoban](models/SequentialSokoban.xml), [CompleteSAW](models/CompleteSAW.xml), [CompleteSAWSmart](models/CompleteSAWSmart.xml). +See examples of inference use in [MultiSokoban9](models/MultiSokoban9.xml), [SokobanLevel1](models/SokobanLevel1.xml), [StairsPath](models/StairsPath.xml), [KnightPatrol](models/KnightPatrol.xml), [CrossCountry](models/CrossCountry.xml), [RegularPath](models/RegularPath.xml), [DiagonalPath](models/DiagonalPath.xml), [EuclideanPath](models/EuclideanPath.xml), [BishopParity](models/BishopParity.xml), [SnellLaw](models/SnellLaw.xml), [SequentialSokoban](models/SequentialSokoban.xml), [CompleteSAW](models/CompleteSAW.xml), [CompleteSAWSmart](models/CompleteSAWSmart.xml), [Island](models/Island.xml). @@ -91,4 +91,4 @@ See examples of `convchain` node use in [ChainMaze](models/ChainMaze.xml), [Chai ## Questions and Answers **Q:** How to make a loop? How to make a sequence repeat?
-**A:** To make a sequence repeat, put a `sequence` node inside a `markov` node. Examples of this: [HamiltonianPath](models/HamiltonianPath.xml), [SelectLargeCaves](models/SelectLargeCaves.xml), [SelectLongKnots](models/SelectLongKnots.xml), [FireNoise](models/FireNoise.xml), [SmartSAW](models/SmartSAW.xml), [FindLongCycle](models/FindLongCycle.xml). Counters in markov/sequence nodes are not supported right now. Instead, you may want to repeat the sequence until some node is matched. +**A:** To make a sequence repeat, put a `sequence` node inside a `markov` node or inside another `sequence` node. Examples of this: [MultiHeadedWalk](models/MultiHeadedWalk.xml), [HamiltonianPath](models/HamiltonianPath.xml), [SelectLargeCaves](models/SelectLargeCaves.xml), [SelectLongKnots](models/SelectLongKnots.xml), [FireNoise](models/FireNoise.xml), [SmartSAW](models/SmartSAW.xml), [FindLongCycle](models/FindLongCycle.xml). Counters in markov/sequence nodes are not supported right now. Instead, you may want to repeat the sequence until some node is matched.