[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [mxgmn]\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\n.vscode/\n.vs/\n\nbin/\nobj/\ndoc/\n\n*.user\n\noutput/\nsource/old/\n"
  },
  {
    "path": "CITATION.cff",
    "content": "cff-version: 1.1.0\nmessage: \"citation\"\nauthors:\n  - family-names: Gumin\n    given-names: Maxim\ntitle: MarkovJunior, a probabilistic programming language based on pattern matching and constraint propagation\nversion: 1.0\ndate-released: 2022-06-01\nkeywords:\n  - probabilistic programming\n  - procedural generation\n  - constraint satisfaction\n  - language\nurl: https://github.com/mxgmn/MarkovJunior\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Maxim Gumin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MarkovJunior.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net10.0</TargetFramework>\n    <OutputPath>bin\\</OutputPath>\n    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n    <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"SixLabors.ImageSharp\" Version=\"3.1.12\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Include=\"models\\*\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"resources\\**\\*\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n    <None Include=\"models.xml\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "README.md",
    "content": "# MarkovJunior\nMarkovJunior is a probabilistic programming language where programs are combinations of rewrite rules and inference is performed via constraint propagation. MarkovJunior is named after mathematician [Andrey Andreyevich Markov](https://en.wikipedia.org/wiki/Andrey_Markov,_Jr.), who defined and studied what is now called [Markov algorithms](https://en.wikipedia.org/wiki/Markov_algorithm).\n<p align=\"center\">\n<img src=\"images/top-iso.gif\"/>\n<img src=\"images/top-mv.gif\"/>\n</p>\n\nIn its basic form, a MarkovJunior program is an ordered list of rewrite rules. For example, [MazeBacktracker](models/MazeBacktracker.xml) (animation on the left below) is a list of 2 rewrite rules:\n1. `RBB=GGR` or \"replace red-black-black with green-green-red\".\n2. `RGG=WWR` or \"replace red-green-green with white-white-red\".\n\nOn each execution step MJ interpreter finds the first rule in the list that has a match on the grid, finds all matches for that rule and applies that rule for a random match. In the [maze backtracker](https://en.wikipedia.org/wiki/Maze_generation_algorithm#Depth-first_search) example, interpreter first applies a bunch of `RBB=GGR` rules. But eventually the green self-avoiding walk gets stuck. At this point the first rule has no matches, so interpreter applies the second rule `RGG=WWR` until the walk gets unstuck. Then it can apply the first rule again, and so on. Interpreter stops when there are no matches for any rule.\n<p align=\"center\">\n<img src=\"images/MazeBacktracker.gif\"/>\n<img src=\"images/multisokoban.gif\"/>\n</p>\n\nProbabilistic inference in MarkovJunior allows to impose constraints on the future state, and generate only those runs that lead to the constrained future. For example, inference in Sokoban rules `{RWB=BRW RB=BR}` makes a group of (red) agents organize (white) crates into specified shapes.\n\nUsing these ideas, we construct [many probabilistic generators](models/) of dungeons, architecture, puzzles and fun simulations.\n<p align=\"center\"><a href=\"images/top-1764.png\"/><img src=\"images/top-882.png\"/></a></p>\n\nAdditional materials:\n1. [Xml syntax overview](syntax.md).\n2. Higher resolution screenshots and more seeds: [ModernHouse](https://github.com/mxgmn/Blog/blob/master/ModernHouse.md), [SeaVilla](https://github.com/mxgmn/Blog/blob/master/SeaVilla.md), [Apartemazements](https://github.com/mxgmn/Blog/blob/master/Apartemazements.md), [CarmaTower](https://github.com/mxgmn/Blog/blob/master/CarmaTower.md), [Escheresque](https://github.com/mxgmn/Blog/blob/master/Escheresque.md), [PillarsOfEternity](https://github.com/mxgmn/Blog/blob/master/PillarsOfEternity.md), [Surface](https://github.com/mxgmn/Blog/blob/master/RandomSurface.md), [Knots](https://twitter.com/ExUtumno/status/895688856304992256).\n3. Unofficial [technical notes](https://gist.github.com/dogles/a926ab890552cc7e45400a930398449d) by Dan Ogles and [code documentation](https://github.com/kaya3/MarkovJunior-docs) by Andrew Kay.\n\n\n\n## Markov algorithms\nA Markov algorithm over an alphabet `A` is an ordered list of rules. Each rule is a string of the form `x=y`, where `x` and `y` are words in `A`, and some rules may be marked as halt rules. Application of a Markov algorithm to a word `w` proceeds as follows:\n1. Find the first rule `x=y` where `x` is a substring of `w`. If there are no such rules, then halt.\n2. Replace the leftmost `x` in `w` by `y`.\n3. If the found rule was a halt rule, then halt. Otherwise, go to step 1.\n\nFor example, consider this Markov algorithm in the alphabet `{0, 1, x}` (ε is the empty word):\n```\n1=0x\nx0=0xx\n0=ε\n```\nIf we apply it to the string `110` we get this sequence of strings:\n```\n110 -> 0x10 -> 0x0x0 -> 00xxx0 -> 00xx0xx -> 00x0xxxx -> 000xxxxxx -> 00xxxxxx -> 0xxxxxx -> xxxxxx\n```\nIn general, this algorithm converts a binary representation of a number into its unary representation.\n\nMarkov's student [Vilnis Detlovs](https://lv.wikipedia.org/wiki/Vilnis_Detlovs) [proved](http://www.mathnet.ru/php/archive.phtml?wshow=paper&jrnid=tm&paperid=1293) that for any Turing machine there exists a Markov algorithm that computes the same function. In comparison, grammars are unordered sets of rewrite rules and L-systems are rewrite rules that are applied in parallel. For more interesting examples of Markov algorithms check [Markov's book](http://www.mathnet.ru/links/1543dd6e347b444e6f3e108fafaf9f2a/tm1178.pdf) or see the greatest common divisor example in the [comment section](#comments) or [multiplication example](https://en.wikipedia.org/wiki/Markov_algorithm#Description) on Wikipedia.\n\nHow would one generalize Markov algorithms to multiple dimensions? First, in multiple dimensions there are no natural ways to insert a string into another string, so the lefts and rights of our rewrite rules should have the same size. Second, there are no natural ways to choose *the leftmost* match. Possible options are:\n* Choose a random match. This is what MJ's `(exists)` nodes do.\n* Choose all matches. There is a problem with this option however because different matches can overlap and have conflicts. Possible solutions are:\n\t* Greedily choose a maximal subset of non-conflicting matches. This is what MJ's `{forall}` nodes do.\n\t* Consider all matches in superposition. That is, instead of separate values, keep waves in each grid cell - boolean vectors that tell which spacetime patterns are forbidden and which are not. And this is how MJ performs inference.\n\nWe lose Turing completeness because our new procedure is not deterministic, but practice shows that this formalism still allows to describe a huge range of interesting random processes.\n\n\n\n## Rewrite rules\nThe simplest MarkovJunior program is probably `(B=W)`. It contains just a single rule `B=W`. On each turn, this program converts a random black square into a white square.\n<p align=\"center\">\n<a href=\"models/Basic.xml\"><img src=\"images/Basic.gif\"/></a>\n<a href=\"models/Growth.xml\"><img src=\"images/Growth.gif\"/></a>\n<a href=\"models/MazeGrowth.xml\"><img src=\"images/MazeGrowth.gif\"/></a>\n<a href=\"models/MazeGrowth.xml\"><img src=\"images/MazeGrowth.png\"/></a><br/>\n(B=W) | (WB=WW) | (WBB=WAW) | (WBB=WAW)\n</p>\n\n[Growth](models/Growth.xml) model `(WB=WW)` is more interesting. On each turn it replaces a black-white pair of adjacent cells `BW` with a white-white pair `WW`. In other words, on each turn it picks a random black cell adjacent to some white cell and color it into white. This model is almost identical to the [Eden growth model](http://digitalassets.lib.berkeley.edu/math/ucb/text/math_s4_v4_article-15.pdf): on each turn both models choose among the same set of black cells. They differ only in probability distributions: a uniform distribution over black cells adjacent to white cells is not the same as a uniform distribution over pairs of adjacent black and white cells.\n\nModel `(WBB=WAW)` generates a maze, with a single line of code! Compare it with an [implementation](https://bl.ocks.org/mbostock/70a28267db0354261476) in a conventional language. Any MarkovJunior model can be run in any number of dimensions without changes. On the right you can see the end result of [MazeGrowth](models/MazeGrowth.xml) in 3d, rendered in [MagicaVoxel](https://ephtracy.github.io/). By default, we use [PICO-8 palette](resources/palette.xml):\n<p align=\"center\"><img src=\"images/palette.png\"/></p>\n\nModel `(RBB=WWR)` is a [self-avoiding random walk](https://en.wikipedia.org/wiki/Self-avoiding_walk). Note that self-avoiding walks in 3d are longer on average than in 2d. In general, comparing the behaviors of similar random processes in different dimensions is a fascinating topic. A [classic result](https://sites.math.washington.edu/~morrow/336_19/papers19/Legrand.pdf) of George Pólya says that a random walk in 2d returns to its initial position with probability one, while in 3d this is no longer the case.\n<p align=\"center\">\n<a href=\"models/RegularSAW.xml\"><img src=\"images/RegularSAW.gif\"/></a>\n<a href=\"models/LoopErasedWalk.xml\"><img src=\"images/LoopErasedWalk.gif\"/></a>\n<a href=\"models/Trail.xml\"><img src=\"images/Trail.gif\"/></a><br/>\n(RBB=WWR) | LoopErasedWalk | (RB=WR RW=WR)\n</p>\n\nWe can put several rules into one **rulenode**. For example, `(RBB=WWR RBW=GWP PWG=PBU UWW=BBU UWP=BBR)` is a [loop-erased random walk](https://en.wikipedia.org/wiki/Loop-erased_random_walk). Trail model `(RB=WR RW=WR)` generates [decent connected caves](https://blog.jrheard.com/procedural-dungeon-generation-drunkards-walk-in-clojurescript).\n\nModel `(RBB=WWR R*W=W*R)` is known as the [Aldous-Broder maze generation algorithm](http://weblog.jamisbuck.org/2011/1/17/maze-generation-aldous-broder-algorithm). The **wildcard** symbol `*` in the input means that *any* color is allowed to be in the square. The wildcard symbol in the output means that the color doesn't change after the application of the rule. Aldous-Broder algorithm takes much more turns on average to generate a maze than MazeGrowth, for example, but it has a nice property that MazeGrowth doesn't have: each maze has the same probability to be generated. In other words, MazeTrail is an unbiased maze generation algorithm, or it samples mazes (or spanning trees) with the uniform distribution. [Wilson's algorithm](http://web.stanford.edu/~yuvalwig/math/teaching/UniformSpanningTrees.pdf) is a more efficient unbiased maze generation algorithm. Compare its [MarkovJunior](images/Wilson.gif) [implementation](models/Wilson.xml) with an [implementation](https://bl.ocks.org/mbostock/11357811) in a conventional language!\n\n\n\n## Combining rulenodes\nWe can put several rulenodes into a **sequence node**, to be run one after the other. In the [River](models/River.xml) model we first construct a stochastic [Voronoi](models/Voronoi.xml) diagram with 2 sources, and use the boundary between the formed regions as a base for a river. Then we spawn a couple more Voronoi seeds to grow forests and simultaneously grow grass from the river. As a result, we get random river valleys!\n<p align=\"center\">\n<a href=\"models/River.xml\"><img src=\"images/River.gif\"/></a>\n<a href=\"models/Apartemazements.xml\"><img src=\"images/Apartemazements.gif\"/></a>\n</p>\n\nIn [Apartemazements](models/Apartemazements.xml) we start with a WFC node and then do constructive postprocessing with rulenodes:\n1. Prepare constraints: mark bottom cells with a separate bottom color, mark the remaining border cells (sides and top) with a separate border color. Border cells should map to Empty, bottom cells should map to all tiles except Down.\n2. Run WFC [Paths](resources/tilesets/Paths.xml) tileset to generate closed stairy cycles.\n3. Randomize light sources.\n4. Drop columns from corners of flat tiles.\n5. Retract double columns, columns that touch ground and columns that touch stairs, except columns growing from corners of the Turn tiles.\n6. Grow windows between neighboring columns.\n7. Merge windows into bigger rectangles. We do this in several steps:\n\t1. Detect uneven patterns of windows when window corners touch window midpoints.\n\t2. Mark these patterns and propagate the markings through the whole lengths of window sides.\n\t3. Merge unmarked pairs of window sides.\n8. Turn the remaining 1x1 windows into walls.\n\nA more interesting way to combine nodes is to put them into a **Markov node**. Markov nodes substantially expand what we can do, because they allow to return to past nodes. When a Markov node is active, interpreter finds its first child node that matches and applies it. On the next turn, it finds the first matching node in the list again, and so on. The simplest example of the Markov node use is [MazeBacktracker](models/MazeBacktracker.xml) explained in the top section.\n\n<p align=\"center\">\n<a href=\"models/NystromDungeon.xml\"><img src=\"images/NystromDungeon.gif\"/></a>\n<a href=\"models/Flowers.xml\"><img src=\"images/Flowers.gif\"/></a>\n</p>\n\nOne of my favorite examples that motivated the development of MarkovJunior is [Bob Nystrom's dungeon generation algorithm](https://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/). It goes as follows:\n1. Draw a grid `{PBB=**P}`.\n2. Spawn a bunch of rooms `(room.png)`.\n3. Generate a maze on the rest of the grid. We can use any maze generation algorithm, but [MazeBacktracker](models/MazeBacktracker.xml) is preferred because it produces fewer branching points.\n4. Make the resulting configuration of rooms and corridors connected. This can be elegantly done with a Markov node `({GWW=**G}(GBW=*WG))`.\n5. Make some additional connections `(GBG=*W* #5)`, so the resulting dungeon has cycles. Dungeons without cycles are pretty boring, since the player has to return through already explored zones.\n6. Retract dead ends `{BBB/BWB=BBB/BBB}`.\n\n<p align=\"center\">\n<a href=\"models/Circuit.xml\"><img src=\"images/Circuit.gif\"/></a>\n<a href=\"models/DungeonGrowth.xml\"><img src=\"images/DungeonGrowth.gif\"/></a>\n</p>\n\nLike in REFAL, Markov nodes can be nested: once we go into a child node, we ignore outer nodes until the child branch completes.\n\n\n\n## Inference\nProbabilistic inference in MarkovJunior allows to impose constraints on the future state, and generate only those runs that lead to the constrained future. In other words, inference connects 2 given states (or partially observed states) with a chain of rewrite rules.\n\nThe simplest example of inference use is connecting 2 points with a path. In the self-avoiding walk model `(RBB=WWR)` we can **observe** a given square on the grid to become `R` red. Then the interpreter would generate only those walks that lead to the observed square. We can set the interpreter to follow the goal more strictly or less strictly by varying the **temperature** parameter. By default, temperature is set to zero.\n<p align=\"center\">\n<img src=\"images/coldest.gif\"/>\n<img src=\"images/cold.gif\"/>\n<img src=\"images/hot.gif\"/>\n<img src=\"images/hottest.gif\"/><br>\nColdest | Cold | Hot | Hottest\n</p>\n\nAnother thing we can do is to observe *all* odd grid squares becoming white or red. Then the interpreter would generate self-avoiding walks that cover the entire grid.\n<p align=\"center\">\n<a href=\"models/CompleteSAW.xml\"><img src=\"images/CompleteSAW.gif\"/></a>\n<a href=\"models/SokobanLevel1.xml\"><img src=\"images/SokobanLevel1.gif\"/></a>\n</p>\n\nWe can engage inference for any rewrite rules. For example, inference for [stair-drawing rules](models/StairsPath.xml) connects 2 points with a stairy path. Inference for rule `R**/**B=B**/**R` generates paths that a chess knight can take. Inference in the [CrossCountry](models/CrossCountry.xml) model connects 2 points with a path taking terrain costs into account. Inference for the Sokoban ruleset `{RB=BR RWB=BRW}` solves Sokoban puzzles or even [multiagent Sokoban puzzles](images/multisokoban.gif)!\n<p align=\"center\"><img src=\"images/StairsPath.gif\"/></a></p>\n\nInference in MarkovJunior is done via unidirectional (fast) or bidirectional (slow, but more powerful) constraint propagation. Unidirectional constraint propagation for rewrite rules can be described equivalently in terms of **rule propagation** fields which generalize Dijkstra fields for arbitrary rewrite rules. Dijkstra fields is a popular technique in grid-based procedural generation ([1](https://groups.google.com/forum/#!topic/rec.games.roguelike.development/6yNIuhSerpM), [2](http://www.roguebasin.com/index.php?title=The_Incredible_Power_of_Dijkstra_Maps), [3](http://www.roguebasin.com/index.php?title=Dijkstra_Maps_Visualized)). They in turn generalize [distance fields](https://iquilezles.org/www/articles/distfunctions/distfunctions.htm) used in computer graphics.\n\nIf constraint propagation completes it doesn't necessarily mean that the goal state is achievable. But if the propagation fails then we know for sure that the goal is not achievable. This allows to catch states where a crate is pushed to the wrong wall in Sokoban, or where the grid-covering walk splits the grid into 2 disconnected parts. In addition to this boolean heuristic, it's worth looking at the minimal number of turns required for constraint propagation to complete. This integer-valued heuristic is [admissible](https://en.wikipedia.org/wiki/Admissible_heuristic), and we use it in A\\* search to sample paths made of rewrite rules between 2 given states.\n\n\n\n## Open problems\n1. **Program synthesis for procedural generation**. William Chyr's talk [\"Level Design in Impossible Geometry\"](https://youtu.be/ed2zmmcEryw?t=1298) is not at all about procedural generation, yet I find [one slide](images/ps-for-procgen.jpg) to be very characteristic for pcg practice. William compares his earlier and later approaches to level design. The earlier one produced chaotic levels, while the later approach produced more structured, more intentional levels based on one central idea. Later levels weren't simpler, yet they were more memorable and easier for players to perceive. To me, the left level looks like it was generated procedurally! It has a very similar feel to my [procedural voxel puzzles](https://twitter.com/ExUtumno/status/971031987304763393). Can we make generators that produce levels that are more like the one on the right? This problem may seem AI-complete. But I'd argue it is very similar to classic genetic programming problems like [Koza's lawnmower problem](https://pdfs.semanticscholar.org/555e/13cc2dd246e3d63ceb00590605f3ff59593d.pdf). For example, take a simple procgen task of [generating Hamiltonian paths on the grid](models/CompleteSAW.xml). Even for small grid sizes like 29x29 this task is already computationally demanding. But do we really need to sample from all possible paths in practice? If we give this task to a human, they would probably draw a spiral or a zigzag curve - these are much more memorable and intentional designs than a random Hamiltonian path, plus they generalize to any grid sizes. To summarize, we can ask the system either to find a random Hamiltonian path or to find a short program that generates Hamiltonian paths. In the first case the result would look like the left level on the slide, and in the second case like the right level. Solving the latter program synthesis problem would create more memorable and intentional generators.\n2. **Model synthesis from examples**. Markov algorithms seem to be a perfect environment for program/model synthesis: no variables, ifs or whiles, nodes can be easily moved around without breaking correctness, models are easy to make differentiable. Random MJ programs are often fun and can produce human-relatable results and behaviors.\n\t1. Can we synthesize a MJ model from a result, or a set of results?\n\t2. Given a maze, is it possible to determine (or assign probabilities) whether it was generated by [MazeGrowth](models/MazeGrowth.xml) or [MazeBacktracker](models/MazeBacktracker.xml)?\n\t3. Solve the [Abstraction and Reasoning Challenge](https://www.kaggle.com/c/abstraction-and-reasoning-challenge) by inferring MarkovJunior models. Adjoint problem: use insights from the ARC challenge to build a better DSL for procedural generation on a grid.\n3. **Custom algorithms that run in the wave space**. To unite the advantages of constructive and constrained-based procedural generation. Related: custom algorithms (MJ rewrite rules) with custom energy functions like Ising energy or ConvChain energy.\n4. Generalize the notion of a pattern.\n5. Investigate MJ-like processes on other (possibly nonregular) grids or arbitrary graphs.\n6. Experiment with interactive extensions of Markov algorithms. It's possible to turn any MJ model into a game by assigning specific rewrite rules or nodes to key presses.\n7. Push the state of the art in grid-based procedural generation. [ModernHouse](https://twitter.com/ExUtumno/status/1141354217774428160) does not yet reach the structural variety of human-designed houses like [Sims 2 houses](https://www.thesimsresource.com/downloads/browse/category/sims2-lots/featured/1/search/modern%20house/). Use more subtle constraints.\n\n\n\n## Comments\nCompared to Turing machines and lambda calculus, Markov algorithms is probably the shortest and simplest way to rigorously define what an algorithm is.\n\nExercise: prove that the following Markov algorithm finds the greatest common divisor of 2 numbers written in a unary representation. For example, if we apply it to `111111*1111111111` we get `11`.\n```\n1a=a1\n1*1=a*\n1*=*b\nb=1\na=c\nc=1\n*=ε (halt)\n```\n\nFast pattern matching. MarkovJunior interpreter samples matches uniformly, but it doesn't scan the whole grid every turn. To keep pattern matching fast, the interpreter remembers previously found matches and searches only around the places that got changed. When a rulenode is encountered for the first time, MJ interpreter uses a multidimensional version of the [Boyer–Moore algorithm](https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm).\n\nStochastic relaxation. Markov nodes have a very nice representations as limits of differentiable nodes. Consider an unordered set of rewrite rules where each rule `r` is assigned a weight `w(r)`. On each step the interpreter finds all matches for all rules and chooses a random match according to the Boltzmann distribution `p(r) ~ exp(-w(r)/t)`. Then in the freezing limit `t->0` we get a Markov node, ordered by weights. What's good about this construction, is that for any `t>0` and for a typical score function, score's average on multiple runs would be a continuous (and smooth for practical purposes) function of weights. This means that one can find the optimal weights by gradient descent and then freeze the system to get the final discrete program.\n\nRead this [essay](https://www.jstor.org/stable/27641983) by [Boris Kushner](https://en.wikipedia.org/wiki/Boris_Kushner_(mathematician)) about A. A. Markov and his work in constructive mathematics.\n\n\n\n## Used work\nMain used work:\n1. Andrey A. Markov, [The Theory of Algorithms](http://www.mathnet.ru/php/archive.phtml?wshow=paper&jrnid=tm&paperid=1117&option_lang=eng), 1951. Markov used these ideas earlier in 1947 in his proof of the algorithmic undecidability of the word problem in semigroups. See also a later [book](http://www.mathnet.ru/php/archive.phtml?wshow=paper&jrnid=tm&paperid=1178&option_lang=eng) with a more detailed treatment. I would be grateful for links to English translations in open access.\n2. Guilherme S. Tows, [Imagegram](https://zaratustra.itch.io/imagegram), 2009. MarkovJunior takes forall-nodes from Imagegram.\n3. Valentin Turchin, [REFAL language](http://fprog.ru/2011/issue7/practice-fp-7-screen.pdf), 1968. MJ takes the idea of nested Markov nodes from REFAL.\n4. Brian Walker et al., [The incredible power of Dijkstra maps](https://groups.google.com/forum/#!topic/rec.games.roguelike.development/6yNIuhSerpM), 2010. A discussion in the the roguelike community that contains many techniques of using Dijkstra maps/distance fields for procedural generation and NPC AI. Later writeups: [1](http://www.roguebasin.com/index.php?title=The_Incredible_Power_of_Dijkstra_Maps), [2](http://www.roguebasin.com/index.php?title=Dijkstra_Maps_Visualized). We generalize Dijkstra maps to arbitrary rewrite rules.\n5. Pavlos S. Efraimidis, Paul Spirakis, [Weighted Random Sampling](https://utopia.duth.gr/~pefraimi/research/data/2007EncOfAlg.pdf), 2005.\n6. Work used in custom nodes: [Model Synthesis](http://graphics.stanford.edu/~pmerrell/thesis.pdf), [Wave Function Collapse Algorithm](https://github.com/mxgmn/WaveFunctionCollapse), [ConvChain Algorithm](https://github.com/mxgmn/ConvChain).\n7. Classic algorithms: [constraint propagation](https://en.wikipedia.org/wiki/Local_consistency), [constraint solving algorithms](https://www.cs.ubc.ca/~mack/Publications/AI77.pdf), [graph traversal](https://en.wikipedia.org/wiki/Graph_traversal), [A* search](https://www.cs.auckland.ac.nz/courses/compsci709s2c/resources/Mike.d/astarNilsson.pdf).\n\nRelated work:\n1. Daniel Ritchie, [Probabilistic Programming for Procedural Modeling and Design](https://dritchie.github.io/pdf/thesis.pdf), 2016.\n2. Lingfeng Yang, [From Execution Traces to Specialized Inference](https://stacks.stanford.edu/file/druid:kq822ym0815/et2si-reduced-opt-augmented.pdf), 2015.\n\nSources of examples:\n1. [BasicKeys](models/BasicKeys.xml) and [Keys](models/Keys.xml) are adaptations of graph grammars formulated by Joris Dormans, [Engineering Emergence: Applied Theory for Game Design](https://www.illc.uva.nl/Research/Publications/Dissertations/DS-2012-12.text.pdf), 2012. Which in turn are development of the earlier work by David Adams, [Automatic Generation of Dungeons for Computer Games](https://pdfs.semanticscholar.org/2502/0f8d955aee07b7dd49a3ec23b1f2a8cf1d06.pdf), 2002. I use a variation of these models to generate key-lock-bridge puzzles in [SeaVilla](models/SeaVilla.xml).\n1. [CarmaTower](models/CarmaTower.xml) is a proceduralization of a [voxel scene](https://twitter.com/Sir_carma/status/851883489628704768) by Antoine Lendrevie.\n1. The [NystromDungeon](models/NystromDungeon.xml) model is a MarkovJunior port of [Bob Nystrom's dungeon generator](https://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/).\n1. [HamiltonianPath](models/HamiltonianPath.xml) algorithm is adapted from [this](http://aip.scitation.org/doi/pdf/10.1063/1.443937) article. Compare it with an [implementation](http://clisby.net/projects/hamiltonian_path/hamiltonian_path_v1.html) in a conventional language.\n1. Room shapes in [DungeonGrowth](models/DungeonGrowth.xml) are taken from the [r/proceduralgeneration post](https://old.reddit.com/r/proceduralgeneration/comments/3pa8a1/my_take_at_a_roguelike_level_generator_ft/). Note that MJ interpreter automatically performs the optimizations described in the post.\n1. The [Wilson](models/Wilson.xml) model is a rewrite rule formulation of the [Wilson's algorithm](https://en.wikipedia.org/wiki/Loop-erased_random_walk#Uniform_spanning_tree). Compare it with an [implementation](https://bl.ocks.org/mbostock/11357811) in a conventional language.\n1. [MazeGrowth](models/MazeGrowth.xml) model is also known as maze generation via random traversal. Compare it with an [implementation](https://bl.ocks.org/mbostock/70a28267db0354261476) in a conventional language.\n1. [Growth](models/Growth.xml) is closely related to the [Eden growth model](http://digitalassets.lib.berkeley.edu/math/ucb/text/math_s4_v4_article-15.pdf).\n1. [BernoulliPercolation](models/BernoulliPercolation.xml) is a well studied model in a [percolation theory](https://en.wikipedia.org/wiki/Percolation_theory).\n1. [NestedGrowth](models/NestedGrowth.xml) is taken from [Imagegram](https://zaratustra.itch.io/imagegram).\n1. [SmoothTrail](models/SmoothTrail.xml) is adapted from [128_mhz's tweet](https://twitter.com/128_mhz/status/953847394403205120).\n1. [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.\n1. [RainbowGrowth](models/RainbowGrowth.xml) was [proposed](https://github.com/mxgmn/MarkovJunior/discussions/25) by [mure](https://github.com/mure).\n1. [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).\n1. [Island](models/Island.xml) model is by [Guillaume Fiette](https://github.com/woldendans/MJ-simple-island).\n1. [LostCity](models/LostCity.xml), [Forest](models/Forest.xml) and [Texture](models/Texture.xml) models are based on the model by [Andrew Kay](https://github.com/kaya3/pattern-match-2d).\n1. [Division](models/Division.xml) and [Rosettes](models/Rosettes.xml) models were developed by [scorpion451](https://github.com/scorpion451).\n\nVoxel 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).\n\n\n\n## How to build\nMarkovJunior interpreter is a console application that depends only on the standard library. Get [.NET Core](https://dotnet.microsoft.com/download) for Windows, Linux or macOS and run\n```\ndotnet run --configuration Release MarkovJunior.csproj\n```\nTo build the application without running it, run\n```\ndotnet publish --configuration Release MarkovJunior.csproj\n```\nAfter this, a self-contained app will appear in the `bin/publish` folder. Run `MarkovJunior.exe`, and it will put all the generated artifacts in the `output` folder.\n\nAlternatively, download and run the latest [release](https://github.com/mxgmn/MarkovJunior/releases) for Windows.\n\nGenerated results are put into the `output` folder. Edit `models.xml` to change model parameters. Open `.vox` files with [MagicaVoxel](https://ephtracy.github.io/).\n\n\n\n## Notable ports, forks and spinoffs\n* Yuu made a [TypeScript version of MarkovJunior](https://github.com/Yuu6883/MarkovJuniorWeb) that [runs on the web](https://yuu6883.github.io/MarkovJuniorWeb/), extended the language and added the ability to bind nodes to keypresses.\n* Aseaday is [porting](https://github.com/aseaday/MarkovJunior.js) MarkovJunior to JavaScript.\n* Andrew Kay added [XML documentation](https://github.com/kaya3/MarkovJunior-docs) to C# source code.\n* [Dan Ogles](https://github.com/dogles) wrote MarkovJunior [technical notes](https://gist.github.com/dogles/a926ab890552cc7e45400a930398449d) with the focus on fields and inference.\n* Andrew Kay designed [MJr](https://github.com/kaya3/MJr), a compiled language based on pattern rewriting.\n* Ashley is writing a [Rust implementation of MarkovJunior](https://github.com/expenses/markov) as a Python library.\n* LMCuber [is porting](https://github.com/LMCuber/PyMarkovJunior) MarkovJunior to Python.\n* William Manning developed a [Julia implementation](https://github.com/heyx3/MarkovJunior.jl) of MarkovJunior that runs in any number of space dimensions.\n\n\n\n## Funding\nMarkovJunior development was [funded](https://github.com/users/mxgmn/sponsorship) by\n1. **[Embark Studios](https://www.embark-studios.com/)**\n2. [Oskar Stålberg](https://twitter.com/OskSta)\n3. [Freehold Games](https://store.steampowered.com/app/333640/Caves_of_Qud/)\n4. [Bob Burrough](https://bobburrough.com/)\n"
  },
  {
    "path": "models/Apartemazements.xml",
    "content": "<sequence values=\"BWN\" symmetry=\"(xy)\">\n  <prl in=\"B\" out=\"W\"/>\n  <prl in=\"***/***/*** ***/*W*/*** ***/***/***\" out=\"***/***/*** ***/*B*/*** ***/***/***\"/>\n  <prl in=\"B W\" out=\"B N\"/>\n  <wfc values=\"BYDAWP RFUENC\" tileset=\"Paths\">\n    <rule in=\"W\" out=\"Empty\"/>\n    <rule in=\"N\" out=\"Empty|Line|Up|Turn|X\"/>\n\n    <prl in=\"B\" out=\"C\" comment=\"draw earth\"/>\n    <prl in=\"C * *\" out=\"B * *\" comment=\"remove extra earth\"/>\n    <prl in=\"C C\" out=\"E N\" comment=\"draw grass\"/>\n    <prl in=\"C\" out=\"N\"/>\n    <prl in=\"Y\" out=\"C\" p=\"0.25\" steps=\"1\"/>\n    <prl in=\"Y\" out=\"B\"/>\n\n    <all comment=\"draw columns\">\n      <rule in=\"D B\" out=\"* F\"/>\n      <rule in=\"F B\" out=\"* F\"/>\n    </all>\n    <all comment=\"remove extra columns\">\n      <rule in=\"FF EE\" out=\"BB EE\"/>\n      <rule in=\"FF FD EE\" out=\"FF BD EE\"/>\n      <rule in=\"F B\" out=\"B B\"/>\n      <rule in=\"B F\" out=\"B B\"/>\n      <rule in=\"F P\" out=\"B P\"/>\n      <rule in=\"P* *F\" out=\"** *B\"/>\n    </all>\n    <all comment=\"draw corner columns\">\n      <rule in=\"*A*/ADB/*B* ***/*B*/***\" out=\"***/***/*** ***/*F*/***\"/>\n      <rule in=\"F B\" out=\"F F\"/>\n    </all>\n    <all comment=\"place windows\">\n      <rule in=\"FBBBF *AAA*\" out=\"*RRR* *****\"/>\n      <rule in=\"FBBBF *RRR*\" out=\"*RRR* *****\"/>\n    </all>\n    <prl comment=\"find h-uneven windows\">\n      <rule in=\"RFFR RFDA\" out=\"U**U U***\"/>\n      <rule in=\"RFDA RFFR\" out=\"U*** U**U\"/>\n    </prl>\n    <all comment=\"mark h-uneven windows\">\n      <rule in=\"R U\" out=\"U *\"/>\n      <rule in=\"U R\" out=\"* U\"/>\n    </all>\n    <prl in=\"RFFR/BBBB\" out=\"*RR*/****\" comment=\"merge h-even windows\"/>\n    <prl in=\"U\" out=\"R\" comment=\"erase markings\"/>\n    <prl comment=\"find v-uneven windows\">\n      <rule in=\"RR DA FR\" out=\"UU ** *U\"/>\n      <rule in=\"FR DA RR\" out=\"*U ** UU\"/>\n    </prl>\n    <all comment=\"mark v-uneven windows\">\n      <rule in=\"RU\" out=\"U*\"/>\n      <rule in=\"UR\" out=\"*U\"/>\n    </all>\n    <prl comment=\"merge v-even windows\">\n      <rule in=\"RB AB RB\" out=\"RB RB RB\"/>\n      <rule in=\"RB DB RB\" out=\"RB RB RB\"/>\n    </prl>\n    <prl comment=\"forget structure\">\n      <rule in=\"F\" out=\"A\"/>\n      <rule in=\"D\" out=\"A\"/>\n      <rule in=\"P\" out=\"A\"/>\n      <rule in=\"U\" out=\"R\"/>\n    </prl>\n    <prl in=\"AAAAA ARRRA ARRRA ARRRA ARRRA AAAAA\" out=\"AAAAA AAAAA AAAAA AAAAA AAAAA AAAAA\"/>\n  </wfc>\n</sequence>\n\n<!--\nSome rendered outputs: https://github.com/mxgmn/Blog/blob/master/Apartemazements.md\n-->\n"
  },
  {
    "path": "models/Backtracker.xml",
    "content": "<markov values=\"BRWU\" origin=\"True\">\n  <one in=\"RB\" out=\"WR\"/>\n  <one in=\"RW\" out=\"UR\"/>\n</markov>\n\n<!--\nGenerates connected cave systems with pools.\n-->\n"
  },
  {
    "path": "models/BacktrackerCycle.xml",
    "content": "<sequence values=\"BRGWA\" origin=\"True\">\n  <union symbol=\"?\" values=\"WA\"/>\n  <markov>\n    <one in=\"RBB\" out=\"GGR\"/>\n    <one in=\"RGG\" out=\"WAR\"/>\n  </markov>\n  <one in=\"R\" out=\"W\"/>\n  <one in=\"WBW\" out=\"WAW\" steps=\"1\"/>\n  <all in=\"BBB/B?B\" out=\"***/*B*\"/>\n  <all in=\"A\" out=\"W\"/>\n</sequence>\n\n<!--\nGenerates a random cycle.\n-->\n"
  },
  {
    "path": "models/Basic.xml",
    "content": "<one values=\"BW\" in=\"B\" out=\"W\"/>\n"
  },
  {
    "path": "models/BasicBrickWall.xml",
    "content": "<sequence values=\"BWO\" symmetry=\"(x)\">\n  <all in=\"B/*\" out=\"O/*\"/>\n  <all in=\"O/*/*\" out=\"W/*/*\"/>\n  <markov>\n    <all in=\"BW\" out=\"BB\"/>\n    <one in=\"W/W/W/W/W/W/W/W\" out=\"*/*/*/B/*/*/*/*\"/>\n  </markov>\n  <one in=\"OOOOOOOO/BBBBBBBB\" out=\"***B****/********\"/>\n  <markov>\n    <all in=\"OW\" out=\"*O\" symmetry=\"(xy)\"/>\n    <all in=\"OO/OB\" out=\"*B/**\"/>\n    <one in=\"OOOO/BBBB/OOOO\" out=\"**B*/****/****\"/>\n    <one in=\"WW/BB/OB\" out=\"*O/**/**\"/>\n  </markov>\n</sequence>\n\n<!--\nTo make BrickWall, add texture and shading.\n-->\n"
  },
  {
    "path": "models/BasicDijkstraDungeon.xml",
    "content": "<sequence values=\"BGW\">\n  <one file=\"BasicDijkstraRoom\" legend=\"BW\" steps=\"1\"/>\n  <all in=\"W\" out=\"G\"/>\n  <markov>\n    <all in=\"GW\" out=\"GG\"/>\n    <path from=\"W\" to=\"G\" on=\"B\" color=\"G\" inertia=\"True\"/>\n    <one file=\"BasicDijkstraRoom\" legend=\"BW\"/>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/BasicDijkstraFill.xml",
    "content": "<sequence values=\"BWG\">\n  <all file=\"BasicDijkstraRoom\" legend=\"BW\"/>\n  <markov>\n    <all in=\"GW\" out=\"GG\"/>\n    <path from=\"G\" to=\"W\" on=\"B\" color=\"G\" inertia=\"True\"/>\n    <one in=\"W\" out=\"G\"/>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/BasicDungeonGrowth.xml",
    "content": "<sequence values=\"BRACDG\" origin=\"True\">\n  <union symbol=\"?\" values=\"BR\"/>\n  <one in=\"**?**/*BBB*/*BBB?/*BBB*/**R**\" out=\"AARAA/ADDDA/ADDDR/ADDDA/AACAA\"/>\n  <one in=\"ACA/BBB\" out=\"ARA/BBB\"/>\n  <all>\n    <rule in=\"C\" out=\"D\"/>\n    <rule in=\"R\" out=\"D\"/>\n  </all>\n  <all in=\"BD\" out=\"*A\"/>\n  <all in=\"DDD/ADA/DDD\" out=\"***/D*D/***\"/>\n  <all in=\"DDD/DAD/DDD\" out=\"***/*D*/***\"/>\n</sequence>\n"
  },
  {
    "path": "models/BasicKeys.xml",
    "content": "<sequence values=\"BDIRGYNOKPFCU\" origin=\"True\">\n  <all in=\"DB*\" out=\"*ID\" comment=\"draw a grid\"/>\n  <one in=\"D\" out=\"G\" limit=\"1\" comment=\"put an end\"/>\n  <one comment=\"put a start far from the end\">\n    <rule in=\"D\" out=\"R\" limit=\"1\"/>\n    <field for=\"R\" from=\"G\" on=\"RID\"/>\n  </one>\n  <one in=\"RID\" out=\"*NO\" limit=\"1\"/>\n  <one temperature=\"2.0\" comment=\"draw a non-optimal path from start to end\">\n    <rule in=\"OID\" out=\"YNO\"/>\n    <rule in=\"OIG\" out=\"YNG\"/>\n    <field for=\"O\" to=\"G\" on=\"GIDO\"/>\n  </one>\n  <one in=\"RNY\" out=\"KFR\" limit=\"1\"/>\n  <markov>\n    <one in=\"UID\" out=\"CCU\"/>\n    <one in=\"RID\" out=\"OCU\"/>\n    <one>\n      <rule in=\"RNY\" out=\"FFR\"/>\n      <rule in=\"ONY\" out=\"FPR\"/>\n      <rule in=\"RNG\" out=\"FFG\"/>\n      <rule in=\"ONG\" out=\"FPG\"/>\n    </one>\n  </markov>\n  <all in=\"CID\" out=\"*CC\"/>\n  <all in=\"I\" out=\"B\"/>\n</sequence>\n\n<!--\nGenerates a random puzzle with keys and locks on the main path. For a variation that can put locks on any paths see Keys.\n\nUsed in SeaVilla.\n-->\n"
  },
  {
    "path": "models/BasicPartitioning.xml",
    "content": "<sequence values=\"BW\">\n  <all in=\"***/*B*/***\" out=\"***/*W*/***\"/>\n  <markov>\n    <one in=\"WWW/WBW\" out=\"WBW/WBW\"/>\n    <one in=\"WWW/BBB\" out=\"WBW/BBB\"/>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/BasicSkyline.xml",
    "content": "<sequence values=\"BEC\" symmetry=\"(x)\">\n  <one in=\"B\" out=\"E\" steps=\"7\"/>\n  <one in=\"EB\" out=\"EE\" steps=\"100\"/>\n  <all in=\"EB\" out=\"CB\"/>\n  <all in=\"C/B\" out=\"C/C\"/>\n</sequence>\n"
  },
  {
    "path": "models/BasicSnake.xml",
    "content": "<sequence values=\"BWDPGR\" origin=\"True\">\n  <all>\n    <rule in=\"WBB\" out=\"**D\"/>\n    <rule in=\"DBB\" out=\"**D\"/>\n  </all>\n  <one in=\"WBD\" out=\"PGR\"/>\n  <one in=\"RBD\" out=\"GGR\" steps=\"2\"/>\n  <one in=\"D\" out=\"W\" steps=\"15\"/>\n  <markov>\n    <one in=\"RBW\" out=\"GGR\"/>\n    <all>\n      <rule in=\"RBD\" out=\"GGR\"/>\n      <rule in=\"PGG\" out=\"DBP\"/>\n    </all>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/BernoulliPercolation.xml",
    "content": "<sequence values=\"RDAUBW\" origin=\"True\">\n  <prl in=\"*R\" out=\"*U\" symmetry=\"()\"/>\n  <prl in=\"U*\" out=\"B*\" symmetry=\"()\"/>\n  <all in=\"DB*\" out=\"*AD\"/>\n  <prl in=\"A\" out=\"D\" p=\"0.517\" steps=\"1\"/>\n  <prl in=\"A\" out=\"B\"/>\n  <path from=\"R\" to=\"U\" on=\"D\" color=\"W\"/>\n</sequence>\n"
  },
  {
    "path": "models/BiasedGrowth.xml",
    "content": "<sequence values=\"NWSB\" origin=\"True\">\n  <prl in=\"N/*\" out=\"S/*\" symmetry=\"()\"/>\n  <prl in=\"*/S\" out=\"*/B\" symmetry=\"()\"/>\n  <one temperature=\"10.0\">\n    <rule in=\"WB\" out=\"WW\"/>\n    <field for=\"W\" to=\"N\" on=\"B\" recompute=\"False\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/BiasedGrowthContraction.xml",
    "content": "<sequence values=\"NWSB\" origin=\"True\">\n  <prl in=\"N/*\" out=\"S/*\" symmetry=\"()\"/>\n  <prl in=\"*/S\" out=\"*/B\" symmetry=\"()\"/>\n  <one in=\"B/W\" out=\"W/B\" symmetry=\"()\" steps=\"20\"/>\n  <one temperature=\"35.0\">\n    <rule in=\"WB\" out=\"WW\"/>\n    <rule in=\"BWW\" out=\"BBW\"/>\n    <field for=\"W\" to=\"N\" on=\"BW\" recompute=\"False\"/>\n    <field for=\"B\" to=\"S\" on=\"BW\" recompute=\"False\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/BiasedMazeGrowth.xml",
    "content": "<sequence values=\"NWAUB\" origin=\"True\">\n  <prl in=\"N/*\" out=\"U/*\" symmetry=\"()\"/>\n  <prl in=\"*/U\" out=\"*/B\" symmetry=\"()\"/>\n  <one temperature=\"10.0\">\n    <rule in=\"WBB\" out=\"WAW\"/>\n    <field for=\"W\" to=\"N\" on=\"B\" recompute=\"False\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/BiasedVoronoi.xml",
    "content": "<sequence values=\"BOS\">\n  <one in=\"B\" out=\"O\" steps=\"1\"/>\n  <one in=\"B\" out=\"S\" steps=\"1\"/>\n  <one>\n    <rule in=\"OB\" out=\"OO\"/>\n    <rule in=\"SB\" out=\"SS\"/>\n\n    <field for=\"S\" to=\"O\" on=\"BS\" recompute=\"True\"/>\n    <field for=\"O\" to=\"S\" on=\"BO\" recompute=\"True\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/BishopParity.xml",
    "content": "<sequence values=\"BRGUW\">\n  <one in=\"B\" out=\"R\" steps=\"1\"/>\n  <one in=\"B\" out=\"G\" steps=\"1\" temperature=\"10.0\">\n    <field for=\"G\" from=\"R\" on=\"B\"/>\n  </one>\n  <one in=\"BBB/BBB/BBB\" out=\"***/*U*/***\" steps=\"1\" temperature=\"10.0\">\n    <field for=\"U\" from=\"RG\" on=\"B\"/>\n  </one>\n  <one>\n    <rule in=\"R*/*B\" out=\"W*/*R\"/>\n    <rule in=\"U*/RB\" out=\"U*/WR\"/>\n    <rule in=\"U*/BR\" out=\"U*/RW\"/>\n\n    <observe value=\"G\" from=\"B\" to=\"R\"/>\n    <observe value=\"R\" to=\"W\"/>\n    <observe value=\"B\" to=\"BW\"/>\n    <observe value=\"U\" to=\"U\"/>\n  </one>\n</sequence>\n\n<!--\nTo capture the target, the bishop might need to step into the magic portal that changes its parity.\n-->\n"
  },
  {
    "path": "models/BlueNoise.xml",
    "content": "<one values=\"BW\" origin=\"True\" temperature=\"0.3\">\n  <rule in=\"B\" out=\"W\"/>\n  <field for=\"W\" from=\"W\" on=\"B\" recompute=\"True\"/>\n</one>\n"
  },
  {
    "path": "models/CarmaTower.xml",
    "content": "﻿<sequence values=\"BtTRL\" origin=\"True\" symmetry=\"(xy)\" folder=\"CarmaTower\">\n  <one in=\"B t\" out=\"t B\" />\n  <one in=\"BB/Bt\" out=\"tt/tt\" steps=\"1\" symmetry=\"()\" />\n  <all steps=\"2\">\n    <rule in=\"tB\" out=\"*t\" />\n    <rule in=\"t*/*B\" out=\"**/*t\" />\n  </all>\n  <prl in=\"t B\" out=\"* t\" steps=\"2\" />\n  <prl in=\"t B\" out=\"* T\" steps=\"1\" />\n  <prl in=\"T B\" out=\"* T\" />\n  <union symbol=\"r\" values=\"RTB\" color=\"45283c\" /> z\n\n  <!--one file=\"block_2_5x4\"/>\n    <one file=\"block_1_2x2\"/--><!--one file=\"block_2_4x3\"/>\n    <one file=\"block_1_3x2\"/--><!--one file=\"block_2_6x4\"/>\n    <one file=\"block_1_2x2\"/--><one>\n    <rule file=\"block_2_4x3\" legend=\"rB*LTR\" />\n    <rule file=\"block_1_5x3\" legend=\"rB*LTR\" />\n  </one>\n  <all symmetry=\"(xyz)\">\n    <rule in=\"BRB\" out=\"*B*\" />\n    <rule in=\"RB/B* B*/**\" out=\"B*/** **/**\" />\n  </all>\n  <prl in=\"R\" out=\"T\" />\n  <all comment=\"add on sides\">\n    <rule in=\"LT/BB\" out=\"**/L*\" />\n    <rule in=\"BL/LL\" out=\"L*/**\" />\n  </all>\n  <prl in=\"t\" out=\"B\" />\n  <all in=\"B T\" out=\"* B\" steps=\"2\" />\n  <all>\n    <rule in=\"BB LT\" out=\"R* **\" />\n    <rule in=\"RB LL\" out=\"*R **\" />\n  </all>\n  <prl in=\"B R\" out=\"R *\" steps=\"1\" />\n  <prl in=\"R\" out=\"L\" />\n  <union symbol=\"^\" values=\"BL\" />\n  <one steps=\"4\">\n    <rule in=\"^^^^/^^^^/^^^^ ^^^^/^^^^/^^^^ TTTT/TTTT/TTTT\" out=\"LLLL/LLLL/LLLL LLLL/LLLL/LLLL ****/****/****\" />\n    <rule in=\"^^^^/^^^^/^^^^ ^^^^/^^^^/^^^^ ^^^^/^^^^/^^^^ TTTT/TTTT/TTTT\" out=\"LLLL/LLLL/LLLL LLLL/LLLL/LLLL LLLL/LLLL/LLLL ****/****/****\" />\n    <rule in=\"^^^^/^^^^/^^^^ ^^^^/^^^^/^^^^ ^^^^/^^^^/^^^^ ^^^^/^^^^/^^^^ TTTT/TTTT/TTTT\" out=\"LLLL/LLLL/LLLL LLLL/LLLL/LLLL LLLL/LLLL/LLLL LLLL/LLLL/LLLL ****/****/****\" />\n    <rule in=\"^^^/^^^ ^^^/^^^ ^^^/^^^ TTT/TTT\" out=\"LLL/LLL LLL/LLL LLL/LLL ***/***\" />\n  </one>\n  <prl in=\"L\" out=\"T\" />\n  <prl in=\"B\" out=\"L\" />\n  <prl in=\"T\" out=\"B\" />\n  <map scale=\"4 4 4\" values=\"BLaDwWgKhORGUuoTtVQMpHlY\" outputValues=\"BaLDWYl\" transparent=\"OoH\" folder=\"CarmaTower\">\n    <rule in=\"L\" out=\"LLLLL/LLLLL/LLLLL/LLLLL/LLLLL LLLLL/LLLLL/LLLLL/LLLLL/LLLLL LLLLL/LLLLL/LLLLL/LLLLL/LLLLL LLLLL/LLLLL/LLLLL/LLLLL/LLLLL LLLLL/LLLLL/LLLLL/LLLLL/LLLLL\" comment=\"EEEEE\" />\n    <prl steps=\"1\">\n      <rule in=\"B\" out=\"L\" />\n      <rule in=\"L\" out=\"B\" />\n    </prl>\n    <sequence symmetry=\"(xyz)\" comment=\"scaffolding\">\n      <all in=\"LB/BB BB/BB\" out=\"**/** **/*O\" />\n      <all in=\"OBBBB/***** *****/****L\" out=\"****O/***** *****/*****\" />\n      <union symbol=\"+\" values=\"BO\" comment=\"black or scaffold\" />\n    </sequence>\n    <all in=\"BBB/+++ LLL/BBB *L*/BBB\" out=\"***/*** ***/*** *W*/***\" comment=\"setting up panels\" />\n    <all>\n      <rule in=\"LWWL\" out=\"*LL*\" />\n      <rule in=\"LWWWL\" out=\"*LLL*\" />\n    </all>\n    <markov>\n      <one in=\"aWWW\" out=\"***a\" />\n      <one>\n        <rule in=\"BLWWW\" out=\"****a\" />\n        <rule in=\"BLWWW\" out=\"****R\" />\n      </one>\n    </markov>\n    <all>\n      <rule in=\"aL/BB\" out=\"L*/**\" />\n      <rule in=\"aWL/BBB\" out=\"LL*/***\" />\n    </all>\n    <one in=\"aWWaWWa\" out=\"***L***\" />\n    <all comment=\"fill down\">\n      <rule in=\"W* L* L+\" out=\"** W* **\" />\n      <rule in=\"a* L* L+\" out=\"** a* **\" />\n    </all>\n    <all comment=\"mark uneven panels\">\n      <rule in=\"WaW L*W\" out=\"RRR **R\" />\n      <rule in=\"WW LW\" out=\"RR *R\" />\n      <rule in=\"WWW\" out=\"RRR\" />\n      <rule in=\"LT/LB\" out=\"*R/**\" />\n    </all>\n    <all symmetry=\"(xyz)\">\n      <rule in=\"RW\" out=\"*R\" />\n      <rule in=\"Ra\" out=\"*R\" />\n    </all>\n    <all comment=\"delete too short panels\">\n      <rule in=\"L a a a a a a a\" out=\"* t t t t t t t\" />\n      <rule in=\"t a\" out=\"* t\" />\n      <rule in=\"tW\" out=\"*T\" />\n      <rule in=\"TW\" out=\"*T\" />\n    </all>\n    <prl>\n      <rule in=\"W\" out=\"R\" />\n      <rule in=\"a\" out=\"R\" />\n      <rule in=\"R\" out=\"L\" />\n    </prl>\n    <all in=\"BBB/+++ LLL/BBB *L*/BBB\" out=\"***/*** ***/*** *H*/***\" comment=\"special windows\" /><union symbol=\"2\" values=\"HW\" /><all>\n      <rule in=\"H L L L L L L\" out=\"H H H W W W W\" />\n      <rule in=\"W+ LB LB\" out=\"** ** H*\" />\n    </all>\n    <all>\n      <rule in=\"L 2 L\" out=\"* R *\" />\n      <rule in=\"L2L\" out=\"*R*\" />\n      <rule in=\"2T\" out=\"R*\" />\n      <rule in=\"22/BL\" out=\"RR/**\" />\n      <rule in=\"2L/BL\" out=\"R*/**\" />\n    </all>\n    <all in=\"R2\" out=\"*R\" symmetry=\"(xyz)\" />\n    <all comment=\"mark long enough windows\">\n      <rule in=\"LHHHHHH LHHHHHH LHHHHHH\" out=\"*VVVVVV *VVVVVV *VVVVVV\" />\n      <rule in=\"VH\" out=\"*V\" />\n      <rule in=\"V W W W W\" out=\"* Q Q Q Q\" />\n      <rule in=\"Q W\" out=\"* Q\" />\n    </all>\n    <prl>\n      <rule in=\"H\" out=\"L\" />\n      <rule in=\"W\" out=\"L\" />\n      <rule in=\"R\" out=\"L\" />\n    </prl>\n    <prl comment=\"delete solo windows\">\n      <rule in=\"Q\" out=\"W\" />\n      <rule in=\"V\" out=\"H\" />\n    </prl>\n    <prl in=\"W * H\" out=\"Q * V\" />\n    <all symmetry=\"(xyz)\">\n      <rule in=\"QW\" out=\"*Q\" />\n      <rule in=\"QH\" out=\"*V\" />\n      <rule in=\"VH\" out=\"*V\" />\n      <rule in=\"VW\" out=\"*Q\" />\n    </all>\n    <prl>\n      <rule in=\"W\" out=\"L\" />\n      <rule in=\"H\" out=\"L\" />\n    </prl>\n    <all symmetry=\"(xyz)\" comment=\"fine scaffold\">\n      <rule in=\"OBB/*L*\" out=\"**H/***\" />\n      <rule in=\"HBB/*L*\" out=\"**H/***\" />\n    </all>\n    <union symbol=\"-\" values=\"OH\" color=\"fec601\" /> H\n    <union symbol=\"=\" values=\"OHoB\" />\n    <all>\n      <rule in=\"T-\" out=\"*B\" />\n      <rule in=\"T*/*-\" out=\"**/*B\" />\n      <rule in=\"t-\" out=\"*B\" />\n      <rule in=\"V-\" out=\"*B\" />\n      <rule in=\"LQ/B-\" out=\"**/*B\" />\n      <rule in=\"Q- L* V*\" out=\"*B ** **\" />\n      <rule in=\"VL/B-\" out=\"**/*B\" />\n      <rule in=\"QL/B-\" out=\"**/*B\" />\n    </all>\n    <all in=\"-B-/*L*\" out=\"*o*/***\" symmetry=\"(xyz)\" />\n    <all in=\"BQ oL\" out=\"H* **\" />\n    <markov symmetry=\"(xyz)\" comment=\"connect special windows\">\n      <all in=\"WQ\" out=\"*W\" />\n      <one in=\"GQ\" out=\"*W\" />\n      <path from=\"Q\" to=\"WG\" on=\"HOo\" color=\"G\" inertia=\"True\" />\n      <one in=\"Q\" out=\"W\" />\n    </markov>\n    <prl in=\"W\" out=\"Q\" /><prl>\n      <rule in=\"oQ\" out=\"B*\" />\n      <rule in=\"OQ\" out=\"B*\" />\n      <rule in=\"HQ\" out=\"B*\" />\n    </prl>\n    <convolution neighborhood=\"VonNeumann\" comment=\"mark edges\">\n      <rule in=\"L\" out=\"p\" sum=\"2..3\" values=\"Bo\" />\n    </convolution>\n    <union symbol=\"5\" values=\"LURG\" /><union symbol=\"6\" values=\"LD\" color=\"222034\" /> b\n    <all symmetry=\"(xyz)\">\n      <rule in=\"QL/==\" out=\"*D/**\" />\n      <rule in=\"DL/==\" out=\"*D/**\" />\n    </all>\n    <all>\n      <rule in=\"LL/LL/Qp\" out=\"*D/*D/**\" />\n      <rule in=\"LL/LL/Vp\" out=\"*D/*D/**\" />\n    </all>\n    <markov symmetry=\"(xyz)\" comment=\"marking small or large faces\">\n      <one in=\"RL/==\" out=\"*R/**\" steps=\"200\" />\n      <all in=\"RL/==\" out=\"UU/**\" />\n      <all>\n        <rule in=\"UR/==\" out=\"UU/**\" />\n        <rule in=\"UL/==\" out=\"UU/**\" />\n      </all>\n      <one in=\"L5/=5\" out=\"R*/**\" />\n    </markov>\n    <one in=\"R\" out=\"K\" steps=\"12\" />\n    <all in=\"KR\" out=\"*K\" symmetry=\"(xyz)\" />\n    <prl>\n      <rule in=\"U\" out=\"L\" />\n      <rule in=\"R\" out=\"L\" />\n      <rule in=\"p\" out=\"L\" />\n    </prl>\n    <markov comment=\"surface prefab placement\">\n      <all in=\"hLL/=== hLL/=== hLL/===\" out=\"*h*/*B* *h*/*B* *h*/*B*\" />\n      <all in=\"UW\" out=\"*U\" symmetry=\"(xyz)\" />\n      <one in=\"GW\" out=\"*U\" symmetry=\"(xyz)\" />\n      <path from=\"W\" to=\"GQU\" on=\"HOo\" color=\"G\" inertia=\"True\" />\n      <one>\n        <rule file=\"side_13x5\" legend=\"*o6-W\" />\n        <rule file=\"side_5x13\" legend=\"*o6-W\" />\n        <rule file=\"side_7x11\" legend=\"*o6-W\" />\n        <rule file=\"side_11x7\" legend=\"*o6-W\" />\n        <!--<altrule file=\"side_15x5\" />\n        <altrule file=\"side_5x15\" />\n        <altrule file=\"side_7x13\" />\n        <altrule file=\"side_13x7\" />\n        <altrule file=\"side_17x5\" />\n        <altrule file=\"side_5x17\" />\n        <altrule file=\"side_7x11\" />\n        <altrule file=\"side_11x7\" />-->\n        <rule file=\"roof_7x11\" legend=\"6*o-W\" />\n        <rule file=\"window_3\" legend=\"OoH*BLh\" />\n        <rule file=\"window_3\" legend=\"OoH*BLh\" />\n      </one>\n    </markov>\n    <markov comment=\"intermediate prefab placement\">\n      <all in=\"UW\" out=\"*U\" symmetry=\"(xyz)\" />\n      <one in=\"GW\" out=\"*U\" symmetry=\"(xyz)\" />\n      <path from=\"W\" to=\"GQU\" on=\"HOo\" color=\"G\" inertia=\"True\" />\n      <one>\n        <rule file=\"roof_7x7\" legend=\"6*o-W\" />\n        <rule file=\"roof_5x9\" legend=\"6*o-W\" />\n      </one>\n    </markov>\n    <markov comment=\"secondary prefab placement\">\n      <all in=\"UW\" out=\"*U\" symmetry=\"(xyz)\" />\n      <one in=\"GW\" out=\"*U\" symmetry=\"(xyz)\" />\n      <path from=\"W\" to=\"GQU\" on=\"HOo\" color=\"G\" inertia=\"True\" />\n      <one>\n        <rule file=\"side_3x5\" legend=\"*o6-W\" />\n        <rule file=\"side_5x3\" legend=\"*o6-W\" />\n        <!--<altrule file=\"side_3x7\" />\n        <altrule file=\"side_7x3\" />-->\n        <rule file=\"side_3x5_fake\" legend=\"*oM6-K\" />\n        <rule file=\"side_5x3_fake\" legend=\"*oM6-K\" />\n        <rule file=\"roof_3x5\" legend=\"6*-oW\" />\n        <rule file=\"window_2\" legend=\"OoH*BLh\" />\n        <rule file=\"window_2\" legend=\"OoH*BLh\" />\n        <rule file=\"window_2\" legend=\"OoH*BLh\" />\n        <rule file=\"window_2\" legend=\"OoH*BLh\" />\n        <rule file=\"holes_4\" legend=\"*oL-Bh\" />\n        <rule file=\"small_windows_3\" legend=\"*oL-Bh\" />\n      </one>\n    </markov>\n    <all in=\"BoB UUU\" out=\"*** *u*\" symmetry=\"(xyz)\" />\n    <markov symmetry=\"(xyz)\" comment=\"draw secondary tubes\">\n      <one>\n        <rule in=\"WUu\" out=\"**W\" />\n        <rule in=\"WUU UUU UUu\" out=\"*** *** **W\" />\n      </one>\n      <path from=\"u\" to=\"W\" on=\"OHo\" color=\"G\" inertia=\"True\" longest=\"True\" />\n      <one in=\"W\" out=\"R\" />\n      <one in=\"u\" out=\"W\" />\n    </markov>\n    <prl in=\"R\" out=\"U\" />\n    <prl in=\"M\" out=\"o\" />\n    <all file=\"small_windows_2\" legend=\"o*L-Bh\" />\n    <all file=\"holes_2\" legend=\"o*L-Bh\" />\n    <markov>\n      <one in=\"hLL/=== hLL/=== hLL/===\" out=\"*h*/*B* *h*/*B* *h*/*B*\" />\n      <one file=\"window_1\" legend=\"OoH*BLh\" />\n    </markov>\n    <all in=\"LLLLL LhhhL LhhhL LhhhL LLLLL\" out=\"***** **L** *LLL* **L** *****\" />\n    <one file=\"small_windows_2\" legend=\"o*L-Bh\" />\n    <one>\n      <rule file=\"holes_h_4\" legend=\"*oL-Bh\" />\n      <rule file=\"holes_v_3\" legend=\"*oL-Bh\" />\n    </one>\n    <prl comment=\"delete fine scaffold\">\n      <rule in=\"H\" out=\"B\" />\n      <rule in=\"o\" out=\"B\" />\n      <rule in=\"O\" out=\"B\" />\n      <rule in=\"D\" out=\"L\" />\n      <rule in=\"K\" out=\"L\" />\n    </prl>\n    <prl in=\"L B B\" out=\"R * *\" />\n    <prl in=\"L B\" out=\"* g\" />\n    <prl in=\"R\" out=\"L\" />\n    <all in=\"LLL/BBB LhL/BBB LLL/BBB BBB/BBB\" out=\"***/*** *L*/*** ***/*** ***/***\" />\n    <all comment=\"propagate holes\">\n      <rule in=\"LLLLL/BBBBB LhLLL/BBBBB LLLLL/BBBBB\" out=\"*****/***** ***h*/***** *****/*****\" />\n      <rule in=\"LLL/BBB LLL/BBB LLL/BBB LhL/BBB LLL/BBB\" out=\"***/*** *h*/*** ***/*** ***/*** ***/***\" />\n    </all>\n    <all in=\"BB BL\" out=\"a* **\" comment=\"top edges\" />\n    <all in=\"aB/Ba BL/BB\" out=\"**/a* **/**\" />\n    <all in=\"hhh/BaB\" out=\"RRR/***\" />\n    <all>\n      <rule in=\"hh/Ba\" out=\"RR/**\" />\n      <rule in=\"h R\" out=\"R *\" />\n    </all>\n    <prl in=\"R\" out=\"L\" />\n    <all in=\"LhL LhL LhL\" out=\"*L* *L* *L*\" />\n    <one in=\"Lhhhhh Lhhhhh Lhhhhh\" out=\"***L** ***L** ***L**\" />\n    <all in=\"hhhLh/BBBBB\" out=\"***u*/*****\" />\n    <prl in=\"u\" out=\"h\" />\n    <one in=\"LLLL LhhL LhhL LhhL LLLL\" out=\"**** *LL* *LL* *LL* ****\" steps=\"7\" comment=\"remove some windows\" />\n    <one in=\"LLL LhL LLL\" out=\"*** *L* ***\" steps=\"3\" />\n    <one in=\"LLL LhL LhL LLL\" out=\"*** *L* *L* ***\" steps=\"2\" />\n    <!--<altone in=\"LLLL LhhL LhhL LhhL LLLL\" out=\"**** *LL* *LL* *LL* ****\" steps=\"22\" comment=\"remove some windows\" />\n    <altone in=\"LLL LhL LLL\" out=\"*** *L* ***\" steps=\"10\" />\n    <altone in=\"LLL LhL LhL LLL\" out=\"*** *L* *L* ***\" steps=\"4\" />-->\n    <all steps=\"1\" comment=\"shorten panels\">\n      <rule in=\"L T\" out=\"* L\" />\n      <rule in=\"L t\" out=\"* L\" />\n      <rule in=\"T L\" out=\"L *\" />\n      <rule in=\"t L\" out=\"L *\" />\n    </all>\n    <all steps=\"1\">\n      <rule in=\"L t\" out=\"* T\" />\n      <rule in=\"t L\" out=\"T *\" />\n    </all>\n    <markov>\n      <all in=\"DU\" out=\"*D\" />\n      <one in=\"UU/UU\" out=\"DD/DD\" />\n    </markov>\n    <convolution neighborhood=\"VonNeumann\" steps=\"1\" comment=\"mark radiator edges\">\n      <rule in=\"U\" out=\"R\" sum=\"2..3\" values=\"U\" />\n    </convolution>\n    <markov symmetry=\"(xyz)\">\n      <all>\n        <rule in=\"uU UU\" out=\"** *u\" />\n        <rule in=\"BU UU\" out=\"** *B\" />\n        <rule in=\"uUU\" out=\"**u\" />\n        <rule in=\"BUU\" out=\"**B\" />\n      </all>\n      <one>\n        <rule in=\"RR RU\" out=\"** *u\" />\n        <rule in=\"RR RU\" out=\"** *B\" />\n      </one>\n    </markov>\n    <prl in=\"R\" out=\"U\" />\n    <all steps=\"1\">\n      <rule in=\"LTB\" out=\"*LT\" />\n      <rule in=\"LtB\" out=\"*Lt\" />\n    </all>\n    <union symbol=\"0\" values=\"LH\" />\n    <all in=\"0hB 0hB 0hB\" out=\"HB* HB* Haa\" comment=\"deepen windows\" />\n    <all in=\"0hB 0hB\" out=\"HB* Haa\" />\n    <all in=\"0hB\" out=\"HB*\" />\n    <all comment=\"deepen special windows\">\n      <rule in=\"QGB\" out=\"*B*\" />\n      <rule in=\"LQB\" out=\"B**\" />\n      <rule in=\"LVB\" out=\"B**\" />\n    </all>\n    <union symbol=\"#\" values=\"Lh\" />\n    <all in=\"#BV\" out=\"h*B\" />\n    <all in=\"BV/VL\" out=\"*B/B*\" />\n    <all>\n      <rule in=\"Lh/LB\" out=\"*R/**\" />\n      <rule in=\"Bh/BB\" out=\"*R/**\" />\n    </all>\n    <one in=\"Rhhhhh Rhhhhh Rhhhhh\" out=\"***R** ***R** ***R**\" />\n    <one comment=\"merge some special windows\">\n      <rule in=\"hRh hRh hRh\" out=\"*h* *h* *h*\" />\n      <rule in=\"hRh hRh hRh\" out=\"*M* *M* *M*\" />\n      <rule in=\"hRh hRh hRh\" out=\"*M* *M* *M*\" />\n    </one>\n    <all in=\"0hB\" out=\"HB*\" comment=\"secondary deepening\" />\n    <prl>\n      <rule in=\"R\" out=\"L\" />\n      <rule in=\"M\" out=\"L\" />\n    </prl>\n    <all in=\"BBBB/BBBB/BBBB BaBB/BGGG/BaBB BBLL/BGLL/BBLL\" out=\"****/GGG*/**** ****/Ga**/**** ****/G***/****\" comment=\"resolve some conflicts between tubes and borders\" />\n    <prl comment=\"color bottoms\">\n      <rule in=\"L B B B B\" out=\"g * * * *\" />\n      <rule in=\"L G B B B\" out=\"g * * * *\" />\n    </prl>\n    <all in=\"LgL\" out=\"*L*\" />\n    <all in=\"BQ*/BLQ/BBB BQ*/BLQ/BBB BQ*/BLQ/BBB BQ*/BLQ/BBB\" out=\"***/***/*** ***/***/*** ***/L**/LL* ***/***/***\" />\n    <union symbol=\"_\" values=\"Tt\" />\n    <markov comment=\"put panel holders\">\n      <one>\n        <rule in=\"__/OB\" out=\"**/*O\" />\n        <rule in=\"TB/OB\" out=\"*O/*O\" />\n        <rule in=\"OT/OB\" out=\"**/*O\" />\n      </one>\n      <one>\n        <rule in=\"BBB/BBB BBT/BBB BBT/BBB BBT/BBB\" out=\"***/*** ***/*** *O*/*OO ***/***\" />\n        <rule in=\"BBB/BBB BBT/BBB BBT/BBB BBT/BBB\" out=\"***/*** ***/*** ***/*** *O*/*OO\" />\n        <rule in=\"BBT/BBB BBT/BBB BBT/BBB BBB/BBB\" out=\"***/*** *O*/*OO ***/*** ***/***\" />\n        <rule in=\"BBT/BBB BBT/BBB BBT/BBB BBB/BBB\" out=\"*O*/*OO ***/*** ***/*** ***/***\" />\n      </one>\n    </markov>\n    <one steps=\"1\">\n      <rule file=\"cut_1\" legend=\"BTt*\" />\n      <rule file=\"cut_2\" legend=\"BTt*\" />\n    </one>\n    <markov>\n      <one>\n        <rule in=\"__/OB\" out=\"**/*O\" />\n        <rule in=\"TB/OB\" out=\"*O/*O\" />\n      </one>\n      <one>\n        <rule in=\"BBB/BBB BBT/BBB BBT/BBB BBT/BBB\" out=\"***/*** ***/*** *O*/*OO ***/***\" />\n        <rule in=\"BBB/BBB BBT/BBB BBT/BBB BBT/BBB\" out=\"***/*** ***/*** ***/*** *O*/*OO\" />\n        <rule in=\"BBT/BBB BBT/BBB BBT/BBB BBB/BBB\" out=\"***/*** *O*/*OO ***/*** ***/***\" />\n        <rule in=\"BBT/BBB BBT/BBB BBT/BBB BBB/BBB\" out=\"*O*/*OO ***/*** ***/*** ***/***\" />\n      </one>\n    </markov>\n    <all in=\"OT/OB\" out=\"**/*O\" />\n    <all in=\"TtT/OOO\" out=\"***/BBB\" />\n    <one steps=\"2\">\n      <rule file=\"bracing_v_3\" legend=\"*LG\" />\n      <rule file=\"bracing_h_3\" legend=\"*GL\" />\n    </one>\n    <one steps=\"3\">\n      <rule file=\"bracing_v_2\" legend=\"*LG\" />\n      <rule file=\"bracing_h_2\" legend=\"*GL\" />\n    </one>\n    <one steps=\"16\">\n      <rule file=\"bracing_v_1\" legend=\"*LG\" />\n      <rule file=\"bracing_h_1\" legend=\"*GL\" />\n      <rule in=\"*LLL/BBGL/BBBL/BBB* *LLL/BBGL/BBBL/BBB* *LLL/BBGL/BBBL/BBB*\" out=\"****/****/****/**** ****/*L**/**L*/**** ****/****/****/****\" />\n    </one>\n    <all>\n      <rule file=\"roof_thing_7x11\" legend=\"DL*a\" />\n      <rule file=\"roof_thing_2\" legend=\"DLRao*\" />\n    </all>\n    <all>\n      <rule file=\"roof_thing_1\" legend=\"DLRao*\" />\n      <rule file=\"roof_thing_5x9\" legend=\"DL*a\" />\n    </all>\n    <prl in=\"o *\" out=\"B B\" />\n    <all in=\"GRL\" out=\"*G*\" />\n    <all in=\"BL/GR\" out=\"**/*G\" />\n    <prl in=\"R\" out=\"B\" />\n    <prl>\n      <rule in=\"G\" out=\"D\" />\n      <rule in=\"Q\" out=\"W\" />\n      <rule in=\"T\" out=\"W\" />\n      <rule in=\"t\" out=\"a\" />\n      <rule in=\"O\" out=\"L\" />\n      <rule in=\"U\" out=\"a\" />\n      <rule in=\"u\" out=\"D\" />\n      <rule in=\"g\" out=\"l\" />\n      <rule in=\"H\" out=\"Y\" />\n    </prl>\n  </map>\n</sequence>\n\n<!--\nSome rendered outputs: https://github.com/mxgmn/Blog/blob/master/CarmaTower.md\n\nTODO: a more modular example in this style.\n-->\n"
  },
  {
    "path": "models/Cave.xml",
    "content": "<sequence values=\"DA\">\n  <prl in=\"***/*D*/***\" out=\"***/*A*/***\"/>\n  <prl in=\"A\" out=\"D\" p=\"0.435\" steps=\"1\"/>\n  <convolution neighborhood=\"Moore\">\n    <rule in=\"A\" out=\"D\" sum=\"5..8\" values=\"D\"/>\n    <rule in=\"D\" out=\"A\" sum=\"6..8\" values=\"A\"/>\n  </convolution>\n  <all in=\"AD/DA\" out=\"AA/DA\"/>\n</sequence>\n"
  },
  {
    "path": "models/CaveContour.xml",
    "content": "<sequence values=\"EFDA\">\n  <prl in=\"***/*E*/***\" out=\"***/*F*/***\"/>\n  <prl in=\"F\" out=\"E\" p=\"0.5\" steps=\"1\"/>\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"F\" out=\"E\" sum=\"5..8\" values=\"E\"/>\n    <rule in=\"E\" out=\"F\" sum=\"5..8\" values=\"F\"/>\n  </convolution>\n  <all>\n    <rule in=\"EF\" out=\"*A\"/>\n    <rule in=\"E*/*F\" out=\"**/*A\"/>\n  </all>\n  <all>\n    <rule in=\"AE\" out=\"*D\"/>\n    <rule in=\"A*/*E\" out=\"**/*D\"/>\n  </all>\n  <prl>\n    <rule in=\"E\" out=\"F\"/>\n    <rule in=\"A\" out=\"D\"/>\n  </prl>\n  <prl in=\"F\" out=\"A\"/>\n  <all in=\"AD/DA\" out=\"D*/**\"/>\n</sequence>\n"
  },
  {
    "path": "models/CentralCrawlers.xml",
    "content": "<sequence values=\"BWUR\" origin=\"True\">\n  <one in=\"BB\" out=\"UR\" steps=\"10\"/>\n  <all temperature=\"5.0\">\n    <rule in=\"URB\" out=\"BUR\"/>\n    <rule in=\"UR/*B\" out=\"BU/*R\"/>\n    <rule in=\"W\" out=\"B\"/>\n    <field for=\"R\" to=\"W\" on=\"B\" recompute=\"False\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/CentralSAW.xml",
    "content": "<one values=\"BRW\" origin=\"True\" in=\"RBB\" out=\"WWR\" temperature=\"0.1\">\n  <field for=\"W\" to=\"R\" on=\"B\" recompute=\"False\"/>\n</one>\n"
  },
  {
    "path": "models/ChainDungeon.xml",
    "content": "<sequence values=\"DABWO\">\n  <all in=\"***/*D*/***\" out=\"***/*B*/***\"/>\n  <one in=\"BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/**BBB***/**BBB***/**BBB***\"\n      out=\"DDDDDDDD/DOOOOOOD/DOOOOOOD/DOOOOOOD/DOOOOOOD/DDDODDDD/**DOD***/**DWD***/********\" steps=\"2\"/>\n  <markov>\n    <all in=\"AW\" out=\"AA\"/>\n    <path from=\"A\" to=\"W\" on=\"B\" color=\"A\" inertia=\"False\"/>\n    <one in=\"W\" out=\"A\"/>\n  </markov>\n  <convchain sample=\"Room\" on=\"B\" black=\"D\" white=\"A\" n=\"3\" steps=\"10\"/>\n  <all in=\"AD/DA\" out=\"AD/AA\"/>\n  <all in=\"DDD/DAD\" out=\"DDD/DDD\"/>\n  <all in=\"OA\" out=\"OO\"/>\n</sequence>\n"
  },
  {
    "path": "models/ChainDungeonMaze.xml",
    "content": "<sequence values=\"DABWO\">\n  <all in=\"***/*D*/***\" out=\"***/*B*/***\"/>\n  <one in=\"BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/**BBB***/**BBB***/**BBB***\"\n      out=\"DDDDDDDD/DOOOOOOD/DOOOOOOD/DOOOOOOD/DOOOOOOD/DDDODDDD/**DOD***/**DWD***/********\" steps=\"10\"/>\n  <markov>\n    <all in=\"AW\" out=\"AA\"/>\n    <path from=\"A\" to=\"W\" on=\"B\" color=\"A\" inertia=\"True\"/>\n    <one in=\"W\" out=\"A\"/>\n  </markov>\n  <convchain sample=\"Maze\" on=\"B\" black=\"D\" white=\"A\" n=\"2\" steps=\"10\"/>\n  <all in=\"AD/DA\" out=\"AD/AA\"/>\n  <all in=\"OA\" out=\"*O\"/>\n</sequence>\n\n<!--\nTODO: Add more room shapes.\n-->\n"
  },
  {
    "path": "models/ChainMaze.xml",
    "content": "<sequence values=\"BDA\">\n  <convchain sample=\"Maze\" on=\"B\" black=\"D\" white=\"A\" n=\"2\" steps=\"10\"/>\n  <all in=\"AD/DA\" out=\"AD/AA\"/>\n</sequence>\n"
  },
  {
    "path": "models/Chase.xml",
    "content": "<sequence values=\"BRW\" periodic=\"True\">\n  <one in=\"B\" out=\"R\" steps=\"20\"/>\n  <one in=\"B\" out=\"W\" steps=\"20\"/>\n  <all temperature=\"1.0\">\n    <rule in=\"RB\" out=\"BR\"/>\n    <rule in=\"WB\" out=\"BW\"/>\n    <rule in=\"RW\" out=\"BR\"/>\n    <field for=\"W\" from=\"R\" on=\"BW\" recompute=\"True\"/>\n    <field for=\"R\" to=\"W\" on=\"BR\" recompute=\"True\"/>\n  </all>\n  <all in=\"RB\" out=\"BR\"/>\n</sequence>\n\n<!--\nEvery red predator tries to catch the closest white creature. White creatures try to run away from red predators.\n\nNonzero temperature causes creatures to make small mistakes.\n-->\n"
  },
  {
    "path": "models/Circuit.xml",
    "content": "<sequence values=\"BtEDGANYOWU\" origin=\"True\">\n  <prl in=\"***/*B*/***\" out=\"***/*E*/***\"/>\n  <all in=\"tEE\" out=\"**t\"/>\n  <one file=\"Chip\" legend=\"*tEADN\" steps=\"2\"/>\n  <markov>\n    <all>\n      <rule in=\"EG\" out=\"tG\"/>\n      <rule in=\"E*/*G\" out=\"t*/*G\"/>\n    </all>\n    <path from=\"A\" to=\"U\" on=\"E\" color=\"G\" inertia=\"True\"/>\n    <all in=\"A\" out=\"U\"/>\n    <one file=\"Chip\" legend=\"*tEADN\"/>\n  </markov>\n  <prl in=\"B\" out=\"E\"/>\n  <all>\n    <rule in=\"EU\" out=\"EE\"/>\n    <rule in=\"EEE/ttE/EEE\" out=\"***/E**/***\"/>\n  </all>\n  <markov>\n    <one>\n      <rule in=\"NUAU\" out=\"NAGG\"/>\n      <rule in=\"UAG\" out=\"GUA\"/>\n      <rule in=\"UA/*G\" out=\"GU/*A\"/>\n    </one>\n    <one in=\"UG\" out=\"UA\"/>\n  </markov>\n  <all in=\"NG\" out=\"NW\"/>\n  <all>\n    <rule in=\"ND\" out=\"DN\"/>\n    <rule in=\"NWGG\" out=\"DWYO\"/>\n    <rule in=\"YOG\" out=\"GYO\"/>\n    <rule in=\"YO/*G\" out=\"GY/*O\"/>\n    <rule in=\"YOAD\" out=\"GGAN\"/>\n  </all>\n</sequence>\n\n<!--\nThis is not how circuits actually work.\n\nTODO: Make a generator of WireWorld networks.\n-->\n"
  },
  {
    "path": "models/ClosedSurface.xml",
    "content": "<sequence values=\"BW\">\n  <all in=\"B\" out=\"W\"/>\n  <all in=\"***/***/*** ***/*W*/*** ***/***/***\" out=\"***/***/*** ***/*B*/*** ***/***/***\"/>\n  <wfc values=\"BW\" tileset=\"Surface\">\n    <rule in=\"W\" out=\"Empty\"/>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/ColoredKnots.xml",
    "content": "<sequence values=\"BRW\">\n  <all in=\"B\" out=\"R\"/>\n  <all in=\"***/***/*** ***/*R*/*** ***/***/***\" out=\"***/***/*** ***/*W*/*** ***/***/***\"/>\n  <wfc values=\"BWIPENDAROYGUCKF\" tileset=\"Knots3D\" tiles=\"Knots3D/3\">\n    <rule in=\"R\" out=\"Empty\"/>\n    <rule in=\"W\" out=\"Line|Turn|Empty\"/>\n    <markov>\n      <all>\n        <rule in=\"IW\" out=\"*I\"/>\n        <rule in=\"PW\" out=\"*P\"/>\n        <rule in=\"EW\" out=\"*E\"/>\n        <rule in=\"NW\" out=\"*N\"/>\n        <rule in=\"DW\" out=\"*D\"/>\n        <rule in=\"AW\" out=\"*A\"/>\n        <rule in=\"RW\" out=\"*R\"/>\n        <rule in=\"OW\" out=\"*O\"/>\n        <rule in=\"YW\" out=\"*Y\"/>\n        <rule in=\"GW\" out=\"*G\"/>\n        <rule in=\"UW\" out=\"*U\"/>\n        <rule in=\"CW\" out=\"*C\"/>\n        <rule in=\"KW\" out=\"*K\"/>\n        <rule in=\"FW\" out=\"*F\"/>\n      </all>\n      <one>\n        <rule in=\"W\" out=\"I\"/>\n        <rule in=\"W\" out=\"P\"/>\n        <rule in=\"W\" out=\"E\"/>\n        <rule in=\"W\" out=\"N\"/>\n        <rule in=\"W\" out=\"D\"/>\n        <rule in=\"W\" out=\"A\"/>\n        <rule in=\"W\" out=\"R\"/>\n        <rule in=\"W\" out=\"O\"/>\n        <rule in=\"W\" out=\"Y\"/>\n        <rule in=\"W\" out=\"G\"/>\n        <rule in=\"W\" out=\"U\"/>\n        <rule in=\"W\" out=\"C\"/>\n        <rule in=\"W\" out=\"K\"/>\n        <rule in=\"W\" out=\"F\"/>\n      </one>\n    </markov>\n  </wfc>\n</sequence>\n\n<!--\nTODO: To avoid listing rules for all colors, introduce logical expressions like u=(!W)&(!B). Then we can write W=u and maybe even uW=uu.\n-->\n"
  },
  {
    "path": "models/CompleteSAW.xml",
    "content": "<sequence values=\"BRWD\" origin=\"True\">\n  <all>\n    <rule in=\"R*B\" out=\"**D\"/>\n    <rule in=\"D*B\" out=\"**D\"/>\n  </all>\n  <one in=\"RBB\" out=\"WWR\" search=\"True\" limit=\"1000\" depthCoefficient=\"-1.0\">\n    <observe value=\"D\" from=\"B\" to=\"RW\"/>\n    <observe value=\"R\" to=\"W\"/>\n    <observe value=\"B\" to=\"BW\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/CompleteSAWSmart.xml",
    "content": "<sequence values=\"BDIORGW\" origin=\"True\">\n  <all>\n    <rule in=\"I*B\" out=\"**D\"/>\n    <rule in=\"D*B\" out=\"**I\"/>\n  </all>\n  <one in=\"DBI\" out=\"OR*\" steps=\"1\"/>\n  <one in=\"D\" out=\"G\" steps=\"1\"/>\n  <all in=\"D\" out=\"I\"/>\n  <one search=\"True\" limit=\"300\" depthCoefficient=\"-1.0\">\n    <rule in=\"ORBBB\" out=\"WWOR*\"/>\n    <rule in=\"ORBBG\" out=\"WWWWO\"/>\n    <rule in=\"BBB/R**/O**\" out=\"OR*/W**/W**\"/>\n    <rule in=\"BBG/R**/O**\" out=\"WWO/W**/W**\"/>\n\n    <observe value=\"I\" from=\"B\" to=\"W\"/>\n    <observe value=\"O\" to=\"W\"/>\n    <observe value=\"R\" to=\"W\"/>\n    <observe value=\"G\" to=\"O\"/>\n    <observe value=\"B\" to=\"BW\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/ConnectedCaves.xml",
    "content": "<sequence values=\"DAW\">\n  <prl in=\"***/*D*/***\" out=\"***/*A*/***\"/>\n  <prl in=\"A\" out=\"D\" p=\"0.5\" steps=\"1\"/>\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"A\" out=\"D\" sum=\"5..8\" values=\"D\"/>\n    <rule in=\"D\" out=\"A\" sum=\"5..8\" values=\"A\"/>\n  </convolution>\n  <markov>\n    <all in=\"WA\" out=\"WW\"/>\n    <path from=\"W\" to=\"A\" on=\"D\" color=\"A\"/>\n    <one in=\"A\" out=\"W\"/>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/ConstrainedCaves.xml",
    "content": "<sequence values=\"DABWOP\">\n  <prl in=\"***/*D*/***\" out=\"***/*B*/***\"/>\n  <one file=\"ConstrainedRoom\" legend=\"BDWO*\" steps=\"5\"/>\n  <markov>\n    <all in=\"AW\" out=\"AA\"/>\n    <path from=\"A\" to=\"W\" on=\"B\" color=\"A\" inertia=\"True\"/>\n    <one in=\"W\" out=\"A\"/>\n  </markov>\n  <prl in=\"B\" out=\"P\" p=\"0.4545\" steps=\"1\"/>\n  <convolution neighborhood=\"Moore\">\n    <rule in=\"P\" out=\"B\" sum=\"5..8\" values=\"BD\"/>\n    <rule in=\"B\" out=\"P\" sum=\"5..8\" values=\"PA\"/>\n  </convolution>\n  <prl in=\"D\" out=\"B\"/>\n</sequence>\n"
  },
  {
    "path": "models/Counting.xml",
    "content": "<convolution values=\"BRGUY\" neighborhood=\"VonNeumann\">\n  <rule in=\"B\" out=\"R\" sum=\"3\" values=\"B\"/>\n  <rule in=\"B\" out=\"G\" sum=\"4\" values=\"B\"/>\n  <rule in=\"B\" out=\"U\" sum=\"5\" values=\"B\"/>\n  <rule in=\"B\" out=\"Y\" sum=\"6\" values=\"B\"/>\n</convolution>\n"
  },
  {
    "path": "models/Coupling.xml",
    "content": "<sequence values=\"EYUD\">\n  <one in=\"E\" out=\"Y\" steps=\"30\"/>\n  <one in=\"E\" out=\"U\" steps=\"30\"/>\n  <all>\n    <rule in=\"YE\" out=\"DY\"/>\n    <rule in=\"UE\" out=\"DU\"/>\n    <rule in=\"YU\" out=\"DD\"/>\n    <rule in=\"Y*/*U\" out=\"D*/*D\"/>\n\n    <field for=\"Y\" to=\"U\" on=\"YUE\" recompute=\"True\"/>\n    <field for=\"U\" to=\"Y\" on=\"YUE\" recompute=\"True\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/Crawlers.xml",
    "content": "<sequence values=\"BUR\">\n  <one in=\"BB\" out=\"UR\" steps=\"12\"/>\n  <all>\n    <rule in=\"URB\" out=\"BUR\"/>\n    <rule in=\"UR/*B\" out=\"BU/*R\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/CrawlersChase.xml",
    "content": "<sequence values=\"BWRUGE\">\n  <one in=\"BB\" out=\"UR\" steps=\"12\"/>\n  <markov>\n    <all>\n      <rule in=\"URB\" out=\"BUR\"/>\n      <rule in=\"UR/*B\" out=\"BU/*R\"/>\n      <rule in=\"URW\" out=\"BEG\"/>\n      <rule in=\"UR/*W\" out=\"BE/*G\"/>\n      <rule in=\"EG/BB\" out=\"UR/RU\"/>\n      <rule in=\"BB/BW\" out=\"**/WB\"/>\n\n      <field for=\"R\" to=\"W\" on=\"B\" recompute=\"True\" essential=\"True\"/>\n      <field for=\"W\" from=\"R\" on=\"B\" recompute=\"True\"/>\n    </all>\n    <one in=\"B\" out=\"W\"/>\n  </markov>\n</sequence>\n\n<!--\nWhite creature tries to run away from a pack of crawlers. As usual, crawlers can break.\n-->\n"
  },
  {
    "path": "models/CrossCountry.xml",
    "content": "<sequence values=\"FUOZGWNR\">\n  <prl in=\"***/*F*/***\" out=\"***/*U*/***\"/>\n  <prl in=\"U\" out=\"F\" p=\"0.435\" steps=\"1\"/>\n  <convolution neighborhood=\"Moore\">\n    <rule in=\"U\" out=\"F\" sum=\"5..8\" values=\"F\"/>\n    <rule in=\"F\" out=\"U\" sum=\"6..8\" values=\"U\"/>\n  </convolution>\n  <all in=\"UF/FU\" out=\"UU/FU\"/>\n  <one in=\"F\" out=\"O\" steps=\"1\"><field for=\"O\" from=\"U\" on=\"F\"/></one>\n  <one in=\"FFFFFFF/FFFFFFF/FFFFFFF/FFFFFFF/FFFFFFF/FFFFFFF/FFFFFFF\" out=\"*******/*******/*******/***G***/*******/*******/*******\" steps=\"1\"><field for=\"G\" from=\"O\" on=\"F\"/></one>\n  <one>\n    <rule in=\"OF\" out=\"NO\"/>\n    <rule in=\"OU\" out=\"NZ\"/>\n    <rule in=\"RU\" out=\"WZ\"/>\n    <rule in=\"RF\" out=\"WO\"/>\n    <rule in=\"Z\" out=\"R\"/>\n\n    <observe value=\"G\" from=\"F\" to=\"O\"/>\n    <observe value=\"F\" to=\"FN\"/>\n    <observe value=\"U\" to=\"UW\"/>\n    <observe value=\"O\" to=\"N\"/>\n  </one>\n</sequence>\n\n<!--\nFinds the shortest path in the rugged terrain. Moving through lakes is 2 times slower than moving through plains. See also SnellLaw.xml.\n-->\n"
  },
  {
    "path": "models/Cycles.xml",
    "content": "<sequence values=\"BWA\" origin=\"True\">\n  <one in=\"WBB\" out=\"WAW\"/>\n  <one in=\"WBW\" out=\"*W*\" steps=\"2\"/>\n  <all in=\"A\" out=\"W\"/>\n  <all in=\"BBB/BWB\" out=\"***/*B*\"/>\n</sequence>\n"
  },
  {
    "path": "models/DenseSAW.xml",
    "content": "<sequence values=\"BRW\" origin=\"True\">\n  <one in=\"RBB\" out=\"WWR\" steps=\"1\"/>\n  <one in=\"RBB\" out=\"WWR\" temperature=\"0.3\">\n    <field for=\"R\" to=\"W\" on=\"B\" recompute=\"True\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/DiagonalPath.xml",
    "content": "<sequence values=\"BRGW\">\n  <one in=\"B\" out=\"R\" steps=\"1\"/>\n  <one in=\"B\" out=\"G\" temperature=\"4.0\" steps=\"1\">\n    <field for=\"G\" from=\"R\" on=\"B\"/>\n  </one>  \n  <one>\n    <rule in=\"RB\" out=\"WR\"/>\n    <rule in=\"R*/*B\" out=\"W*/*R\"/>\n    <observe value=\"G\" from=\"B\" to=\"R\"/>\n    <observe value=\"R\" to=\"W\"/>\n    <observe value=\"B\" to=\"BW\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/Digger.xml",
    "content": "<markov values=\"BRW\" origin=\"True\">\n  <one in=\"RB\" out=\"WR\"/>\n  <one in=\"RW\" out=\"WR\"/>\n</markov>\n"
  },
  {
    "path": "models/DijkstraDungeon.xml",
    "content": "<sequence values=\"BGWRDU\">\n  <union symbol=\"?\" values=\"BD\"/>\n  <one file=\"DijkstraRoom\" legend=\"?DW*BR\" steps=\"1\"/>\n  <all in=\"W\" out=\"G\"/>\n  <markov>\n    <one in=\"GR\" out=\"GG\"/>\n    <path from=\"R\" to=\"G\" on=\"B\" color=\"G\" inertia=\"True\"/>\n    <one file=\"DijkstraRoom\" legend=\"?DW*BR\"/>\n  </markov>\n  <all>\n    <rule in=\"G\" out=\"W\"/>\n    <rule in=\"D\" out=\"B\"/>\n  </all>\n  <all in=\"RW\" out=\"*R\"/>\n  <all in=\"R\" out=\"B\"/>\n</sequence>\n"
  },
  {
    "path": "models/Division.xml",
    "content": "<sequence values=\"WBUuAaI\" origin=\"true\">\n    <prl in=\"***/*W*/***\" out=\"***/*U*/***\"/>\n    <prl in=\"W\" out=\"I\"/>\n    <prl in=\"U\" out=\"W\"/>\n\n    <!-- rounder origin target -->\n    <prl in=\"WWWWW/WWWWW/WWWWW/WWWWW/BWWWW\" out=\"B***B/B**B*/B*B**/BB***/B****\" steps=\"1\"/>\n\n    <!-- clean division, would be a lot more versatile if one of the fields could be given a constant value instead of needing to set up two references-->\n    <all temperature=\"0\" steps=\"1\">\n        <rule in=\"W\" out=\"U\"/>\n        <rule in=\"W\" out=\"u\"/>\n        <field for=\"U\" to=\"I\" on=\"W\" essential=\"False\" recompute=\"True\"/>\n        <field for=\"u\" to=\"B\" on=\"W\" essential=\"False\" recompute=\"True\"/>\n    </all>\n\n    <!-- blur -->\n    <all temperature=\"2\" steps=\"1\">\n        <rule in=\"U\" out=\"U\"/>\n        <rule in=\"U\" out=\"A\"/>\n        <rule in=\"u\" out=\"a\"/>\n        <rule in=\"u\" out=\"u\"/>\n        <field for=\"U\" to=\"I\" on=\"U\" essential=\"False\" recompute=\"True\"/>\n        <field for=\"A\" to=\"u\" on=\"U\" essential=\"False\" recompute=\"True\"/>\n        <field for=\"a\" to=\"U\" on=\"u\" essential=\"False\" recompute=\"True\"/>\n        <field for=\"u\" to=\"B\" on=\"u\" essential=\"False\" recompute=\"True\"/>\n    </all>\n\n    <!-- Recursive contract/dilate. Very handy for edge finding on byzantine shapes. Strays left behind on noisy edges can be cleaned up but can also be useful sometimes.-->\n    <all temperature=\"0\" steps=\"2\">\n        <rule in=\"A\" out=\"U\"/>\n        <rule in=\"A\" out=\"A\"/>\n        <rule in=\"a\" out=\"a\"/>\n        <rule in=\"a\" out=\"u\"/>\n        <field for=\"U\" to=\"U\" on=\"A\" essential=\"False\" recompute=\"True\"/>\n        <field for=\"A\" to=\"a\" on=\"A\" essential=\"False\" recompute=\"True\"/>\n        <field for=\"a\" to=\"A\" on=\"a\" essential=\"False\" recompute=\"True\"/>\n        <field for=\"u\" to=\"u\" on=\"a\" essential=\"False\" recompute=\"True\"/>\n    </all>\n    <!-- There's a bunch of ways to use this, especially when you start adding obstacles and noise fields to give \"flow resistance\" in different directions.-->\n    <!-- Adding min, max and constant options would hugely expand the potential here, opening up potential for fuzzy calculations in the vein of Gaussian, Laplacian, and Lagrangian methods.-->\n</sequence>\n"
  },
  {
    "path": "models/DualRetraction.xml",
    "content": "<sequence values=\"BRUW\" origin=\"True\">\n  <one>\n    <rule in=\"BB*BB/BBBBB/BBBBR/BBBBB/BB*BB\" out=\"**R**/*WWW*/*WWWU/*WWW*/**R**\"/>\n    <rule in=\"BBBBB/BBBBB/RBBB*/BBBBB/BBBBB\" out=\"*****/*****/UWWWR/*****/*****\"/>\n  </one>\n  <all>\n    <rule in=\"R\" out=\"W\"/>\n    <rule in=\"U\" out=\"W\"/>\n  </all>\n  <all>\n    <rule in=\"BBB/BWB\" out=\"BBB/BBB\"/>\n    <rule in=\"WWW/WBW\" out=\"WWW/WWW\"/>\n  </all>\n  <all in=\"BBB/WWB/WWB\" out=\"***/*B*/***\"/>\n</sequence>\n"
  },
  {
    "path": "models/DualRetraction3D.xml",
    "content": "<sequence values=\"BRUWG\" origin=\"True\" folder=\"DualRetraction\">\n  <one>\n    <rule file=\"Room1\" legend=\"B*RUW\"/>\n    <rule file=\"Room2\" legend=\"B*RGWU\"/>\n  </one>\n  <all>\n    <rule in=\"R\" out=\"W\"/>\n    <rule in=\"U\" out=\"W\"/>\n  </all>\n  <all>\n    <rule in=\"BBB/BBB/BBB BBB/BWB/BBB\" out=\"***/***/*** ***/*B*/***\"/>\n    <rule in=\"WWW/GBG\" out=\"***/*G*\"/>\n    <rule in=\"GGG/GBG\" out=\"***/*G*\"/>\n    <rule in=\"BBB/BBB/BBB GGG/GWG/GGG\" out=\"***/***/*** ***/*G*/***\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/DungeonGrowth.xml",
    "content": "<sequence values=\"WRBUPY\" origin=\"True\" folder=\"DungeonGrowth\">\n  <union symbol=\"?\" values=\"BR\"/>\n  <!--<prl in=\"***/*W*/***\" out=\"***/*B*/***\"/>-->\n  <prl in=\"*****/*****/**W**/*****/*****\" out=\"*****/*****/**B**/*****/*****\"/>\n  <one>\n    <rule file=\"Room1\"  legend=\"*?WRBPU\"/>\n    <rule file=\"Room2\"  legend=\"*?WRBPU\"/>\n    <rule file=\"Room3\"  legend=\"*?WRBPU\"/>\n    <rule file=\"Room4\"  legend=\"*?WRBPU\"/>\n    <rule file=\"Room5\"  legend=\"*?WRBPU\"/>\n    <rule file=\"Room7\"  legend=\"*?WRBPU\"/>\n    <rule file=\"Room13\" legend=\"*?WRBPU\"/>\n    <rule file=\"Room8\"  legend=\"*?WRBPU\"/>\n    <rule file=\"Room9\"  legend=\"*WBPR?U\"/>\n    <rule file=\"Room10\" legend=\"*W?BRPU\"/>\n    <rule file=\"Room11\" legend=\"*?WRBPU\"/>\n    <rule file=\"Room12\" legend=\"*WBP?RU\"/>\n    <rule file=\"Room14\" legend=\"*?WRBPU\"/>\n    <rule file=\"Room15\" legend=\"*WBPRU?\"/>\n    <rule file=\"Room16\" legend=\"*?WRBPU\"/>\n    <rule file=\"Room17\" legend=\"*?WRBPU\"/>\n    <rule file=\"Room18\" legend=\"*?WRBPU\"/>\n    <rule file=\"Room6\"  legend=\"*?WRBPU\"/>\n  </one>\n  <one in=\"WUW/BBB\" out=\"WRW/BBB\"/>\n  <all in=\"U\" out=\"P\"/>\n  <markov>\n    <all>\n      <rule in=\"RY\" out=\"UU\"/>\n      <rule in=\"UR\" out=\"UU\"/>\n      <rule in=\"UY\" out=\"UU\"/>\n      <rule in=\"BU\" out=\"WU\"/>\n      <rule in=\"B*/*U\" out=\"W*/*U\"/>\n    </all>\n    <path from=\"R\" to=\"Y\" on=\"B\" color=\"U\" inertia=\"True\" longest=\"True\"/>\n    <one in=\"Y\" out=\"W\"/>\n    <one in=\"R\" out=\"Y\"/>\n  </markov>\n  <all>\n    <rule in=\"U\" out=\"P\"/>\n    <rule in=\"W\" out=\"B\"/>\n  </all>\n  <all in=\"BBB/BPB\" out=\"***/*B*\"/>\n  <all>\n    <rule in=\"BP\" out=\"WP\"/>\n    <rule in=\"B*/*P\" out=\"W*/*P\"/>\n  </all>\n</sequence>\n\n<!--\nRoom shapes and placement algorithm are taken from\nhttps://old.reddit.com/r/proceduralgeneration/comments/3pa8a1/my_take_at_a_roguelike_level_generator_ft/\n\nNote that MJ interpreter automatically performs optimizations 1-3 from the post.\n-->\n"
  },
  {
    "path": "models/DwarfPath.xml",
    "content": "<sequence values=\"BRWAPGEY\">\n  <one in=\"BBBBBB/BBBBBB/BBBBBB/BBBBBB/BBBBBB/BBBBBB\" out=\"******/*AAAA*/*A**A*/*A**A*/*AAAA*/******\" steps=\"1\"/>\n  <one in=\"BBB/BBB\" out=\"WWW/WWW\" steps=\"2\"/>\n  <one in=\"B\" out=\"R\" steps=\"1\"/>\n  <markov>\n    <one>\n      <rule in=\"GE\" out=\"BG\"/>\n      <rule in=\"GA\" out=\"RY\"/>\n    </one>\n    <path from=\"G\" to=\"A\" on=\"B\" color=\"E\" inertia=\"False\"/>\n    <one>\n      <rule in=\"RP\" out=\"BR\"/>\n      <rule in=\"RW\" out=\"BG\"/>\n    </one>\n    <path from=\"R\" to=\"W\" on=\"B\" color=\"P\" inertia=\"False\"/>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/Dwarves.xml",
    "content": "<sequence values=\"BRWGNAEP\">\n  <one in=\"BBBBBB/BBBBBB/BBBBBB/BBBBBB/BBBBBB/BBBBBB\" out=\"******/*NNNN*/*N**N*/*N**N*/*NNNN*/******\" steps=\"1\"/>\n  <one in=\"BBB/BBB\" out=\"WWW/WWW\" steps=\"2\"/>\n  <one in=\"B\" out=\"R\" steps=\"2\"/>\n  <all>\n    <rule in=\"RB\" out=\"BR\"/>\n    <rule in=\"RW\" out=\"BE\"/>\n    <rule in=\"E\" out=\"G\"/>\n    <rule in=\"P\" out=\"R\"/>\n    <rule in=\"GB\" out=\"BG\"/>\n    <rule in=\"GN\" out=\"PA\"/>\n\n    <field for=\"R\" to=\"W\" on=\"B\" recompute=\"True\"/>\n    <field for=\"G\" to=\"N\" on=\"B\" recompute=\"True\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/Escher.xml",
    "content": "<sequence values=\"BR\">\n  <all in=\"B\" out=\"R\"/>\n  <all in=\"***/***/*** ***/*R*/*** ***/***/***\" out=\"***/***/*** ***/*B*/*** ***/***/***\"/>\n  <wfc values=\"BAYRW\" tileset=\"Escher\">\n    <rule in=\"R\" out=\"Empty\"/>\n    <all>\n      <rule in=\"WBW\" out=\"*W*\"/>\n      <rule in=\"RBR\" out=\"*R*\"/>\n    </all>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/EscherSurface.xml",
    "content": "<sequence values=\"BW\">\n  <all in=\"B\" out=\"W\"/>\n  <all in=\"***/***/*** ***/*W*/*** ***/***/***\" out=\"***/***/*** ***/*B*/*** ***/***/***\"/>\n  <wfc values=\"BADG\" tileset=\"EscherSurface\">\n    <rule in=\"W\" out=\"Empty\"/>\n    <all>\n      <rule in=\"ABA\" out=\"*A*\"/>\n      <rule in=\"GBG\" out=\"*G*\"/>\n      <rule in=\"DBD\" out=\"*D*\"/>\n    </all>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/EuclideanPath.xml",
    "content": "<sequence values=\"BRGWOAD\">\n  <one in=\"BBB/BBB/BBB\" out=\"***/*D*/***\" steps=\"8\"/>\n  <one in=\"D\" out=\"A\" steps=\"1\"/>\n  <one in=\"D\" out=\"R\" steps=\"1\"><field for=\"R\" from=\"A\" on=\"BD\"/></one>\n  <one in=\"D\" out=\"G\" steps=\"1\"><field for=\"G\" from=\"R\" on=\"BD\"/></one>\n  <all>\n    <rule in=\"D\" out=\"B\"/>\n    <rule in=\"A\" out=\"B\"/>\n  </all>\n  <one>\n    <rule in=\"RB\" out=\"WO\"/>\n    <rule in=\"O\" out=\"R\"/>\n\n    <rule in=\"R*/*B\" out=\"W*/*A\"/>\n    <rule in=\"A\" out=\"D\"/>\n    <rule in=\"D\" out=\"R\"/>\n\n    <observe value=\"G\" from=\"B\" to=\"R\"/>\n    <observe value=\"B\" to=\"BW\"/>\n    <observe value=\"R\" to=\"W\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/FindLongCycle.xml",
    "content": "<sequence values=\"BWARDEU\" origin=\"True\">\n  <union symbol=\"?\" values=\"BDE\"/>\n  <one in=\"WBB\" out=\"WAW\"/>\n  <markov>\n    <markov>\n      <one in=\"RAW\" out=\"**R\" steps=\"20\"/>\n      <all>\n        <rule in=\"RAW\" out=\"UAU\"/>\n        <rule in=\"UAW\" out=\"UAU\"/>\n        <rule in=\"UAR\" out=\"UAU\"/>\n      </all>\n      <sequence>\n        <one in=\"WBW\" out=\"RBR\" steps=\"1\"/>\n        <all>\n          <rule in=\"B?B/?W?\" out=\"***/*E*\"/>\n          <rule in=\"???/BAB\" out=\"***/*D*\"/>\n        </all>\n      </sequence>\n    </markov>\n    <markov>\n      <all>\n        <rule in=\"R\" out=\"W\"/>\n        <rule in=\"WDE\" out=\"WAW\"/>\n      </all>\n    </markov>\n  </markov>\n  <one in=\"B?B/?U?\" out=\"***/*R*\"/>\n  <one in=\"RBR\" out=\"UAU\"/>\n  <prl>\n    <rule in=\"U\" out=\"W\"/>\n    <rule in=\"D\" out=\"E\"/>\n  </prl>\n</sequence>\n"
  },
  {
    "path": "models/FireNoise.xml",
    "content": "<sequence values=\"BGOERK\">\n  <prl steps=\"75\">\n    <rule in=\"OG\" out=\"*O\"/>\n    <rule in=\"O*/*G\" out=\"**/*O\"/>\n    <rule in=\"B\" out=\"G\" p=\"0.01\"/>\n    <rule in=\"O\" out=\"B\"/>\n    <rule in=\"G\" out=\"O\" p=\"0.0001\"/>\n  </prl>\n  <all in=\"*G*/GBG\" out=\"***/*G*\"/>\n  <all>\n    <rule in=\"*B*/BGB/*B*\" out=\"***/*B*/***\"/>\n    <rule in=\"*BB*/BGGB/*BB*\" out=\"****/*BB*/****\"/>\n  </all>\n  <markov>\n    <sequence>\n      <one in=\"G\" out=\"R\" steps=\"1\"/>\n      <all in=\"RG\" out=\"RR\" steps=\"10\"/>\n      <all in=\"RG\" out=\"EE\"/>\n      <all>\n        <rule in=\"ER\" out=\"*E\"/>\n        <rule in=\"EG\" out=\"*E\"/>\n      </all>\n    </sequence>\n  </markov>\n  <markov>\n    <sequence>\n      <one in=\"B\" out=\"K\" steps=\"1\"/>\n      <all in=\"KB\" out=\"*K\" steps=\"10\"/>\n      <all in=\"KB\" out=\"GG\"/>\n      <all>\n        <rule in=\"GB\" out=\"*G\"/>\n        <rule in=\"GK\" out=\"*G\"/>\n      </all>\n    </sequence>\n  </markov>\n  <prl>\n    <rule in=\"K\" out=\"E\"/>\n    <rule in=\"G\" out=\"B\"/>\n  </prl>\n</sequence>\n\n<!--\nA noise-like generator based on the forest-fire model https://en.wikipedia.org/wiki/Forest-fire_model\n-->\n"
  },
  {
    "path": "models/Flowers.xml",
    "content": "<sequence values=\"NGUEPY\" symmetry=\"(x)\">\n  <all in=\"N/*/*/*\" out=\"G/*/*/*\"/>\n  <all in=\"G/*/*/*/*\" out=\"U/*/*/*/*\"/>\n  <markov>\n    <one>\n      <rule in=\"UUU/UUU/UPU\" out=\"***/*P*/*E*\"/>\n      <rule in=\"UUU/UUU/UUU/PUU/**U\" out=\"***/*P*/*E*/EE*/***\"/>\n      <rule in=\"UUUUU/UUUUU/UUUUU/UUPUU/U***U\" out=\"*****/*P*P*/*E*E*/*EEE*/*****\"/>\n      <rule in=\"UUU/UPU/UEU/UEU\" out=\"*Y*/YEY/*Y*/***\"/>\n    </one>\n    <one in=\"UUUUU/UUUUU/UUUUU/GGGGG/NNNNN\" out=\"*****/**P**/**E**/**E**/**E**\"/>\n  </markov>\n  <all in=\"***/*P*/***\" out=\"*Y*/YEY/*Y*\"/>\n</sequence>\n"
  },
  {
    "path": "models/Forest.xml",
    "content": "<sequence values=\"BIEW\">\n  <one in=\"BB*/BBB/*B*\" out=\"***/*I*/***\"/>\n  <all in=\"*I*/IBI\" out=\"***/*I*\"/>\n  <all in=\"*B*/BIB/*B*\" out=\"***/*W*/***\"/>\n  <one in=\"I\" out=\"E\" steps=\"2\"/>\n  <markov>\n    <all in=\"EI\" out=\"*E\"/>\n    <one in=\"EBI/EBI\" out=\"**E/**E\"/>\n  </markov>\n  <all in=\"E*W\" out=\"**E\"/>\n  <prl>\n    <rule in=\"I\" out=\"B\"/>\n    <rule in=\"W\" out=\"B\"/>\n  </prl>\n</sequence>\n"
  },
  {
    "path": "models/ForestFire.xml",
    "content": "<prl values=\"BGR\">\n  <rule in=\"RG\" out=\"*R\"/>\n  <rule in=\"R*/*G\" out=\"**/*R\"/>\n  <rule in=\"B\" out=\"G\" p=\"0.01\"/>\n  <rule in=\"R\" out=\"B\"/>\n  <rule in=\"G\" out=\"R\" p=\"0.0001\"/>\n</prl>\n\n<!--\nhttps://en.wikipedia.org/wiki/Forest-fire_model\n-->\n"
  },
  {
    "path": "models/ForestFireCA.xml",
    "content": "<convolution values=\"BGR\" neighborhood=\"Moore\">\n  <rule in=\"G\" out=\"R\" sum=\"1..8\" values=\"R\"/>\n  <rule in=\"R\" out=\"B\"/>\n  <rule in=\"B\" out=\"G\" p=\"0.01\"/>\n  <rule in=\"G\" out=\"R\" p=\"0.0001\"/>\n</convolution>\n"
  },
  {
    "path": "models/GameOfLife.xml",
    "content": "<sequence values=\"DA\">\n  <prl in=\"D\" out=\"A\" p=\"0.5\" steps=\"1\"/>\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"D\" out=\"A\" sum=\"3\" values=\"A\"/>\n    <rule in=\"A\" out=\"D\" sum=\"0,1,4..8\" values=\"A\"/>\n  </convolution>\n</sequence>\n"
  },
  {
    "path": "models/GoTo.xml",
    "content": "<markov values=\"BRWD\" origin=\"True\">\n  <one>\n    <rule in=\"RW\" out=\"BR\"/>\n    <rule in=\"RD\" out=\"BR\"/>\n  </one>\n  <path from=\"R\" to=\"W\" on=\"B\" color=\"D\"/>\n  <one in=\"B\" out=\"W\"/>\n</markov>\n"
  },
  {
    "path": "models/GoToGradient.xml",
    "content": "<markov values=\"BRW\" origin=\"True\">\n  <one in=\"RW\" out=\"BR\"/>\n  <one temperature=\"1.0\">\n    <rule in=\"RB\" out=\"BR\"/>\n    <field for=\"R\" to=\"W\" on=\"B\" recompute=\"True\" essential=\"True\"/>\n  </one>\n  <one in=\"B\" out=\"W\"/>\n</markov>\n"
  },
  {
    "path": "models/GrowTo.xml",
    "content": "<sequence values=\"BUN\">\n  <one in=\"B\" out=\"U\" steps=\"1\"/>\n  <one in=\"B\" out=\"N\" steps=\"1\"/>\n  <one in=\"NB\" out=\"NN\" temperature=\"10.0\">\n    <field for=\"N\" to=\"U\" on=\"B\" recompute=\"False\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/Growth.xml",
    "content": "<one values=\"BW\" origin=\"True\" in=\"WB\" out=\"WW\"/>\n"
  },
  {
    "path": "models/GrowthCompetition.xml",
    "content": "<sequence values=\"BEY\" periodic=\"True\">\n  <one in=\"B\" out=\"E\" steps=\"5\"/>\n  <one in=\"B\" out=\"Y\" steps=\"5\"/>\n  <one>\n    <rule in=\"EB\" out=\"EE\"/>\n    <rule in=\"YB\" out=\"YY\"/>\n    <rule in=\"EY\" out=\"EE\"/>\n    <rule in=\"YE\" out=\"YY\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/GrowthContraction.xml",
    "content": "<one values=\"BW\" origin=\"True\">\n  <rule in=\"WB\" out=\"WW\"/>\n  <rule in=\"BWW\" out=\"BBW\"/>\n</one>\n"
  },
  {
    "path": "models/GrowthWalk.xml",
    "content": "<one values=\"BW\" origin=\"True\">\n  <rule in=\"WB\" out=\"BW\"/>\n  <rule in=\"WB\" out=\"WW\"/>\n</one>\n"
  },
  {
    "path": "models/HamiltonianPath.xml",
    "content": "<sequence values=\"BRWGU\" origin=\"True\">\n  <one symmetry=\"()\">\n    <rule in=\"B/B/R\" out=\"R/B/B\"/>\n    <rule in=\"BBR\" out=\"RBB\"/>\n  </one>\n  <markov symmetry=\"(x)\">\n    <one in=\"RBB\" out=\"WWR\"/>\n    <one in=\"R/B/B\" out=\"W/W/R\"/>\n  </markov>\n  <one in=\"BBB/BWB\" out=\"***/*R*\"/>\n  <markov>\n    <one in=\"UWW\" out=\"UUU\"/>\n    <sequence>\n      <one in=\"UWG\" out=\"RBW\"/>\n      <all in=\"U\" out=\"W\"/>\n    </sequence>\n    <one in=\"RBW\" out=\"UWG\"/>\n  </markov>\n</sequence>\n\n<!--\nStarts with a fixed Hamiltonian path and applies transformations to it to generate other Hamiltonian paths (containing only one connectivity component).\n\nFor a simpler version that doesn't care about maintaining a single connectivity component see HamiltonianPaths.\n\nAlgorithm adapted from: https://aip.scitation.org/doi/10.1063/1.443937\n\nCompare with an implementation in a conventional language: http://clisby.net/projects/hamiltonian_path/hamiltonian_path_v1.html\n-->\n"
  },
  {
    "path": "models/HamiltonianPaths.xml",
    "content": "<sequence values=\"BRWG\" origin=\"True\">\n  <one in=\"RBB\" out=\"WWR\" steps=\"1\"/>\n  <one in=\"R*B/***/W**\" out=\"WWR/***/***\" symmetry=\"(xy+)\"/>\n  <one in=\"BBB/BWB\" out=\"***/*R*\"/>\n  <markov>\n    <all in=\"GWW\" out=\"WBR\"/>\n    <all in=\"WBR\" out=\"GWW\"/>\n  </markov>\n</sequence>\n\n<!--\nSee HamiltonianPath for a modification that maintains a single connectivity component.\n\nAlgorithm adapted from: https://aip.scitation.org/doi/10.1063/1.443937\n\nCompare with an implementation in a conventional language: http://clisby.net/projects/hamiltonian_path/hamiltonian_path_v1.html\n-->\n"
  },
  {
    "path": "models/Hills.xml",
    "content": "<sequence values=\"BA\">\n  <prl in=\"B\" out=\"A\"/>\n  <prl in=\"A *\" out=\"B *\" symmetry=\"()\"/>\n  <one in=\"AB\" out=\"AA\" steps=\"8000\"/>\n  <convolution neighborhood=\"NoCorners\" periodic=\"True\">\n    <rule in=\"B\" out=\"A\" sum=\"10..18\" values=\"A\"/>\n    <rule in=\"A\" out=\"B\" sum=\"10..18\" values=\"B\"/>\n  </convolution>\n</sequence>\n"
  },
  {
    "path": "models/IrregularMazeGrowth.xml",
    "content": "<one values=\"BW\" origin=\"True\" in=\"*BB/WBB/*BB\" out=\"***/WW*/***\"/>\n"
  },
  {
    "path": "models/IrregularSAW.xml",
    "content": "<one values=\"BRW\" origin=\"True\" in=\"*BB/RBB/*BB\" out=\"***/WR*/***\"/>\n"
  },
  {
    "path": "models/Island.xml",
    "content": "<sequence values=\"UWBRGEYINSOTus\" origin=\"True\">\n  <prl in=\"***/*U*/***\" out=\"***/*Y*/***\"/>\n\n  <one in=\"WY\" out=\"WW\" steps=\"1\" comment=\"draw an island skeleton\"/>\n  <one in=\"YWY/YYY\" out=\"YWY/YWY\" steps=\"400\" comment=\"steps define the lenght of the backbone\"/>\n  <one in=\"YWY/YWY/YYY\" out=\"YWY/YRY/YYY\" />\n  <one in=\"YYY/WWW\" out=\"YWY/WWW\" steps=\"20\" comment=\"adjust steps to change number of branches. More branches = rounder, smoother island with less inner water\"/>\n  <one in=\"YWY/YYY\" out=\"YWY/YWY\" steps=\"2300\" comment=\"adjust steps for the length of branches. More lenght = bigger island\"/>\n  <all in=\"R\" out=\"W\" />\n\n  <prl in=\"Y\" out=\"U\" p=\"0.00003\" steps=\"1\" comment=\"adjust p to reduce lakes\"/>\n\n  <one comment=\"Voronoi\">\n    <rule in=\"WY\" out=\"WW\"/>\n    <rule in=\"UY\" out=\"UU\"/>\n  </one>\n  <convolution neighborhood=\"Moore\" comment=\"smoothing\">\n    <rule in=\"U\" out=\"W\" sum=\"5..8\" values=\"W\"/>\n    <rule in=\"W\" out=\"U\" sum=\"5..8\" values=\"U\"/>\n  </convolution>\n\n  <all in=\"U/*\" out=\"I/*\" symmetry=\"(x)\" comment=\"paint ocean to find out coasts. Sea will be U, lakes I, deep ocean B\"/>\n  <all in=\"UI\" out=\"UU\"/>\n  <all in=\"WU\" out=\"WR\"/>\n  <prl in=\"RU\" out=\"RR\" steps=\"30\" p=\"0.5\" comment=\"steps = size of the shallow water around the coast\"/>\n  <prl in=\"U\" out=\"B\"/>\n  <prl in=\"R\" out=\"U\"/>\n\n  <all in=\"UW\" out=\"UG\" comment=\"spawn a few river sources (paint coast green on some thickness to force sources to be inland)\"/>\n  <all in=\"GW\" out=\"GG\" steps=\"80\" comment=\"increase steps to push sources back from the sea\"/>\n  <prl in=\"WW\" out=\"WI\" p=\"0.000015\" steps=\"1\" comment=\"play with p to change the number of sources\"/>\n  <all in=\"G\" out=\"W\"/>\n\n  <sequence>\n    <one in=\"IW\" out=\"IR\" steps=\"1\">\n      <field for=\"R\" from=\"U\" on=\"W\"/>\n    </one>\n    <one>\n      <rule in=\"RU\" out=\"NU\"/>\n      <rule in=\"RO\" out=\"NO\"/>\n      <rule in=\"NNNRW/WWWWW\" out=\"NNWWW/WWNNR\" comment=\"break long, straight lines\" />\n      <rule in=\"RW\" out=\"NR\" />\n\n      <observe value=\"W\" to=\"WN\"/>\n      <observe value=\"R\" to=\"N\"/>\n    </one>\n\n    <all in=\"N\" out=\"O\" comment=\"color done (O) so that we can merge rivers\"/>\n    <all in=\"OI\" out=\"OO\" />\n  </sequence>\n\n  <prl steps=\"2\" p=\"0.8\" comment=\"widen the connection with the sea\">\n    <rule in=\"UO/UW\" out=\"UO/UO\" />\n    <rule in=\"UO/WW\" out=\"UO/OO\" />\n  </prl>\n  <convolution neighborhood=\"Moore\" periodic=\"True\" comment=\"smooth\">\n    <rule in=\"W\" out=\"O\" sum=\"6..8\" values=\"O\"/>\n  </convolution>\n\n  <prl in=\"WU\" out=\"YU\" steps=\"1\" p=\"0.005\" comment=\"generate beaches starting points\"/>\n  <all steps=\"30\" comment=\"march them along the coast\">\n    <rule in=\"UU/YW\" out=\"UU/YY\" />\n    <rule in=\"UW/YW\" out=\"UY/YY\" />\n  </all>\n  <prl in=\"WY\" out=\"YY\" steps=\"3\" p=\"0.4\" comment=\"widen them with a bit of randomness\"/>\n  <convolution neighborhood=\"Moore\" periodic=\"True\" comment=\"smooth them\">\n    <rule in=\"Y\" out=\"W\" sum=\"5..8\" values=\"W\"/>\n    <rule in=\"W\" out=\"Y\" sum=\"5..8\" values=\"Y\"/>\n  </convolution>\n\n  <all in=\"O\" out=\"u\" comment=\"lakes and rivers back to lake blue\"/>\n  <all in=\"UY\" out=\"TY\" />\n  <all in=\"UW\" out=\"TW\" />\n  <all in=\"uW\" out=\"TW\" />\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"U\" out=\"T\" sum=\"5..8\" values=\"T\"/>\n    <rule in=\"u\" out=\"T\" sum=\"5..8\" values=\"T\"/>\n  </convolution>\n\n  <prl in=\"UTW\" out=\"UTG\" p=\"0.1\" steps=\"1\" comment=\"seed forests and plains\"/>\n  <prl in=\"YW\" out=\"YG\" p=\"1\" steps=\"1\" />\n  <prl in=\"WTW\" out=\"WTE\" p=\"0.05\" steps=\"1\" />\n  <prl in=\"W\" out=\"G\" p=\"0.00001\" steps=\"1\" />\n\n  <one comment=\"grow them, forest 3x stronger\">\n    <rule in=\"WE\" out=\"EE\" />\n    <rule in=\"WE\" out=\"EE\" />\n    <rule in=\"WE\" out=\"EE\" />\n    <rule in=\"WG\" out=\"GG\" />\n  </one>\n\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"G\" out=\"E\" sum=\"5..8\" values=\"E\"/>\n    <rule in=\"E\" out=\"G\" sum=\"6..8\" values=\"O\"/>\n  </convolution>\n  <all in=\"W\" out=\"G\" comment=\"if any white, it's grass (islands). TODO: Eliminate islands next to lakes or islands too small\"/>\n  <all in=\"B\" out=\"I\" comment=\"change deep ocean color\"/>\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"U\" out=\"I\" sum=\"5..8\" values=\"I\"/>\n    <rule in=\"I\" out=\"U\" sum=\"5..8\" values=\"U\"/>\n  </convolution>\n\n  <all in=\"GE\" out=\"GR\" comment=\"let's grow mountains\"/>\n  <all in=\"TE\" out=\"TR\" />\n  <prl in=\"E\" out=\"S\" p=\"0.0001\" steps=\"1\" />\n  <one>\n    <rule in=\"RE\" out=\"RR\" />\n    <rule in=\"SE\" out=\"SS\" />\n    <rule in=\"SE\" out=\"SS\" />\n  </one>\n  <all in=\"R\" out=\"E\" />\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"E\" out=\"S\" sum=\"5..8\" values=\"S\"/>\n    <rule in=\"S\" out=\"E\" sum=\"5..8\" values=\"E\"/>\n  </convolution>\n\n  <all in=\"ES\" out=\"ER\" />\n  <prl in=\"S\" out=\"s\" p=\"0.0002\" steps=\"1\" />\n  <one>\n    <rule in=\"RS\" out=\"RR\" />\n    <rule in=\"RS\" out=\"RR\" />\n    <rule in=\"RS\" out=\"RR\" />\n    <rule in=\"sS\" out=\"ss\" />\n    <rule in=\"sS\" out=\"ss\" />\n    <rule in=\"sS\" out=\"ss\" />\n    <rule in=\"sS\" out=\"ss\" />\n  </one>\n  <all in=\"R\" out=\"S\" />\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"S\" out=\"s\" sum=\"5..8\" values=\"s\"/>\n    <rule in=\"s\" out=\"S\" sum=\"5..8\" values=\"S\"/>\n  </convolution>\n\n  <sequence comment=\"final step, trace back rivers to mountains\">\n    <one in=\"EEE/ETE\" out=\"EEE/ERE\" steps=\"1\" />\n    <one>\n      <rule in=\"RS\" out=\"NS\" />\n      <rule in=\"RO\" out=\"NO\" />\n      <rule in=\"NNNRE/EEEEE\" out=\"NNEEE/EENNR\" comment=\"break long, straight lines\" />\n      <rule in=\"RE\" out=\"NR\" />\n\n      <observe value=\"E\" to=\"EN\"/>\n      <observe value=\"R\" to=\"N\"/>\n    </one>\n\n    <one in=\"NS\" out=\"RN\" steps=\"5\" />\n    <all in=\"R\" out=\"T\" />\n    <all in=\"N\" out=\"T\" />\n  </sequence>\n  <prl in=\"u\" out=\"U\"/>\n</sequence>\n\n<!--\nMade by Guillaume Fiette https://github.com/woldendans/MJ-simple-island\n-->\n"
  },
  {
    "path": "models/Keys.xml",
    "content": "<sequence values=\"BADGRIFYUP\" origin=\"True\">\n  <prl in=\"ABB*\" out=\"*DDA\"/>\n  <one in=\"A\" out=\"G\" steps=\"1\"/>\n  <one steps=\"1\">\n    <rule in=\"A\" out=\"R\"/>\n    <field for=\"R\" from=\"G\" on=\"RDA\"/>\n  </one>\n  <one in=\"RDDA\" out=\"*IYU\" steps=\"1\"/>\n  <one temperature=\"2.0\">\n    <rule in=\"UDDA\" out=\"FIYU\"/>\n    <rule in=\"UDDG\" out=\"FIYG\"/>\n    <field for=\"U\" to=\"G\" on=\"GDAU\"/>\n  </one>\n  <markov>\n    <one in=\"UDDA\" out=\"FIYU\"/>\n    <one in=\"IBB*/FDDA\" out=\"P***/*IYU\"/>\n  </markov>\n  <prl>\n    <rule in=\"Y\" out=\"F\"/>\n    <rule in=\"I\" out=\"F\"/>\n    <rule in=\"D\" out=\"B\"/>\n  </prl>\n</sequence>\n\n<!--\nGenerates a random puzzle with keys and locks.\n\nAdapted graph grammar from the dissertation \"Engineering Emergence. Applied Theory for Game Design\" by Joris Dormans.\n-->\n"
  },
  {
    "path": "models/KnightPatrol.xml",
    "content": "<markov values=\"BRGWA\" origin=\"True\">\n  <one in=\"R**/**B\" out=\"W**/**R\">\n    <observe value=\"G\" from=\"B\" to=\"R\"/>\n    <observe value=\"B\" to=\"BW\"/>\n    <observe value=\"R\" to=\"W\"/>\n  </one>\n  <prl in=\"W\" out=\"A\"/>\n  <one in=\"B\" out=\"G\"/>\n</markov>\n"
  },
  {
    "path": "models/Knots2D.xml",
    "content": "<sequence values=\"DB\">\n  <all in=\"***/*D*/***\" out=\"***/*B*/***\"/>\n  <wfc tileset=\"Knots2D\" values=\"IE\">\n    <rule in=\"D\" out=\"Empty\"/>\n    <rule in=\"B\" out=\"Empty|Line|Turn|Cross\"/>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/Knots3D.xml",
    "content": "<sequence values=\"BW\">\n  <all in=\"B\" out=\"W\"/>\n  <all in=\"***/***/*** ***/*W*/*** ***/***/***\" out=\"***/***/*** ***/*B*/*** ***/***/***\"/>\n  <wfc values=\"BFE\" tileset=\"Knots3D\" tiles=\"Knots3D/Tubes\">\n    <rule in=\"W\" out=\"Empty\"/>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/Laplace.xml",
    "content": "<one values=\"BW\" origin=\"True\" temperature=\"12.0\">\n  <rule in=\"B\" out=\"W\"/>\n  <field for=\"W\" to=\"W\" on=\"B\" recompute=\"False\"/>\n</one>\n"
  },
  {
    "path": "models/Lightning.xml",
    "content": "<sequence values=\"NBRGUW\">\n  <all in=\"***/*N*/***\" out=\"***/*B*/***\"/>\n  <one in=\"B\" out=\"R\" steps=\"1\"/>\n  <markov>\n    <one>\n      <rule in=\"RU\" out=\"*W\"/>\n      <rule in=\"WU\" out=\"*W\"/>\n      <rule in=\"WG\" out=\"*N\"/>\n    </one>\n    <path from=\"R\" to=\"G\" on=\"B\" color=\"U\"/>\n    <all in=\"W\" out=\"B\"/>\n    <one in=\"N\" out=\"G\"/>\n  </markov>\n</sequence>\n\n<!--\nTODO: modify path block so that the direction probabilities are proportional to the corresponding lengths.\n-->\n"
  },
  {
    "path": "models/LoopErasedWalk.xml",
    "content": "<one values=\"BRWGOU\" origin=\"True\">\n  <rule in=\"RBB\" out=\"WWR\"/>\n  <rule in=\"RBW\" out=\"GWO\"/>\n  <rule in=\"OWG\" out=\"OBU\"/>\n  <rule in=\"UWW\" out=\"BBU\"/>\n  <rule in=\"UWO\" out=\"BBR\"/>\n</one>\n"
  },
  {
    "path": "models/LoopGrowth.xml",
    "content": "<sequence values=\"BAW\" origin=\"True\">\n  <one in=\"BBB/BAB/BBB\" out=\"WAW/ABA/WAW\"/>\n  <one in=\"BBB/BBB/WAW\" out=\"WAW/ABA/WBW\"/>\n  <all in=\"A\" out=\"W\"/>\n</sequence>\n"
  },
  {
    "path": "models/LostCity.xml",
    "content": "<sequence values=\"BWRDIE\">\n  <one in=\"B\" out=\"I\" steps=\"4\" comment=\"make a few lakes by random growth\"/>\n  <one in=\"IB\" out=\"*I\" steps=\"24000\"/>\n  <convolution neighborhood=\"Moore\">\n    <rule in=\"I\" out=\"B\" sum=\"5..8\" values=\"B\"/>\n    <rule in=\"B\" out=\"I\" sum=\"5..8\" values=\"I\"/>\n  </convolution>\n\n  <one in=\"B\" out=\"R\" steps=\"20\" comment=\"make some land by a self-avoiding random walk with backtracking\"/>\n  <markov>\n    <all in=\"RBB\" out=\"WWR\"/>\n    <all in=\"RWW\" out=\"DDR\"/>\n  </markov>\n  <all in=\"R\" out=\"D\"/>\n\n  <one in=\"BBWBB\" out=\"**B**\" comment=\"erode narrow sections of land\"/>  \n  <all>\n    <rule in=\"IW\" out=\"*D\"/>\n    <rule in=\"I*/*W\" out=\"**/*D\"/>\n    <rule in=\"DW\" out=\"*D\"/>\n  </all>\n  <prl in=\"D\" out=\"B\"/>\n\n  <prl steps=\"2\">\n    <rule in=\"WB\" out=\"*D\"/>\n    <rule in=\"DB\" out=\"*D\" p=\"0.5\"/>\n  </prl>\n  <convolution neighborhood=\"Moore\">\n    <rule in=\"D\" out=\"B\" sum=\"5..8\" values=\"B\"/>\n    <rule in=\"B\" out=\"D\" sum=\"5..8\" values=\"DI\"/>\n  </convolution>\n\n  <one in=\"BB*/BBB/*B*\" out=\"***/*E*/***\"/>\n  <all in=\"*E*/EBE\" out=\"***/*E*\"/>\n  <prl in=\"D\" out=\"B\"/>\n</sequence>\n\n<!--\nBased on the model by Andrew Kay https://github.com/kaya3/pattern-match-2d\n-->\n"
  },
  {
    "path": "models/MarchingSquares.xml",
    "content": "<sequence values=\"BW\">\n  <prl in=\"B\" out=\"W\" p=\"0.5\" steps=\"1\"/>\n  <map scale=\"3 3 1\" values=\"DA\">\n    <rule in=\"BB/BB\" out=\"DDD/DDD/DDD\"/>\n    <rule in=\"BB/BW\" out=\"DDD/DDD/DDA\"/>\n    <rule in=\"BB/WW\" out=\"DDD/DDD/AAA\"/>\n    <rule in=\"BW/WB\" out=\"DDA/DDD/ADD\"/>\n    <rule in=\"BW/WW\" out=\"DDA/DDA/AAA\"/>\n    <rule in=\"WW/WW\" out=\"AAA/AAA/AAA\"/>\n  </map>\n</sequence>\n"
  },
  {
    "path": "models/MazeBacktracker.xml",
    "content": "<markov values=\"BRGW\" origin=\"True\">\n  <one in=\"RBB\" out=\"GGR\"/>\n  <one in=\"RGG\" out=\"WWR\"/>\n</markov>\n"
  },
  {
    "path": "models/MazeGrowth.xml",
    "content": "<one values=\"BWA\" in=\"WBB\" out=\"WAW\" origin=\"True\"/>\n\n<!--\nCompare to an implementation in a conventional language: https://bl.ocks.org/mbostock/70a28267db0354261476\n\nNote that MJ interpreter automatically performs optimizations. It doesn't forget past matches, so it searches only over a small part of the grid each turn.\n-->\n"
  },
  {
    "path": "models/MazeMap.xml",
    "content": "<sequence symmetry=\"()\" values=\"BOEIN\">\n  <all>\n    <rule in=\"B\" out=\"O\"/>\n    <rule in=\"B\" out=\"E\"/>\n    <rule in=\"B\" out=\"I\"/>\n    <rule in=\"B\" out=\"N\"/>\n  </all>\n  <map scale=\"2 2 1\" values=\"DA\">\n    <rule in=\"O\" out=\"DD/DA\" comment=\"1 cell\"/>\n    <rule in=\"E\" out=\"DD/AA\" comment=\"horizontal 2 cells\"/>\n    <rule in=\"I\" out=\"DA/DA\" comment=\"vertical 2 cells\"/>\n    <rule in=\"N\" out=\"DA/AA\" comment=\"3 cells\"/>\n  </map>\n</sequence>\n"
  },
  {
    "path": "models/MazeTrail.xml",
    "content": "<one values=\"BRW\" origin=\"True\">\n  <rule in=\"RBB\" out=\"WWR\"/>\n  <rule in=\"R*W\" out=\"W*R\"/>\n</one>\n\n<!--\nAlso known as Aldous-Broder algorithm.\n\nLike Wilson's algorithm, generates each maze with equal probability (samples mazes uniformly). Though very slowly.\n-->\n"
  },
  {
    "path": "models/ModernHouse.xml",
    "content": "﻿<sequence values=\"BD\" symmetry=\"(xy)\">\n  <prl in=\"B\" out=\"D\" />\n  <wfc tileset=\"Partition\" values=\"BVL vSaOon\" overlap=\"-3\">\n    <rule in=\"D\" out=\"Nothing|I|T\" />\n    <prl>\n      <rule in=\"LBBBL\" out=\"*LSL*\" />\n      <rule in=\"V***/****/****/***B\" out=\"****/****/****/***O\" />\n    </prl>\n    <prl in=\"*****/*****/**V**/*****/*****\" out=\"*****/*****/**v**/*****/*****\" />\n    <prl in=\"V*****v\" out=\"******n\" />\n    <prl in=\"nBBBBBn\" out=\"*aaaaa*\" />\n    <prl>\n      <rule in=\"a**O**S\" out=\"***o***\" />\n      <rule in=\"V***/****/****/***O\" out=\"****/****/****/***o\" />\n    </prl>\n    <prl in=\"anBBBBBV*\" out=\"**aaaaa*a\" />\n    <prl>\n      <rule in=\"a\" out=\"L\" />\n      <rule in=\"n\" out=\"V\" />\n      <rule in=\"v\" out=\"V\" />\n      <rule in=\"VLLLLLV\" out=\"***S***\" />\n      <rule in=\"VBBBBBV\" out=\"***S***\" />\n    </prl>\n    <map scale=\"1/3 1/3 1/3\" values=\"BVSsto yORr CcaGP\">\n      <rule in=\"***/***/*** ***/*O*/*** ***/***/***\" out=\"t\" />\n      <rule in=\"***/***/*** ***/*o*/*** ***/***/***\" out=\"o\" />\n      <rule in=\"***/***/*** BBB/LSL/BBB ***/***/***\" out=\"S\" />\n      <rule in=\"***/***/*** BBB/BSB/BBB ***/***/***\" out=\"s\" />\n      <rule in=\"***/***/*** ***/*V*/*** ***/***/***\" out=\"V\" />\n      <prl>\n        <rule in=\"* t\" out=\"* y\" />\n        <rule in=\"***/*S*/***\" out=\"***/*C*/***\" />\n        <rule in=\"***/*s*/***\" out=\"***/*c*/***\" />\n      </prl>\n      <union symbol=\".\" values=\"yr\" />\n      <markov>\n        <all>\n          <rule in=\"R*y\" out=\"**r\" />\n          <rule in=\"r*y\" out=\"**r\" />\n        </all>\n        <one in=\"******* .*.*.*. .*y*.*.\" out=\"******* ******* **R****\" /> - можно и ослабить это условие, но давайте пока так\n      </markov>\n      <prl>\n        <rule in=\"r *\" out=\"y *\" />\n        <rule in=\"* c\" out=\"* P\" />\n        <rule in=\"* C\" out=\"* G\" />\n      </prl>\n      <prl in=\"P *\" out=\"a *\" />\n      <wfc tileset=\"PartitionedEdges\" values=\"BOaFXowSLsV JKkARrWhNYnxevDUZdQu\" transparent=\"x\">\n        <rule in=\"t\" out=\"Empty\" />\n        <rule in=\"o\" out=\"o\" />\n        <rule in=\"r\" out=\"Empty|Line|Turn|X|ContactUp\" />\n        <rule in=\"y\" out=\"Empty|Line|Turn|X|ContactDown|ContactUp|Bridge\" />\n        <rule in=\"R\" out=\"ContactUp\" />\n        <rule in=\"S\" out=\"01\" />\n        <rule in=\"s\" out=\"00\" />\n        <rule in=\"c\" out=\"00|10\" />\n        <rule in=\"a\" out=\"00|10|Down|Up0\" />\n        <rule in=\"P\" out=\"00|10|Up0\" />\n        <rule in=\"C\" out=\"01|11\" />\n        <rule in=\"G\" out=\"01|11\" /> Up1\n\n        <rule in=\"V\" out=\"Nothing|I|T|Cross\" /><prl>\n          <rule in=\"w\" out=\"B\" />\n          <rule in=\"F\" out=\"B\" />\n          <rule in=\"s\" out=\"S\" />\n          <rule in=\"B\" out=\"J\" />\n        </prl><prl>\n          <rule in=\"S**O a***\" out=\"***K ****\" comment=\"mark nodes above stairs\" />\n          <rule in=\"a*** S**O\" out=\"**** ***K\" comment=\"mark nodes below stairs\" />\n          <rule in=\"J *\" out=\"B *\" />\n        </prl><prl in=\"OBB*BBO\" out=\"*aa*aa*\" comment=\"fill the grid\" /><prl in=\"Xaa*aaO\" out=\"****A**\" comment=\"mark nodes near stairs as gates\" /><prl comment=\"cancel multi gates\">\n          <rule in=\"AaOaA\" out=\"wwOww\" />\n          <rule in=\"AaO/**a/**A\" out=\"wwO/**w/**w\" />\n        </prl><markov>\n          <one>\n            <rule in=\"WR\" out=\"*F\" />\n            <rule in=\"WA\" out=\"*N\" />\n          </one>\n          <path from=\"A\" to=\"R\" on=\"aOS\" color=\"W\" longest=\"True\" inertia=\"True\" />\n          <one in=\"A\" out=\"R\" />\n        </markov><path from=\"R\" to=\"W\" on=\"aOS\" color=\"h\" inertia=\"True\" comment=\"some gates could have been blocked, so connect them to the main path\" /><prl>\n          <rule in=\"hR\" out=\"*W\" />\n          <rule in=\"aaXaa\" out=\"WW*WW\" />\n          <rule in=\"a a\" out=\"W W\" />\n          <rule in=\"h\" out=\"W\" />\n          <rule in=\"F\" out=\"W\" />\n          <rule in=\"N\" out=\"W\" />\n          <rule in=\"w\" out=\"W\" />\n          <rule in=\"WO\" out=\"*Y\" />\n          <rule in=\"WS\" out=\"*s\" />\n        </prl><all comment=\"repair markers on the path\">\n          <rule in=\"sWWW\" out=\"***Y\" />\n          <rule in=\"YWWW\" out=\"***s\" />\n        </all><one in=\"s J\" out=\"x *\" steps=\"1\" /><markov comment=\"connect components\">\n          <one>\n            <rule in=\"xWW*WWs\" out=\"******x\" />\n            <rule in=\"x W W s\" out=\"* * * x\" />\n            <rule in=\"s W W x\" out=\"x * * *\" />\n            <rule in=\"xWWY\" out=\"***n\" />\n            <rule in=\"sWWn\" out=\"x***\" />\n          </one>\n          <one>\n            <rule in=\"nFFFFFF\" out=\"******n\" />\n            <rule in=\"nFFFFFY\" out=\"******n\" />\n          </one>\n          <path from=\"n\" to=\"Y\" on=\"aOSs\" color=\"F\" inertia=\"True\" />\n        </markov><prl>\n          <rule in=\"a\" out=\"B\" />\n          <rule in=\"nFFFFFn\" out=\"*WWxWW*\" />\n          <rule in=\"x\" out=\"S\" />\n        </prl><prl in=\"LS\" out=\"*s\" /><union symbol=\"@\" values=\"nOKX\" /><one in=\"B n J\" out=\"Y * *\" steps=\"1\" /><markov comment=\"propagate insides\">\n          <all>\n            <rule in=\"Y*****B ***S**@\" out=\"******Y *******\" />\n            <rule in=\"*******/Y*****B ******W/***s**@\" out=\"*******/******Y *******/*******\" />\n            <rule in=\"Y******/*******/*******/*******/*******/*******/******B ***s***/*******/*******/s******/*******/*******/******n\" out=\"*******/*******/*******/*******/*******/*******/******Y *******/*******/*******/*******/*******/*******/*******\" />\n          </all>\n          <all>\n            <rule in=\"Y*****B\" out=\"******R\" />\n            <rule in=\"R*****B\" out=\"******R\" />\n          </all>\n          <one in=\"B*****B/*******/*******/*******/*******/*******/B*****B @W****@/*******/*******/*******/*******/*******/@*****@ *******/*******/*******/*******/*******/*******/******* Y*****Y/*******/*******/*******/*******/*******/Y*****Y\" out=\"Y*****Y/*******/*******/*******/*******/*******/Y*****Y *******/*******/*******/*******/*******/*******/******* *******/*******/*******/*******/*******/*******/******* *******/*******/*******/*******/*******/*******/*******\" />\n          <one in=\"B* @W ** Y*\" out=\"Y* ** ** **\" />\n          <one in=\"B* @W\" out=\"Y* **\" />\n          <one in=\"B*****B/*******/*******/*******/*******/*******/B*****B @*****@/*******/*******/*******/*******/*******/@*****@ *******/*******/*******/*******/*******/*******/******* Y*****Y/*******/*******/*******/*******/*******/Y*****Y\" out=\"Y*****Y/*******/*******/*******/*******/*******/Y*****Y *******/*******/*******/*******/*******/*******/******* *******/*******/*******/*******/*******/*******/******* *******/*******/*******/*******/*******/*******/*******\" />\n          <one in=\"B @ * Y\" out=\"Y * * *\" />\n        </markov><prl>\n          <rule in=\"R\" out=\"B\" />\n          <rule in=\"*****/*****/**Y**/*****/***** BB*BB/BB*BB/*****/BB*BB/BB*BB\" out=\"*****/*****/*****/*****/***** FF*FF/FF*FF/*****/FF*FF/FF*FF\" />\n        </prl><prl>\n          <rule in=\"FLF\" out=\"*B*\" />\n          <rule in=\"FLB\" out=\"we*\" />\n          <rule in=\"eLV\" out=\"*e*\" />\n          <rule in=\"Y O\" out=\"* R\" />\n        </prl><prl>\n          <rule in=\"wBw\" out=\"*w*\" />\n          <rule in=\"wBB/eVB/Bew\" out=\"*ww/**w/***\" />\n          <rule in=\"F\" out=\"B\" />\n          <rule in=\"Y\" out=\"B\" />\n          <rule in=\"s\" out=\"S\" />\n          <rule in=\"L J\" out=\"B *\" />\n          <rule in=\"o\" out=\"O\" />\n        </prl><prl>\n          <rule in=\"*****/*****/**S**/*****/*****\" out=\"*****/*****/**s**/*****/*****\" />\n          <rule in=\"%B***B% JJJJJJJ\" out=\"*FF*FF* *******\" />\n        </prl><prl in=\"S**O\" out=\"***o\" /><markov comment=\"make an entrance\">\n          <all>\n            <rule in=\"N*****o\" out=\"******N\" />\n            <rule in=\"nUUUUUU\" out=\"*WWSWWn\" />\n            <rule in=\"nUUUUUoBBSB\" out=\"*WWSWWNWW*W\" />\n          </all>\n          <path from=\"o\" to=\"n\" on=\"FsOR\" color=\"U\" inertia=\"True\" longest=\"True\" />\n        </markov><prl>\n          <rule in=\"WN\" out=\"*n\" />\n          <rule in=\"wFw\" out=\"*w*\" />\n        </prl><prl>\n          <rule in=\"F\" out=\"B\" />\n          <rule in=\"N\" out=\"O\" />\n          <rule in=\"s\" out=\"S\" />\n        </prl><prl comment=\"fill contours with floors\">\n          <rule in=\"RBB*BBR\" out=\"*aa*aa*\" />\n          <rule in=\"RBB*BBn\" out=\"*aa*aa*\" />\n          <rule in=\"R\" out=\"Y\" />\n        </prl><all in=\"BBBBBBB/aa*aaYB/BBBBBBB\" out=\"*******/BB*BB**/*******\" comment=\"retract dead ends\" /><union symbol=\"#\" values=\"hU\" /><union symbol=\"$\" values=\"sQ\" /><union symbol=\"%\" values=\"OnR\" />\n        === WALLS AND WINDOWS ===\n        на существенные и несущественные стены нужно разделить еще до стягивания\n        принцип такой: если окно проходит по контуру сверху или снизу, то оно существенное, остальные нет\n\n        <prl in=\"B\" out=\"x\" /><prl in=\"* x\" out=\"* B\" /><prl in=\"S B B S\" out=\"* h h *\" /><prl in=\"888/8S8/888\" out=\"***/*s*/***\" /><prl in=\"1*****1/3**s**3/1*****1\" out=\"*******/***S***/*******\" /><prl in=\"s**2/***1\" out=\"Q***/****\" /><all comment=\"mark boundary walls as windows\">\n          <rule in=\"e* *h *h **\" out=\"** *U *U **\" />\n          <rule in=\"** *h *h e*\" out=\"** *U *U **\" />\n          <rule in=\"U $ h h\" out=\"* * U U\" />\n          <rule in=\"h h $ U\" out=\"U U * *\" />\n        </all><markov>\n          <all comment=\"retract windows\">\n            <rule in=\"#* #* *W\" out=\"r* r* **\" comment=\"retract walls over the main path\" />\n            <rule in=\"U U s J\" out=\"r r * *\" />\n            <rule in=\"x s # #\" out=\"* * r r\" />\n            <rule in=\"U U $ r\" out=\"r r * *\" />\n            <rule in=\"r $ U U\" out=\"* * r r\" />\n            <rule in=\"# r\" out=\"r *\" />\n            <rule in=\"h h s r\" out=\"r r * *\" />\n            <rule in=\"r s h h\" out=\"* * r r\" />\n          </all>\n          <one in=\"***r*** Raa*aaY\" out=\"******* nAA*AAR\" comment=\"move through unobstructed edge\" />\n          <one in=\"***h*** Raa*aaY\" out=\"***r*** nAA*AAR\" comment=\"move through inner wall\" />\n          <one in=\"***r*** Raa*aaR\" out=\"******* nAA*AAn\" comment=\"connect through unobstructed edge\" />\n          <one in=\"***h*** Raa*aaR\" out=\"***r*** nAA*AAn\" comment=\"connect through inner wall\" />\n          <one in=\"***r*** Raa*aan\" out=\"******* nAA*AA*\" comment=\"loop through unobstructed edge\" />\n          <one in=\"***h*** Raa*aan\" out=\"***r*** nAA*AA*\" comment=\"loop through inner wall\" />\n          <one in=\"***r*** naa*aaY\" out=\"******* *AA*AAR\" comment=\"sprout through unobstructed edge\" />\n          <one in=\"***h*** naa*aaY\" out=\"***r*** *AA*AAR\" comment=\"sprout through inner wall\" />\n          <one in=\"***U*** Raa*aaY\" out=\"***r*** nAA*AAR\" comment=\"move through window\" />\n          <one in=\"***U*** Raa*aaR\" out=\"***r*** nAA*AAn\" comment=\"connect through window\" />\n          <one in=\"***U*** Raa*aan\" out=\"***r*** nAA*AA*\" comment=\"loop through window\" />\n          <one in=\"***U*** naa*aaY\" out=\"***r*** *AA*AAR\" comment=\"sprout through window\" />\n        </markov><prl>\n          <rule in=\"x\" out=\"B\" />\n          <rule in=\"r\" out=\"B\" />\n          <rule in=\"R\" out=\"n\" />\n          <rule in=\"s\" out=\"S\" />\n          <rule in=\"Q\" out=\"S\" />\n          <rule in=\"* * Y\" out=\"* * O\" />\n        </prl><prl in=\"***B*** naa*aan\" out=\"******* *AA*AA*\" comment=\"make all dirs open in open areas\" /><prl in=\"n\" out=\"Y\" /><prl in=\"WY\" out=\"*n\" />\n\n        как раньше, нужно удалить окна, примыкающие к контурам снаружи\n        альтернатива: окна как разграничители внешности и внутренности (когда-то хотел сделать внутренность как пересечение платформ!)\n        кстати, можно усилить условие перехода вверх на этаж, чтобы платформы пересекались по большим кускам\n        <union symbol=\"1\" values=\"WaA\" /><union symbol=\"2\" values=\"nXY\" /><union symbol=\"3\" values=\"nY\" /><union symbol=\"4\" values=\"WA\" /><union symbol=\"5\" values=\"Bk\" /><union symbol=\"6\" values=\"BUR\" /><union symbol=\"8\" values=\"BL\" /><union symbol=\"?\" values=\"Qu\" /><union symbol=\",\" values=\"e\" /> Be e\n        <union symbol=\"^\" values=\"Vu\" /><union symbol=\".\" values=\"BJX\" />\n\n        === COLUMNS ===\n        <all comment=\"mark insides of the bottom floor\">\n          <rule in=\"wB JJ\" out=\"*F **\" />\n          <rule in=\"FB\" out=\"*F\" />\n          <rule in=\"F*B\" out=\"**F\" />\n          <rule in=\"*44*44*/4*****4/4*****4/***V***/4*****4/4*****4/*44*44*\" out=\"*******/*******/*******/***x***/*******/*******/*******\" />\n        </all><prl comment=\"mark vertices that can be a base of a column\">\n          <rule in=\"2*****2/*******/*******/***V***/*******/*******/2*****2\" out=\"*******/*******/*******/***v***/*******/*******/*******\" />\n          <rule in=\"2*****2/*******/***w***/**eVe**\" out=\"*******/*******/*******/***v***\" />\n          <rule in=\"2***/****/**we/**eV\" out=\"****/****/****/***v\" />\n          <rule in=\"2*****2/*******/****w**/**eV***/***e***/*******/******2\" out=\"*******/*******/*******/***v***/*******/*******/*******\" />\n          <rule in=\"VF JJ\" out=\"v* **\" />\n          <rule in=\"V*/*w JJ/JJ\" out=\"v*/** **/**\" />\n        </prl><all comment=\"drop columns\">\n          <rule in=\"v B B\" out=\"* D D\" />\n          <rule in=\"D V B B\" out=\"* * D D\" />\n          <rule in=\"x B B\" out=\"* D D\" />\n        </all><all comment=\"retract columns\">\n          <rule in=\"D D V .\" out=\"r r * *\" />\n          <rule in=\"D D V r\" out=\"r r * *\" />\n          <rule in=\"D D x\" out=\"r r *\" />\n        </all><prl in=\"O*****O/*BBBBB*/*BBBBB*/*BB*BB*/*BBBBB*/*BBBBB*/O*****O JJJJJJJ/JJJJJJJ/JJJJJJJ/JJJJJJJ/JJJJJJJ/JJJJJJJ/JJJJJJJ\" out=\"*******/*RRRRR*/*RRRRR*/*RR*RR*/*RRRRR*/*RRRRR*/******* *******/*******/*******/*******/*******/*******/*******\" comment=\"mark grass nodes\" /><prl in=\"R*/*O JJ/JJ\" out=\"**/** **/*X\" /><prl>\n          <rule in=\"r\" out=\"B\" />\n          <rule in=\"F\" out=\"B\" />\n          <rule in=\"v\" out=\"V\" />\n          <rule in=\"x\" out=\"V\" />\n          <rule in=\"R\" out=\"B\" />\n        </prl><prl in=\"V*** B**h B**h\" out=\"**** ***B ***B\" comment=\"erase walls not bounded by columns\" /><prl comment=\"expand walls and windows\">\n          <rule in=\"V*****V **BhB** **BhB**\" out=\"******* **h*h** **h*h**\" />\n          <rule in=\"V*****V **BUB** **BUB**\" out=\"******* **U*U** **U*U**\" />\n        </prl>\n\n        === OVERHANGS ===\n        <prl in=\"*****/*****/**L**/*****/*****\" out=\"*****/*****/**Q**/*****/*****\" /><prl in=\"L\" out=\"B\" /><prl in=\"Q\" out=\"L\" /><prl>\n          <rule in=\"***/UUU/*** BBB/***/BBB ***/UUU/***\" out=\"***/***/*** rrr/***/rrr ***/***/***\" />\n          <rule in=\"L*e/BBB\" out=\"***/rrr\" />\n          <rule in=\"*e*/e*L/*LB\" out=\"***/***/**r\" />\n          <rule in=\"n*/*B\" out=\"**/*r\" />\n          <rule in=\"X*/*B\" out=\"**/*r\" />\n          <rule in=\"LBBBBBL\" out=\"*rrrrr*\" comment=\"erase thin rectangles\" />\n        </prl><all in=\"rB\" out=\"*r\" /><prl in=\"eB/*L\" out=\"*F/**\" /><all in=\"FB\" out=\"*F\" /><prl>\n          <rule in=\"rLr\" out=\"*B*\" />\n          <rule in=\"rLB\" out=\"*B*\" />\n          <rule in=\"BLB\" out=\"*B*\" />\n        </prl><all>\n          <rule in=\"r\" out=\"B\" />\n          <rule in=\"LFFFFF/*LL*LL/LFFFFF\" out=\"******/*FF*FF/******\" />\n          <rule in=\"FFFFFFF/F*LL*LL/FFFFFFF\" out=\"*******/**FF*FF/*******\" />\n        </all><markov comment=\"select a disjoint subset of rectangles\">\n          <all>\n            <rule in=\"NF\" out=\"*N\" />\n            <rule in=\"LN/V*\" out=\"**/Q*\" />\n            <rule in=\"NNN/NVN/NNN\" out=\"***/*Q*/***\" />\n          </all>\n          <all>\n            <rule in=\"RF\" out=\"*R\" />\n            <rule in=\"Q B B\" out=\"* Z Z\" />\n            <rule in=\"B B Q\" out=\"Z Z *\" />\n            <rule in=\"Z * B B\" out=\"* * Z Z\" />\n            <rule in=\"B B * Z\" out=\"Z Z * *\" />\n            <rule in=\"Z*/** **/*F\" out=\"**/** **/*R\" />\n            <rule in=\"**/*F Z*/**\" out=\"**/*R **/**\" />\n          </all>\n          <one in=\"LF/VL\" out=\"*N/Q*\" />\n        </markov><prl>\n          <rule in=\"RLB\" out=\"*B*\" />\n          <rule in=\"RLR\" out=\"*B*\" />\n          <rule in=\"F\" out=\"B\" />\n        </prl><prl>\n          <rule in=\"R\" out=\"B\" />\n          <rule in=\"Z\" out=\"B\" />\n          <rule in=\"Q\" out=\"V\" />\n          <rule in=\"N\" out=\"B\" />\n          <rule in=\"o\" out=\"B\" />\n          <rule in=\"K\" out=\"B\" />\n        </prl>\n\n        === MERGING ===\n        <prl in=\"UUU*B\" out=\"kkk**\" comment=\"mark all composite windows\" /><prl comment=\"merge elementary windows vertically\">\n          <rule in=\"***/UUU/*** BBB/BSB/BBB ***/UUU/***\" out=\"***/***/*** ***/Q*Q/*** ***/***/***\" />\n          <rule in=\"UUU eSe UUU\" out=\"*** u*u ***\" />\n        </prl><all comment=\"detect and propagate horizontal mismatches\">\n          5=Bk, ?=Qq\n          <rule in=\"U***5 ?**** U***U\" out=\"Z**** ***** Z***Z\" /><rule in=\"U***U ?**** U***5\" out=\"Z***Z ***** Z****\" /><rule in=\"U Z\" out=\"Z *\" /><rule in=\"Z U\" out=\"* Z\" /><rule in=\"U * Z\" out=\"Z * *\" /><rule in=\"Z * U\" out=\"* * Z\" /><rule in=\"U***U/***** *****/**e** U***U/*****\" out=\"Z***Z/***** *****/***** Z***Z/*****\" comment=\"ban merges that cross contours\" /><rule in=\"5***U ee*ee\" out=\"****Z *****\" comment=\"ban merges that create broken contours\" /><rule in=\"U***U *w*ee\" out=\"Z***Z *****\" comment=\"ban merges that create broken contours\" /><rule in=\"U***U/*****/**U**\" out=\"Z***Z/*****/**Z**\" comment=\"ban merges near triple points\" /><rule in=\"h***U/*****/**U**\" out=\"****Z/*****/**Z**\" comment=\"ban merges near triple points\" /><rule in=\"**5**/*****/5*D*U/*****/**5**\" out=\"*****/*****/****Z/*****/*****\" comment=\"ban merges near endpoints\" />\n        </all><prl in=\"U*D*U\" out=\"F*x*F\" comment=\"merge windows horizontally\" /><all comment=\"mark windows for stylization\">\n          <rule in=\"*ee*ee*ee*ee* D*****x*****x D*****x*****x *ee*ee*ee*ee*\" out=\"************* ******o*****o ******o*****o *************\" />\n          <rule in=\"*ee*ee*ee*ee* o*****x*****x o*****x*****x *ee*ee*ee*ee*\" out=\"************* ******o*****o ******o*****o *************\" />\n        </all><all comment=\"burn odd style windows\">\n          <rule in=\"o*****x*****D o*****x*****D\" out=\"R************ R************\" />\n          <rule in=\"o*****x*****o o*****x*****o\" out=\"R***********R R***********R\" />\n          <rule in=\"R*****o R*****o\" out=\"******R ******R\" />\n        </all><prl>\n          <rule in=\"R\" out=\"x\" />\n          <rule in=\"o**U**D\" out=\"******K\" />\n        </prl><all>\n          <rule in=\"**U/***/U*D ***/***/?** **5/***/U*D\" out=\"**Z/***/Z** ***/***/*** ***/***/Z**\" />\n          <rule in=\"**5/***/U*D ***/***/?** **U/***/U*D\" out=\"***/***/Z** ***/***/*** **Z/***/Z**\" />\n          <rule in=\"U Z\" out=\"Z *\" />\n          <rule in=\"Z U\" out=\"* Z\" />\n          <rule in=\"U * Z\" out=\"Z * *\" />\n          <rule in=\"Z * U\" out=\"* * Z\" />\n          <rule in=\"5*D*U ee**? 5*D*U\" out=\"****Z ***** ****Z\" comment=\"ban corner merges near hanging contours\" />\n        </all><prl in=\"**U**/*****/U*D*B/*****/**B**\" out=\"**F**/*****/F*x**/*****/*****\" /><prl>\n          <rule in=\"Z\" out=\"U\" />\n          <rule in=\"UUU u*u UUU\" out=\"*** e*e ***\" />\n        </prl><prl in=\"x V x\" out=\"* u *\" /><prl comment=\"merge remaining elementary windows horizontally\">\n          <rule in=\",***, U*D*U U*D*U B***B\" out=\"***** N*x*N N*x*N *****\" />\n          <rule in=\",***, U*D*U U*D*U e***e\" out=\"***** N*x*N N*x*N *****\" />\n          <rule in=\"**,**/*****/****,/*****/***** **U**/*****/B*D*U/*****/**B** **U**/*****/B*D*U/*****/**B** **B**/*****/****B/*****/*****\" out=\"*****/*****/*****/*****/***** **N**/*****/**x*N/*****/***** **N**/*****/**x*N/*****/***** *****/*****/*****/*****/*****\" />\n          <rule in=\"**,**/*****/****,/*****/***** **U**/*****/B*D*U/*****/**B** **U**/*****/B*D*U/*****/**B** **e**/*****/****e/*****/*****\" out=\"*****/*****/*****/*****/***** **N**/*****/**x*N/*****/***** **N**/*****/**x*N/*****/***** *****/*****/*****/*****/*****\" />\n        </prl><prl>\n          <rule in=\"F\" out=\"U\" />\n          <rule in=\"N\" out=\"U\" />\n          <rule in=\"K\" out=\"D\" />\n        </prl><prl>\n          <rule in=\"kB*Bk\" out=\"*K*K*\" />\n          <rule in=\"kkB*/***B/***k/***k\" out=\"**K*/***K/****/****\" />\n          <rule in=\"kBD\" out=\"*K*\" />\n        </prl><all comment=\"mark bounded wood\">\n          <rule in=\"Bkkk\" out=\"*RRR\" />\n          <rule in=\"Rk\" out=\"*R\" />\n          <rule in=\"k*B*R\" out=\"R****\" />\n          <rule in=\"R*B/***/B*k\" out=\"***/***/**R\" />\n          <rule in=\"kkk kkk B*B RRR\" out=\"RRR RRR *** ***\" />\n          <rule in=\"RRR B*B kkk kkk\" out=\"*** *** RRR RRR\" />\n        </all><prl>\n          <rule in=\"K\" out=\"B\" />\n          <rule in=\"k*B*k\" out=\"**x**\" />\n          <rule in=\"kk*B/****/***k/***k\" out=\"***x/****/****/****\" />\n        </prl><prl in=\"k\" out=\"U\" /><prl in=\"R\" out=\"k\" /><all comment=\"burn solitary wood\">\n          <rule in=\"**6*****6**/***********/6***kkk***6/***********/**6*****6**\" out=\"***********/***********/****RRR****/***********/***********\" />\n          <rule in=\"kkk kkk B*B RRR\" out=\"RRR RRR *** ***\" />\n          <rule in=\"RRR B*B kkk kkk\" out=\"*** *** RRR RRR\" />\n        </all><prl in=\"R\" out=\"B\" /><prl in=\"D*UUU*D\" out=\"**hhh**\" comment=\"convert solo windows to walls\" /><prl in=\"h Q h\" out=\"* B *\" /><all comment=\"erase overhangs that touch merged windows\">\n          <rule in=\"uLL\" out=\"*RR\" />\n          <rule in=\"R*LL\" out=\"**RR\" />\n          <rule in=\"R**/*LL\" out=\"***/*RR\" />\n        </all><prl in=\"R\" out=\"B\" /><prl>\n          <rule in=\"BBB/BBB/BBB BBB/BVB/BBB .../.../...\" out=\"***/***/*** ***/*B*/*** ***/***/***\" />\n          <rule in=\"BBB/BBB/BBB BBB/BSB/BBB .../.../...\" out=\"***/***/*** ***/*B*/*** ***/***/***\" />\n          <rule in=\"BBB/BBB/BBB BBB/BsB/BBB .../.../...\" out=\"***/***/*** ***/*B*/*** ***/***/***\" />\n          <rule in=\"BBB/BBB/BBB BBB/BOB/BBB BBB/BBB/BBB\" out=\"***/***/*** ***/*B*/*** ***/***/***\" />\n          <rule in=\"BBB/BBB/BBB BBB/BOB/BBB JJJ/JJJ/JJJ\" out=\"***/***/*** ***/*B*/*** ***/***/***\" />\n        </prl>\n        <prl>\n          <rule in=\"J\" out=\"B\" />\n          <rule in=\"WWS** B*W** **W*B **SWW\" out=\"***** F**** ****F *****\" />\n        </prl>\n        <map scale=\"3/3 3/3 6/3\" regular=\"False\" values=\"BlWdUONgJoH faZEhLkFGCc XwMmTyKYR\" outputValues=\"BlWdUONgJoH\" transparent=\"T\" folder=\"ModernHouse\">\n          29+1 values\n          <rule in=\"BBB/BBB/BBB ***/*n*/*** BBB/BBB/BBB\" fout=\"Node\" legend=\"*fO\" />\n          <rule in=\"BBB/BBB/BBB ***/*Y*/*** BBB/BBB/BBB\" fout=\"SecondaryNode\" legend=\"*RlO\" />\n          <rule in=\"BBB/BBB/BBB ***/WXW/*** BBB/BBF/BBB\" fout=\"ContactTop\" legend=\"*aO\" />\n          <rule in=\"BBB/FBB/BBB ***/WXW/*** BBB/BBB/BBB\" fout=\"ContactBottom\" legend=\"*aO\" />\n          <rule in=\"BBB/FBB/BBB ***/WXW/*** BBB/BBF/BBB\" fout=\"Bridge\" legend=\"*aO\" />\n          <rule in=\"***/***/*** ***/*O*/*** ***/*X*/***\" fout=\"Grass\" legend=\"*g\" />\n          <rule in=\"***/***/*** ***/WSW/*** ***/***/***\" fout=\"Dir\" legend=\"*aO\" />\n          <rule in=\"***/***/*** ***/ASA/*** ***/***/***\" fout=\"SecondaryDir\" legend=\"*EO\" />\n          <rule in=\"***/***/*** ***/aSa/*** ***/***/***\" fout=\"TertiaryDir\" legend=\"*L\" />\n          <rule in=\"***/***/*** ***/WS*/*** ***/*W*/***\" fout=\"Down\" legend=\"a*O\" />\n          <rule in=\"***/*W*/*** ***/*SW/*** ***/***/***\" fout=\"Up\" legend=\"*aO\" />\n          <rule in=\"***/***/*** weB/wSB/weB ***/***/***\" fout=\"Edge\" legend=\"*dW\" />\n          <rule in=\"***/***/*** ***/*S*/*** *U*/*U*/*U*\" fout=\"WindowTop\" legend=\"*U\" />\n          <rule in=\"*U*/*U*/*U* ***/*S*/*** ***/***/***\" fout=\"WindowBottom\" legend=\"*U\" />\n          <rule in=\"***/***/*** *?*/*S*/*?* ***/***/***\" fout=\"WindowMid\" legend=\"*U\" />\n          <rule in=\"***/***/*** ***/*S*/*** *h*/*h*/*h*\" fout=\"WallTop\" legend=\"*h\" />\n          <rule in=\"*h*/*h*/*h* ***/*S*/*** ***/***/***\" fout=\"WallBottom\" legend=\"*h\" />\n          <rule in=\"***/***/*** ***/*S*/*** *k*/*k*/*k*\" fout=\"WoodTop\" legend=\"*k\" />\n          <rule in=\"*k*/*k*/*k* ***/*S*/*** ***/***/***\" fout=\"WoodBottom\" legend=\"*k\" />\n          <rule in=\"BBB/BBB/BBB BBB/LSL/BBB BBB/BBB/BBB\" fout=\"FrameDir\" legend=\"*N\" />\n          <rule in=\"***/***/*** weB/eV8/B8B ***/***/***\" fout=\"Out\" legend=\"*dWG\" />\n          <rule in=\"***/***/*** weB/wV8/weB ***/***/***\" fout=\"Wall\" legend=\"*dW\" />\n          <rule in=\"***/***/*** www/wVe/weB ***/***/***\" fout=\"In\" legend=\"*dW\" />\n          <rule in=\"***/***/*** ***/*V*/*** ***/*D*/***\" fout=\"ColumnTop\" legend=\"*Z\" />\n          <rule in=\"***/*D*/*** ***/*V*/*** ***/***/***\" fout=\"ColumnBottom\" legend=\"*Z\" />\n          <rule in=\"***/***/*** ***/*^*/*** ***/*x*/***\" fout=\"GlassColumnTop\" legend=\"*U\" /> ^ = Vq\n          <rule in=\"***/*x*/*** ***/*^*/*** ***/***/***\" fout=\"GlassColumnBottom\" legend=\"*U\" />\n          <rule in=\"***/*x*/*** ***/*u*/*** ***/*x*/***\" fout=\"GlassColumnMid\" legend=\"*U\" />\n          <rule in=\"BBB/BBB/BBB B8B/8VL/B8B BBB/BBB/BBB\" fout=\"FrameVertex\" legend=\"*F\" />\n          <rule in=\"***/***/*** ***/*V*/*** ***/*o*/***\" fout=\"StyleTop\" legend=\"*C\" />\n          <rule in=\"***/*o*/*** ***/*V*/*** ***/***/***\" fout=\"StyleBottom\" legend=\"*C\" />\n\n          <one in=\"R\" out=\"Y\" steps=\"9\"/>\n          <!--<prl in=\"R\" out=\"Y\" p=\"0.5\" steps=\"1\"/>-->\n          <prl in=\"R\" out=\"B\"/>\n          \n          <prl steps=\"1\">\n            <rule in=\"B****/*fff*/*f*f*/*fff*/*****\" out=\"fffff/f***f/f***f/f***f/fffff\" />\n            <rule in=\"B****/*lll*/*l*l*/*lll*/*****\" out=\"lllll/l***l/l***l/l***l/lllll\" />\n            <rule in=\"NBF\" out=\"*N*\" />\n          </prl>\n          <prl steps=\"1\">\n            <rule in=\"BaOaB\" out=\"a***a\" />\n            <rule in=\"WaOaW\" out=\"a***a\" />\n            <rule in=\"BaOaW\" out=\"a***a\" />\n            <rule in=\"daOad\" out=\"a***a\" />\n            <rule in=\"BEOEB\" out=\"E***E\" />\n            <rule in=\"BLLLB\" out=\"L***L\" />\n          </prl><prl in=\"B\" out=\"J\" /><prl in=\"J * *\" out=\"B * *\" /><prl>\n            <rule in=\"Z B Z\" out=\"* Z *\" />\n            <rule in=\"Z B J\" out=\"* Z *\" />\n          </prl><all>\n            <rule in=\"d Z\" out=\"* X\" />\n            <rule in=\"Z d\" out=\"X *\" />\n            <rule in=\"G Z\" out=\"* X\" />\n            <rule in=\"Z G\" out=\"X *\" />\n            <rule in=\"X Z\" out=\"* X\" />\n            <rule in=\"Z X\" out=\"X *\" />\n          </all><prl>\n            <rule in=\"Z*hhh*Z\" out=\"**yyy**\" />\n            <rule in=\"Z*hhh*U\" out=\"**yyy**\" />\n            <rule in=\"U*hhh*U\" out=\"**yyy**\" />\n          </prl><prl>\n            <rule in=\"J J\" out=\"* o\" />\n            <rule in=\"f\" out=\"a\" />\n            <rule in=\"E\" out=\"l\" />\n            <rule in=\"OaaO\" out=\"*OO*\" />\n            <rule in=\"OllO\" out=\"*OO*\" />\n            <rule in=\"Z d Z\" out=\"* Z *\" />\n            <rule in=\"Z d J\" out=\"* Z *\" />\n            <rule in=\"Z G Z\" out=\"* Z *\" />\n            <rule in=\"Z G J\" out=\"* Z *\" />\n            <rule in=\"X d X\" out=\"* X *\" />\n            <rule in=\"X d J\" out=\"* X *\" />\n            <rule in=\"X G X\" out=\"* X *\" />\n            <rule in=\"X G J\" out=\"* X *\" />\n            <rule in=\"hB Ba\" out=\"** h*\" />\n            <rule in=\"hB Bl\" out=\"** h*\" />\n          </prl><union symbol=\"1\" values=\"al\" /><union symbol=\".\" values=\"BJ\" /><prl in=\"ByB/ByB/ByB ByB/ByB/ByB ByB/ByB/ByB ByB/ByB/ByB ByB/ByB/ByB BB1/BB1/BB1 .../.../...\" out=\"*B*/*B*/*B* *B*/*B*/*B* *B*/*B*/*B* *B*/*B*/*B* *B*/*B*/*B* ***/***/*** ***/***/***\" comment=\"erase some inner walls\" /><prl>\n            <rule in=\"yB Ba\" out=\"** y*\" />\n            <rule in=\"yB Bl\" out=\"** y*\" />\n          </prl><prl>\n            <rule in=\"ZB\" out=\"*H\" />\n            <rule in=\"Z*/*B\" out=\"**/*H\" />\n            <rule in=\"XB\" out=\"*w\" />\n            <rule in=\"X*/*B\" out=\"**/*w\" />\n            <rule in=\"lll/lOl/lll\" out=\"***/*l*/***\" />\n            <rule in=\"h B h\" out=\"* h *\" />\n            <rule in=\"y B y\" out=\"* y *\" />\n          </prl><prl>\n            <rule in=\"hB\" out=\"*M\" />\n            <rule in=\"h*/*B\" out=\"**/*M\" />\n            <rule in=\"yB\" out=\"*m\" />\n            <rule in=\"y*/*B\" out=\"**/*m\" />\n            <rule in=\"FN/NB\" out=\"**/*T\" />\n          </prl><all in=\"TB\" out=\"*T\" /><markov>\n            <all>\n              <rule in=\"NTNTTT\" out=\"**N*N*\" />\n              <rule in=\"TTT/TNT\" out=\"*N*/***\" />\n            </all>\n            <one in=\"NTTTTT/NTTTTT/NTTTTT/WWWWWW\" out=\"**N*N*/**N*N*/**N*N*/******\" />\n          </markov><prl>\n            <rule in=\"NFN\" out=\"*N*\" />\n            <rule in=\"T\" out=\"B\" />\n          </prl><all in=\"F B\" out=\"* F\" comment=\"drop wooden columns\" /><prl>\n            <rule in=\"F\" out=\"N\" />\n            <rule in=\"G\" out=\"d\" />\n          </prl><prl>\n            <rule in=\"gBBBBBg\" out=\"**g*g**\" comment=\"finalize grass\" />\n            <rule in=\"UBU\" out=\"*U*\" comment=\"finalize windows\" />\n            <rule in=\"L\" out=\"l\" />\n            <rule in=\"a\" out=\"l\" />\n          </prl><prl in=\"W**/***/**g\" out=\"***/***/**B\" comment=\"erase grass too close to the house\" /><prl in=\"lll/lBl/lll\" out=\"***/*l*/***\" /><prl steps=\"1\">\n            <rule in=\"B g\" out=\"g *\" />\n            <rule in=\"Uw\" out=\"*d\" />\n          </prl><prl>\n            <rule in=\"OOOOOOO/OlllllO/OlllllO/OlllllO/OlllllO/OlllllO/OOOOOOO\" out=\"*******/*OOOOO*/*OOOOO*/*OOOOO*/*OOOOO*/*OOOOO*/*******\" />\n            <rule in=\"U l\" out=\"* d\" />\n          </prl><prl in=\"O*/*l\" out=\"**/*a\" /><prl in=\"k*k*B\" out=\"****K\" /><prl>\n            <rule in=\"K\" out=\"k\" />\n            <rule in=\"k B k\" out=\"* k *\" />\n            <rule in=\"k B J\" out=\"* k *\" />\n          </prl><prl>\n            <rule in=\"CBUUU\" out=\"*CCCC\" />\n            <rule in=\"CBC\" out=\"*C*\" />\n          </prl><all>\n            <rule in=\"*****/WWWWW *CCCC/wBBBB\" out=\"*****/***** ****c/****c\" />\n            <rule in=\"*****/WWWWW cCCCC/cBBBB\" out=\"*****/***** ****c/****c\" />\n          </all><all in=\"cc CB\" out=\"** cc\" /><prl in=\"cBBB/cCCC cBBB/cCCC WWWW/****\" out=\"*ccc/*ccc *ccc/*ccc ****/****\" /><prl in=\"B W\" out=\"W *\" steps=\"1\" comment=\"rise contours\" /><prl in=\"UWB/WWB/BBB\" out=\"*B*/BB*/***\" comment=\"erase contours near windows\" /><prl in=\"UWB\" out=\"*B*\" /><prl>\n            <rule in=\"c\" out=\"W\" />\n            <rule in=\"C\" out=\"U\" />\n          </prl><prl>\n            <rule in=\"w\" out=\"W\" /><rule in=\"M\" out=\"W\" /><rule in=\"m\" out=\"H\" /><rule in=\"a\" out=\"l\" /> O\n            <rule in=\"UUW\" out=\"**d\" /><rule in=\"U U W\" out=\"* * d\" /><rule in=\"W B J\" out=\"* W *\" /><rule in=\"H B J\" out=\"* H *\" /><rule in=\"W B W\" out=\"* W *\" /><rule in=\"W l l W\" out=\"* W W *\" /><rule in=\"hW dB\" out=\"** *W\" />\n          </prl><prl>\n            <rule in=\"k\" out=\"N\" />\n            <rule in=\"h\" out=\"d\" />\n            <rule in=\"X\" out=\"d\" />\n            <rule in=\"y\" out=\"d\" />\n            <rule in=\"Z\" out=\"d\" />\n          </prl>\n        </map>\n      </wfc>\n    </map>\n  </wfc>\n</sequence>\n<!--\nRendered outputs: https://twitter.com/ExUtumno/status/1141354217774428160\n-->"
  },
  {
    "path": "models/MultiHeadedDungeon.xml",
    "content": "<sequence values=\"BRWGO\" origin=\"True\">\n  <markov>\n    <sequence>\n      <one in=\"R***B\" out=\"*WWWG\" steps=\"3\"/>\n      <all in=\"R\" out=\"O\"/>\n      <all in=\"G\" out=\"R\"/>\n    </sequence>\n  </markov>\n  <all in=\"BBB/BOB\" out=\"***/*R*\"/>\n  <all in=\"RBBBR\" out=\"OWWWO\"/>\n  <all in=\"R\" out=\"O\"/>\n  <all in=\"BBBBB/BOWWW/BBBBB\" out=\"*****/*BBBB/*****\"/>\n  <all in=\"OWWWO/W***W/W***W/W***W/OBBBO\" out=\"*****/*****/*****/*****/*WWW*\"/>\n  <all in=\"*******/*OWWWO*/*W***W*/*W*B*W*/*W***W*/*OWWWO*/*******\" out=\"WWWWWWW/W*****W/W*WWW*W/W*WGW*W/W*WWW*W/W*****W/WWWWWWW\"/>\n  <all in=\"O\" out=\"W\"/>\n  <prl in=\"GWWWG/WWWWW/WWWWW/WWWWW/GWWWG\" out=\"BBBBB/BBBBB/BBBBB/BBBBB/BBBBB\"/>\n  <all in=\"G\" out=\"W\"/>\n</sequence>\n"
  },
  {
    "path": "models/MultiHeadedWalk.xml",
    "content": "<sequence values=\"BRGW\" origin=\"True\">\n  <sequence>\n    <one in=\"RB\" out=\"*G\" steps=\"3\"/>\n    <all in=\"R\" out=\"W\"/>\n    <all in=\"G\" out=\"R\"/>\n  </sequence>\n</sequence>\n"
  },
  {
    "path": "models/MultiHeadedWalkDungeon.xml",
    "content": "<sequence values=\"BRGW\" origin=\"True\">\n  <markov>\n    <sequence>\n      <one in=\"RBB\" out=\"*WG\" steps=\"3\"/>\n      <all in=\"R\" out=\"W\"/>\n      <all in=\"G\" out=\"R\"/>\n    </sequence>\n  </markov>\n  <all in=\"BBB/BWB\" out=\"***/*R*\"/>\n  <all in=\"RBR\" out=\"WWW\"/>\n  <all in=\"R\" out=\"W\"/>\n  <all in=\"BBB/BWB\" out=\"***/*B*\"/>\n  <all in=\"WWW/WBW\" out=\"***/*W*\"/>\n</sequence>\n"
  },
  {
    "path": "models/MultiSokoban8.xml",
    "content": "<sequence values=\"BRWI\">\n  <one in=\"BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB/BBBBBBBB\" out=\"BBBBBBBR/BWWBBBBR/BWBBBBBR/BWBBBBBB/BWBBBBBB/BWBBBBBB/BWWBIIII/BBBBIIII\" symmetry=\"()\"/>\n  <all search=\"True\" depthCoefficient=\"1.0\">\n    <rule in=\"RB\" out=\"BR\"/>\n    <rule in=\"RWB\" out=\"BRW\"/>\n\n    <observe value=\"I\" from=\"B\" to=\"W\"/>\n    <observe value=\"B\" to=\"BR\"/>\n    <observe value=\"W\" to=\"BR\"/>\n    <observe value=\"R\" to=\"BR\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/MultiSokoban9.xml",
    "content": "<sequence values=\"BRWIA\">\n  <one in=\"BBBBBBBBB/BBBBBBBBB/BBBBBBBBB/BBBBBBBBB/BBBBBBBBB/BBBBBBBBB/BBBBBBBBB/BBBBBBBBB/BBBBBBBBB\" out=\"BBBBBBBBB/BBBBBBBBB/BWWWWWBBR/BWBBBWBBR/BWBBBWBBR/BWBBBAIII/BWWWWAIII/BBBBBIIII/BBBBBIIII\" symmetry=\"()\"/>\n  <all search=\"True\" depthCoefficient=\"0.5\">\n    <rule in=\"RB\" out=\"BR\"/>\n    <rule in=\"RWB\" out=\"BRW\"/>\n\n    <observe value=\"I\" from=\"B\" to=\"W\"/>\n    <observe value=\"A\" from=\"W\" to=\"W\"/>\n    <observe value=\"B\" to=\"BR\"/>\n    <observe value=\"W\" to=\"BR\"/>\n    <observe value=\"R\" to=\"BR\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/NestedGrowth.xml",
    "content": "<all values=\"BWAD\" origin=\"True\">\n  <rule in=\"WB\" out=\"WW\"/>\n  <rule in=\"WD\" out=\"WW\"/>\n  <rule in=\"AW\" out=\"AA\"/>\n  <rule in=\"DA\" out=\"DD\"/>\n  <rule in=\"*WWW*/WWWWW/WWWWW/WWWWW/*WWW*\" out=\"*WWW*/WWWWW/WWAWW/WWWWW/*WWW*\"/>\n  <rule in=\"*WWW*/WWWWW/WWWWW/WWWWW/*WWW*\" out=\"*WWW*/WWWWW/WWWWW/WWWWW/*WWW*\"/>\n\n  <rule in=\"*AAA*/AAAAA/AAAAA/AAAAA/*AAA*\" out=\"*AAA*/AAAAA/AADAA/AAAAA/*AAA*\"/>\n  <rule in=\"*AAA*/AAAAA/AAAAA/AAAAA/*AAA*\" out=\"*AAA*/AAAAA/AAAAA/AAAAA/*AAA*\"/>\n\n  <rule in=\"*DDD*/DDDDD/DDDDD/DDDDD/*DDD*\" out=\"*DDD*/DDDDD/DDWDD/DDDDD/*DDD*\"/>\n  <rule in=\"*DDD*/DDDDD/DDDDD/DDDDD/*DDD*\" out=\"*DDD*/DDDDD/DDDDD/DDDDD/*DDD*\"/>\n</all>\n\n<!--\nFrom Imagegram: https://zaratustra.itch.io/imagegram\n-->\n"
  },
  {
    "path": "models/NoDeadEnds.xml",
    "content": "<markov values=\"BRWA\" origin=\"True\">\n  <one in=\"RBB\" out=\"WAR\"/>\n  <one in=\"WBB\" out=\"WAR\"/>\n  <all in=\"RBR\" out=\"WAW\"/>\n  <all in=\"RBW\" out=\"WAW\"/>\n</markov>\n\n<!--\nGenerates a maze with loops and without dead ends.\n\nUses RegularSAWRestart model.\n\nUsed in ModernHouse in inner wall placement.\n-->\n"
  },
  {
    "path": "models/Noise.xml",
    "content": "<sequence values=\"EWB\">\n  <prl in=\"E/*\" out=\"W/*\" symmetry=\"(x)\"/>\n  <prl in=\"*/W\" out=\"*/B\" symmetry=\"(x)\"/>\n  <one>\n    <rule in=\"EB\" out=\"EE\"/>\n    <rule in=\"WB\" out=\"WW\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/NystromDungeon.xml",
    "content": "<sequence values=\"BPWRG\" origin=\"True\">\n  <all in=\"PBB\" out=\"**P\" comment=\"draw a grid\"/>\n  <one in=\"PBPBPBPBP/BBBBBBBBB/PBPBPBPBP/BBBBBBBBB/PBPBPBPBP/BBBBBBBBB/PBPBPBPBP\" out=\"WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW\"/>\n  <markov comment=\"generate corridors with MazeBacktracker\">\n    <one in=\"RBP\" out=\"GGR\"/>\n    <one in=\"GGR\" out=\"RWW\"/>\n    <one in=\"P\" out=\"R\"/>\n  </markov>\n  <one in=\"R\" out=\"G\" steps=\"1\" comment=\"leave only one start\"/>\n  <all in=\"R\" out=\"W\" comment=\"forget other starts\"/>\n  <markov comment=\"connect components\">\n    <all in=\"GWW\" out=\"**G\"/>\n    <one in=\"GBW\" out=\"*WG\"/>\n  </markov>\n  <one in=\"GBG\" out=\"*W*\" steps=\"5\" comment=\"insert cycles\"/>\n  <!--<one in=\"WGBG/WW**\" out=\"**W*/****\" steps=\"5\"/>-->\n  <all in=\"G\" out=\"W\" comment=\"forget structure\"/>\n  <all in=\"BBB/BWB\" out=\"BBB/BBB\" comment=\"retract dead ends\"/>\n</sequence>\n\n<!--\nGenerates a dungeon with rooms and corridors, containing a fixed amount of cycles.\n\nAdaptation of http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/\nCompare to implementations in conventional languages: https://github.com/munificent/rooms-and-mazes\n\nFor shorter versions that use connectivity patterns, see NystromKruskal and KruskalNystrom.\n-->\n"
  },
  {
    "path": "models/OddScale.xml",
    "content": "<sequence values=\"BW\" symmetry=\"()\">\n  <prl in=\"***/*B*/***\" out=\"***/*W*/***\"/>\n  <prl in=\"W\" out=\"B\" p=\"0.5\" steps=\"1\"/>\n  <map scale=\"2 2 1\" values=\"BFA\">\n    <rule in=\"W\" out=\"FAF/AAA/FAF\"/>\n  </map>\n</sequence>\n\n<!--\nNotice that the scale doesn't coincide with the rule output size.\n\nUseful for making rooms for regular dungeon generators.\n-->\n"
  },
  {
    "path": "models/OddScale3D.xml",
    "content": "<sequence values=\"BW\" symmetry=\"()\">\n  <prl in=\"***/***/*** ***/*B*/*** ***/***/***\" out=\"***/***/*** ***/*W*/*** ***/***/***\"/>\n  <prl in=\"W\" out=\"B\" p=\"0.5\" steps=\"1\"/>\n  <map scale=\"6 6 6\" values=\"BADYWU\" symmetry=\"()\">\n    <rule in=\"W\" out=\"DADADAD/AAAAAAA/DADADAD/AAAAAAA/DADADAD/AAAAAAA/DADADAD AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA DADADAD/AAAAAAA/DADADAD/AAAAAAA/DADADAD/AAAAAAA/DADADAD AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA DADADAD/AAAAAAA/DADADAD/AAAAAAA/DADADAD/AAAAAAA/DADADAD AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA/AAAAAAA DADADAD/AAAAAAA/DADADAD/AAAAAAA/DADADAD/AAAAAAA/DADADAD\"/>\n    <sequence symmetry=\"(xyz)\" comment=\"\">\n      <all>\n        <rule in=\"DB\" out=\"Y*\"/>\n        <rule in=\"D*/*B\" out=\"Y*/**\"/>\n      </all>\n      <all in=\"D\" out=\"A\"/>\n      <markov>\n        <one in=\"WAY\" out=\"WUW\"/>\n        <one in=\"Y\" out=\"W\"/>\n      </markov>\n      <all in=\"U\" out=\"W\"/>\n    </sequence>\n  </map>\n</sequence>\n\n<!--\nAfter this scale you can run all kinds of algorithms on the surface. Used in SurfaceDungeon and CarmaTower.\n-->\n"
  },
  {
    "path": "models/OpenCave.xml",
    "content": "<sequence values=\"DA\">\n  <prl in=\"***/*D*/***\" out=\"***/*A*/***\"/>\n  <prl in=\"A\" out=\"D\" p=\"0.5\" steps=\"1\"/>\n  <convolution neighborhood=\"Moore\" periodic=\"True\">\n    <rule in=\"A\" out=\"D\" sum=\"5..8\" values=\"D\"/>\n    <rule in=\"D\" out=\"A\" sum=\"5..8\" values=\"A\"/>\n  </convolution>\n</sequence>\n"
  },
  {
    "path": "models/OpenCave3D.xml",
    "content": "<sequence values=\"DA\">\n  <prl in=\"***/***/*** ***/*D*/*** ***/***/***\" out=\"***/***/*** ***/*A*/*** ***/***/***\"/>\n  <prl in=\"A\" out=\"D\" p=\"0.5\" steps=\"1\"/>\n  <convolution neighborhood=\"NoCorners\">\n    <rule in=\"A\" out=\"D\" sum=\"10..18\" values=\"D\"/>\n    <rule in=\"D\" out=\"A\" sum=\"10..16\" values=\"A\"/>\n  </convolution>\n</sequence>\n"
  },
  {
    "path": "models/OrganicMechanic.xml",
    "content": "<sequence values=\"BGRKYEFUIP\" origin=\"True\">\n  <one in=\"BB\" out=\"KR\" steps=\"12\"/>\n  <all>\n    <rule in=\"KRB\" out=\"BKR\"/>\n    <rule in=\"KR/*B\" out=\"BK/*R\"/>\n\n    <rule in=\"GB\" out=\"BG\"/>\n    <rule in=\"GI\" out=\"BF\"/>\n    <rule in=\"F\" out=\"Y\"/>\n    <rule in=\"YB\" out=\"BY\"/>\n    <rule in=\"YU/B*\" out=\"KR/E*\"/>\n    <rule in=\"E\" out=\"G\"/>\n    <rule in=\"*B*/BRB/*B*\" out=\"***/*U*/***\"/>\n    <rule in=\"*B*/BKB/*B*\" out=\"***/*I*/***\"/>\n\n    <field for=\"G\" on=\"B\" to=\"I\" recompute=\"True\"/>\n    <field for=\"Y\" on=\"B\" to=\"U\" recompute=\"True\"/>\n    <field for=\"P\" on=\"R\" to=\"R\" recompute=\"True\"/>\n  </all>\n</sequence>\n\n<!--\nStandard crawlers wander around and break, but now there is a benevolent mechanic that repairs them.\n-->\n"
  },
  {
    "path": "models/OrientedEscher.xml",
    "content": "<sequence values=\"BRW\" origin=\"True\">\n  <prl in=\"B\" out=\"W\"/>\n  <prl in=\"***/*W*/***\" out=\"***/*B*/***\"/>\n  <wfc values=\"BODAWEU\" tileset=\"OrientedEscher\" periodic=\"True\">\n    <rule in=\"W\" out=\"Empty\"/>\n    <rule in=\"R\" out=\"T\"/>\n    <all>\n      <rule in=\"O\" out=\"B\"/>\n      <rule in=\"U\" out=\"E\"/>\n    </all>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/PaintCompetition.xml",
    "content": "<sequence values=\"UYSN\">\n  <all>\n    <rule in=\"U\" out=\"S\"/>\n    <rule in=\"U\" out=\"N\"/>\n  </all>\n  <one in=\"S\" out=\"U\" steps=\"5\"/>\n  <one in=\"N\" out=\"Y\" steps=\"5\"/>\n  <all>\n    <rule in=\"US\" out=\"SU\"/>\n    <rule in=\"UN\" out=\"SU\"/>\n    <rule in=\"YS\" out=\"NY\"/>\n    <rule in=\"YN\" out=\"NY\"/>\n    <field for=\"Y\" to=\"S\" on=\"N\" recompute=\"True\"/>\n    <field for=\"U\" to=\"N\" on=\"S\" recompute=\"True\"/>\n  </all>\n</sequence>\n\n<!--\nYellow creatures try to paint everything into yellow, blue try to paint everything into blue.\n-->\n"
  },
  {
    "path": "models/ParallelGrowth.xml",
    "content": "<all values=\"BW\" origin=\"True\" in=\"WB\" out=\"*W\"/>\n"
  },
  {
    "path": "models/ParallelMazeGrowth.xml",
    "content": "<all values=\"BWA\" origin=\"True\" in=\"WBB\" out=\"WAW\"/>\n\n<!--\nCompare the generated mazes with the results of MazeGrowth.\n-->\n"
  },
  {
    "path": "models/ParallelWalk.xml",
    "content": "<sequence values=\"BR\">\n  <one in=\"B\" out=\"R\" steps=\"24\"/>\n  <all in=\"RB\" out=\"BR\"/>\n</sequence>\n"
  },
  {
    "path": "models/Partitioning.xml",
    "content": "<sequence values=\"BD\" symmetry=\"(xy)\">\n  <all in=\"B\" out=\"D\"/>\n  <wfc values=\"BCA\" tileset=\"Partition\">\n    <rule in=\"D\" out=\"Nothing|I|T\"/>\n    <sequence>\n      <all in=\"AC\" out=\"*A\"/>\n      <all in=\"C\" out=\"B\"/>\n    </sequence>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/Percolation.xml",
    "content": "<sequence values=\"RUBDW\">\n  <prl in=\"*R\" out=\"*U\" symmetry=\"()\"/>\n  <prl in=\"U*\" out=\"D*\" symmetry=\"()\"/>\n  <prl in=\"D\" out=\"B\" p=\"0.4\" steps=\"1\"/>\n  <path from=\"R\" to=\"U\" on=\"D\" color=\"W\"/>\n</sequence>\n"
  },
  {
    "path": "models/PeriodicEscher.xml",
    "content": "<sequence values=\"BW\">\n  <all in=\"B\" out=\"W\"/>\n  <all in=\"*****/*****/**W**/*****/*****\" out=\"*****/*****/**B**/*****/*****\"/>\n  <wfc values=\"BWARGUI\" tileset=\"Escher\" periodic=\"True\">\n    <rule in=\"W\" out=\"Empty\"/>\n    <sequence>\n      <all>\n        <rule in=\"ABA\" out=\"*A*\"/>\n        <rule in=\"WBA\" out=\"*A*\"/>\n      </all>\n      <all symmetry=\"()\">\n        <rule in=\"RI\" out=\"RB\"/>\n        <rule in=\"R/I\" out=\"G/B\"/>\n        <rule in=\"R I\" out=\"U B\"/>\n        <rule in=\"IR\" out=\"BI\"/>\n        <rule in=\"I/R\" out=\"B/I\"/>\n        <rule in=\"I R\" out=\"B I\"/>\n      </all>\n    </sequence>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/PillarsOfEternity.xml",
    "content": "<sequence values=\"BRW\" symmetry=\"(xy)\">\n  <all in=\"B\" out=\"W\"/>\n  <all in=\"***/*W*/***\" out=\"***/*B*/***\"/>\n  <wfc values=\"BEAWDRF\" outputValues=\"BWAE\" tileset=\"OrientedStairs\" periodic=\"True\">\n    <rule in=\"W\" out=\"Empty\"/>\n    <all>\n      <rule in=\"R\" out=\"W\"/>\n      <rule in=\"A\" out=\"W\"/>\n    </all>\n    <all in=\"B\" out=\"A\"/>\n    <all in=\"A *\" out=\"B *\"/>\n    <all>\n      <rule in=\"D B\" out=\"D F\"/>\n      <rule in=\"F B\" out=\"F F\"/>\n    </all>\n    <all>\n      <rule in=\"WF\" out=\"*R\"/>\n      <rule in=\"F W\" out=\"R *\"/>\n      <rule in=\"F A\" out=\"R *\"/>\n      <rule in=\"F R\" out=\"R B\"/>\n    </all>\n    <all>\n      <rule in=\"R\" out=\"B\"/>\n      <rule in=\"A\" out=\"B\"/>\n      <rule in=\"F\" out=\"W\"/>\n      <rule in=\"D\" out=\"W\"/>\n    </all>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/Push.xml",
    "content": "<sequence values=\"BWP\">\n  <one in=\"B\" out=\"W\" steps=\"120\"/>\n  <one in=\"B\" out=\"P\" steps=\"20\"/>\n  <all>\n    <rule in=\"PB\" out=\"BP\"/>\n    <rule in=\"PWB\" out=\"BPW\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/PutColoredLs.xml",
    "content": "<markov values=\"BWRGU\">\n  <all>\n    <rule in=\"RW\" out=\"RR\"/>\n    <rule in=\"GW\" out=\"GG\"/>\n    <rule in=\"UW\" out=\"UU\"/>\n  </all>\n  <!--<one in=\"W\" out=\"R|G|U\"/>-->\n  <one>\n    <rule in=\"W\" out=\"R\"/>\n    <rule in=\"W\" out=\"G\"/>\n    <rule in=\"W\" out=\"U\"/>\n  </one>\n  <one in=\"BBB*/BBB*/BBBB/BBBB/BBBB\" out=\"BBB*/BWB*/BWBB/BWWB/BBBB\"/>\n</markov>\n"
  },
  {
    "path": "models/PutLs.xml",
    "content": "<all values=\"BW\" in=\"BBB*/BBB*/BBBB/BBBB/BBBB\" out=\"BBB*/BWB*/BWBB/BWWB/BBBB\"/>\n"
  },
  {
    "path": "models/RainbowGrowth.xml",
    "content": "<one values=\"BROYGUIP\" origin=\"True\">\n  <rule in=\"RB\" out=\"RO\"/>\n  <rule in=\"OB\" out=\"OY\"/>\n  <rule in=\"YB\" out=\"YG\"/>\n  <rule in=\"GB\" out=\"GU\"/>\n  <rule in=\"UB\" out=\"UI\"/>\n  <rule in=\"IB\" out=\"IP\"/>\n  <rule in=\"PB\" out=\"PR\"/>\n</one>\n"
  },
  {
    "path": "models/RandomWalk.xml",
    "content": "<one values=\"BR\" origin=\"True\" in=\"RB\" out=\"BR\"/>\n"
  },
  {
    "path": "models/Rectangle.xml",
    "content": "<sequence values=\"BRW\">\n  <one in=\"B\" out=\"R\" steps=\"1\"/>\n  <one in=\"RB\" out=\"WW\"/>\n  <one in=\"BBB/BWB\" out=\"*W*/***\" steps=\"5\"/>\n  <all>\n    <rule in=\"WB\" out=\"*W\"/>\n    <rule in=\"BB/WB\" out=\"*W/**\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/RegularPath.xml",
    "content": "<sequence values=\"BAWRGY\" origin=\"True\">\n  <all in=\"A*B\" out=\"**A\"/>\n  <one in=\"A\" out=\"R\" steps=\"1\"/>\n  <one in=\"A\" out=\"Y\" steps=\"10\"/>\n  <all in=\"A\" out=\"B\"/>\n  <markov>\n    <one in=\"RBB\" out=\"WWR\">\n      <observe value=\"G\" from=\"B\" to=\"R\"/>\n      <observe value=\"B\" to=\"BW\"/>\n      <observe value=\"R\" to=\"W\"/>\n    </one>\n    <prl in=\"W\" out=\"A\"/>\n    <one in=\"Y\" out=\"G\"/>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/RegularSAW.xml",
    "content": "<one values=\"BRW\" origin=\"True\" in=\"RBB\" out=\"WWR\"/>\n\n<!--\nCompare the average length of the walk in 2d and 3d.\n-->\n"
  },
  {
    "path": "models/RegularSAWRestart.xml",
    "content": "<markov values=\"BRWA\" origin=\"True\">\n  <one in=\"RBB\" out=\"WAR\"/>\n  <one in=\"WBB\" out=\"WAR\"/>\n</markov>\n"
  },
  {
    "path": "models/River.xml",
    "content": "<sequence values=\"BWRUGE\">\n  <one in=\"B\" out=\"W\" steps=\"1\"/>\n  <one in=\"B\" out=\"R\" steps=\"1\"/>\n  <one>\n    <rule in=\"RB\" out=\"RR\"/>\n    <rule in=\"WB\" out=\"WW\"/>\n  </one>\n  <all in=\"RW\" out=\"UU\"/>\n  <all>\n    <rule in=\"W\" out=\"B\"/>\n    <rule in=\"R\" out=\"B\"/>\n  </all>\n  <all in=\"UB\" out=\"UU\" steps=\"1\"/>\n  <all in=\"BU/UB\" out=\"U*/**\"/>\n  <all in=\"UB\" out=\"*G\"/>\n  <one in=\"B\" out=\"E\" steps=\"13\"/>\n  <one>\n    <rule in=\"EB\" out=\"*E\"/>\n    <rule in=\"GB\" out=\"*G\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/Rosettes.xml",
    "content": "<sequence values=\"WBUKkAaNnOoYyI\" origin=\"true\">\n    <prl in=\"***/*W*/***\" out=\"***/*U*/***\"/>\n    <prl in=\"W\" out=\"I\"/>\n    <prl in=\"U\" out=\"W\"/>\n    <all in=\"WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW\" out=\"WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWaWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW/WWWWWWWWW\" steps=\"1\"/>\n\n    <!-- measure out some survey points as substitute for field max/min capping-->\n    <all temperature=\"0\" steps=\"10\">\n        <rule in=\"aW\" out=\"aK\"/>\n        <rule in=\"KW\" out=\"AK\"/>\n        <field for=\"K\" from=\"a\" on=\"WA\" essential=\"False\" recompute=\"True\"/>\n        <field for=\"A\" from=\"a\" on=\"KA\" essential=\"False\" recompute=\"True\"/>\n    </all>\n\n    <all temperature=\"0\" steps=\"1\">\n        <rule in=\"A\" out=\"W\"/>\n        <rule in=\"K\" out=\"A\"/>\n    </all>\n    <markov>\n        <!-- bootleg voronoi edges using field strengths, would need less handholding if W field could be given a static value -->\n        <all steps=\"1\">\n            <rule in=\"W\" out=\"K\"/>\n            <rule in=\"W\" out=\"W\"/>\n            <field for=\"W\" to=\"a\" on=\"W\" essential=\"False\" recompute=\"True\"/>\n            <field for=\"K\" to=\"BKUA\" on=\"W\" essential=\"False\" recompute=\"True\"/>\n        </all>\n        \n        <!-- bootleg voronoi part two -->\n        <convolution neighborhood=\"Moore\" steps=\"2\">\n            <rule in=\"K\" out=\"W\" sum=\"1..8\" values=\"a\"/>\n            <rule in=\"K\" out=\"U\" sum=\"1..8\" values=\"W\"/>\n        </convolution>\n        \n        <!-- clean up the masking-->\n        <all in=\"K\" out=\"W\" steps=\"1\"/>\n        <all in=\"K\" out=\"W\" steps=\"1\"/>\n        \n        <all temperature=\"0\" steps=\"1\">\n            <rule in=\"W\" out=\"k\"/>\n            <rule in=\"k\" out=\"W\"/>\n            <field for=\"k\" to=\"a\" on=\"Wk\" essential=\"False\" recompute=\"True\"/>\n            <field for=\"W\" to=\"I\" on=\"k\" essential=\"False\" recompute=\"True\"/>\n        </all>\n        \n        <all temperature=\"0\" steps=\"2\">\n            <rule in=\"W\" out=\"Y\"/>\n            <rule in=\"B\" out=\"Y\"/>\n            <rule in=\"A\" out=\"Y\"/>\n            <rule in=\"k\" out=\"N\"/>\n            <rule in=\"a\" out=\"N\"/>\n            <rule in=\"U\" out=\"n\"/>\n        </all>\n    </markov>\n</sequence>\n"
  },
  {
    "path": "models/SAWRestart.xml",
    "content": "<markov values=\"BRW\" origin=\"True\">\n  <one in=\"RB\" out=\"WR\"/>\n  <one in=\"WB\" out=\"WR\"/>\n</markov>\n\n<!--\nSaw it here: ....\n-->\n"
  },
  {
    "path": "models/SeaVilla.xml",
    "content": "﻿<sequence values=\"Bwy\" symmetry=\"(xy)\">\n  <prl in=\"B\" out=\"w\"/>\n  <prl in=\"w *\" out=\"y *\"/>\n  <prl in=\"***/*y*/***\" out=\"***/*B*/***\"/>\n  <wfc values=\"BaV OoYhSAWwRrlgvPpxbUuCcKkXGEFi\" tileset=\"MarchingHills\" overlap=\"-3\">\n    <rule in=\"w\" out=\"Stone\"/>\n    <rule in=\"y\" out=\"Empty\"/>\n\n    <prl in=\"BBBBB/BBBBB/BBBBB/BBBBB/BBBBB aBBBa/BBBBB/BBBBB/BBBBB/aBBBa\" out=\"*****/*****/**Y**/*****/***** *****/*hah*/*aaa*/*hah*/*****\"/>\n    <prl in=\"BBBBB/BBBBB/BBBBB hBBBh/aBBBa/hBBBh\" out=\"*****/**S**/***** *AAA*/*aaa*/*AAA*\"/>\n    <prl in=\"BBB/BBB/BBB hah/BBB/BBB\" out=\"***/***/*S* ***/AaA/AaA\"/>\n    <prl in=\"aBBBa\" out=\"*AaA*\"/>\n    <prl steps=\"1\">\n      <rule in=\"a B\" out=\"* a\"/>\n      <rule in=\"h B\" out=\"* a\"/>\n      <rule in=\"A B\" out=\"* a\"/>\n    </prl>\n    <prl comment=\"mark water\">\n      <rule in=\"Y * *\" out=\"O * *\"/>\n      <rule in=\"S * *\" out=\"W * *\"/>\n      <rule in=\"V * *\" out=\"l * *\"/>\n    </prl>\n    <prl>\n      <rule in=\"Y\" out=\"w\"/>\n      <rule in=\"S\" out=\"w\"/>\n      <rule in=\"V\" out=\"w\"/>\n    </prl>\n    <prl>\n      <rule in=\"O\" out=\"Y\"/>\n      <rule in=\"W\" out=\"S\"/>\n      <rule in=\"l\" out=\"V\"/>\n    </prl>\n    <prl in=\"***/*A*/***\" out=\"***/*W*/***\"/>\n    <prl in=\"BwB/BBB ***/AaA\" out=\"*u*/*** ***/***\"/>\n    <prl in=\"W\" out=\"A\"/>\n    <markov comment=\"draw all possible bridges\">\n      <one in=\"YR/BB/*B/SB\" out=\"*r/**/**/**\" comment=\"cancel purposeless bridge\"/>\n      <one in=\"BYB/BRB/BBB/BSB BhB/BBB/BBB/AaA\" out=\"***/*B*/***/*** ***/*a*/*a*/***\" comment=\"connect\"/>\n      <one comment=\"continue\">\n        <rule in=\"BSB/BRB/BBB/BBB/BBB *a*/BBB/BBB/BBB/BBB\" out=\"***/*B*/***/*Y*/*R* ***/*a*/*a*/*h*/***\"/>\n        <rule in=\"BYB/BRB/BBB/BBB/BBB *h*/BBB/BBB/BBB/BBB\" out=\"***/*B*/***/*S*/*R* ***/*a*/*a*/*a*/***\"/>\n      </one>\n      <one comment=\"retract\">\n        <rule in=\"BBB/BBB/BYB/BrB *a*/BaB/BhB/BBB\" out=\"*r*/***/*B*/*B* *B*/*B*/*B*/***\"/>\n        <rule in=\"BBB/BBB/BSB/BrB *a*/BaB/BaB/BBB\" out=\"*r*/***/*B*/*B* *B*/*B*/*B*/***\"/>\n      </one>\n      <one in=\"R\" out=\"r\" comment=\"fail\"/>\n      <one in=\"BSB/BBB AaA/BBB\" out=\"***/*R* ***/***\" comment=\"start\"/>\n    </markov>\n    <prl in=\"r\" out=\"B\"/>\n    <all in=\"aaa/BBB BSB/BBB AaA/BaB\" out=\"*r*/*** ***/*** ***/***\" comment=\"ban stairs above bridges\"/>\n    <all in=\"YBBSBBY\" out=\"*ii*ii*\" comment=\"draw all possible paths\"/>\n    <all comment=\"draw stairs\">\n      <rule in=\"BBBBB YBBSB aaaaB aaaaB Y**Si\" out=\"***** *ii** ***i* ***i* *****\"/>\n      <rule in=\"BBBBB YBBSB aaaaB aaaaB YBBSB aaaaB aaaaB Y**Si\" out=\"***** *ii** ***i* ***i* ***** ***i* ***i* *****\"/>\n      <rule in=\"BBBBB YBBSB aaaaB aaaaB YBBSB aaaaB aaaaB YBBSB aaaaB aaaaB Y**Si\" out=\"***** *ii** ***i* ***i* ***** ***i* ***i* ***** ***i* ***i* *****\"/>\n      <rule in=\"BBBBB YBBSB aaaaB aaaaB wBBw*\" out=\"***** *ii** ***i* ***i* *****\"/>\n    </all>\n    <all in=\"*BB* uBBw\" out=\"**** ***u\" comment=\"find external descents\"/>\n    <all comment=\"prepare endpoints for the big cycle\">\n      <rule in=\"iYi\" out=\"*x*\"/>\n      <rule in=\"Yi/i*\" out=\"x*/**\"/>\n    </all>\n    <prl>\n      <rule in=\"r\" out=\"a\"/>\n      <rule in=\"Y\" out=\"o\"/>\n      <rule in=\"S\" out=\"o\"/>\n      <rule in=\"a i\" out=\"* F\"/>\n      <rule in=\"a x\" out=\"* b\"/>\n    </prl>\n    <one in=\"x\" out=\"R\" steps=\"1\"/>\n    <one steps=\"1\">\n      <rule in=\"x\" out=\"l\"/>\n      <field for=\"l\" from=\"R\" on=\"ioxFb\"/> не уверен насчет Fb\n    </one>\n    <one in=\"R\" out=\"x\"/>\n    <markov>\n      <one in=\"lW\" out=\"R*\" symmetry=\"(xyz)\"/>\n      <path from=\"x\" to=\"l\" on=\"iox\" color=\"W\" longest=\"True\"/>\n    </markov>\n    <one in=\"Wx\" out=\"Wl\" symmetry=\"(xyz)\"/>\n    <prl>\n      <rule in=\"F\" out=\"i\"/>\n      <rule in=\"b\" out=\"x\"/>\n    </prl>\n    <markov>\n      <one in=\"Sl\" out=\"SW\" symmetry=\"(xyz)\"/>\n      <path from=\"R\" to=\"l\" on=\"iox\" color=\"S\"/>\n    </markov>\n    <all>\n      <rule in=\"lB\" out=\"*l\"/>\n      <rule in=\"lW\" out=\"*l\"/>\n    </all>\n    <prl>\n      <rule in=\"S\" out=\"W\"/>\n      <rule in=\"x\" out=\"o\"/>\n    </prl>\n    <one in=\"RWW*\" out=\"xpPR\" symmetry=\"(xyz)\"/>\n    <prl in=\"R\" out=\"x\"/>\n    <all comment=\"remove stairs incompatible with paths\">\n      <rule in=\"iio aai aai **x\" out=\"BB* **a **a ***\"/>\n      <rule in=\"iio aai aai BBo aai aai **x\" out=\"BB* **a **a *** **a **a ***\"/>\n      <rule in=\"iio aai aai BBo aai aai BBo aai aai **x\" out=\"BB* **a **a *** **a **a *** **a **a ***\"/>\n    </all>\n    <path from=\"x\" to=\"u\" on=\"io\" color=\"W\" comment=\"connect to water\"/>\n    <one in=\"W\" out=\"S\" steps=\"1\"/>\n    <one in=\"SW\" out=\"SS\" symmetry=\"(xyz)\"/>\n    <all symmetry=\"(xyz)\">\n      <rule in=\"uWWW\" out=\"*iio\"/>\n      <rule in=\"oWWW\" out=\"*iio\"/>\n      <rule in=\"oWWx\" out=\"*ii*\"/>\n      <rule in=\"uSSS\" out=\"*Ppu\"/>\n      <rule in=\"uSSx\" out=\"xPp*\"/>\n    </all>\n    <prl in=\"u * *\" out=\"x * *\"/>\n    <all comment=\"fail on wrong stair turn\">\n      <rule in=\"gB\" out=\"*g\"/>\n      <rule in=\"xPp p** P** xpP\" out=\"ggg ggg ggg ggg\"/>\n      <rule in=\"xpP P** p** xPp\" out=\"ggg ggg ggg ggg\"/>\n      <rule in=\"xPp p** P** *** p** P** xpP\" out=\"ggg ggg ggg ggg ggg ggg ggg\"/>\n      <rule in=\"xpP P** p** *** P** p** xPp\" out=\"ggg ggg ggg ggg ggg ggg ggg\"/>\n      <rule in=\"xPp p** P** *** p** P** *** p** P** xpP\" out=\"ggg ggg ggg ggg ggg ggg ggg ggg ggg ggg\"/>\n      <rule in=\"xpP P** p** *** P** p** *** P** p** xPp\" out=\"ggg ggg ggg ggg ggg ggg ggg ggg ggg ggg\"/>\n    </all>\n    <all comment=\"delete unused descents\">\n      <rule in=\"iio aai aai BBu\" out=\"BB* **a **a ***\"/>\n      <rule in=\"iio aai aai BBw\" out=\"BB* **a **a ***\"/>\n    </all>\n    <prl>\n      <rule in=\"BxB AaA\" out=\"*b* ***\"/>\n      <rule in=\"BxB B*B\" out=\"*b* ***\"/>\n      <rule in=\"***/*o*/*** hah/aaa/hah\" out=\"***/*O*/*** ***/***/***\"/>\n      <rule in=\"o h\" out=\"O *\"/>\n    </prl>\n    <markov comment=\"build trees to prepare key placement\">\n      <one>\n        <rule in=\"iYF\" out=\"r**\"/>\n        <rule in=\"iY/*F\" out=\"r*/**\"/>\n      </one>\n      <one in=\"F\" out=\"i\"/>\n      <one>\n        <rule in=\"YiioiiO\" out=\"*SS*SSY\"/>\n        <rule in=\"Yiio*** ***i*** ***i*** ***oiiO\" out=\"*SS**** ***S*** ***S*** ****SSY\"/>\n        <rule in=\"***oiiO ***i*** ***i*** Yiio***\" out=\"****SSY ***S*** ***S*** *SS****\"/>\n        <rule in=\"Yiio*** ***i*** ***i*** ***o*** ***i*** ***i*** ***oiiO\" out=\"*SS**** ***S*** ***S*** ******* ***S*** ***S*** ****SSY\"/>\n        <rule in=\"***oiiO ***i*** ***i*** ***o*** ***i*** ***i*** Yiio***\" out=\"****SSY ***S*** ***S*** ******* ***S*** ***S*** *SS****\"/>\n        <rule in=\"Yiio*** ***i*** ***i*** ***o*** ***i*** ***i*** ***o*** ***i*** ***i*** ***oiiO\" out=\"*SS**** ***S*** ***S*** ******* ***S*** ***S*** ******* ***S*** ***S*** ****SSY\"/>\n        <rule in=\"***oiiO ***i*** ***i*** ***o*** ***i*** ***i*** ***o*** ***i*** ***i*** Yiio***\" out=\"****SSY ***S*** ***S*** ******* ***S*** ***S*** ******* ***S*** ***S*** *SS****\"/>\n      </one>\n      <one in=\"xPpbP/i****\" out=\"Y**c*/F****\"/>\n    </markov>\n    <all in=\"pY\" out=\"*C\"/>\n    <path from=\"Y\" to=\"C\" on=\"SoCY\" color=\"l\" longest=\"True\"/>\n    <prl>\n      <rule in=\"S\" out=\"i\"/>\n      <rule in=\"r\" out=\"i\"/>\n    </prl>\n    <one in=\"Yl\" out=\"K*\" steps=\"1\"/>\n    <one in=\"Yl\" out=\"X*\" steps=\"1\"/>\n    <one in=\"Yl\" out=\"R*\" steps=\"1\"/>\n    <all symmetry=\"(xyz)\">\n      <rule in=\"Klll\" out=\"***K\"/>\n      <rule in=\"KllC\" out=\"***k\"/>\n      <rule in=\"Xlll\" out=\"***X\"/>\n      <rule in=\"XllC\" out=\"***G\"/>\n      <rule in=\"Rlll\" out=\"***R\"/>\n      <rule in=\"RllC\" out=\"***r\"/>\n      <rule in=\"Ylll\" out=\"*iiY\"/>\n      <rule in=\"YllC\" out=\"*ii*\"/>\n      <rule in=\"kPpc\" out=\"o**k\"/>\n      <rule in=\"GPpc\" out=\"o**G\"/>\n      <rule in=\"rPpc\" out=\"o**r\"/>\n    </all>\n    <all symmetry=\"(xyz)\">\n      <rule in=\"lKl\" out=\"*o*\"/>\n      <rule in=\"lK/*l\" out=\"*o/**\"/>\n      <rule in=\"lXl\" out=\"*o*\"/>\n      <rule in=\"lX/*l\" out=\"*o/**\"/>\n      <rule in=\"lRl\" out=\"*o*\"/>\n      <rule in=\"lR/*l\" out=\"*o/**\"/>\n    </all>\n    <prl>\n      <rule in=\"BPB BaB\" out=\"*** *E*\"/>\n      <rule in=\"BpB BaB\" out=\"*** *E*\"/>\n      <rule in=\"BbB BaB\" out=\"*** *E*\"/>\n    </prl>\n    <prl>\n      <rule in=\"P\" out=\"l\"/>\n      <rule in=\"p\" out=\"l\"/>\n      <rule in=\"C\" out=\"o\"/>\n      <rule in=\"c\" out=\"o\"/>\n      <rule in=\"x\" out=\"o\"/>\n      <rule in=\"b\" out=\"o\"/>\n      <rule in=\"Y\" out=\"o\"/>\n    </prl>\n    <prl>\n      <rule in=\"***/*o*/*** hah/aaa/hah\" out=\"***/*Y*/*** ***/***/***\"/>\n      <rule in=\"o h\" out=\"Y *\"/>\n    </prl>\n    <all>\n      <rule in=\"Ylll\" out=\"***o\"/>\n      <rule in=\"olll\" out=\"***Y\"/>\n      <rule in=\"o l l l\" out=\"* * * o\"/>\n      <rule in=\"l l l o\" out=\"o * * *\"/>\n    </all>\n    <prl in=\"Yl\" out=\"O*\"/>\n    <union symbol=\"6\" values=\"PY\"/>\n    <markov comment=\"mark open areas\">\n      <all>\n        <rule in=\"Piioii6\" out=\"*SS*SSP\"/>\n        <rule in=\"Piio*** ***i*** ***i*** ***oii6\" out=\"*SS**** ***S*** ***S*** ****SSP\"/>\n        <rule in=\"***oii6 ***i*** ***i*** Piio***\" out=\"****SSP ***S*** ***S*** *SS****\"/>\n        <rule in=\"Piio*** ***i*** ***i*** ***o*** ***i*** ***i*** ***oii6\" out=\"*SS**** ***S*** ***S*** ******* ***S*** ***S*** ****SSP\"/>\n        <rule in=\"***oii6 ***i*** ***i*** ***o*** ***i*** ***i*** Piio***\" out=\"****SSP ***S*** ***S*** ******* ***S*** ***S*** *SS****\"/>\n        <rule in=\"Piio*** ***i*** ***i*** ***o*** ***i*** ***i*** ***o*** ***i*** ***i*** ***oii6\" out=\"*SS**** ***S*** ***S*** ******* ***S*** ***S*** ******* ***S*** ***S*** ****SSP\"/>\n        <rule in=\"***oii6 ***i*** ***i*** ***o*** ***i*** ***i*** ***o*** ***i*** ***i*** Piio***\" out=\"****SSP ***S*** ***S*** ******* ***S*** ***S*** ******* ***S*** ***S*** *SS****\"/>\n      </all>\n      <all in=\"P\" out=\"p\"/>\n      <one in=\"O\" out=\"P\"/>\n    </markov>\n    <prl>\n      <rule in=\"p\" out=\"o\"/>\n      <rule in=\"i\" out=\"B\"/>\n    </prl>\n    <all symmetry=\"(xyz)\">\n      <rule in=\"ullo\" out=\"***O\"/>\n      <rule in=\"Ollo\" out=\"***O\"/>\n      <rule in=\"OSSo\" out=\"***O\"/>\n    </all>\n    <one steps=\"1\" comment=\"place the goal\">\n      <rule in=\"***/*o*/*** hah/aaa/hah\" out=\"***/*U*/*** ***/***/***\"/>\n      <field for=\"U\" from=\"u\" on=\"UKXRkGrSaOol\"/>\n    </one>\n    <one comment=\"detect trivial levels\">\n      <rule in=\"BBB/BUB/BBB\" out=\"***/*Y*/***\"/>\n      <rule in=\"* Y\" out=\"Y *\"/>\n      <rule in=\"Y*\" out=\"*Y\"/>\n    </one>\n    <prl in=\"O\" out=\"o\"/>\n    <union symbol=\"?\" values=\"aBhA\"/>\n    <all comment=\"retract dead ends\">\n      <rule in=\"????/????/???? ????/?oSS/???? ????/????/????\" out=\"****/****/**** ****/*oBB/**** ****/****/****\"/>\n      <rule in=\"???/???/??? ???/?o?/??? ???/?S?/??? ???/?S?/???\" out=\"***/***/*** ***/*o*/*** ***/*B*/*** ***/*B*/***\"/>\n      <rule in=\"???/?S?/??? ???/?S?/??? ???/?o?/??? ???/???/???\" out=\"***/*B*/*** ***/*B*/*** ***/*o*/*** ***/***/***\"/>\n    </all>\n    <all in=\"oSSoSSo/S*****S/S*****S/o**V**o/S*****S/S*****S/oSSoSSo\" out=\"*******/*PPPPP*/*PPPPP*/*PPvPP*/*PPPPP*/*PPPPP*/*******\" comment=\"detect rooms\"/>\n    <all comment=\"repair removed stairs\">\n      <rule in=\"ABA\" out=\"*a*\"/>\n      <rule in=\"aBa\" out=\"*a*\"/>\n    </all>\n    <prl>\n      <rule in=\"l\" out=\"i\"/>\n      <rule in=\"S\" out=\"i\"/>\n      <rule in=\"V\" out=\"o\"/>\n      <rule in=\"u\" out=\"w\"/>\n    </prl>\n    <prl in=\"o i i o i i o i i o\" out=\"O * * O * * O * * O\" comment=\"mark triple ladders\"/>\n    <prl in=\"o i i o i i o\" out=\"O * * O * * O\" comment=\"mark double ladders\"/>\n    <all comment=\"delete unused bridges\">\n      <rule in=\"hB\" out=\"a*\"/>\n      <rule in=\"BBB/BoB/BBB BBB/aaa/BBB\" out=\"***/*B*/*** ***/BBB/***\"/>\n      <rule in=\"EaE\" out=\"*E*\"/>\n    </all>\n\n    <union symbol=\"!\" values=\"Bi\"/>\n    <map scale=\"5/3 5/3 6/3\" values=\"BaWUyogEpTCOYMAPeDFiKkXGRrwxb\" folder=\"SeaVilla\">\n      <rule in=\"BBB/BBB/BBB BBB/BoB/BBB aaB/aaB/BBB\" fout=\"TopOut\" legend=\"Ea*g\"/>\n      <rule in=\"BBB/BBB/BBB BBB/BoB/BBB aaa/aaa/aaB\" fout=\"TopIn\" legend=\"Ea*g\"/>\n      <rule in=\"BBB/BBB/BBB BBB/BoB/BBB aaB/aaB/aaB\" fout=\"TopWall\" legend=\"Ea*g\"/>\n      <rule in=\"BBB/BBB/BBB BBB/BoB/BBB aaa/aaa/aaa\" fout=\"TopStone\" legend=\"E*g\"/>\n\n      <rule in=\"aaB/aaB/BBB BBB/BoB/BBB aaB/aaB/BBB\" fout=\"Out\" legend=\"E*a\"/>\n      <rule in=\"aaa/aaa/aaB BBB/BoB/BBB aaa/aaa/aaB\" fout=\"In\" legend=\"Ea*\"/>\n      <rule in=\"aaB/aaB/aaB BBB/BoB/BBB aaB/aaB/aaB\" fout=\"Wall\" legend=\"E*a\"/>\n      <rule in=\"aaa/aaa/aaa BBB/BoB/BBB aaa/aaa/aaa\" fout=\"Stone\" legend=\"Ea*\"/>\n\n      <rule in=\"BBB/aaB/aaB BBB/BoB/BBB aaa/aaa/aaB\" fout=\"OutIn\" legend=\"Ea*\"/>\n      <rule in=\"aaB/aaB/BBB BBB/BoB/BBB aaB/aaB/aaB\" fout=\"OutWall\" legend=\"E*a\"/>\n      <rule in=\"aaB/aaB/aaB BBB/BoB/BBB aaa/aaa/aaB\" fout=\"OutIn\" legend=\"Ea*\"/>\n      <rule in=\"aaa/aaa/aaB BBB/BoB/BBB aaa/aaa/aaa\" fout=\"Stone\" legend=\"Ea*\"/>\n      <rule in=\"aaB/aaB/aaB BBB/BoB/BBB aaa/aaa/aaa\" fout=\"Stone\" legend=\"Ea*\"/>\n      <rule in=\"aaB/aaB/BBB BBB/BoB/BBB aaa/aaa/aaa\" fout=\"Stone\" legend=\"Ea*\"/>\n\n      <rule in=\"BBB/BBB/BBB BBB/BoB/BBB hah/aaa/hah\" fout=\"TopCross\" legend=\"Eg*\"/>\n      <rule in=\"aaa/aaa/aaa BBB/BoB/BBB hah/aaa/hah\" fout=\"Cross\" legend=\"P*\"/>\n\n      <rule in=\"BBB/BBB/BBB BBB/ioB/BBB AAB/aiB/AAB\" fout=\"StairsTop\" legend=\"*\"/>\n      <rule in=\"aaB/aiB/aaB BBB/Boi/BBB AAA/aaa/AAA\" fout=\"StairsBottom\" legend=\"EabW*e\"/>\n      <rule in=\"BBB/BBB/BBB BBB/iOB/BBB AAB/aiB/AAB\" fout=\"LadderTop\" legend=\"E*W\"/>\n      <rule in=\"aaB/aiB/aaB BBB/BOi/BBB AAA/aaa/AAA\" fout=\"LadderBottom\" legend=\"EaW*D\"/>\n      <rule in=\"aaB/aiB/aaB BBB/BOB/BBB AAB/aiB/AAB\" fout=\"Ladder\" legend=\"EaD*\"/>\n      <rule in=\"aaB/aiB/aaB BBB/BwB/BBB AAA/aaa/AAA\" fout=\"Pier\" legend=\"yUw*\"/>\n\n      <rule in=\"BBB/BBB/BBB BBB/BoB/BBB AAB/aaB/AAB\" fout=\"TopEdge\" legend=\"Ex*ag\"/>\n      <rule in=\"aaB/aaB/aaB BBB/BoB/BBB AAB/aaB/AAB\" fout=\"Edge\" legend=\"E*\"/>\n      <rule in=\"aaB/aaB/aaB BBB/BoB/BBB AAA/aaa/AAA\" fout=\"Dir\" legend=\"Ea*\"/>\n      <rule in=\"BBB/BBB/BBB BBB/BoB/BBB AAA/aaa/AAA\" fout=\"TopDir\" legend=\"E*g\"/>\n      <rule in=\"aaa/aaa/aaa BBB/BoB/BBB AAA/aaa/AAA\" fout=\"Dir\" legend=\"Ea*\"/>\n\n      <rule in=\"BBB/BBB/BBB BBB/BwB/BBB aaa/aaa/aaa\" fout=\"Water\" legend=\"yU*\"/>\n      <rule in=\"BBB/BBB/BBB BBB/BwB/BBB hah/aaa/hah\" fout=\"Water\" legend=\"yU*\"/>\n      <rule in=\"BBB/BBB/BBB BBB/BwB/BBB AAA/aaa/AAA\" fout=\"Water\" legend=\"yU*\"/>\n      <rule in=\"aaB/aaB/aaB BBB/BwB/BBB AAA/aaa/AAA\" fout=\"Water\" legend=\"yU*\"/>\n      <rule in=\"aaB/aaB/aaB BBB/BwB/BBB aaa/aaa/aaa\" fout=\"WaterWall\" legend=\"yaU*\"/>\n      <rule in=\"aaa/aaa/aaB BBB/BwB/BBB aaa/aaa/aaa\" fout=\"WaterWall\" legend=\"yaU*\"/>\n      <rule in=\"aaB/aaB/BBB BBB/BwB/BBB aaa/aaa/aaa\" fout=\"WaterWall\" legend=\"yaU*\"/>\n      <rule in=\"aaa/aaa/aaa BBB/BwB/BBB aaa/aaa/aaa\" fout=\"WaterWall\" legend=\"yaU*\"/>\n      <rule in=\"aaa/aaa/aaa BBB/BwB/BBB hah/aaa/hah\" fout=\"Water\" legend=\"yU*\"/>\n      <rule in=\"aaa/aaa/aaa BBB/BwB/BBB AAA/aaa/AAA\" fout=\"Water\" legend=\"yU*\"/>\n\n      <rule in=\"BBB/BBB/BBB BBB/ioi/BBB AAA/aaa/AAA\" fout=\"Line\" legend=\"EW*\"/>\n      <rule in=\"aaa/aaa/aaa BBB/ioi/BBB AAA/aaa/AAA\" fout=\"Line\" legend=\"EW*\"/>\n      <rule in=\"aaB/aaB/aaB BBB/ioi/BBB AAA/aaa/AAA\" fout=\"Line\" legend=\"EW*\"/>\n      <rule in=\"BBB/BBB/BBB B*B/io*/B*B hah/aaa/hah\" fout=\"CrossX\" legend=\"EW*\"/>\n      <rule in=\"aaa/aaa/aaa B*B/io*/B*B hah/aaa/hah\" fout=\"CrossX\" legend=\"EW*\"/>\n\n      <rule in=\"***/***/*** PPP/PvP/PPP aaa/aaa/aaa\" fout=\"Room\" legend=\"T*\"/>\n      <rule in=\"***/***/*** PiB/io!/B!B hah/aaa/hah\" fout=\"RoomOut\" legend=\"TaWE*\"/>\n      <rule in=\"***/***/*** PiP/ioi/PiB hah/aaa/hah\" fout=\"RoomIn\" legend=\"TaW*\"/>\n      <rule in=\"***/***/*** PiB/ioi/BiP hah/aaa/hah\" fout=\"RoomDouble\" legend=\"TaW*\"/>\n      <rule in=\"***/***/*** PiB/io!/PiB hah/aaa/hah\" fout=\"RoomWall\" legend=\"TaW*\"/>\n      <rule in=\"***/***/*** PiP/ioi/PiP hah/aaa/hah\" fout=\"Room\" legend=\"T*\"/>\n      <rule in=\"***/***/*** PiP/PoP/PiP AaA/AaA/AaA\" fout=\"Room\" legend=\"T*\"/>\n      <rule in=\"***/***/*** PiB/PoB/PiB AaA/AaA/AaA\" fout=\"RoomWall\" legend=\"TaW*\"/>\n\n      <rule in=\"BBB/BBB/BBB BBB/ioi/BBB BBB/aaa/BBB\" fout=\"Line\" legend=\"EW*\"/>\n      <rule in=\"BBB/BBB/BBB BBB/ioi/BBB BBB/EEE/BBB\" fout=\"BridgeWood\" legend=\"*o\"/>\n      <rule in=\"BBB/BBB/BBB BBB/ioi/BBB AAB/aaa/AAB\" fout=\"Line\" legend=\"EW*\"/>\n      <rule in=\"aaB/aaB/aaB BBB/ioi/BBB AAB/aaa/AAB\" fout=\"Line\" legend=\"EW*\"/>\n      <rule in=\"BBB/BBB/BBB BBB/BoB/BBB AAB/aaa/AAB\" fout=\"TopEdge\" legend=\"Ex*ag\"/>\n      <rule in=\"aaB/aaB/aaB BBB/BoB/BBB AAB/aaa/AAB\" fout=\"Edge\" legend=\"E*\"/>\n      <rule in=\"BBB/BBB/BBB BBB/ioi/BBB AAB/aaE/AAB\" fout=\"TopContactWood\" legend=\"Eo*W\"/>\n      <rule in=\"aaB/aaB/aaB BBB/ioi/BBB AAB/aaE/AAB\" fout=\"ContactWood\" legend=\"Eo*W\"/>\n\n      <rule in=\"***/***/*** ***/iU*/*** ***/***/***\" fout=\"Goal\" legend=\"EW*C\"/>\n      <rule in=\"***/***/*** BBB/BKi/BBB ***/***/***\" fout=\"KeyK\" legend=\"EW*KOM\"/>\n      <rule in=\"***/***/*** BBB/BXi/BBB ***/***/***\" fout=\"KeyL\" legend=\"EW*XOM\"/>\n      <rule in=\"***/***/*** BBB/BRi/BBB ***/***/***\" fout=\"KeyR\" legend=\"EW*ROM\"/>\n      <rule in=\"***/***/*** BBB/iki/BBB ***/***/***\" fout=\"LockK\" legend=\"EW*kY\"/>\n      <rule in=\"***/***/*** BBB/iGi/BBB ***/***/***\" fout=\"LockL\" legend=\"EW*GY\"/>\n      <rule in=\"***/***/*** BBB/iri/BBB ***/***/***\" fout=\"LockR\" legend=\"EW*rY\"/>\n\n        <one file=\"DrawPier\" legend=\"BowaEW\"/>\n        <all comment=\"finalize pier\">\n          <rule in=\"o U\" out=\"* o\"/>\n          <rule in=\"o y\" out=\"* o\"/>\n        </all>\n        <prl comment=\"complete roads\">\n          <rule in=\"WEW\" out=\"*W*\"/>\n          <rule in=\"WEB BBW\" out=\"*W* ***\"/>\n        </prl>\n        <prl comment=\"fill some gaps\">\n          <rule in=\"aBa\" out=\"*a*\"/>\n          <rule in=\"aEa\" out=\"*a*\"/>\n          <rule in=\"DBa\" out=\"*a*\"/>\n          <rule in=\"a B a\" out=\"* a *\"/>\n          <rule in=\"a B E\" out=\"* a *\"/>\n          <rule in=\"a E\" out=\"* a\"/>\n          <rule in=\"EaE *B* *U*\" out=\"*** *a* ***\"/>\n        </prl>\n        <prl comment=\"fill stair gaps\">\n          <rule in=\"aBe\" out=\"*a*\"/>\n          <rule in=\"aUe\" out=\"*a*\"/>\n        </prl>\n        <prl in=\"e\" out=\"B\" comment=\"finalize stairs\"/>\n        <all>\n          <rule in=\"PE\" out=\"*P\" comment=\"mark redundant floors\"/>\n          <rule in=\"a U\" out=\"* a\"/>\n          <rule in=\"a y\" out=\"* a\"/>\n        </all>\n        <prl in=\"P\" out=\"B\"/>\n        <prl comment=\"remove redundant walls\">\n          <rule in=\"BBB BaB BaB BaB BaB BaB *a*\" out=\"*** *B* *B* *B* *B* *B* *E*\"/>\n          <rule in=\"BaB BaB BaB BaB BaB BaB BBB\" out=\"*B* *B* *B* *B* *B* *B* ***\"/>\n        </prl>\n        <all comment=\"continue columns above rooms\">\n          <rule in=\"aaa/aaa/aaa BBB/BBB/BBB\" out=\"***/***/*** aaa/aaa/aaa\"/>\n          <rule in=\"aaa/aaa/aaa TTT/TTT/TTT\" out=\"***/***/*** aaa/aaa/aaa\"/>\n        </all>\n        <union symbol=\"?\" values=\"BaKXRkGr\"/>\n        <all in=\"??g??\" out=\"**B**\" comment=\"remove thin grass\"/>\n        <all in=\"BBYBYBYB\" out=\"*Y*Y*Y*Y\" comment=\"finalize doors\"/>\n        <prl in=\"Y E\" out=\"* Y\"/>\n        <all>\n          <rule in=\"YE\" out=\"*a\"/>\n          <rule in=\"Y*/*E\" out=\"**/*a\"/>\n        </all>\n        <union symbol=\"@\" values=\"aEW\"/>\n        <prl symmetry=\"(xyz)\">\n          <rule in=\"@B\" out=\"*A\"/>\n          <rule in=\"@*/*B\" out=\"**/*A\"/>\n        </prl>\n        <markov>\n          <one>\n            <rule in=\"pK\" out=\"*p\"/>\n            <rule in=\"pk\" out=\"*p\"/>\n          </one>\n          <path from=\"K\" to=\"k\" on=\"A\" color=\"p\" inertia=\"True\"/>\n        </markov>\n        <markov>\n          <one>\n            <rule in=\"pX\" out=\"*p\"/>\n            <rule in=\"pG\" out=\"*p\"/>\n          </one>\n          <path from=\"X\" to=\"G\" on=\"A\" color=\"p\" inertia=\"True\"/>\n        </markov>\n        <markov>\n          <one>\n            <rule in=\"pR\" out=\"*p\"/>\n            <rule in=\"pr\" out=\"*p\"/>\n          </one>\n          <path from=\"R\" to=\"r\" on=\"A\" color=\"p\" inertia=\"True\"/>\n        </markov>\n        <prl>\n          <rule in=\"A\" out=\"B\"/>\n          <rule in=\"k\" out=\"B\"/>\n          <rule in=\"G\" out=\"B\"/>\n          <rule in=\"r\" out=\"B\"/>\n          <rule in=\"O p\" out=\"* O\"/>\n          <rule in=\"b a\" out=\"* E\"/>\n          <rule in=\"aax aBB aBB\" out=\"**a *aa *a*\"/>\n        </prl>\n        <prl comment=\"final colors\">\n          <rule in=\"i\" out=\"E\"/>\n          <rule in=\"F\" out=\"a\"/>\n          <rule in=\"D\" out=\"a\"/>\n          <rule in=\"b\" out=\"a\"/>\n          <rule in=\"x\" out=\"a\"/>\n        </prl>\n    </map>\n\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/SelectLargeCaves.xml",
    "content": "<sequence values=\"DARG\">\n  <prl in=\"***/*D*/***\" out=\"***/*A*/***\"/>\n  <prl in=\"A\" out=\"D\" p=\"0.435\" steps=\"1\"/>\n  <convolution neighborhood=\"Moore\">\n    <rule in=\"A\" out=\"D\" sum=\"5..8\" values=\"D\"/>\n    <rule in=\"D\" out=\"A\" sum=\"6..8\" values=\"A\"/>\n  </convolution>\n  <all in=\"AD/DA\" out=\"DD/DA\"/>\n  <markov>\n    <sequence>\n      <one in=\"A\" out=\"R\" steps=\"1\"/>\n      <one in=\"RA\" out=\"*R\" steps=\"200\"/>\n      <all in=\"RA\" out=\"GG\"/>\n      <all>\n        <rule in=\"GR\" out=\"*G\"/>\n        <rule in=\"GA\" out=\"*G\"/>\n      </all>\n    </sequence>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/SelectLongKnots.xml",
    "content": "<sequence values=\"BRW\">\n  <prl in=\"B\" out=\"R\"/>\n  <prl in=\"***/***/*** ***/*R*/*** ***/***/***\" out=\"***/***/*** ***/*W*/*** ***/***/***\"/>\n  <wfc values=\"BWYU\" tileset=\"Knots3D\" tiles=\"Knots3D/3\">\n    <rule in=\"R\" out=\"Empty\"/>\n    <rule in=\"W\" out=\"Line|Turn|Empty\"/>\n    <markov>\n      <sequence>\n        <one in=\"W\" out=\"Y\" steps=\"1\"/>\n        <all in=\"YW\" out=\"YY\" steps=\"60\"/>\n        <all>\n          <rule in=\"YW\" out=\"UU\"/>\n          <rule in=\"UY\" out=\"UU\"/>\n          <rule in=\"UW\" out=\"UU\"/>\n        </all>\n      </sequence>\n    </markov>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/SelfAvoidingWalk.xml",
    "content": "<one values=\"BRW\" origin=\"True\" in=\"RB\" out=\"WR\"/>\n\n<!--\nCompare the average length of the walk in 2d and 3d.\n-->\n"
  },
  {
    "path": "models/SequentialSnake.xml",
    "content": "<sequence values=\"BWEPRUG\" origin=\"True\">\n  <one in=\"WBB\" out=\"PER\"/>\n  <one in=\"RBB\" out=\"EER\" steps=\"10\"/>\n  <markov>\n    <one>\n      <rule in=\"RBB\" out=\"GGU\"/>\n      <rule in=\"EEG\" out=\"GGG\"/>\n      <rule in=\"PEG\" out=\"BBP\"/>\n    </one>\n    <all>\n      <rule in=\"G\" out=\"E\"/>\n      <rule in=\"U\" out=\"R\"/>\n    </all>\n    <all>\n      <rule in=\"R\" out=\"P\"/>\n      <rule in=\"P\" out=\"R\"/>\n    </all>\n  </markov>\n</sequence>\n\n<!--\nLooks like a hack at first, but then this is exactly how many real noodle-like organisms move! Because in MJ, like the real world, only local interactions are allowed.\n-->\n"
  },
  {
    "path": "models/SequentialSokoban.xml",
    "content": "<sequence values=\"BRWIYAGD\">\n  <one in=\"B\" out=\"R\" steps=\"1\"/>\n  <one in=\"BBBBBB/BBBBBB/BBBBBB/BBBBBB/BBBBBB/BBBBBB\" out=\"******/******/**II**/**II**/******/******\" steps=\"1\"/>\n  <one in=\"BBB/BBB/BBB\" out=\"***/*Y*/***\" steps=\"4\"/>\n  <markov>\n    <one search=\"True\">\n      <rule in=\"RB\" out=\"BR\"/>\n      <rule in=\"RWB\" out=\"BRW\"/>\n      <rule in=\"RWD\" out=\"BRA\"/>\n\n      <observe value=\"B\" to=\"BR\"/>\n      <observe value=\"R\" to=\"BR\"/>\n      <observe value=\"W\" to=\"BR\"/>\n      <observe value=\"G\" from=\"D\" to=\"A\"/>\n    </one>\n    <one in=\"Y\" out=\"W\" steps=\"1\"/>\n    <one in=\"I\" out=\"G\" steps=\"1\"/>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/Sewers.xml",
    "content": "<sequence values=\"BC\">\n  <all in=\"***/*B*/***\" out=\"***/*C*/***\"/>\n  <wfc sample=\"Sewers\" n=\"3\" values=\"BnyN\">\n    <rule in=\"B\" out=\"B|y\"/>\n    <markov>\n      <one in=\"Nn\" out=\"*N\"/>\n      <path from=\"N\" to=\"n\" on=\"B\" color=\"N\" inertia=\"True\"/>\n      <path from=\"N\" to=\"n\" on=\"y\" color=\"N\" inertia=\"True\"/>\n      <one in=\"n\" out=\"N\"/>\n    </markov>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/SmartSAW.xml",
    "content": "<sequence values=\"BRDYGWEUN\" origin=\"True\">\n  <union symbol=\"?\" values=\"BD\"/>\n  <union symbol=\"_\" values=\"BN\"/>\n  <all>\n    <rule in=\"RBB\" out=\"**D\"/>\n    <rule in=\"DBB\" out=\"**D\"/>\n  </all>\n  <markov>\n    <markov>\n      <markov>\n        <one in=\"Y_D\" out=\"UEG\"/>\n        <one in=\"YWW\" out=\"DNR\"/>\n      </markov>\n      <all in=\"G_D\" out=\"*EG\"/>\n      <markov>\n        <one in=\"RBD\" out=\"WWY\"/>\n        <one in=\"RWW\" out=\"DNR\"/>\n      </markov>\n      <sequence>\n        <one in=\"**D/**_/WWU\" out=\"***/***/RND\"/>\n        <all>\n          <rule in=\"*_*/_G_\" out=\"***/*D*\"/>\n          <rule in=\"*?*/?E?\" out=\"***/*B*\"/>\n          <rule in=\"*_*/_U_\" out=\"***/*R*\"/>\n        </all>\n      </sequence>\n    </markov>\n    <prl in=\"N\" out=\"B\"/>\n  </markov>\n</sequence>\n\n<!--\nI was trying to implement depth-first search with early exit in MJ, and made this.\n\nIt goes much further than the naive self-avoiding walk.\n-->\n"
  },
  {
    "path": "models/SmarterDigger.xml",
    "content": "<markov values=\"WRB\" origin=\"True\">\n  <one in=\"RW\" out=\"BR\"/>\n  <path from=\"R\" to=\"W\" on=\"B\" color=\"W\"/>\n</markov>\n\n<!--\nFor an alternative formulaion using fields, replace path block with this:\n\n<one in=\"RB\" out=\"BR\">\n  <field for=\"R\" on=\"B\" to=\"W\" recompute=\"False\"/>\n</one>\n-->\n"
  },
  {
    "path": "models/SmoothTrail.xml",
    "content": "<sequence values=\"BRW\" origin=\"True\">\n  <one steps=\"3000\">\n    <rule in=\"RB\" out=\"WR\"/>\n    <rule in=\"RW\" out=\"WR\"/>\n  </one>\n  <one in=\"R\" out=\"W\"/>\n  <all>\n    <rule in=\"WB/BW\" out=\"WB/WW\"/>\n    <rule in=\"BBB/BWB\" out=\"***/*B*\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/Snake.xml",
    "content": "<sequence values=\"BDRUGEPCW\" origin=\"True\">\n  <all in=\"DBB\" out=\"**D\"/>\n  <one in=\"BDB/BBB/BDB\" out=\"***/PGR/***\" steps=\"1\"/>\n  <!--<one in=\"BDB/BBB/BDB BBB/BBB/BBB BDB/BBB/BDB\" out=\"***/***/*** ***/PGR/*** ***/***/***\" steps=\"1\" d=\"3\"/>-->\n  <markov>\n    <one>\n      <rule in=\"RDW\" out=\"GGR\"/>\n      <rule in=\"RDD\" out=\"EEU\"/>\n      <rule in=\"GGE\" out=\"EEE\"/>\n      <rule in=\"PGE\" out=\"BBC\"/>\n    </one>\n    <all>\n      <rule in=\"E\" out=\"G\"/>\n      <rule in=\"U\" out=\"R\"/>\n      <rule in=\"C\" out=\"P\"/>\n    </all>\n    <path from=\"R\" to=\"W\" on=\"B\" color=\"D\" inertia=\"True\"/>\n    <one in=\"DBD/BBB/DBD\" out=\"***/*W*/***\"/>\n    <!--<one in=\"DBD/BBB/DBD BBB/BBB/BBB DBD/BBB/DBD\" out=\"***/***/*** ***/*W*/*** ***/***/***\" d=\"3\"/>-->\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/SnellLaw.xml",
    "content": "<sequence values=\"BUGIAWSR\" origin=\"True\">\n  <all in=\"UB\" out=\"*U\" symmetry=\"(x)\"/>\n  <prl in=\"U*\" out=\"G*\" symmetry=\"()\"/>\n  <prl in=\"*G\" out=\"*I\" symmetry=\"()\"/>\n  <all symmetry=\"(x)\">\n    <rule in=\"U/B\" out=\"B/U\"/>\n    <rule in=\"B/G\" out=\"G/B\"/>\n    <rule in=\"IB\" out=\"*I\"/>\n  </all>\n  <all in=\"I/B\" out=\"*/I\" symmetry=\"()\"/>\n  <one>\n    <rule in=\"U**/**I\" out=\"A**/**S\"/>\n    <rule in=\"S\" out=\"U\"/>\n    <rule in=\"U**/**B\" out=\"A**/**R\"/>\n    <rule in=\"R**/**B\" out=\"W**/**R\"/>\n\n    <observe value=\"G\" from=\"B\" to=\"R\"/>\n    <observe value=\"U\" to=\"A\"/>\n    <observe value=\"B\" to=\"BW\"/>\n    <observe value=\"I\" to=\"IA\"/>\n  </one>\n  <one in=\"R\" out=\"W\"/>\n</sequence>\n"
  },
  {
    "path": "models/SoftPath.xml",
    "content": "<sequence values=\"BADYUGRW\" origin=\"True\">\n  <all>\n    <rule in=\"ABB\" out=\"**D\"/>\n    <rule in=\"DBB\" out=\"**D\"/>\n  </all>\n  <one in=\"D\" out=\"Y\" steps=\"1\" temperature=\"20.0\">\n    <field for=\"Y\" from=\"A\" on=\"BD\"/>\n  </one>\n  <one in=\"D\" out=\"G\" steps=\"1\" temperature=\"20.0\">\n    <field for=\"G\" from=\"Y\" on=\"BD\"/>\n  </one>\n  <prl>\n    <rule in=\"D\" out=\"B\"/>\n    <rule in=\"A\" out=\"B\"/>\n  </prl>\n  <one temperature=\"2.0\">\n    <rule in=\"YBB\" out=\"UWR\"/>\n    <rule in=\"RBB\" out=\"WWR\"/>\n    <rule in=\"RBG\" out=\"WWG\"/>\n    <field for=\"R\" to=\"G\" on=\"BR\"/>\n  </one>\n</sequence>\n\n<!--\nSimilar endpoint initialization is used in SeaVilla for the main path.\n-->\n"
  },
  {
    "path": "models/SokobanLevel1.xml",
    "content": "<sequence values=\"BRWDI\">\n  <one file=\"Sokoban1\" legend=\"BDWIR\" symmetry=\"()\"/>\n  <one search=\"True\">\n    <rule in=\"RB\" out=\"BR\"/>\n    <rule in=\"RWB\" out=\"BRW\"/>\n\n    <observe value=\"I\" from=\"B\" to=\"W\"/>\n    <observe value=\"B\" to=\"BR\"/>\n    <observe value=\"W\" to=\"BR\"/>\n    <observe value=\"R\" to=\"BR\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/SokobanLevel2.xml",
    "content": "<sequence values=\"BRWDIA\">\n  <one file=\"Sokoban2\" legend=\"BIWARD\" symmetry=\"()\"/>\n  <one search=\"True\" depthCoefficient=\"1.0\">\n    <rule in=\"RB\" out=\"BR\"/>\n    <rule in=\"RWB\" out=\"BRW\"/>\n\n    <observe value=\"I\" from=\"B\" to=\"W\"/>\n    <observe value=\"A\" from=\"W\" to=\"W\"/>\n    <observe value=\"B\" to=\"BR\"/>\n    <observe value=\"W\" to=\"BR\"/>\n    <observe value=\"R\" to=\"BR\"/>\n  </one>\n</sequence>\n\n<!--\nTry solving it yourself: https://www.sokobanonline.com/play/web-archive/razorflame/ionic-catalysts-xi/58022_ionic-catalysts-xi-452\n-->\n"
  },
  {
    "path": "models/StableCrawlers.xml",
    "content": "<sequence values=\"BUR\">\n  <one in=\"BBB/BBB/BBB\" out=\"BBB/URB/BBB\" steps=\"20\"/>\n  <all>\n    <rule in=\"**BB/URBB/**BB\" out=\"**BB/BURB/**BB\"/>\n    <rule in=\"UR*/*B*/BBB\" out=\"BU*/*R*/BBB\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/StairsPath.xml",
    "content": "<sequence values=\"BDROAGE\" origin=\"True\" symmetry=\"(xy)\">\n  <all in=\"D**B\" out=\"***D\" symmetry=\"(xyz)\"/>\n  <one in=\"DBBDBBD\" out=\"B**GEBB\" steps=\"1\" comment=\"finish\"/>\n  <one in=\"DBBDBBD\" out=\"RO*****\" steps=\"1\" comment=\"start\"/>\n  <prl in=\"D\" out=\"B\"/>\n  <one>\n    <rule in=\"ROBBB\" out=\"DAARO\" comment=\"forward\"/>\n    <rule in=\"ROBB/*BBB/***B/***B\" out=\"DAAR/*BBO/****/****\" comment=\"Turn\"/>\n    <rule in=\"ROBBB BBBBB BBBBB BBBBB\" out=\"DAADB BBBAB BBBAB DBBRO\" comment=\"stairs down\"/>\n    <rule in=\"BBBBB BBBBB BBBBB ROBBB\" out=\"DBBRO BBBAB BBBAB DAADB\" comment=\"stairs up\"/>\n\n    <observe value=\"G\" from=\"B\" to=\"R\"/>\n    <observe value=\"E\" from=\"B\" to=\"O\"/>\n    <observe value=\"R\" to=\"D\"/>\n    <observe value=\"O\" to=\"A\"/>\n    <observe value=\"B\" to=\"BDA\"/>\n  </one>\n  <one in=\"ROBB\" out=\"DAAD\"/>\n  <map scale=\"5/3 5/3 5/3\" values=\"BWAR\" folder=\"Stairs\">\n    <rule in=\"BBB/BBB/BBB BBB/ADA/BBB BBB/BBB/BBB\" fout=\"Line\" legend=\"BAW\"/>\n    <rule in=\"BBB/BBB/BBB BBB/BDA/BAB BBB/BBB/BBB\" fout=\"Turn\" legend=\"BAW\"/>\n    <rule in=\"BBB/BBB/BBB BBB/ADB/BBB BBB/BAB/BBB\" fout=\"Down\" legend=\"BAW\"/>\n    <rule in=\"BBB/BAB/BBB BBB/BDA/BBB BBB/BBB/BBB\" fout=\"Up\" legend=\"BAW\"/>\n    <rule in=\"BBB/BBB/BBB BBB/ADB/BBB BBB/BBB/BBB\" fout=\"End\" legend=\"BAWR\"/>\n  </map>\n</sequence>\n\n<!--\nConnects 2 given points with the shortest path with stairs.\n-->\n"
  },
  {
    "path": "models/StochasticVoronoi.xml",
    "content": "<sequence values=\"BYE\">\n  <one in=\"B\" out=\"E\" steps=\"10\"/>\n  <one in=\"B\" out=\"Y\" steps=\"10\"/>\n  <one>\n    <rule in=\"EB\" out=\"EE\"/>\n    <rule in=\"YB\" out=\"YY\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/StormySnellLaw.xml",
    "content": "<sequence values=\"WUGIABSR\" origin=\"True\">\n  <all in=\"UW\" out=\"*U\" symmetry=\"(x)\"/>\n  <prl in=\"U*\" out=\"G*\" symmetry=\"()\"/>\n  <prl in=\"*G\" out=\"*A\" symmetry=\"()\"/>\n  <all symmetry=\"(x)\">\n    <rule in=\"U/W\" out=\"W/U\"/>\n    <rule in=\"W/G\" out=\"G/W\"/>\n    <rule in=\"AW\" out=\"*A\"/>\n  </all>\n  <all in=\"A/W\" out=\"*/A\" symmetry=\"()\"/>\n  <one in=\"W\" out=\"B\" steps=\"8\"/>\n  <one in=\"A\" out=\"I\" steps=\"8\"/>\n  <prl in=\"W\" out=\"A\"/>\n  <one>\n    <rule in=\"BA\" out=\"BB\"/>\n    <rule in=\"IA\" out=\"II\"/>\n  </one>\n  <one>\n    <rule in=\"U**/**I\" out=\"A**/**S\"/>\n    <rule in=\"S\" out=\"U\"/>\n    <rule in=\"U**/**B\" out=\"A**/**R\"/>\n    <rule in=\"R**/**B\" out=\"W**/**R\"/>\n\n    <observe value=\"G\" from=\"B\" to=\"R\"/>\n    <observe value=\"U\" to=\"A\"/>\n    <observe value=\"B\" to=\"BW\"/>\n    <observe value=\"I\" to=\"IA\"/>\n  </one>\n  <one in=\"R\" out=\"W\"/>\n</sequence>\n"
  },
  {
    "path": "models/StrangeDungeon.xml",
    "content": "<sequence values=\"BWE\">\n  <prl in=\"B\" out=\"E\"/>\n  <prl in=\"***/*E*/***\" out=\"***/*B*/***\" d=\"2\"/>\n  <prl in=\"***/***/*** ***/*E*/*** ***/***/***\" out=\"***/***/*** ***/*B*/*** ***/***/***\" d=\"3\"/>\n  <one in=\"B\" out=\"W\" steps=\"100\"/>\n  <all>\n    <rule in=\"BBB/BWB\" out=\"BWB/BWB\"/>\n    <rule in=\"WB/BW\" out=\"WW/BW\"/>\n    <rule in=\"WBW\" out=\"WWW\"/>\n  </all>\n  <all in=\"E\" out=\"B\"/>\n  <all in=\"BBB/BWB\" out=\"***/*B*\"/>\n</sequence>\n"
  },
  {
    "path": "models/StrangeGrowth.xml",
    "content": "<one values=\"BW\" in=\"**B/WB*/**B\" out=\"***/*W*/***\" origin=\"True\"/>\n"
  },
  {
    "path": "models/StrangeNoise.xml",
    "content": "<sequence values=\"BW\">\n  <one in=\"B\" out=\"W\" steps=\"1\"/>\n  <one temperature=\"2.0\" periodic=\"False\">\n    <rule in=\"B\" out=\"W\"/>\n    <field for=\"W\" to=\"W\" on=\"B\" recompute=\"True\"/>\n  </one>\n</sequence>\n"
  },
  {
    "path": "models/SubmergedKnots.xml",
    "content": "<sequence values=\"BW\">\n  <all in=\"B\" out=\"W\"/>\n  <all in=\"***/***/*** ***/*W*/*** ***/***/***\" out=\"***/***/*** ***/*B*/*** ***/***/***\"/>\n  <wfc values=\"BWU\" tileset=\"Knots3D\" tiles=\"Knots3D/4\">\n    <rule in=\"W\" out=\"Empty\"/>\n    <sequence>\n      <all in=\"B\" out=\"U\"/>\n      <all in=\"U *\" out=\"B *\" symmetry=\"()\"/>\n      <one in=\"UB\" out=\"UU\" steps=\"42000\"/>\n    </sequence>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/Surface.xml",
    "content": "<sequence values=\"B\">\n  <wfc values=\"BW\" tileset=\"Surface\"/>\n</sequence>\n"
  },
  {
    "path": "models/Tetris.xml",
    "content": "<sequence values=\"CIBWRAO\" symmetry=\"(x)\">\n  <union symbol=\"?\" values=\"ICB\"/>\n  <prl in=\"*/C\" out=\"*/I\"/>\n  <prl in=\"*/*/*/*/I\" out=\"*/*/*/*/W\"/>\n  <prl in=\"W/*\" out=\"B/*\"/>\n  <markov>\n    <one in=\"IB\" out=\"*I\"/>\n    <one in=\"AR\" out=\"AA\" symmetry=\"(xy)\"/>\n    <one in=\"R/W\" out=\"A/W\"/>\n    <prl>\n      <rule in=\"R/B\" out=\"*/R\"/>\n      <rule in=\"R/I\" out=\"*/R\"/>\n      <rule in=\"?/R\" out=\"*/B\"/>\n    </prl>\n    <one in=\"A\" out=\"W\"/>\n    <one symmetry=\"(xy)\">\n      <rule in=\"III/III\" out=\"*R*/RRR\"/>\n      <rule in=\"III/III\" out=\"RR*/*RR\"/>\n      <rule in=\"III/III\" out=\"R**/RRR\"/>\n      <rule in=\"III/III\" out=\"RR*/RR*\"/>\n    </one>\n  </markov>\n  <all in=\"WI\" out=\"O*\"/>\n  <all symmetry=\"(xy)\">\n    <rule in=\"O*\" out=\"OO\"/>\n    <rule in=\"OO\" out=\"BB\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/Texture.xml",
    "content": "<sequence values=\"BIURW\">\n  <one in=\"BB*/BBB/*B*\" out=\"***/*I*/***\"/>\n  <all in=\"*I*/IBI\" out=\"***/*I*\"/>\n  <all in=\"*B*/BIB/*B*\" out=\"***/*W*/***\"/>\n  <markov>\n    <all>\n      <rule in=\"UI\" out=\"*U\"/>\n      <rule in=\"RI\" out=\"*R\"/>\n    </all>\n    <one>\n      <rule in=\"I\" out=\"U\"/>\n      <rule in=\"I\" out=\"R\"/>\n    </one>\n  </markov>\n</sequence>\n"
  },
  {
    "path": "models/TileDungeon.xml",
    "content": "<sequence values=\"PB\">\n  <all in=\"***/*P*/***\" out=\"***/*B*/***\"/>\n  <wfc tileset=\"Dungeon\" values=\"ABW\">\n    <rule in=\"P\" out=\"Stone\"/>\n    <markov>\n      <all in=\"WA\" out=\"*W\"/>\n      <path from=\"A\" to=\"W\" on=\"B\" color=\"W\"/>\n      <one in=\"A\" out=\"W\"/>\n    </markov>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/TilePath.xml",
    "content": "<sequence values=\"BAR\">\n  <all in=\"***/*B*/***\" out=\"***/*A*/***\" comment=\"color all inner cells gray\"/>\n  <one in=\"BBB/BAA/BAA\" out=\"***/***/**R\" symmetry=\"()\" comment=\"color 2 cells near opposite corners red\"/>\n  <one in=\"AAB/AAB/BBB\" out=\"R**/***/***\" symmetry=\"()\"/>\n  <wfc tileset=\"Knots2D\" values=\"EUIR\">\n    <rule in=\"B\" out=\"Empty\"/>\n    <rule in=\"A\" out=\"Empty|Line|Turn\"/>\n    <rule in=\"R\" out=\"End\"/>\n\n    <one in=\"EEE/EUE\" out=\"***/*I*\" comment=\"color river endpoints indigo\"/>\n    <all in=\"IU\" out=\"*I\" comment=\"color the whole path indigo\"/>\n    <one in=\"EEE/EIE\" out=\"***/*R*\" comment=\"color endpoints red\"/>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/Trail.xml",
    "content": "<one values=\"BRW\" origin=\"True\">\n  <rule in=\"RB\" out=\"WR\"/>\n  <rule in=\"RW\" out=\"WR\"/>\n</one>\n"
  },
  {
    "path": "models/Voronoi.xml",
    "content": "<sequence values=\"BYE\">\n  <one in=\"B\" out=\"E\" steps=\"10\"/>\n  <one in=\"B\" out=\"Y\" steps=\"10\"/>\n  <all>\n    <rule in=\"EB\" out=\"*E\"/>\n    <rule in=\"YB\" out=\"*Y\"/>\n  </all>\n</sequence>\n"
  },
  {
    "path": "models/WaveBrickWall.xml",
    "content": "<sequence values=\"B\" symmetry=\"()\">\n  <wfc sample=\"BrickWall\" values=\"bNn\" n=\"3\" periodic=\"True\" shannon=\"True\">\n    <rule in=\"B\" out=\"b|N|n\"/>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/WaveDungeon.xml",
    "content": "<sequence values=\"BWDG\">\n  <all in=\"***/*B*/***\" out=\"***/*D*/***\"/>\n  <one in=\"DDDD/DDDD/DDDD\" out=\"WWWW/WWWW/WWWW\" steps=\"5\"/>\n  <markov>\n    <all in=\"GW\" out=\"*G\"/>\n    <path from=\"G\" to=\"W\" on=\"D\" color=\"W\" inertia=\"True\"/>\n    <one in=\"W\" out=\"G\"/>\n  </markov>\n  <all in=\"G\" out=\"W\"/>\n  <wfc sample=\"Dungeon\" values=\"BWP\" n=\"3\">\n    <rule in=\"B\" out=\"B\"/>\n    <rule in=\"W\" out=\"W\"/>\n\n    <one in=\"W\" out=\"P\" steps=\"1\"/>\n    <all in=\"PW\" out=\"*P\"/>\n    <all in=\"W\" out=\"B\"/>\n  </wfc>\n</sequence>\n\n<!--<sequence values=\"PB\">\n  <all in=\"***/*P*/***\" out=\"***/*B*/***\"/>\n  <wfc sample=\"Skew2\" n=\"3\" values=\"BWE\" shannon=\"True\">\n    <rule in=\"P\" out=\"B\"/>\n    <markov>\n      <all in=\"EW\" out=\"*E\"/>\n      <path from=\"E\" to=\"W\" on=\"B\" color=\"W\"/>\n      <one in=\"W\" out=\"E\"/>\n    </markov>\n  </wfc>\n</sequence>-->\n"
  },
  {
    "path": "models/WaveFlowers.xml",
    "content": "<sequence values=\"BW\" symmetry=\"(x)\">\n  <all in=\"B/*/*\" out=\"W/*/*\"/>\n  <wfc sample=\"Flowers\" values=\"zYgN\" n=\"3\" periodic=\"True\" shannon=\"True\">\n    <rule in=\"B\" out=\"N|g\"/>\n    <rule in=\"W\" out=\"z|g|Y\"/>\n  </wfc>\n</sequence>\n"
  },
  {
    "path": "models/Wilson.xml",
    "content": "<sequence values=\"BWIRKGYU\" origin=\"True\">\n  <all>\n    <rule in=\"WBB\" out=\"**I\"/>\n    <rule in=\"IBB\" out=\"**I\"/>\n  </all>\n  <markov>\n    <one>\n      <rule in=\"RBI\" out=\"KKR\"/>\n      <rule in=\"RBK\" out=\"GKY\"/>\n      <rule in=\"RBW\" out=\"WWW\"/>\n\n      <rule in=\"WKK\" out=\"WWW\"/>\n\n      <rule in=\"YKG\" out=\"YBU\"/>\n      <rule in=\"UKK\" out=\"IBU\"/>\n      <rule in=\"UKY\" out=\"IBR\"/>\n    </one>\n    <one in=\"I\" out=\"R\"/>\n  </markov>\n</sequence>\n\n<!--\nGenerates a random maze, in a way that each maze has the same probability of appearing.\n\nCompare to an implementation in a conventional language: https://bl.ocks.org/mbostock/11357811\n-->\n"
  },
  {
    "path": "models/WolfBasedApproach.xml",
    "content": "<sequence values=\"DEIWRSA\">\n  <prl in=\"***/*D*/***\" out=\"***/*E*/***\"/>\n  <one in=\"E\" out=\"W\" steps=\"15\"/>\n  <one in=\"E\" out=\"R\" steps=\"1\"/>\n  <all>\n    <rule in=\"RI\" out=\"IR\"/>\n    <rule in=\"RE\" out=\"SR\"/>\n    <rule in=\"WI\" out=\"IW\"/>\n    <rule in=\"WE\" out=\"IW\"/>\n    <rule in=\"RW\" out=\"RR\"/>\n    <rule in=\"R*/*W\" out=\"R*/*R\"/>\n\n    <field for=\"W\" to=\"E\" on=\"I\" recompute=\"True\"/>\n    <field for=\"R\" to=\"W\" on=\"EI\" recompute=\"True\" essential=\"True\"/>\n  </all>\n  <prl>\n    <rule in=\"E\" out=\"D\"/>\n    <rule in=\"S\" out=\"A\"/>\n    <rule in=\"R\" out=\"A\"/>\n    <rule in=\"I\" out=\"A\"/>\n  </prl>\n  <all>\n    <rule in=\"DA/AD\" out=\"**/*A\"/>\n    <rule in=\"AAA/ADA/AAA\" out=\"***/*A*/***\"/>\n  </all>\n  <all in=\"DDD/DAD\" out=\"***/*D*\"/>\n</sequence>\n\n<!--\n1. Sheep eat grass.\n2. Wolves chase sheep.\n3. Wolves eat sheep and multiply.\n4. Wolves mark their territory with a trail, no other wolf can cross it.\n5. We just generated a dungeon.\n-->\n"
  },
  {
    "path": "models.xml",
    "content": "<models>\n  <model name=\"Apartemazements\" size=\"8\" d=\"3\" amount=\"4\"/>\n  <model name=\"Apartemazements\" size=\"8\" d=\"3\" iso=\"True\" pixelsize=\"6\" gui=\"150\"/>\n  <model name=\"CarmaTower\" length=\"12\" width=\"12\" height=\"18\"/>\n  <model name=\"CarmaTower\" length=\"12\" width=\"12\" height=\"18\" iso=\"True\" amount=\"1\"/>\n  <model name=\"ModernHouse\" length=\"9\" width=\"9\" height=\"4\" amount=\"4\"/>\n  <model name=\"ModernHouse\" length=\"9\" width=\"9\" height=\"4\" iso=\"True\"/>\n  <model name=\"SeaVilla\" length=\"10\" width=\"10\" height=\"4\" amount=\"4\"/>\n  <model name=\"SeaVilla\" length=\"10\" width=\"10\" height=\"4\" iso=\"True\"/>\n  <model name=\"Island\" size=\"800\" steps=\"-1\" pixelsize=\"1\">\n    <color symbol=\"I\" value=\"abcbe8\" comment=\"sea\"/>\n    <color symbol=\"U\" value=\"cee6f9\" comment=\"coast and lakes\"/>\n    <color symbol=\"T\" value=\"2b8aa9\" comment=\"rivers\"/>\n    <color symbol=\"Y\" value=\"f1ebc2\" comment=\"sand\"/>\n    <color symbol=\"G\" value=\"a8d1a5\" comment=\"grass\"/>\n    <color symbol=\"E\" value=\"93c08d\" comment=\"forest\"/>\n    <color symbol=\"S\" value=\"d0d8ac\" comment=\"slopes\"/>\n    <color symbol=\"s\" value=\"e9e0b2\" comment=\"hills\"/>\n  </model>\n  \n  <model name=\"Backtracker\" size=\"89\"/>\n  <model name=\"BacktrackerCycle\" size=\"59\"/>\n  <model name=\"Basic\" size=\"60\" steps=\"1000\"/>\n  <model name=\"BasicBrickWall\" size=\"30\"/>\n  <model name=\"BasicDijkstraDungeon\" size=\"60\"/>\n  <model name=\"BasicDijkstraFill\" size=\"60\"/>\n  <model name=\"BasicDungeonGrowth\" size=\"59\"/>\n  <model name=\"BasicPartitioning\" size=\"60\" steps=\"1000\"/>\n  <model name=\"BernoulliPercolation\" size=\"359\"/>\n  <model name=\"BiasedGrowth\" size=\"120\" steps=\"1000\"/>\n  <model name=\"BiasedGrowthContraction\" size=\"120\" steps=\"5000\"/>\n  <model name=\"BiasedMazeGrowth\" size=\"117\" steps=\"400\"/>\n  <model name=\"BiasedVoronoi\" size=\"80\" steps=\"1000\"/>\n  <model name=\"BishopParity\" size=\"60\"/>\n  <model name=\"BlueNoise\" size=\"80\" steps=\"500\"/>\n  <model name=\"Cave\" size=\"60\"/>\n  <model name=\"CaveContour\" size=\"80\"/>\n  <model name=\"CentralCrawlers\" size=\"60\" steps=\"300\"/>\n  <model name=\"CentralSAW\" size=\"59\"/>\n  <model name=\"ChainDungeon\" size=\"60\"/>\n  <model name=\"ChainDungeonMaze\" size=\"60\"/>\n  <model name=\"ChainMaze\" size=\"60\"/>\n  <model name=\"Circuit\" size=\"59\" steps=\"1200\"/>\n  <model name=\"ClosedSurface\" size=\"12\" d=\"3\"/>\n  <model name=\"ColoredKnots\" size=\"12\" d=\"3\"/>\n  <model name=\"CompleteSAW\" size=\"19\"/>\n  <model name=\"CompleteSAWSmart\" size=\"23\"/>\n  <model name=\"ConnectedCaves\" size=\"60\"/>\n  <model name=\"ConstrainedCaves\" size=\"60\"/>\n  <model name=\"Counting\" size=\"8\" d=\"3\"/>\n  <model name=\"Coupling\" size=\"80\"/>\n  <model name=\"CrawlersChase\" size=\"60\" steps=\"200\"/>\n  <model name=\"CrossCountry\" size=\"80\"/>\n  <model name=\"Cycles\" size=\"59\"/>\n  <model name=\"DenseSAW\" size=\"59\" amount=\"4\"/>\n  <model name=\"DiagonalPath\" size=\"80\"/>\n  <model name=\"Digger\" size=\"359\" steps=\"50000\"/>\n  <model name=\"DijkstraDungeon\" size=\"40\" amount=\"4\"/>\n  <model name=\"Division\" size=\"75\"/>\n  <model name=\"DualRetraction\" size=\"59\"/>\n  <model name=\"DualRetraction3D\" size=\"27\" d=\"3\"/>\n  <model name=\"DungeonGrowth\" size=\"79\" amount=\"4\"/>\n  <model name=\"Dwarves\" size=\"20\" steps=\"85\"/>\n  <model name=\"Escher\" size=\"8\" d=\"3\"/>\n  <model name=\"EscherSurface\" size=\"8\" d=\"3\"/>\n  <model name=\"EuclideanPath\" size=\"80\"/>\n  <model name=\"FindLongCycle\" size=\"27\" pixelsize=\"10\"/>\n  <model name=\"FireNoise\" size=\"300\"/>\n  <model name=\"Flowers\" size=\"60\"/>\n  <model name=\"Forest\" size=\"240\"/>\n  <model name=\"ForestFire\" size=\"600\" steps=\"80\" pixelsize=\"1\"/>\n  <model name=\"ForestFireCA\" size=\"600\" steps=\"80\" pixelsize=\"1\"/>\n  <model name=\"GameOfLife\" size=\"120\" steps=\"100\"/>\n  <model name=\"Growth\" size=\"359\" steps=\"40000\"/>\n  <model name=\"Growth\" size=\"29\" d=\"3\" steps=\"3000\"/>\n  <model name=\"GrowthCompetition\" size=\"80\" steps=\"30000\"/>\n  <model name=\"GrowthContraction\" size=\"79\" steps=\"10000\"/>\n  <model name=\"GrowthWalk\" size=\"79\" steps=\"5000\"/>\n  <model name=\"GrowTo\" size=\"120\" steps=\"2000\"/>\n  <model name=\"HamiltonianPath\" size=\"39\" steps=\"150000\"/>\n  <model name=\"HamiltonianPaths\" size=\"39\" steps=\"1000\"/>\n  <model name=\"Hills\" length=\"40\" width=\"40\" height=\"12\" d=\"3\"/>\n  <model name=\"IrregularMazeGrowth\" size=\"79\"/>\n  <model name=\"IrregularSAW\" size=\"79\"/>\n  <model name=\"Keys\" size=\"33\"/>\n  <model name=\"Keys\" size=\"23\" d=\"3\"/>\n  <model name=\"KnightPatrol\" size=\"60\" steps=\"200\"/>\n  <model name=\"Knots2D\" size=\"12\"/>\n  <model name=\"Knots3D\" size=\"8\" d=\"3\"/>\n  <model name=\"Laplace\" size=\"60\" steps=\"1000\"/>\n  <model name=\"LoopGrowth\" size=\"37\"/>\n  <model name=\"LostCity\" size=\"256\" pixelsize=\"3\"/>\n  <model name=\"MarchingSquares\" size=\"20\"/>\n  <model name=\"MazeBacktracker\" size=\"359\" steps=\"20000\"/>\n  <model name=\"MazeBacktracker\" size=\"967\" pixelsize=\"1\" steps=\"200000\"/>\n  <model name=\"MazeGrowth\" size=\"359\"/>\n  <model name=\"MazeGrowth\" size=\"27\" d=\"3\"/>\n  <model name=\"MazeMap\" size=\"30\"/>\n  <model name=\"MazeTrail\" size=\"59\" steps=\"1000\"/>\n  <model name=\"MazeTrail\" size=\"27\" d=\"3\" steps=\"1000\"/>\n  <model name=\"MultiHeadedDungeon\" size=\"55\"/>\n  <model name=\"MultiHeadedWalk\" size=\"99\"/>\n  <model name=\"MultiHeadedWalkDungeon\" size=\"59\"/>\n  <model name=\"NestedGrowth\" size=\"80\" steps=\"100\"/>\n  <model name=\"NoDeadEnds\" size=\"39\"/>\n  <model name=\"NoDeadEnds\" size=\"19\" d=\"3\"/>\n  <model name=\"Noise\" size=\"80\"/>\n  <model name=\"Noise\" size=\"24\" d=\"3\"/>\n  <model name=\"NystromDungeon\" size=\"39\" amount=\"4\" pixelsize=\"8\" gui=\"150\"/>\n  <model name=\"OddScale\" size=\"8\"/>\n  <model name=\"OddScale3D\" size=\"8\" d=\"3\"/>\n  <model name=\"OpenCave\" size=\"80\"/>\n  <model name=\"OpenCave3D\" size=\"40\" d=\"3\"/>\n  <model name=\"OrganicMechanic\" size=\"30\" steps=\"200\"/>\n  <model name=\"OrientedEscher\" size=\"6\" d=\"3\"/>\n  <model name=\"PaintCompetition\" size=\"48\" steps=\"400\"/>\n  <model name=\"ParallelGrowth\" size=\"29\" d=\"3\" steps=\"24\"/>\n  <model name=\"ParallelMazeGrowth\" size=\"179\"/>\n  <model name=\"Partitioning\" length=\"11\" width=\"11\" height=\"3\"/>\n  <model name=\"Percolation\" size=\"360\"/>\n  <model name=\"PeriodicEscher\" size=\"8\" d=\"3\"/>\n  <model name=\"PillarsOfEternity\" size=\"9\" d=\"3\"/>\n  <model name=\"Push\" size=\"40\" steps=\"8000\"/>\n  <model name=\"PutColoredLs\" size=\"30\"/>\n  <model name=\"PutLs\" size=\"50\"/>\n  <model name=\"RainbowGrowth\" size=\"59\" steps=\"1600\"/>\n  <model name=\"RegularPath\" size=\"79\"/>\n  <model name=\"RegularSAW\" size=\"39\"/>\n  <model name=\"RegularSAW\" size=\"19\" d=\"3\"/>\n  <model name=\"RegularSAWRestart\" size=\"39\"/>\n  <model name=\"RegularSAWRestart\" size=\"19\" d=\"3\"/>\n  <model name=\"River\" size=\"80\" amount=\"4\"/>\n  <model name=\"River\" size=\"20\" d=\"3\" amount=\"1\"/>\n  <model name=\"Rosettes\" size=\"75\"/>\n  <model name=\"SAWRestart\" size=\"79\" steps=\"2000\"/>\n  <model name=\"SelectLargeCaves\" size=\"60\"/>\n  <model name=\"SelectLongKnots\" size=\"10\" d=\"3\"/>\n  <model name=\"SelfAvoidingWalk\" size=\"39\"/>\n  <model name=\"Sewers\" size=\"40\"/>\n  <model name=\"SmarterDigger\" size=\"40\" steps=\"800\"/>\n  <model name=\"SmartSAW\" size=\"19\"/>\n  <model name=\"SmoothTrail\" size=\"59\"/>\n  <model name=\"SnellLaw\" size=\"80\"/>\n  <model name=\"SoftPath\" size=\"180\"/>\n  <model name=\"SoftPath\" size=\"80\" d=\"3\"/>\n  <model name=\"StairsPath\" size=\"33\" d=\"3\"/>\n  <model name=\"StochasticVoronoi\" size=\"200\"/>\n  <model name=\"StochasticVoronoi\" size=\"50\" d=\"3\" steps=\"-1\"/>\n  <model name=\"StormySnellLaw\" size=\"120\"/>\n  <model name=\"StrangeNoise\" size=\"60\" steps=\"500\"/>  \n  <model name=\"StrangeDungeon\" size=\"59\"/>\n  <model name=\"StrangeDungeon\" size=\"29\" d=\"3\"/>\n  <model name=\"StrangeGrowth\" size=\"59\" steps=\"700\"/>\n  <model name=\"SubmergedKnots\" size=\"12\" d=\"3\"/>\n  <model name=\"Surface\" size=\"10\" d=\"3\"/>\n  <model name=\"Tetris\" size=\"30\" steps=\"1500\"/>\n  <model name=\"Texture\" size=\"240\"/>\n  <model name=\"TileDungeon\" size=\"12\" amount=\"4\"/>\n  <model name=\"TilePath\" size=\"20\"/>\n  <model name=\"Trail\" size=\"59\" steps=\"3000\"/>\n  <model name=\"Voronoi\" size=\"240\"/>\n  <model name=\"Voronoi\" size=\"60\" d=\"3\"/>\n  <model name=\"WaveBrickWall\" size=\"50\"/>\n  <model name=\"WaveDungeon\" size=\"50\"/>\n  <model name=\"WaveFlowers\" size=\"60\"/>\n  <model name=\"WolfBasedApproach\" size=\"70\" steps=\"300\"/>\n  \n  gifs\n  <!--<model name=\"Wilson\" size=\"59\" steps=\"1000\" gui=\"150\" gif=\"True\"/>-->\n  <!--<model name=\"BasicSnake\" size=\"23\" steps=\"100\" gif=\"True\"/>-->\n  <!--<model name=\"Chase\" size=\"60\" steps=\"200\" gif=\"True\"/>-->\n\n  <!--<model name=\"MultiSokoban9\" size=\"9\" amount=\"1\" pixelsize=\"20\" gif=\"True\"/>-->\n  <!--<model name=\"MultiSokoban8\" size=\"8\" amount=\"1\" pixelsize=\"20\" gif=\"True\"/>-->\n  <!--<model name=\"SokobanLevel1\" length=\"14\" width=\"9\" amount=\"1\" pixelsize=\"21\" gif=\"True\"/>-->\n  <!--<model name=\"SokobanLevel2\" length=\"5\" width=\"4\" amount=\"1\" pixelsize=\"21\" gif=\"True\"/>-->\n  <!--<model name=\"SequentialSokoban\" size=\"20\" gif=\"True\"/>-->\n\n  benchmarks\n  <!--<model name=\"MazeGrowth\" size=\"967\" amount=\"20\" pixelsize=\"1\" steps=\"200000\"/> 1.7-->\n  <!--<model name=\"ParallelMazeGrowth\" size=\"967\" amount=\"20\" pixelsize=\"1\"/> 2.0-->\n  <!--<model name=\"MazeBacktracker\" size=\"967\" amount=\"20\" pixelsize=\"1\" steps=\"200000\"/> 2.3-->\n</models>\n\n<!--\n\n-->\n"
  },
  {
    "path": "resources/palette.xml",
    "content": "﻿<colors>\n  <color symbol=\"B\" value=\"000000\" comment=\"Black\"/>\n  <color symbol=\"I\" value=\"1D2B53\" comment=\"Indigo\"/>\n  <color symbol=\"P\" value=\"7E2553\" comment=\"Purple\"/>\n  <color symbol=\"E\" value=\"008751\" comment=\"Emerald\"/>\n  <color symbol=\"N\" value=\"AB5236\" comment=\"browN\"/>\n  <color symbol=\"D\" value=\"5F574F\" comment=\"Dead, Dark\"/>\n  <color symbol=\"A\" value=\"C2C3C7\" comment=\"Alive, grAy\"/>\n  <color symbol=\"W\" value=\"FFF1E8\" comment=\"White\"/>\n  <color symbol=\"R\" value=\"FF004D\" comment=\"Red\"/>\n  <color symbol=\"O\" value=\"FFA300\" comment=\"Orange\"/>\n  <color symbol=\"Y\" value=\"FFEC27\" comment=\"Yellow\"/>\n  <color symbol=\"G\" value=\"00E436\" comment=\"Green\"/>\n  <color symbol=\"U\" value=\"29ADFF\" comment=\"blUe\"/>\n  <color symbol=\"S\" value=\"83769C\" comment=\"Slate\"/>\n  <color symbol=\"K\" value=\"FF77A8\" comment=\"pinK\"/>\n  <color symbol=\"F\" value=\"FFCCAA\" comment=\"Fawn\"/>\n  \n  <color symbol=\"b\" value=\"291814\" comment=\"black\"/>\n  <color symbol=\"i\" value=\"111d35\" comment=\"indigo\"/>\n  <color symbol=\"p\" value=\"422136\" comment=\"purple\"/>\n  <color symbol=\"e\" value=\"125359\" comment=\"emerald\"/>\n  <color symbol=\"n\" value=\"742f29\" comment=\"brown\"/>\n  <color symbol=\"d\" value=\"49333b\" comment=\"dead, dark\"/>\n  <color symbol=\"a\" value=\"a28879\" comment=\"alive, gray\"/>\n  <color symbol=\"w\" value=\"f3ef7d\" comment=\"white\"/>\n  <color symbol=\"r\" value=\"be1250\" comment=\"red\"/>\n  <color symbol=\"o\" value=\"ff6c24\" comment=\"orange\"/>\n  <color symbol=\"y\" value=\"a8e72e\" comment=\"yellow\"/>\n  <color symbol=\"g\" value=\"00b543\" comment=\"green\"/>\n  <color symbol=\"u\" value=\"065ab5\" comment=\"blue\"/>\n  <color symbol=\"s\" value=\"754665\" comment=\"slate\"/>\n  <color symbol=\"k\" value=\"ff6e59\" comment=\"pink\"/>\n  <color symbol=\"f\" value=\"ff9d81\" comment=\"fawn\"/>\n  \n  <color symbol=\"C\" value=\"00ffff\" comment=\"Cyan\"/>\n  <color symbol=\"c\" value=\"5fcde4\" comment=\"cyan\"/>\n  <color symbol=\"H\" value=\"e4bb40\" comment=\"Honey\"/>\n  <color symbol=\"h\" value=\"8a6f30\" comment=\"honey\"/>\n  <color symbol=\"J\" value=\"4b692f\" comment=\"Jungle\"/>\n  <color symbol=\"j\" value=\"45107e\" comment=\"jungle\"/>\n  <color symbol=\"L\" value=\"847e87\" comment=\"Light\"/>\n  <color symbol=\"l\" value=\"696a6a\" comment=\"light\"/>\n  <color symbol=\"M\" value=\"ff00ff\" comment=\"Magenta\"/>\n  <color symbol=\"m\" value=\"9c09cc\" comment=\"magenta\"/>\n  <color symbol=\"Q\" value=\"9badb7\" comment=\"aQua\"/>\n  <color symbol=\"q\" value=\"3f3f74\" comment=\"aqua\"/>\n  <color symbol=\"T\" value=\"37946e\" comment=\"Teal\"/>\n  <color symbol=\"t\" value=\"323c39\" comment=\"teal\"/>\n  <color symbol=\"V\" value=\"8f974a\" comment=\"oliVe\"/>\n  <color symbol=\"v\" value=\"524b24\" comment=\"olive\"/>\n  <color symbol=\"X\" value=\"ff0000\" comment=\"X\"/>\n  <color symbol=\"x\" value=\"d95763\" comment=\"x\"/>\n  <color symbol=\"Z\" value=\"ffffff\" comment=\"Z\"/>\n  <color symbol=\"z\" value=\"cbdbfc\" comment=\"z\"/>\n</colors>\n"
  },
  {
    "path": "resources/settings.xml",
    "content": "<settings\n  squareSize=\"7\" smallSquareSize=\"3\" maxwidth=\"10\" zshift=\"2\"\n  hindent=\"30\" hgap=\"2\" harrow=\"10\" hline=\"14\"\n  vskip=\"2\" smallvskip=\"2\" fontshift=\"2\" afterfont=\"4\"\n  dense=\"True\" d3=\"True\"\n  background=\"222222\" inactive=\"666666\" active=\"ffffff\"\n/>\n"
  },
  {
    "path": "resources/tilesets/Dungeon.xml",
    "content": "<tileset>\n\t<tiles checksum=\"28\">\n    <tile name=\"Empty\"/>\n    <tile name=\"Out\" weight=\"0.5\"/>\n\t\t<tile name=\"In\" weight=\"0.5\"/>\n    <tile name=\"Wall\" weight=\"2.0\"/>\n    <tile name=\"Stone\"/>\n\n    <tile name=\"Line\" legend=\"BW\" weight=\"1.0\"/>\n    <tile name=\"Turn\" legend=\"BW\" weight=\"0.25\"/>\n    <tile name=\"T\" legend=\"BW\" weight=\"0.5\"/>\n    <tile name=\"Door\" legend=\"BW\" weight=\"0.5\"/>\n  </tiles>\n\t<neighbors>\n    <neighbor left=\"Empty\" right=\"Empty\"/>\n    <neighbor left=\"Out\" right=\"Empty\"/>\n    <neighbor left=\"Wall\" right=\"Empty\"/>\n    <neighbor left=\"Door\" right=\"Empty\"/>\n    <neighbor left=\"z Wall\" right=\"Out\"/>\n    <neighbor left=\"z Wall\" right=\"z Wall\"/>\n    <neighbor left=\"In\" right=\"z Wall\"/>\n    <neighbor left=\"Stone\" right=\"Wall\"/>\n    <neighbor left=\"z Wall\" right=\"z Door\"/>\n    <neighbor left=\"zz In\" right=\"In\"/>\n    <neighbor left=\"Stone\" right=\"In\"/>\n    <neighbor left=\"In\" right=\"z Door\"/>\n    <neighbor left=\"Stone\" right=\"Stone\"/>\n    <neighbor left=\"Stone\" right=\"z Line\"/>\n    <neighbor left=\"Stone\" right=\"Turn\"/>\n    <neighbor left=\"Stone\" right=\"z T\"/>\n    <neighbor left=\"Line\" right=\"Line\"/>\n    <neighbor left=\"Turn\" right=\"Line\"/>\n    <neighbor left=\"Line\" right=\"T\"/>\n    <neighbor left=\"Line\" right=\"zzz T\"/>\n    <neighbor left=\"Line\" right=\"Door\"/>\n    <neighbor left=\"Turn\" right=\"zz Turn\"/>\n    <neighbor left=\"Turn\" right=\"T\"/>\n    <neighbor left=\"Turn\" right=\"Door\"/>\n    <neighbor left=\"T\" right=\"zz T\"/>\n\n    <neighbor left=\"z Out\" right=\"Out\"/>\n    <neighbor left=\"Out\" right=\"z Out\"/>\n    <neighbor left=\"Out\" right=\"zz Out\"/>\n    <neighbor left=\"Wall\" right=\"z Out\"/>\n    <neighbor left=\"In\" right=\"Out\"/>\n    <neighbor left=\"z Door\" right=\"Out\"/>\n    <neighbor left=\"Door\" right=\"z Out\"/>\n    <neighbor left=\"Wall\" right=\"zz Wall\"/>\n    <neighbor left=\"zz Wall\" right=\"Wall\"/>\n    <neighbor left=\"zz Wall\" right=\"In\"/>\n    <neighbor left=\"z Line\" right=\"Wall\"/>\n    <neighbor left=\"z Turn\" right=\"Wall\"/>\n    <neighbor left=\"zzz T\" right=\"Wall\"/>\n    <neighbor left=\"Wall\" right=\"zz Door\"/>\n    <neighbor left=\"z In\" right=\"In\"/>\n    <neighbor left=\"z Line\" right=\"In\"/>\n    <neighbor left=\"z Turn\" right=\"In\"/>\n    <neighbor left=\"zz Turn\" right=\"In\"/>\n    <neighbor left=\"zzz T\" right=\"In\"/>\n    <neighbor left=\"z Line\" right=\"z Line\"/>\n    <neighbor left=\"z Line\" right=\"Turn\"/>\n    <neighbor left=\"z Line\" right=\"z T\"/>\n    <neighbor left=\"z Turn\" right=\"Turn\"/>\n    <neighbor left=\"zz Turn\" right=\"Turn\"/>\n    <neighbor left=\"Turn\" right=\"z Turn\"/>\n    <neighbor left=\"zzz T\" right=\"Turn\"/>\n    <neighbor left=\"Turn\" right=\"zz T\"/>\n    <neighbor left=\"Turn\" right=\"zzz T\"/>\n    <neighbor left=\"zzz T\" right=\"z T\"/>\n    <neighbor left=\"T\" right=\"T\"/>\n    <neighbor left=\"T\" right=\"zzz T\"/>\n    <neighbor left=\"z T\" right=\"zzz T\"/>\n    <neighbor left=\"T\" right=\"Door\"/>\n    <neighbor left=\"z T\" right=\"Door\"/>\n    <neighbor left=\"Door\" right=\"zz Door\"/>\n    <neighbor left=\"zz Door\" right=\"Door\"/>\n    <neighbor left=\"z Door\" right=\"z Door\"/>\n\t</neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/Escher.xml",
    "content": "<tileset fullSymmetry=\"True\">\n\t<tiles>\n\t\t<tile name=\"Empty\"/>\n\t\t<tile name=\"X\" weight=\"0.1\"/>\n\t\t<tile name=\"Line\"/>\n\t\t<tile name=\"Turn\"/>\n\t\t<tile name=\"Stairs\" weight=\"100.0\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"Empty\" right=\"Empty\"/>\n\t\t<neighbor left=\"Empty\" right=\"y Line\"/>\n\t\t<neighbor left=\"Empty\" right=\"yyy Line\"/>\n\t\t<neighbor left=\"Empty\" right=\"z Line\"/>\n\t\t<neighbor left=\"Empty\" right=\"Turn\"/>\n\t\t<neighbor left=\"Empty\" right=\"y Turn\"/>\n\t\t<neighbor left=\"Empty\" right=\"yyy Turn\"/>\n\t\t<neighbor left=\"Empty\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"Empty\"/>\n\n\t\t<neighbor left=\"Empty\" right=\"y X\"/>\n\t\t<neighbor left=\"Empty\" right=\"yyy X\"/>\n\t\t<neighbor left=\"X\" right=\"X\"/>\n\t\t<neighbor left=\"y X\" right=\"yyy X\"/>\n\t\t<neighbor left=\"yyy X\" right=\"y X\"/>\n\t\t<neighbor left=\"X\" right=\"Line\"/>\n\t\t<neighbor left=\"y X\" right=\"yyy Line\"/>\n\t\t<neighbor left=\"yyy X\" right=\"y Line\"/>\n\t\t<neighbor left=\"y X\" right=\"xz Line\"/>\n\t\t<neighbor left=\"X\" right=\"z Turn\"/>\n\t\t<neighbor left=\"y X\" right=\"yyy Turn\"/>\n\t\t<neighbor left=\"yyy X\" right=\"y Turn\"/>\n\t\t<neighbor left=\"y X\" right=\"x Turn\"/>\n\t\t<neighbor left=\"xxx X\" right=\"Stairs\"/>\n\t\t<neighbor left=\"yyy X\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"y X\" right=\"y Stairs\"/>\n\t\t\n\t\t<neighbor left=\"Line\" right=\"Line\"/>\n\t\t<neighbor left=\"z Line\" right=\"z Line\"/>\n\t\t<neighbor left=\"yyy Line\" right=\"y Line\"/>\n\t\t<neighbor left=\"yyy Line\" right=\"xy Line\"/>\n\t\t<neighbor left=\"y Line\" right=\"yyy Line\"/>\n\t\t<neighbor left=\"y Line\" right=\"xyyy Line\"/>\n\t\t<neighbor left=\"y Line\" right=\"z Line\"/>\n\t\t<neighbor left=\"y Line\" right=\"xz Line\"/>\n\t\t\n\t\t<neighbor left=\"Turn\" right=\"Line\"/>\n\t\t<neighbor left=\"z Line\" right=\"Turn\"/>\n\t\t<neighbor left=\"yyy Line\" right=\"y Turn\"/>\n\t\t<neighbor left=\"y Line\" right=\"yyy Turn\"/>\n\t\t<neighbor left=\"y Line\" right=\"Turn\"/>\n\t\t<neighbor left=\"y Line\" right=\"x Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"z Line\"/>\n\t\t\n\t\t<neighbor left=\"xxx Line\" right=\"Stairs\"/>\n\t\t<neighbor left=\"yyy Line\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"xyyy Line\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"y Line\" right=\"y Stairs\"/>\n\n\t\t<neighbor left=\"Turn\" right=\"z Turn\"/>\n\t\t<neighbor left=\"Turn\" right=\"zz Turn\"/>\n\t\t<neighbor left=\"z Turn\" right=\"Turn\"/>\n\t\t<neighbor left=\"zz Turn\" right=\"Turn\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"y Turn\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"xy Turn\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"xxy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"yyy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xyyy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxyyy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xx Turn\"/>\n\t\t\n\t\t<neighbor left=\"xxx Turn\" right=\"Stairs\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"xxz Stairs\"/>\n\t\t<neighbor left=\"y Turn\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xy Stairs\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxy Stairs\"/>\n\t\t\n\t\t<neighbor left=\"Stairs\" right=\"zz Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"zz Stairs\" right=\"Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"xy Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"xxy Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xzz Stairs\"/>\n\t\t\n\t\t<neighbor left=\"Stairs\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xy Stairs\"/>\n\t\t\n\t\t<neighbor left=\"z Line\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"z Line\" right=\"yyy Stairs\"/>\n\t\t<neighbor left=\"z Turn\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"z Turn\" right=\"yyy Stairs\"/>\n\t\t<neighbor left=\"zz Turn\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"zz Turn\" right=\"yyy Stairs\"/>\n\t\t\n\t\t<!--neighbor left=\"x Line\" right=\"Stairs\"/>\n\t\t<neighbor left=\"x X\" right=\"Stairs\"/>\n\t\t<neighbor left=\"x Turn\" right=\"Stairs\"/-->\n\t</neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/EscherSurface.xml",
    "content": "<tileset fullSymmetry=\"True\">\n\t<tiles>\n\t\t<tile name=\"Empty\"/>\n\t\t<tile name=\"Line\"/>\n\t\t<tile name=\"Turn\"/>\n\t\t<!--tile name=\"T\"/-->\n\t\t<!--tile name=\"X\"/-->\n\t\t<tile name=\"Stairs\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"Empty\" right=\"Empty\"/>\n\t\t<neighbor left=\"Empty\" right=\"z Line\"/>\n\t\t<neighbor left=\"Empty\" right=\"Turn\"/>\n\t\t<!--neighbor left=\"Empty\" right=\"z T\"/-->\n\t\t<neighbor left=\"Empty\" right=\"y Line\"/>\n\t\t<neighbor left=\"Empty\" right=\"y Turn\"/>\n\t\t<!--neighbor left=\"Empty\" right=\"y T\"/-->\n\t\t<!--neighbor left=\"Empty\" right=\"y X\"/-->\n\t\t<neighbor left=\"Empty\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"Empty\" right=\"y Stairs\"/>\n\n\t\t<!--neighbor left=\"Line\" right=\"Line\"/-->\n\t\t<neighbor left=\"z Line\" right=\"z Line\"/>\n\t\t<neighbor left=\"y Line\" right=\"y Line\"/>\n\t\t<neighbor left=\"y Line\" right=\"xy Line\"/>\n\t\t<neighbor left=\"z Line\" right=\"y Line\" condition=\"Dense\"/>\n\t\t<neighbor left=\"z Line\" right=\"xy Line\" condition=\"Dense\"/>\n\t\t\n\t\t<neighbor left=\"Line\" right=\"z Turn\"/>\n\t\t<neighbor left=\"z Line\" right=\"Turn\"/>\n\t\t<neighbor left=\"y Line\" right=\"y Turn\"/>\n\t\t<neighbor left=\"z Line\" right=\"y Turn\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Line\" right=\"Turn\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Line\" right=\"x Turn\" condition=\"Dense\"/>\n\t\t\n\t\t<!--neighbor left=\"Line\" right=\"T\"/>\n\t\t<neighbor left=\"Line\" right=\"zzz T\"/>\n\t\t<neighbor left=\"z Line\" right=\"z T\"/>\n\t\t<neighbor left=\"y Line\" right=\"y T\"/>\n\t\t<neighbor left=\"y Line\" right=\"xy T\"/>\n\t\t<neighbor left=\"z Line\" right=\"y T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"z Line\" right=\"xy T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Line\" right=\"z T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Line\" right=\"xz T\" condition=\"Dense\"/-->\n\t\t<!--neighbor left=\"Line\" right=\"X\"/>\n\t\t<neighbor left=\"y Line\" right=\"y X\"/>\n\t\t<neighbor left=\"z Line\" right=\"y X\" condition=\"Dense\"/-->\n\t\t\n\t\t<neighbor left=\"x Line\" right=\"Stairs\"/>\n\t\t<neighbor left=\"y Line\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"z Line\" right=\"z Stairs\" condition=\"Dense\"/>\n\t\t<neighbor left=\"z Line\" right=\"xz Stairs\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Line\" right=\"y Stairs\" condition=\"Dense\"/>\n\n\t\t<!--neighbor left=\"Turn\" right=\"z Turn\"/-->\n\t\t<neighbor left=\"Turn\" right=\"zz Turn\"/>\n\t\t<neighbor left=\"z Turn\" right=\"Turn\"/>\n\t\t<neighbor left=\"zz Turn\" right=\"Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"y Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxy Turn\"/>\n\t\t<neighbor left=\"z Turn\" right=\"y Turn\" condition=\"Dense\"/>\n\t\t<neighbor left=\"z Turn\" right=\"xy Turn\" condition=\"Dense\"/>\n\t\t\n\t\t<!--neighbor left=\"Turn\" right=\"T\"/>\n\t\t<neighbor left=\"Turn\" right=\"zz T\"/>\n\t\t<neighbor left=\"Turn\" right=\"zzz T\"/>\n\t\t<neighbor left=\"z Turn\" right=\"z T\"/>\n\t\t<neighbor left=\"y Turn\" right=\"y T\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxy T\"/>\n\t\t<neighbor left=\"z Turn\" right=\"y T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"z Turn\" right=\"xy T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"z Turn\" right=\"xxy T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Turn\" right=\"z T\" condition=\"Dense\"/-->\n\t\t<!--neighbor left=\"Turn\" right=\"X\"/>\n\t\t<neighbor left=\"y Turn\" right=\"y X\"/>\n\t\t<neighbor left=\"z Turn\" right=\"y X\" condition=\"Dense\"/-->\n\t\t\n\t\t<!--neighbor left=\"x Turn\" right=\"Stairs\"/-->\n\t\t<neighbor left=\"Stairs\" right=\"y Turn\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xy Turn\"/>\n\t\t<neighbor left=\"Stairs\" right=\"Turn\" condition=\"Dense\"/>\n\t\t<neighbor left=\"Stairs\" right=\"x Turn\" condition=\"Dense\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xx Turn\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"y Turn\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"xy Turn\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"xxy Turn\" condition=\"Dense\"/>\n\t\t\n\t\t<!--neighbor left=\"T\" right=\"T\"/>\n\t\t<neighbor left=\"T\" right=\"zz T\"/>\n\t\t<neighbor left=\"T\" right=\"zzz T\"/>\n\t\t<neighbor left=\"z T\" right=\"zzz T\"/>\n\t\t<neighbor left=\"zzz T\" right=\"z T\"/>\n\t\t<neighbor left=\"y T\" right=\"y T\"/>\n\t\t<neighbor left=\"y T\" right=\"xy T\"/>\n\t\t<neighbor left=\"y T\" right=\"xxy T\"/>\n\t\t<neighbor left=\"zzz T\" right=\"y T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"zzz T\" right=\"xy T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"T\" right=\"X\"/>\n\t\t<neighbor left=\"z T\" right=\"X\"/>\n\t\t<neighbor left=\"y T\" right=\"y X\"/>\n\t\t<neighbor left=\"y X\" right=\"z T\" condition=\"Dense\"/>\n\t\t\n\t\t<neighbor left=\"x T\" right=\"Stairs\"/>\n\t\t<neighbor left=\"xz T\" right=\"Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"y T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xy T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xxy T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"Stairs\" right=\"z T\" condition=\"Dense\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xz T\" condition=\"Dense\"/-->\n\n\t\t<!--neighbor left=\"X\" right=\"X\"/>\n\t\t<neighbor left=\"y X\" right=\"y X\"/>\n\t\t<neighbor left=\"x X\" right=\"Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"y X\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"y X\" condition=\"Dense\"/-->\n\t\t\n\t\t<neighbor left=\"z Stairs\" right=\"zzz Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"xy Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"xxy Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"zz Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"z Stairs\" condition=\"Dense\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"zz Stairs\" condition=\"Dense\"/>\n\n\t\t<neighbor left=\"Stairs\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xy Stairs\"/>\n\n\t\t<neighbor left=\"z Line\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"z Line\" right=\"yyy Stairs\"/>\n\t\t<neighbor left=\"z Turn\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"z Turn\" right=\"yyy Stairs\"/>\n\t\t<neighbor left=\"zz Turn\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"zz Turn\" right=\"yyy Stairs\"/>\n\n\t\t<neighbor left=\"x Line\" right=\"Stairs\"/>\n\t\t<!--neighbor left=\"x X\" right=\"Stairs\"/-->\n\t\t<neighbor left=\"x Turn\" right=\"Stairs\"/>\n\t</neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/Knots2D.xml",
    "content": "<tileset>\n  <tiles checksum=\"13\">\n    <tile name=\"Empty\"/>\n    <tile name=\"Line\"/>\n    <tile name=\"Turn\"/>\n    <tile name=\"Cross\" weight=\"2.5\"/>\n    <tile name=\"End\" weight=\"0.01\"/>\n  </tiles>\n  <neighbors>\n    <neighbor left=\"Empty\" right=\"Empty\"/>\n    <neighbor left=\"Empty\" right=\"z Line\"/>\n    <neighbor left=\"Empty\" right=\"Turn\"/>\n    <neighbor left=\"z Line\" right=\"z Line\"/>\n    <neighbor left=\"Line\" right=\"Line\"/>\n    <neighbor left=\"z Line\" right=\"Turn\"/>\n    <neighbor left=\"Turn\" right=\"Line\"/>\n    <neighbor left=\"z Turn\" right=\"Turn\"/>\n    <neighbor left=\"zz Turn\" right=\"Turn\"/>\n    <neighbor left=\"Turn\" right=\"z Turn\"/>\n    <neighbor left=\"Turn\" right=\"zz Turn\"/>\n    <neighbor left=\"Cross\" right=\"Line\"/>\n    <neighbor left=\"z Cross\" right=\"Line\"/>\n    <neighbor left=\"Turn\" right=\"Cross\"/>\n    <neighbor left=\"Turn\" right=\"z Cross\"/>\n    <neighbor left=\"Cross\" right=\"Cross\"/>\n    <neighbor left=\"Cross\" right=\"z Cross\"/>\n    <neighbor left=\"z Cross\" right=\"z Cross\"/>\n\n    <neighbor left=\"End\" right=\"Empty\"/>\n    <neighbor left=\"z End\" right=\"Empty\"/>\n    <neighbor left=\"Line\" right=\"End\"/>\n    <neighbor left=\"End\" right=\"z Line\"/>\n    <neighbor left=\"z End\" right=\"z Line\"/>\n    <neighbor left=\"Turn\" right=\"End\"/>\n    <neighbor left=\"End\" right=\"Turn\"/>\n    <neighbor left=\"z End\" right=\"Turn\"/>\n    <neighbor left=\"zzz End\" right=\"Turn\"/>\n    <neighbor left=\"Cross\" right=\"End\"/>\n    <neighbor left=\"zz End\" right=\"End\"/>\n    <neighbor left=\"End\" right=\"zz End\"/>\n    <neighbor left=\"z End\" right=\"z End\"/>\n    <neighbor left=\"z End\" right=\"zzz End\"/>\n  </neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/Knots3D.xml",
    "content": "<tileset fullSymmetry=\"True\">\n  <tiles>\n    <tile name=\"Empty\"/>\n    <tile name=\"Line\"/>\n    <tile name=\"Turn\"/>\n  </tiles>\n  <neighbors>\n    <neighbor left=\"Empty\" right=\"Empty\"/>\n    <neighbor left=\"Empty\" right=\"Line\"/>\n    <neighbor left=\"Line\" right=\"Line\"/>\n    <neighbor left=\"y Line\" right=\"y Line\"/>\n    <neighbor left=\"Line\" right=\"x Line\"/>\n\n    <neighbor left=\"Empty\" right=\"Turn\"/>\n    <neighbor left=\"Empty\" right=\"y Turn\"/>\n    <neighbor left=\"Line\" right=\"Turn\"/>\n    <neighbor left=\"Line\" right=\"x Turn\"/>\n    <neighbor left=\"Line\" right=\"y Turn\"/>\n    <neighbor left=\"y Line\" right=\"z Turn\"/>\n\n    <neighbor left=\"z Turn\" right=\"Turn\"/>\n    <neighbor left=\"zz Turn\" right=\"Turn\"/>\n    <neighbor left=\"Turn\" right=\"z Turn\"/>\n    <neighbor left=\"Turn\" right=\"zz Turn\"/>\n    <neighbor left=\"Turn\" right=\"xz Turn\"/>\n    <neighbor left=\"zx Turn\" right=\"Turn\"/>\n    <neighbor left=\"zzx Turn\" right=\"Turn\"/>\n    <neighbor left=\"zzzx Turn\" right=\"Turn\"/>\n\n    <neighbor left=\"y Turn\" right=\"y Turn\"/>\n    <neighbor left=\"y Turn\" right=\"yyy Turn\"/>\n    <neighbor left=\"y Turn\" right=\"zzyyy Turn\"/>\n  </neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/MarchingHills.xml",
    "content": "<tileset>\n  <tiles>\n    <tile name=\"Empty\" weight=\"2.3\"/>\n    <tile name=\"Stone\" weight=\"1.7\"/>\n    <tile name=\"Wall\" weight=\"1.3\"/>\n    <tile name=\"In\"/>\n    <tile name=\"Out\"/>\n  </tiles>\n  <neighbors>\n    <neighbor left=\"Empty\" right=\"Empty\"/>\n    <neighbor left=\"Empty\" right=\"z Out\"/>\n    <neighbor left=\"Wall\" right=\"Empty\"/>\n    <neighbor left=\"z Wall\" right=\"Out\"/>\n    <neighbor left=\"In\" right=\"Out\"/>\n    <neighbor left=\"z Wall\" right=\"z Wall\"/>\n    <neighbor left=\"In\" right=\"z Wall\"/>\n    <neighbor left=\"zz Wall\" right=\"In\"/>\n    <neighbor left=\"Stone\" right=\"Wall\"/>\n    <neighbor left=\"z In\" right=\"In\"/> - new!\n    <neighbor left=\"zz In\" right=\"In\"/>\n    <neighbor left=\"Stone\" right=\"In\"/>\n    <neighbor left=\"Stone\" right=\"Stone\"/>\n\n    <neighbor top=\"Empty\" bottom=\"Empty\"/>\n    <neighbor top=\"Empty\" bottom=\"Out\"/>\n    <neighbor top=\"Empty\" bottom=\"Wall\"/>\n    <neighbor top=\"Empty\" bottom=\"In\"/>\n    <neighbor top=\"Empty\" bottom=\"Stone\"/>\n    <neighbor top=\"Out\" bottom=\"Out\"/>\n    <neighbor top=\"Out\" bottom=\"Wall\"/>\n    <!--<neighbor top=\"Out\" bottom=\"In\"/>-->\n    <neighbor top=\"z Out\" bottom=\"In\"/>\n    <neighbor top=\"Out\" bottom=\"Stone\"/>\n    <neighbor top=\"Wall\" bottom=\"Wall\"/>\n    <neighbor top=\"Wall\" bottom=\"In\"/>\n    <neighbor top=\"Wall\" bottom=\"Stone\"/>\n    <neighbor top=\"In\" bottom=\"In\"/>\n    <neighbor top=\"In\" bottom=\"Stone\"/>\n    <neighbor top=\"Stone\" bottom=\"Stone\"/>\n\n    <neighbor left=\"zz Wall\" right=\"Wall\"/>\n    <neighbor left=\"zz Wall\" right=\"In\"/>\n    <neighbor left=\"z In\" right=\"In\"/>\n  </neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/OrientedEscher.xml",
    "content": "<tileset fullSymmetry=\"True\">\n\t<tiles>\n\t\t<tile name=\"Empty\"/>\n\t\t<tile name=\"Turn\"/>\n\t\t<tile name=\"Stairs\"/>\n\t\t<tile name=\"Cube\"/>\n\t\t<tile name=\"T\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"Empty\" right=\"Empty\"/>\n\t\t<neighbor left=\"Stairs\" right=\"Empty\"/>\n\t\t<neighbor left=\"Empty\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"Empty\" right=\"Turn\"/>\n\t\t<neighbor left=\"z Turn\" right=\"Empty\"/>\n\t\t<neighbor left=\"Empty\" right=\"y Turn\"/>\n\n\t\t<neighbor left=\"z Stairs\" right=\"zzz Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"y Stairs\"/>\n\t\t<!--neighbor left=\"y Stairs\" right=\"xy Stairs\" comment=\"too touching stairs\"/-->\n\t\t<neighbor left=\"y Stairs\" right=\"xxy Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"zz Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"z Stairs\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"zz Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"y Stairs\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xy Stairs\"/>\n\n\t\t<neighbor left=\"xxx Turn\" right=\"Stairs\"/>\n\t\t<neighbor left=\"zz Stairs\" right=\"xxxz Turn\"/>\n\t\t\n\t\t<neighbor left=\"Stairs\" right=\"Turn\"/>\n\t\t<neighbor left=\"Stairs\" right=\"zzz Turn\"/>\n\t\t<neighbor left=\"Stairs\" right=\"x Turn\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xxx Turn\"/>\n\t\t<neighbor left=\"Stairs\" right=\"y Turn\"/>\n\t\t<neighbor left=\"Stairs\" right=\"yyy Turn\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"y Turn\"/>\n\t\t\n\t\t<neighbor left=\"y Turn\" right=\"y Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxxy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"yyy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xyyy Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxxyyy Turn\"/>\n\n\t\t<neighbor left=\"y Turn\" right=\"Turn\"/>\n\t\t<neighbor left=\"xy Turn\" right=\"Turn\"/>\n\t\t<neighbor left=\"xxy Turn\" right=\"Turn\"/>\n\t\t<neighbor left=\"y Turn\" right=\"zzz Turn\"/>\n\t\t<neighbor left=\"xxxy Turn\" right=\"zzz Turn\"/>\n\t\t<neighbor left=\"xxy Turn\" right=\"zzz Turn\"/>\n\n\t\t<neighbor left=\"Empty\" right=\"Cube\"/>\n\t\t<neighbor left=\"y Cube\" right=\"y Cube\"/>\n\t\t<neighbor left=\"Stairs\" right=\"Cube\"/>\n\t\t<neighbor left=\"y Stairs\" right=\"Cube\"/>\n\t\t<neighbor left=\"Cube\" right=\"y Turn\"/>\n\t\t<neighbor left=\"Cube\" right=\"Turn\"/>\n\t\t<neighbor left=\"z Turn\" right=\"Cube\"/>\n\n\t\t<neighbor left=\"Empty\" right=\"zzz T\"/>\n\t\t<neighbor left=\"Empty\" right=\"y T\"/>\n\t\t<neighbor left=\"Empty\" right=\"yyy T\"/>\n\t\t<neighbor left=\"z Stairs\" right=\"xxx T\"/>\n\t\t<neighbor left=\"z Stairs\" right=\"yx T\"/>\n\t\t<neighbor left=\"Stairs\" right=\"y T\"/>\n\t\t<neighbor left=\"Stairs\" right=\"xy T\"/>\n\t\t<neighbor left=\"Stairs\" right=\"yyy T\"/>\n\t\t<neighbor left=\"Cube\" right=\"zzz T\"/>\n\t\t<neighbor left=\"Cube\" right=\"y T\"/>\n\t\t<neighbor left=\"Cube\" right=\"xyyy T\"/>\n\n\t\t<neighbor left=\"y T\" right=\"y T\"/>\n\t\t<neighbor left=\"y T\" right=\"xxy T\"/>\n\t\t<neighbor left=\"y T\" right=\"xyyy T\"/>\n\n\t\t<neighbor left=\"y Turn\" right=\"xxxy T\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"xxy T\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxxyyy T\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"xxyyy T\"/>\n\n\t\t<neighbor left=\"y Turn\" right=\"zzz T\"/>\n\t\t<neighbor left=\"y Turn\" right=\"xxzzz T\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"xzzz T\"/>\n\t\t<neighbor left=\"yyy Turn\" right=\"xxxzzz T\"/>\n\t\t<neighbor left=\"xz Turn\" right=\"y T\"/>\n\t\t<neighbor left=\"yxz Turn\" right=\"y T\"/>\n\t\t<neighbor left=\"z Turn\" right=\"yyy T\"/>\n\t\t<neighbor left=\"zz Turn\" right=\"yyy T\"/>\n\n\t\t<neighbor left=\"y T\" right=\"zzz T\"/>\n\t\t<neighbor left=\"yyy T\" right=\"xzzz T\"/>\n\t\t<neighbor left=\"yyy T\" right=\"xxxzzz T\"/>\n\t</neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/OrientedStairs.xml",
    "content": "<tileset>\n  <tiles>\n    <tile name=\"Empty\"/>\n    <tile name=\"Cube\" weight=\"0.5\"/>\n    <tile name=\"StairsT\"/>\n    <tile name=\"StairsB\"/>\n    <tile name=\"Turn\"/>\n    <tile name=\"T1\" weight=\"10.0\"/>\n    <tile name=\"T2\" weight=\"10.0\"/>\n  </tiles>\n  <neighbors>\n    <neighbor left=\"Empty\" right=\"Empty\"/>\n    <neighbor left=\"Empty\" right=\"StairsT\"/>\n    <neighbor left=\"Empty\" right=\"z StairsT\"/>\n    <neighbor left=\"Empty\" right=\"z StairsB\"/>\n    <neighbor left=\"Empty\" right=\"zz StairsB\"/>\n    <neighbor left=\"Empty\" right=\"Turn\"/>\n    <neighbor left=\"Empty\" right=\"zzz Turn\"/>\n\n    <neighbor left=\"Empty\" right=\"Cube\"/>\n    <neighbor left=\"Cube\" right=\"z StairsT\"/>\n    <neighbor left=\"Cube\" right=\"z StairsB\"/>\n    <neighbor left=\"Cube\" right=\"Turn\"/>\n    <neighbor left=\"Cube\" right=\"zzz Turn\"/>\n    <neighbor top=\"Cube\" bottom=\"Cube\"/>\n\n    <neighbor left=\"z StairsT\" right=\"z StairsT\"/>\n    <neighbor left=\"z StairsT\" right=\"z StairsB\"/>\n    <neighbor left=\"StairsT\" right=\"StairsB\"/>\n    <neighbor left=\"StairsB\" right=\"StairsT\"/>\n    <neighbor left=\"z StairsB\" right=\"StairsT\"/>\n    <neighbor left=\"StairsB\" right=\"z StairsT\"/>\n    <neighbor left=\"z StairsB\" right=\"z StairsB\"/>\n    <neighbor left=\"StairsT\" right=\"z Turn\"/>\n    <neighbor left=\"zz StairsB\" right=\"zz Turn\"/>\n\n    <neighbor left=\"Empty\" right=\"zzz T1\"/>\n    <neighbor left=\"Empty\" right=\"zzz T2\"/>\n    <neighbor left=\"Cube\" right=\"zzz T1\"/>\n    <neighbor left=\"Cube\" right=\"zzz T2\"/>\n    <neighbor left=\"StairsT\" right=\"z T1\"/>\n    <neighbor left=\"z T1\" right=\"StairsT\"/>\n    <neighbor left=\"StairsT\" right=\"T2\"/>\n    <neighbor left=\"z T2\" right=\"StairsT\"/>\n    <neighbor left=\"T1\" right=\"StairsB\"/>\n    <neighbor left=\"StairsB\" right=\"zzz T1\"/>\n    <neighbor left=\"zzz T2\" right=\"StairsB\"/>\n    <neighbor left=\"StairsB\" right=\"zzz T2\"/>\n\n    <neighbor top=\"Empty\" bottom=\"Empty\"/>\n    <neighbor top=\"StairsT\" bottom=\"Empty\"/>\n    <neighbor top=\"Empty\" bottom=\"StairsB\"/>\n    <neighbor top=\"Empty\" bottom=\"Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"Empty\"/>\n\n    <neighbor top=\"StairsT\" bottom=\"StairsB\"/>\n    <neighbor top=\"StairsB\" bottom=\"StairsT\"/>\n    <neighbor top=\"StairsT\" bottom=\"Turn\"/>\n    <neighbor top=\"StairsT\" bottom=\"z Turn\"/>\n    <neighbor top=\"StairsT\" bottom=\"zz Turn\"/>\n    <neighbor top=\"StairsT\" bottom=\"zzz Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"StairsB\"/>\n    <neighbor top=\"z Turn\" bottom=\"StairsB\"/>\n    <neighbor top=\"zz Turn\" bottom=\"StairsB\"/>\n    <neighbor top=\"zzz Turn\" bottom=\"StairsB\"/>\n    <neighbor top=\"Turn\" bottom=\"Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"z Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"zz Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"zzz Turn\"/>\n\n    <neighbor top=\"Empty\" bottom=\"T1\"/>\n    <neighbor top=\"T1\" bottom=\"Empty\"/>\n    <neighbor top=\"Empty\" bottom=\"T2\"/>\n    <neighbor top=\"T2\" bottom=\"Empty\"/>\n    <neighbor top=\"StairsT\" bottom=\"T1\"/>\n    <neighbor top=\"StairsT\" bottom=\"z T1\"/>\n    <neighbor top=\"StairsT\" bottom=\"z T2\"/>\n    <neighbor top=\"StairsT\" bottom=\"zzz T2\"/>\n    <neighbor top=\"z T1\" bottom=\"StairsB\"/>\n    <neighbor top=\"zzz T1\" bottom=\"StairsB\"/>\n    <neighbor top=\"T2\" bottom=\"StairsB\"/>\n    <neighbor top=\"zzz T2\" bottom=\"StairsB\"/>\n    <neighbor top=\"Turn\" bottom=\"T1\"/>\n    <neighbor top=\"T1\" bottom=\"Turn\"/>\n    <neighbor top=\"zzz Turn\" bottom=\"T1\"/>\n    <neighbor top=\"zz Turn\" bottom=\"T1\"/>\n    <neighbor top=\"T1\" bottom=\"zz Turn\"/>\n    <neighbor top=\"z Turn\" bottom=\"T2\"/>\n    <neighbor top=\"T2\" bottom=\"z Turn\"/>\n    <neighbor top=\"zzz Turn\" bottom=\"T2\"/>\n    <neighbor top=\"T2\" bottom=\"zzz Turn\"/>\n    <neighbor top=\"T2\" bottom=\"zz Turn\"/>\n  </neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/Partition.xml",
    "content": "<tileset>\n  <tiles>\n    <tile name=\"Nothing\" weight=\"2.0\"/>\n    <tile name=\"I\"/>\n    <tile name=\"T\"/>\n  </tiles>\n  <neighbors>\n    <neighbor left=\"Nothing\" right=\"Nothing\"/>\n    <neighbor left=\"Nothing\" right=\"I\"/>\n    <neighbor left=\"Nothing\" right=\"T\"/>\n    <neighbor left=\"z I\" right=\"z I\"/>\n    <neighbor left=\"z I\" right=\"z T\"/>\n    <neighbor left=\"T\" right=\"z I\"/>\n    <neighbor left=\"z T\" right=\"zzz T\"/>\n\n    <neighbor top=\"Nothing\" bottom=\"Nothing\"/>\n    <neighbor top=\"Nothing\" bottom=\"I\"/>\n    <neighbor top=\"Nothing\" bottom=\"T\"/>\n    <neighbor top=\"I\" bottom=\"Nothing\"/>\n    <neighbor top=\"I\" bottom=\"I\"/>\n    <neighbor top=\"I\" bottom=\"z I\"/>\n    <neighbor top=\"T\" bottom=\"Nothing\"/>\n    <neighbor top=\"T\" bottom=\"T\"/>\n  </neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/PartitionedEdges.xml",
    "content": "<tileset>\n  <tiles>\n    <tile name=\"Empty\"/> 1\n    <tile name=\"Line\"/> 2\n    <tile name=\"Turn\"/> 4\n    <tile name=\"X\" weight=\"0.1\"/> 1\n    <tile name=\"ContactDown\"/> 4\n    <tile name=\"ContactUp\"/> 4\n    <tile name=\"Bridge\"/> 4\n    <tile name=\"o\"/> 1\n    21\n\n    <tile name=\"00\" weight=\"1.5\"/> 2\n    <tile name=\"01\"/> 2\n    <tile name=\"10\" weight=\"1.5\"/> 2\n    <tile name=\"11\"/> 2\n    <tile name=\"Down\"/> 4\n    <tile name=\"Up0\"/> 4\n    <tile name=\"Up1\"/> 4\n    20\n\n    <tile name=\"Nothing\" weight=\"10.0\"/> 1\n    <tile name=\"I\"/> 2\n    <tile name=\"T\" weight=\"0.1\"/> 4\n    <tile name=\"Cross\"/> 1\n    8\n\n    49\n  </tiles>\n  <neighbors>\n    <neighbor left=\"Empty\" right=\"00\"/>\n    <neighbor left=\"Empty\" right=\"01\"/>\n    <neighbor left=\"Down\" right=\"Empty\"/>\n    <neighbor left=\"Up0\" right=\"Empty\"/>\n    <neighbor left=\"Up1\" right=\"Empty\"/>\n    <neighbor left=\"o\" right=\"00\"/>\n    <neighbor left=\"o\" right=\"01\"/>\n    <neighbor left=\"Down\" right=\"o\"/>\n    <neighbor left=\"Up0\" right=\"o\"/>\n    <neighbor left=\"Up1\" right=\"o\"/>\n    <neighbor left=\"z Line\" right=\"00\"/>\n    <neighbor left=\"z Line\" right=\"01\"/>\n    <neighbor left=\"Line\" right=\"10\"/>\n    <neighbor left=\"Line\" right=\"11\"/>\n    <neighbor left=\"z Turn\" right=\"00\"/>\n    <neighbor left=\"z Turn\" right=\"01\"/>\n    <neighbor left=\"Turn\" right=\"10\"/>\n    <neighbor left=\"Turn\" right=\"11\"/>\n    <neighbor left=\"X\" right=\"10\"/>\n    <neighbor left=\"X\" right=\"11\"/>\n    <neighbor left=\"Up0\" right=\"z Line\"/>\n    <neighbor left=\"Up1\" right=\"z Line\"/>\n    <neighbor left=\"Up0\" right=\"Turn\"/>\n    <neighbor left=\"Up1\" right=\"Turn\"/>\n    <neighbor left=\"z ContactDown\" right=\"00\"/>\n    <neighbor left=\"z ContactUp\" right=\"00\"/>\n    <neighbor left=\"z ContactDown\" right=\"01\"/>\n    <neighbor left=\"z ContactUp\" right=\"01\"/>\n    <neighbor left=\"10\" right=\"ContactDown\"/>\n    <neighbor left=\"11\" right=\"ContactDown\"/>\n    <neighbor left=\"10\" right=\"ContactUp\"/>\n    <neighbor left=\"11\" right=\"ContactUp\"/>\n    <neighbor left=\"ContactDown\" right=\"Down\"/>\n    <neighbor left=\"ContactUp\" right=\"Up0\"/>\n    <neighbor left=\"ContactUp\" right=\"Up1\"/>\n    <neighbor left=\"z Bridge\" right=\"00\"/>\n    <neighbor left=\"z Bridge\" right=\"01\"/>\n    <neighbor left=\"Bridge\" right=\"Down\"/>\n    <neighbor left=\"zz Up0\" right=\"Bridge\"/>\n    <neighbor left=\"zz Up1\" right=\"Bridge\"/>\n\n    <neighbor left=\"Nothing\" right=\"z 00\"/>\n    <neighbor left=\"Nothing\" right=\"z 10\"/>\n    <neighbor left=\"Nothing\" right=\"z Down\"/>\n    <neighbor left=\"Nothing\" right=\"z Up0\"/>\n    <neighbor left=\"I\" right=\"z 00\"/>\n    <neighbor left=\"I\" right=\"z 10\"/>\n    <neighbor left=\"I\" right=\"z Down\"/>\n    <neighbor left=\"I\" right=\"z Up0\"/>\n    <neighbor left=\"z I\" right=\"z 01\"/>\n    <neighbor left=\"z I\" right=\"z 11\"/>\n    <neighbor left=\"z I\" right=\"z Up1\"/>\n    <neighbor left=\"zz T\" right=\"z 00\"/>\n    <neighbor left=\"zz T\" right=\"z 10\"/>\n    <neighbor left=\"zz T\" right=\"z Down\"/>\n    <neighbor left=\"zz T\" right=\"z Up0\"/>\n    <neighbor left=\"T\" right=\"z 01\"/>\n    <neighbor left=\"T\" right=\"z 11\"/>\n    <neighbor left=\"T\" right=\"z Up1\"/>\n    <neighbor left=\"z T\" right=\"z 01\"/>\n    <neighbor left=\"z T\" right=\"z 11\"/>\n    <neighbor left=\"z T\" right=\"z Up1\"/>\n    <neighbor left=\"z T\" right=\"zzz Up1\"/>\n    <neighbor left=\"Cross\" right=\"z 01\"/>\n    <neighbor left=\"Cross\" right=\"z 11\"/>\n    <neighbor left=\"Cross\" right=\"z Up1\"/>\n\n    <neighbor top=\"Empty\" bottom=\"Empty\"/>\n    <neighbor top=\"Empty\" bottom=\"Line\"/>\n    <neighbor top=\"Empty\" bottom=\"Turn\"/>\n    <neighbor top=\"Empty\" bottom=\"X\"/>\n    <neighbor top=\"Empty\" bottom=\"ContactDown\"/>\n    <neighbor top=\"Empty\" bottom=\"ContactUp\"/>\n    <neighbor top=\"Empty\" bottom=\"Bridge\"/>\n    <neighbor top=\"Line\" bottom=\"Empty\"/>\n    <neighbor top=\"Line\" bottom=\"Line\"/>\n    <neighbor top=\"Line\" bottom=\"z Line\"/>\n    <neighbor top=\"Line\" bottom=\"Turn\"/>\n    <neighbor top=\"Line\" bottom=\"X\"/>\n    <neighbor top=\"Line\" bottom=\"ContactDown\"/>\n    <neighbor top=\"z Line\" bottom=\"ContactDown\"/>\n    <neighbor top=\"Turn\" bottom=\"Empty\"/>\n    <neighbor top=\"Turn\" bottom=\"Line\"/>\n    <neighbor top=\"Turn\" bottom=\"Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"z Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"zz Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"X\"/>\n    <neighbor top=\"Turn\" bottom=\"ContactDown\"/>\n    <neighbor top=\"Turn\" bottom=\"zz ContactDown\"/>\n    <neighbor top=\"X\" bottom=\"Empty\"/>\n    <neighbor top=\"X\" bottom=\"Line\"/>\n    <neighbor top=\"X\" bottom=\"Turn\"/>\n    <neighbor top=\"X\" bottom=\"X\"/>\n    <neighbor top=\"ContactUp\" bottom=\"Empty\"/>\n    <neighbor top=\"ContactUp\" bottom=\"Line\"/>\n    <neighbor top=\"ContactUp\" bottom=\"z Line\"/>\n    <neighbor top=\"ContactUp\" bottom=\"Turn\"/>\n    <neighbor top=\"zz ContactUp\" bottom=\"Turn\"/>\n    <neighbor top=\"ContactUp\" bottom=\"X\"/>\n    <neighbor top=\"ContactUp\" bottom=\"ContactDown\"/>\n    <neighbor top=\"ContactUp\" bottom=\"z ContactDown\"/>\n    <neighbor top=\"ContactUp\" bottom=\"zz ContactDown\"/>\n    <neighbor top=\"ContactDown\" bottom=\"Empty\"/>\n    <neighbor top=\"Bridge\" bottom=\"Empty\"/>\n    <!--<neighbor top=\"ContactDown\" bottom=\"z Line\"/>\n    <neighbor top=\"ContactDown\" bottom=\"z Turn\"/>\n    <neighbor top=\"ContactDown\" bottom=\"z ContactDown\"/>\n    <neighbor top=\"Bridge\" bottom=\"z Line\"/>\n    <neighbor top=\"Bridge\" bottom=\"z Turn\"/>\n    <neighbor top=\"Bridge\" bottom=\"z ContactDown\"/>-->\n\n    <neighbor top=\"o\" bottom=\"Empty\"/>\n    <neighbor top=\"o\" bottom=\"Line\"/>\n    <neighbor top=\"o\" bottom=\"Turn\"/>\n    <neighbor top=\"o\" bottom=\"X\"/>\n    <neighbor top=\"o\" bottom=\"ContactDown\"/>\n    <neighbor top=\"o\" bottom=\"ContactUp\"/>\n    <neighbor top=\"o\" bottom=\"Bridge\"/>\n    <neighbor top=\"o\" bottom=\"o\"/>\n    <neighbor top=\"Empty\" bottom=\"o\"/>\n    <neighbor top=\"Line\" bottom=\"o\"/>\n    <neighbor top=\"Turn\" bottom=\"o\"/>\n    <neighbor top=\"X\" bottom=\"o\"/>\n    <neighbor top=\"ContactDown\" bottom=\"o\"/>\n    <neighbor top=\"ContactUp\" bottom=\"o\"/>\n    <neighbor top=\"Bridge\" bottom=\"o\"/>\n\n    <neighbor top=\"00\" bottom=\"00\"/>\n    <neighbor top=\"00\" bottom=\"01\"/>\n    <neighbor top=\"00\" bottom=\"10\"/>\n    <neighbor top=\"00\" bottom=\"11\"/>\n    <neighbor top=\"00\" bottom=\"Down\"/>\n    <neighbor top=\"01\" bottom=\"00\"/>\n    <neighbor top=\"01\" bottom=\"01\"/>\n    <neighbor top=\"01\" bottom=\"10\"/>\n    <neighbor top=\"01\" bottom=\"11\"/>\n    <neighbor top=\"01\" bottom=\"Down\"/>\n    <neighbor top=\"10\" bottom=\"00\"/>\n    <neighbor top=\"10\" bottom=\"01\"/>\n    <neighbor top=\"10\" bottom=\"10\"/>\n    <neighbor top=\"10\" bottom=\"11\"/>\n    <neighbor top=\"10\" bottom=\"Down\"/>\n    <neighbor top=\"11\" bottom=\"00\"/>\n    <neighbor top=\"11\" bottom=\"01\"/>\n    <neighbor top=\"11\" bottom=\"10\"/>\n    <neighbor top=\"11\" bottom=\"11\"/>\n    <neighbor top=\"11\" bottom=\"Down\"/>\n    <neighbor top=\"Down\" bottom=\"zz Up0\"/>\n    <neighbor top=\"Down\" bottom=\"zz Up1\"/>\n    <neighbor top=\"Up0\" bottom=\"00\"/>\n    <neighbor top=\"Up0\" bottom=\"01\"/>\n    <neighbor top=\"Up0\" bottom=\"10\"/>\n    <neighbor top=\"Up0\" bottom=\"11\"/>\n    <neighbor top=\"Up0\" bottom=\"Down\"/>\n    <neighbor top=\"Up0\" bottom=\"zz Down\"/>\n    <neighbor top=\"Up1\" bottom=\"00\"/>\n    <neighbor top=\"Up1\" bottom=\"01\"/>\n    <neighbor top=\"Up1\" bottom=\"10\"/>\n    <neighbor top=\"Up1\" bottom=\"11\"/>\n    <neighbor top=\"Up1\" bottom=\"Down\"/>\n    <neighbor top=\"Up1\" bottom=\"zz Down\"/>\n\n    <neighbor top=\"Nothing\" bottom=\"Nothing\"/>\n    <neighbor top=\"Nothing\" bottom=\"I\"/>\n    <neighbor top=\"Nothing\" bottom=\"T\"/>\n    <neighbor top=\"Nothing\" bottom=\"Cross\"/>\n    <neighbor top=\"I\" bottom=\"Nothing\"/>\n    <neighbor top=\"I\" bottom=\"I\"/>\n    <neighbor top=\"I\" bottom=\"z I\"/>\n    <neighbor top=\"I\" bottom=\"T\"/>\n    <neighbor top=\"z I\" bottom=\"T\"/>\n    <neighbor top=\"I\" bottom=\"Cross\"/>\n    <neighbor top=\"T\" bottom=\"Nothing\"/>\n    <neighbor top=\"T\" bottom=\"I\"/>\n    <neighbor top=\"T\" bottom=\"z I\"/>\n    <neighbor top=\"T\" bottom=\"T\"/>\n    <neighbor top=\"T\" bottom=\"z T\"/>\n    <neighbor top=\"T\" bottom=\"zz T\"/>\n    <neighbor top=\"T\" bottom=\"Cross\"/>\n    <neighbor top=\"Cross\" bottom=\"Nothing\"/>\n    <neighbor top=\"Cross\" bottom=\"I\"/>\n    <neighbor top=\"Cross\" bottom=\"T\"/>\n    <neighbor top=\"Cross\" bottom=\"Cross\"/>\n  </neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/Paths.xml",
    "content": "<tileset>\n  <tiles>\n    <tile name=\"Empty\" weight=\"2.0\"/>\n    <tile name=\"Line\"/>\n    <tile name=\"Turn\"/>\n    <tile name=\"X\" weight=\"0.1\"/>\n    <tile name=\"Down\" weight=\"4.0\"/>\n    <tile name=\"Up\" weight=\"4.0\"/>\n  </tiles>\n  <neighbors>\n    <neighbor left=\"Empty\" right=\"Empty\"/>\n    <neighbor left=\"Empty\" right=\"z Line\"/>\n    <neighbor left=\"Empty\" right=\"Turn\"/>\n    <neighbor left=\"Empty\" right=\"z Down\"/>\n    <neighbor left=\"Down\" right=\"Empty\"/>\n    <neighbor left=\"Empty\" right=\"z Up\"/>\n    <neighbor left=\"Up\" right=\"Empty\"/>\n\n    <neighbor left=\"Line\" right=\"Line\"/>\n    <neighbor left=\"z Line\" right=\"z Line\"/>\n    <neighbor left=\"z Line\" right=\"Turn\"/>\n    <neighbor left=\"Line\" right=\"z Turn\"/>\n    <neighbor left=\"Line\" right=\"X\"/>\n    <neighbor left=\"Line\" right=\"Down\"/>\n    <neighbor left=\"z Line\" right=\"z Down\"/>\n    <neighbor left=\"Line\" right=\"Up\"/>\n    <neighbor left=\"z Line\" right=\"z Up\"/>\n    ~ <neighbor left=\"Up\" right=\"z Line\"/>\n\n    <neighbor left=\"Turn\" right=\"z Turn\"/>\n    <neighbor left=\"Turn\" right=\"zz Turn\"/>\n    <neighbor left=\"z Turn\" right=\"Turn\"/>\n    <neighbor left=\"zz Turn\" right=\"Turn\"/>\n    <neighbor left=\"Turn\" right=\"X\"/>\n    <neighbor left=\"Turn\" right=\"Down\"/>\n    <neighbor left=\"z Down\" right=\"Turn\"/>\n    <neighbor left=\"zzz Down\" right=\"Turn\"/>\n    <neighbor left=\"Turn\" right=\"Up\"/>\n    <neighbor left=\"z Up\" right=\"Turn\"/>\n    <neighbor left=\"zzz Up\" right=\"Turn\"/>\n    ~ <neighbor left=\"Up\" right=\"Turn\"/>\n\n    <neighbor left=\"X\" right=\"X\"/>\n    <neighbor left=\"X\" right=\"Down\"/>\n    <neighbor left=\"X\" right=\"Up\"/>\n    <neighbor left=\"z Down\" right=\"z Down\"/>\n    <neighbor left=\"z Down\" right=\"zzz Down\"/>\n    <neighbor left=\"z Up\" right=\"z Up\"/>\n    <neighbor left=\"z Up\" right=\"zzz Up\"/>\n    <neighbor left=\"z Down\" right=\"z Up\"/>\n    <neighbor left=\"z Down\" right=\"zzz Up\"/>\n    <neighbor left=\"zz Up\" right=\"Down\"/>\n    <neighbor left=\"Up\" right=\"zz Down\"/>\n    ~ <neighbor left=\"Up\" right=\"z Down\"/>\n\n    <neighbor top=\"Empty\" bottom=\"Empty\"/>\n    <neighbor top=\"Empty\" bottom=\"Line\"/>\n    <neighbor top=\"Empty\" bottom=\"Turn\"/>\n    <neighbor top=\"Empty\" bottom=\"X\"/>\n    <neighbor top=\"Empty\" bottom=\"Down\"/>\n    <neighbor top=\"Line\" bottom=\"Empty\"/>\n    <neighbor top=\"Line\" bottom=\"Line\"/>\n    <neighbor top=\"Line\" bottom=\"z Line\"/>\n    <neighbor top=\"Line\" bottom=\"Turn\"/>\n    <neighbor top=\"Line\" bottom=\"X\"/>\n    <neighbor top=\"Line\" bottom=\"Down\"/>\n    <neighbor top=\"z Line\" bottom=\"Down\"/>\n    <neighbor top=\"Turn\" bottom=\"Empty\"/>\n    <neighbor top=\"Turn\" bottom=\"Line\"/>\n    <neighbor top=\"Turn\" bottom=\"Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"z Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"zz Turn\"/>\n    <neighbor top=\"Turn\" bottom=\"X\"/>\n    <neighbor top=\"Turn\" bottom=\"Down\"/>\n    <neighbor top=\"Turn\" bottom=\"zz Down\"/>\n    <neighbor top=\"X\" bottom=\"Empty\"/>\n    <neighbor top=\"X\" bottom=\"Line\"/>\n    <neighbor top=\"X\" bottom=\"Turn\"/>\n    <neighbor top=\"X\" bottom=\"X\"/>\n    <neighbor top=\"X\" bottom=\"Down\"/>\n    <neighbor top=\"Down\" bottom=\"zz Up\"/>\n    <neighbor top=\"Up\" bottom=\"Empty\"/>\n    <neighbor top=\"Up\" bottom=\"Line\"/>\n    <neighbor top=\"Up\" bottom=\"z Line\"/>\n    <neighbor top=\"Up\" bottom=\"Turn\"/>\n    <neighbor top=\"zz Up\" bottom=\"Turn\"/>\n    <neighbor top=\"Up\" bottom=\"X\"/>\n    <neighbor top=\"Up\" bottom=\"Down\"/>\n    <neighbor top=\"Up\" bottom=\"zz Down\"/>\n    ~ <neighbor top=\"Up\" bottom=\"z Down\"/>\n  </neighbors>\n</tileset>\n"
  },
  {
    "path": "resources/tilesets/Surface.xml",
    "content": "<tileset fullSymmetry=\"True\">\n  <tiles>\n    <tile name=\"Empty\"/> 1\n    <tile name=\"Plane\"/> 3\n    <tile name=\"Angle\"/> 12\n    <tile name=\"Corner\"/> 8\n    <tile name=\"Concave\"/> 24\n  </tiles>\n  <neighbors>\n    <neighbor left=\"Empty\" right=\"Empty\"/>\n    <neighbor left=\"Empty\" right=\"y Plane\"/>\n    <neighbor left=\"Empty\" right=\"Angle\"/>\n    <neighbor left=\"Empty\" right=\"Corner\"/>\n    <neighbor left=\"Plane\" right=\"Plane\"/>\n    <neighbor left=\"y Plane\" right=\"y Plane\"/>\n    <neighbor left=\"y Plane\" right=\"Angle\"/>\n    <neighbor left=\"Plane\" right=\"yx Angle\"/>\n    <neighbor left=\"y Plane\" right=\"Corner\"/>\n\n    <neighbor left=\"z Angle\" right=\"Angle\"/>\n    <neighbor left=\"zz Angle\" right=\"Angle\"/>\n    <neighbor left=\"xz Angle\" right=\"Angle\"/>\n    <neighbor left=\"y Angle\" right=\"y Angle\"/>\n    <neighbor left=\"Angle\" right=\"z Angle\"/>\n    <neighbor left=\"Angle\" right=\"zz Angle\"/>\n\n    <neighbor left=\"z Angle\" right=\"Corner\"/>\n    <neighbor left=\"zz Angle\" right=\"Corner\"/>\n    <neighbor left=\"y Angle\" right=\"y Corner\"/>\n\n    <neighbor left=\"z Corner\" right=\"Corner\"/>\n    <neighbor left=\"zz Corner\" right=\"Corner\"/>\n    <neighbor left=\"yz Corner\" right=\"Corner\"/>\n    <neighbor left=\"yzz Corner\" right=\"Corner\"/>\n    <neighbor left=\"Corner\" right=\"z Corner\"/>\n\n    <neighbor left=\"y Concave\" right=\"Empty\"/>\n    <neighbor left=\"Plane\" right=\"Concave\"/>\n    <neighbor left=\"x Angle\" right=\"Concave\"/>\n    <neighbor left=\"xxx Angle\" right=\"Concave\"/>\n    <neighbor left=\"xz Angle\" right=\"yyy Concave\"/>\n    <neighbor left=\"xzz Angle\" right=\"yyy Concave\"/>\n    <neighbor left=\"y Angle\" right=\"y Concave\"/>\n    <neighbor left=\"yzzz Angle\" right=\"z Concave\"/>\n    <neighbor left=\"Corner\" right=\"y Concave\"/>\n    <neighbor left=\"zzz Corner\" right=\"z Concave\"/>\n    <neighbor left=\"z Corner\" right=\"yyy Concave\"/>\n    <neighbor left=\"yz Corner\" right=\"yyy Concave\"/>\n    <neighbor left=\"zz Corner\" right=\"yyy Concave\"/>\n    <neighbor left=\"yzz Corner\" right=\"yyy Concave\"/>\n    <neighbor left=\"y Concave\" right=\"yyy Concave\"/>\n    <neighbor left=\"xy Concave\" right=\"yyy Concave\"/>\n    <neighbor left=\"xxy Concave\" right=\"yyy Concave\"/>\n\n    <neighbor left=\"z Concave\" right=\"Concave\"/>\n    <neighbor left=\"zz Concave\" right=\"Concave\"/>\n    <neighbor left=\"yy Concave\" right=\"Concave\"/>\n    <neighbor left=\"zyy Concave\" right=\"Concave\"/>\n    <neighbor left=\"Concave\" right=\"z Concave\"/>\n    <neighbor left=\"zzz Concave\" right=\"y Concave\"/>\n  </neighbors>\n</tileset>\n"
  },
  {
    "path": "source/AllNode.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass AllNode : RuleNode\n{\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        if (!base.Load(xelem, parentSymmetry, grid)) return false;\n        matches = new List<(int, int, int, int)>();\n        matchMask = AH.Array2D(rules.Length, grid.state.Length, false);\n        return true;\n    }\n\n    void Fit(int r, int x, int y, int z, bool[] newstate, int MX, int MY)\n    {\n        Rule rule = rules[r];\n        for (int dz = 0; dz < rule.OMZ; dz++) for (int dy = 0; dy < rule.OMY; dy++) for (int dx = 0; dx < rule.OMX; dx++)\n                {\n                    byte value = rule.output[dx + dy * rule.OMX + dz * rule.OMX * rule.OMY];\n                    if (value != 0xff && newstate[x + dx + (y + dy) * MX + (z + dz) * MX * MY]) return;\n                }\n        last[r] = true;\n        for (int dz = 0; dz < rule.OMZ; dz++) for (int dy = 0; dy < rule.OMY; dy++) for (int dx = 0; dx < rule.OMX; dx++)\n                {\n                    byte newvalue = rule.output[dx + dy * rule.OMX + dz * rule.OMX * rule.OMY];\n                    if (newvalue != 0xff)\n                    {\n                        int sx = x + dx, sy = y + dy, sz = z + dz;\n                        int i = sx + sy * MX + sz * MX * MY;\n                        newstate[i] = true;\n                        grid.state[i] = newvalue;\n                        ip.changes.Add((sx, sy, sz));\n                    }\n                }\n    }\n\n    override public bool Go()\n    {\n        if (!base.Go()) return false;\n        lastMatchedTurn = ip.counter;\n\n        if (trajectory != null)\n        {\n            if (counter >= trajectory.Length) return false;\n            Array.Copy(trajectory[counter], grid.state, grid.state.Length);\n            counter++;\n            return true;\n        }\n\n        if (matchCount == 0) return false;\n\n        int MX = grid.MX, MY = grid.MY;\n        if (potentials != null)\n        {\n            double firstHeuristic = 0;\n            bool firstHeuristicComputed = false;\n\n            List<(int, double)> list = new();\n            for (int m = 0; m < matchCount; m++)\n            {\n                var (r, x, y, z) = matches[m];\n                double? heuristic = Field.DeltaPointwise(grid.state, rules[r], x, y, z, fields, potentials, grid.MX, grid.MY);\n                if (heuristic != null)\n                {\n                    double h = (double)heuristic;\n                    if (!firstHeuristicComputed)\n                    {\n                        firstHeuristic = h;\n                        firstHeuristicComputed = true;\n                    }\n                    double u = ip.random.NextDouble();\n                    list.Add((m, temperature > 0 ? Math.Pow(u, Math.Exp((h - firstHeuristic) / temperature)) : -h + 0.001 * u));\n                }\n            }\n            (int, double)[] ordered = list.OrderBy(pair => -pair.Item2).ToArray();\n            for (int k = 0; k < ordered.Length; k++)\n            {\n                var (r, x, y, z) = matches[ordered[k].Item1];\n                matchMask[r][x + y * MX + z * MX * MY] = false;\n                Fit(r, x, y, z, grid.mask, MX, MY);\n            }\n        }\n        else\n        {\n            int[] shuffle = new int[matchCount];\n            shuffle.Shuffle(ip.random);\n            for (int k = 0; k < shuffle.Length; k++)\n            {\n                var (r, x, y, z) = matches[shuffle[k]];\n                matchMask[r][x + y * MX + z * MX * MY] = false;\n                Fit(r, x, y, z, grid.mask, MX, MY);\n            }\n        }\n\n        for (int n = ip.first[lastMatchedTurn]; n < ip.changes.Count; n++)\n        {\n            var (x, y, z) = ip.changes[n];\n            grid.mask[x + y * MX + z * MX * MY] = false;\n        }\n        counter++;\n        matchCount = 0;\n        return true;\n    }\n}\n"
  },
  {
    "path": "source/ArrayHelper.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\n\nstatic class AH\n{\n    public static T[][][] Array3D<T>(int MX, int MY, int MZ, T value)\n    {\n        T[][][] result = new T[MX][][];\n        for (int x = 0; x < result.Length; x++)\n        {\n            result[x] = new T[MY][];\n            T[][] resultx = result[x];\n            for (int y = 0; y < resultx.Length; y++)\n            {\n                resultx[y] = new T[MZ];\n                resultx[y].AsSpan().Fill(value);\n            }\n        }\n        return result;\n    }\n\n    public static T[][] Array2D<T>(int MX, int MY, T value)\n    {\n        T[][] result = new T[MX][];\n        for (int x = 0; x < result.Length; x++)\n        {\n            result[x] = new T[MY];\n            result[x].AsSpan().Fill(value);\n        }\n        return result;\n    }\n\n    public static T[] Array1D<T>(int length, Func<int, T> f)\n    {\n        T[] result = new T[length];\n        for (int i = 0; i < result.Length; i++) result[i] = f(i);\n        return result;\n    }\n    public static T[] Array1D<T>(int length, T value)\n    {\n        T[] result = new T[length];\n        result.AsSpan().Fill(value);\n        return result;\n    }\n\n    public static T[] FlatArray3D<T>(int MX, int MY, int MZ, Func<int, int, int, T> f)\n    {\n        T[] result = new T[MX * MY * MZ];\n        for (int z = 0; z < MZ; z++) for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++) result[z * MX * MY + y * MX + x] = f(x, y, z);\n        return result;\n    }\n\n    public static void Set2D<T>(this T[][] a, T value) { for (int y = 0; y < a.Length; y++) a[y].AsSpan().Fill(value); }\n    \n    public static bool Same(byte[] t1, byte[] t2) => t1.AsSpan().SequenceEqual(t2);\n}\n"
  },
  {
    "path": "source/ConvChain.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Xml.Linq;\n\nclass ConvChainNode : Node\n{\n    int N;\n    double temperature;\n    double[] weights;\n\n    public byte c0, c1;\n    bool[] substrate;\n    byte substrateColor;\n    int counter, steps;\n\n    public bool[] sample;\n    public int SMX, SMY;\n\n    override protected bool Load(XElement xelem, bool[] symmetry, Grid grid)\n    {\n        if (grid.MZ != 1)\n        {\n            Interpreter.WriteLine(\"convchain currently works only for 2d\");\n            return false;\n        }\n\n        string name = xelem.Get<string>(\"sample\");\n        string filename = $\"resources/samples/{name}.png\";\n        int[] bitmap;\n        (bitmap, SMX, SMY, _) = Graphics.LoadBitmap(filename);\n        if (bitmap == null)\n        {\n            Interpreter.WriteLine($\"couldn't load ConvChain sample {filename}\");\n            return false;\n        }\n        sample = new bool[bitmap.Length];\n        for (int i = 0; i < sample.Length; i++) sample[i] = bitmap[i] == -1;\n\n        N = xelem.Get(\"n\", 3);\n        steps = xelem.Get(\"steps\", -1);\n        temperature = xelem.Get(\"temperature\", 1.0);\n        c0 = grid.values[xelem.Get<char>(\"black\")];\n        c1 = grid.values[xelem.Get<char>(\"white\")];\n        substrateColor = grid.values[xelem.Get<char>(\"on\")];\n\n        substrate = new bool[grid.state.Length];\n\n        weights = new double[1 << (N * N)];\n        for (int y = 0; y < SMY; y++) for (int x = 0; x < SMX; x++)\n            {\n                bool[] pattern = Helper.Pattern((dx, dy) => sample[(x + dx) % SMX + (y + dy) % SMY * SMX], N);\n                var symmetries = SymmetryHelper.SquareSymmetries(pattern, q => Helper.Rotated(q, N), q => Helper.Reflected(q, N), (q1, q2) => false, symmetry);\n                foreach (bool[] q in symmetries) weights[q.Index()] += 1;\n            }\n\n        for (int k = 0; k < weights.Length; k++) if (weights[k] <= 0) weights[k] = 0.1;\n        return true;\n    }\n\n    void Toggle(byte[] state, int i) => state[i] = state[i] == c0 ? c1 : c0;\n    override public bool Go()\n    {\n        if (steps > 0 && counter >= steps) return false;\n\n        int MX = grid.MX, MY = grid.MY;\n        byte[] state = grid.state;\n\n        if (counter == 0)\n        {\n            bool anySubstrate = false;\n            for (int i = 0; i < substrate.Length; i++) if (state[i] == substrateColor)\n                {\n                    state[i] = ip.random.Next(2) == 0 ? c0 : c1;\n                    substrate[i] = true;\n                    anySubstrate = true;\n                }\n            counter++;\n            return anySubstrate;\n        }\n\n        for (int k = 0; k < state.Length; k++)\n        {\n            int r = ip.random.Next(state.Length);\n            if (!substrate[r]) continue;\n\n            int x = r % MX, y = r / MX;\n            double q = 1;\n\n            for (int sy = y - N + 1; sy <= y + N - 1; sy++) for (int sx = x - N + 1; sx <= x + N - 1; sx++)\n                {\n                    int ind = 0, difference = 0;\n                    for (int dy = 0; dy < N; dy++) for (int dx = 0; dx < N; dx++)\n                        {\n                            int X = sx + dx;\n                            if (X < 0) X += MX;\n                            else if (X >= MX) X -= MX;\n\n                            int Y = sy + dy;\n                            if (Y < 0) Y += MY;\n                            else if (Y >= MY) Y -= MY;\n\n                            bool value = state[X + Y * MX] == c1;\n                            int power = 1 << (dy * N + dx);\n                            ind += value ? power : 0;\n                            if (X == x && Y == y) difference = value ? power : -power;\n                        }\n\n                    q *= weights[ind - difference] / weights[ind];\n                }\n\n            if (q >= 1) { Toggle(state, r); continue; }\n            if (temperature != 1) q = Math.Pow(q, 1.0 / temperature);\n            if (q > ip.random.NextDouble()) Toggle(state, r);\n        }\n\n        counter++;\n        return true;\n    }\n\n    override public void Reset()\n    {\n        for (int i = 0; i < substrate.Length; i++) substrate[i] = false;\n        counter = 0;\n    }\n}\n"
  },
  {
    "path": "source/Convolution.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass ConvolutionNode : Node\n{\n    ConvolutionRule[] rules;\n    int[] kernel;\n    bool periodic;\n    public int counter, steps;\n\n    int[][] sumfield;\n\n    static readonly Dictionary<string, int[]> kernels2d = new()\n    {\n        [\"VonNeumann\"] = new int[9] { 0, 1, 0, 1, 0, 1, 0, 1, 0 },\n        [\"Moore\"] = new int[9] { 1, 1, 1, 1, 0, 1, 1, 1, 1 },\n    };\n    static readonly Dictionary<string, int[]> kernels3d = new()\n    {\n        [\"VonNeumann\"] = new int[27] { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },\n        [\"NoCorners\"] = new int[27] { 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0 },\n    };\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        XElement[] xrules = xelem.Elements(\"rule\").ToArray();\n        if (xrules.Length == 0) xrules = new[] { xelem };\n        rules = new ConvolutionRule[xrules.Length];\n        for (int k = 0; k < rules.Length; k++)\n        {\n            rules[k] = new();\n            if (!rules[k].Load(xrules[k], grid)) return false;\n        }\n\n        steps = xelem.Get(\"steps\", -1);\n        periodic = xelem.Get(\"periodic\", false);\n        string neighborhood = xelem.Get<string>(\"neighborhood\");\n        kernel = grid.MZ == 1 ? kernels2d[neighborhood] : kernels3d[neighborhood];\n\n        sumfield = AH.Array2D(grid.state.Length, grid.C, 0);\n        return true;\n    }\n\n    override public void Reset()\n    {\n        counter = 0;\n    }\n\n    override public bool Go()\n    {\n        if (steps > 0 && counter >= steps) return false;\n        int MX = grid.MX, MY = grid.MY, MZ = grid.MZ;\n\n        sumfield.Set2D(0);\n        if (MZ == 1)\n        {\n            for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++)\n                {\n                    int[] sums = sumfield[x + y * MX];\n                    for (int dy = -1; dy <= 1; dy++) for (int dx = -1; dx <= 1; dx++)\n                        {\n                            int sx = x + dx;\n                            int sy = y + dy;\n\n                            if (periodic)\n                            {\n                                if (sx < 0) sx += MX;\n                                else if (sx >= MX) sx -= MX;\n                                if (sy < 0) sy += MY;\n                                else if (sy >= MY) sy -= MY;\n                            }\n                            else if (sx < 0 || sy < 0 || sx >= MX || sy >= MY) continue;\n\n                            sums[grid.state[sx + sy * MX]] += kernel[dx + 1 + (dy + 1) * 3];\n                        }\n                }\n        }\n        else\n        {\n            for (int z = 0; z < MZ; z++) for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++)\n                    {\n                        int[] sums = sumfield[x + y * MX + z * MX * MY];\n                        for (int dz = -1; dz <= 1; dz++) for (int dy = -1; dy <= 1; dy++) for (int dx = -1; dx <= 1; dx++)\n                                {\n                                    int sx = x + dx;\n                                    int sy = y + dy;\n                                    int sz = z + dz;\n\n                                    if (periodic)\n                                    {\n                                        if (sx < 0) sx += MX;\n                                        else if (sx >= MX) sx -= MX;\n                                        if (sy < 0) sy += MY;\n                                        else if (sy >= MY) sy -= MY;\n                                        if (sz < 0) sz += MZ;\n                                        else if (sz >= MZ) sz -= MZ;\n                                    }\n                                    else if (sx < 0 || sy < 0 || sz < 0 || sx >= MX || sy >= MY || sz >= MZ) continue;\n\n                                    sums[grid.state[sx + sy * MX + sz * MX * MY]] += kernel[dx + 1 + (dy + 1) * 3 + (dz + 1) * 9];\n                                }\n                    }\n        }        \n\n        bool change = false;\n        for (int i = 0; i < sumfield.Length; i++)\n        {\n            int[] sums = sumfield[i];\n            byte input = grid.state[i];\n            for (int r = 0; r < rules.Length; r++)\n            {\n                ConvolutionRule rule = rules[r];\n                if (input == rule.input && rule.output != grid.state[i] && (rule.p == 1.0 || ip.random.Next() < rule.p * int.MaxValue))\n                {\n                    bool success = true;\n                    if (rule.sums != null)\n                    {\n                        int sum = 0;\n                        for (int c = 0; c < rule.values.Length; c++) sum += sums[rule.values[c]];\n                        success = rule.sums[sum];\n                    }\n                    if (success)\n                    {\n                        grid.state[i] = rule.output;\n                        change = true;\n                        break;\n                    }\n                }\n            }\n        }\n\n        counter++;\n        return change;\n    }\n\n    class ConvolutionRule\n    {\n        public byte input, output;\n        public byte[] values;\n        public bool[] sums;\n        public double p;\n\n        public bool Load(XElement xelem, Grid grid)\n        {\n            input = grid.values[xelem.Get<char>(\"in\")];\n            output = grid.values[xelem.Get<char>(\"out\")];\n            p = xelem.Get(\"p\", 1.0);\n\n            static int[] interval(string s)\n            {\n                if (s.Contains('.'))\n                {\n                    string[] bounds = s.Split(\"..\");\n                    int min = int.Parse(bounds[0]);\n                    int max = int.Parse(bounds[1]);\n                    int[] result = new int[max - min + 1];\n                    for (int i = 0; i < result.Length; i++) result[i] = min + i;\n                    return result;\n                }\n                else return new int[1] { int.Parse(s) };\n            };\n\n            string valueString = xelem.Get<string>(\"values\", null);\n            string sumsString = xelem.Get<string>(\"sum\", null);\n            if (valueString != null && sumsString == null)\n            {\n                Interpreter.WriteLine($\"missing \\\"sum\\\" attribute at line {xelem.LineNumber()}\");\n                return false;\n            }\n            if (valueString == null && sumsString != null)\n            {\n                Interpreter.WriteLine($\"missing \\\"values\\\" attribute at line {xelem.LineNumber()}\");\n                return false;\n            }\n            \n            if (valueString != null)\n            {\n                values = valueString.Select(c => grid.values[c]).ToArray();\n\n                sums = new bool[28];\n                string[] intervals = sumsString.Split(',');\n                foreach (string s in intervals) foreach (int i in interval(s)) sums[i] = true;\n            }\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "source/Field.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass Field\n{\n    public bool recompute, inversed, essential;\n    public int zero, substrate;\n\n    public Field(XElement xelem, Grid grid)\n    {\n        recompute = xelem.Get(\"recompute\", false);\n        essential = xelem.Get(\"essential\", false);\n        string on = xelem.Get<string>(\"on\");\n        substrate = grid.Wave(on);\n\n        string zeroSymbols = xelem.Get<string>(\"from\", null);\n        if (zeroSymbols != null) inversed = true;\n        else zeroSymbols = xelem.Get<string>(\"to\");\n        zero = grid.Wave(zeroSymbols);\n    }\n\n    public bool Compute(int[] potential, Grid grid)\n    {\n        int MX = grid.MX, MY = grid.MY, MZ = grid.MZ;\n        var front = new Queue<(int, int, int, int)>();\n\n        int ix = 0, iy = 0, iz = 0;\n        for (int i = 0; i < grid.state.Length; i++)\n        {\n            potential[i] = -1;\n            byte value = grid.state[i];\n            if ((zero & 1 << value) != 0)\n            {\n                potential[i] = 0;\n                front.Enqueue((0, ix, iy, iz));\n            }\n\n            ix++;\n            if (ix == MX)\n            {\n                ix = 0; iy++;\n                if (iy == MY) { iy = 0; iz++; }\n            }\n        }\n\n        if (!front.Any()) return false;\n        while (front.Any())\n        {\n            var (t, x, y, z) = front.Dequeue();\n            var neighbors = Neighbors(x, y, z, MX, MY, MZ);\n            for (int n = 0; n < neighbors.Count; n++)\n            {\n                var (nx, ny, nz) = neighbors[n];\n                int i = nx + ny * grid.MX + nz * grid.MX * grid.MY;\n                byte v = grid.state[i];\n                if (potential[i] == -1 && (substrate & 1 << v) != 0)\n                {\n                    front.Enqueue((t + 1, nx, ny, nz));\n                    potential[i] = t + 1;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    static List<(int, int, int)> Neighbors(int x, int y, int z, int MX, int MY, int MZ)\n    {\n        List<(int, int, int)> result = new();\n\n        if (x > 0) result.Add((x - 1, y, z));\n        if (x < MX - 1) result.Add((x + 1, y, z));\n        if (y > 0) result.Add((x, y - 1, z));\n        if (y < MY - 1) result.Add((x, y + 1, z));\n        if (z > 0) result.Add((x, y, z - 1));\n        if (z < MZ - 1) result.Add((x, y, z + 1));\n\n        return result;\n    }\n\n    public static int? DeltaPointwise(byte[] state, Rule rule, int x, int y, int z, Field[] fields, int[][] potentials, int MX, int MY)\n    {\n        int sum = 0;\n        int dz = 0, dy = 0, dx = 0;\n        for (int di = 0; di < rule.input.Length; di++)\n        {\n            byte newValue = rule.output[di];\n            if (newValue != 0xff && (rule.input[di] & 1 << newValue) == 0)\n            {\n                int i = x + dx + (y + dy) * MX + (z + dz) * MX * MY;\n                int newPotential = potentials[newValue][i];\n                if (newPotential == -1) return null;\n\n                byte oldValue = state[i];\n                int oldPotential = potentials[oldValue][i];\n                sum += newPotential - oldPotential;\n\n                if (fields != null)\n                {\n                    Field oldField = fields[oldValue];\n                    if (oldField != null && oldField.inversed) sum += 2 * oldPotential;\n                    Field newField = fields[newValue];\n                    if (newField != null && newField.inversed) sum -= 2 * newPotential;\n                }\n            }\n\n            dx++;\n            if (dx == rule.IMX)\n            {\n                dx = 0; dy++;\n                if (dy == rule.IMY) { dy = 0; dz++; }\n            }\n        }\n        return sum;\n    }\n}\n"
  },
  {
    "path": "source/GUI.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nstatic class GUI\n{\n    static readonly int S, SMALL, MAXWIDTH, ZSHIFT, HINDENT, HGAP, HARROW, HLINE, VSKIP, SMALLVSKIP, FONTSHIFT, AFTERFONT;\n    static readonly bool DENSE, D3;\n    public static readonly int BACKGROUND, INACTIVE, ACTIVE;\n\n    const string FONT = \"Tamzen8x16r\", TITLEFONT = \"Tamzen8x16b\";\n    static readonly (bool[], int FX, int FY)[] fonts;\n\n    static readonly char[] legend = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ 12345abcdefghijklmnopqrstuvwxyz\\u03bb67890{}[]()<>$*-+=/#_%^@\\\\&|~?'\\\"`!,.;:\".ToCharArray();\n    static readonly Dictionary<char, byte> map;\n    static GUI()\n    {\n        map = new Dictionary<char, byte>();\n        for (int i = 0; i < legend.Length; i++) map.Add(legend[i], (byte)i);\n        fonts = new (bool[], int, int)[2];\n\n        (int[] bitmap, int width, int height, _) = Graphics.LoadBitmap($\"resources/fonts/{FONT}.png\");\n        int b0 = bitmap[0];\n        int b1 = bitmap[width - 1];\n        fonts[0] = (bitmap.Select(argb => argb != b0 && argb != b1).ToArray(), width / 32, height / 3);\n        \n        (bitmap, width, height, _) = Graphics.LoadBitmap($\"resources/fonts/{TITLEFONT}.png\");\n        b0 = bitmap[0];\n        b1 = bitmap[width - 1];\n        fonts[1] = (bitmap.Select(argb => argb != b0 && argb != b1).ToArray(), width / 32, height / 3);\n\n        XElement settings = XDocument.Load(\"resources/settings.xml\").Root;\n        S = settings.Get(\"squareSize\", 7);\n        SMALL = settings.Get(\"smallSquareSize\", 3);\n        MAXWIDTH = settings.Get(\"maxwidth\", 10);\n        ZSHIFT = settings.Get(\"zshift\", 2);\n        HINDENT = settings.Get(\"hindent\", 30);\n        HGAP = settings.Get(\"hgap\", 2);\n        HARROW = settings.Get(\"harrow\", 10);\n        HLINE = settings.Get(\"hline\", 14);\n        VSKIP = settings.Get(\"vskip\", 2);\n        SMALLVSKIP = settings.Get(\"smallvskip\", 2);\n        FONTSHIFT = settings.Get(\"fontshift\", 2);\n        AFTERFONT = settings.Get(\"afterfont\", 4);\n        DENSE = settings.Get(\"dense\", true);\n        D3 = settings.Get(\"d3\", true);\n        BACKGROUND = (255 << 24) + Convert.ToInt32(settings.Get(\"background\", \"222222\"), 16);\n        INACTIVE = (255 << 24) + Convert.ToInt32(settings.Get(\"inactive\", \"666666\"), 16);\n        ACTIVE = (255 << 24) + Convert.ToInt32(settings.Get(\"active\", \"ffffff\"), 16);\n    }\n\n    public static void Draw(string name, Branch root, Branch current, int[] bitmap, int WIDTH, int HEIGHT, Dictionary<char, int> palette)\n    {\n        void drawRectangle(int x, int y, int width, int height, int color)\n        {\n            if (y + height > HEIGHT) return;\n            for (int dy = 0; dy < height; dy++) for (int dx = 0; dx < width; dx++) bitmap[x + dx + (y + dy) * WIDTH] = color;\n        };\n        void drawSquare(int x, int y, int S, char c) => drawRectangle(x, y, S, S, palette[c]);\n        void drawShadedSquare(int x, int y, int S, int color)\n        {\n            drawRectangle(x, y, S, S, color);\n            drawRectangle(x + S, y, 1, S + 1, BACKGROUND);\n            drawRectangle(x, y + S, S + 1, 1, BACKGROUND);\n        };\n        void drawHLine(int x, int y, int length, int color, bool dashed = false)\n        {\n            if (length <= 0 || x < 0 || x + length >= WIDTH) return;\n            if (!dashed) drawRectangle(x, y, length, 1, color);\n            else\n            {\n                if (y >= HEIGHT) return;\n                int shift = length % 4 == 0 ? 1 : 0;\n                for (int dx = 0; dx < length; dx++) if ((dx + shift) / 2 % 2 == 0) bitmap[x + dx + y * WIDTH] = color;\n            }\n        }\n        void drawVLine(int x, int y, int height, int color)\n        {\n            if (x < 0) return;\n            int yend = Math.Min(y + height, HEIGHT);\n            drawRectangle(x, y, 1, yend - y, color);\n        }\n        int write(string s, int x, int y, int color, int font = 0)\n        {\n            int fontshift = font == 0 ? FONTSHIFT : 0;\n            var (f, FX, FY) = fonts[font];\n            if (y - FONTSHIFT + FY >= HEIGHT) return -1;\n            for (int i = 0; i < s.Length; i++)\n            {\n                int p = map[s[i]];\n                int px = p % 32, py = p / 32;\n                for (int dy = 0; dy < FY; dy++) for (int dx = 0; dx < FX; dx++)\n                        if (f[px * FX + dx + (py * FY + dy) * FX * 32]) bitmap[x + i * FX + dx + (y + dy - fontshift) * WIDTH] = color;\n            }\n            return s.Length * FX;\n        };\n\n        Dictionary<Node, (int level, int height)> lh = new();\n        void drawDash(Node node, bool markov, bool active)\n        {\n            if (node == root) return;\n            (int level, int height) = lh[node];\n            int extra = markov ? 3 : 1;\n            drawHLine(level * HINDENT - HLINE - HGAP - extra, height + S / 2, (node is MarkovNode || node is SequenceNode ? HINDENT : HLINE) + extra, active ? ACTIVE : INACTIVE);\n        };\n        void drawBracket(Branch branch, int level, int n, bool active)\n        {\n            int first = lh[branch.nodes[0]].height;\n            int last = lh[branch.nodes[n]].height;\n            int x = (level + 1) * HINDENT - HGAP - HLINE;\n            int color = active ? ACTIVE : INACTIVE;\n            bool markov = branch is MarkovNode;\n            drawVLine(x, first + S / 2, last - first + 1, color);\n            drawVLine(x - (markov ? 3 : 1), first + S / 2, last - first + 1, color);\n        };\n        int drawArray(byte[] a, int x, int y, int MX, int MY, int MZ, char[] characters, int S)\n        {\n            for (int dz = 0; dz < MZ; dz++) for (int dy = 0; dy < MY; dy++) for (int dx = 0; dx < MX; dx++)\n                    {\n                        byte i = a[dx + dy * MX + dz * MX * MY];\n                        int color = i != 0xff ? palette[characters[i]] : (D3 ? INACTIVE : BACKGROUND);\n                        drawShadedSquare(x + dx * S + (MZ - dz - 1) * ZSHIFT, y + dy * S + (MZ - dz - 1) * ZSHIFT, S, color);\n                    }\n            return MX * S + (MZ - 1) * ZSHIFT;\n        };\n        void drawSample(int x, int y, bool[] sample, int MX, int MY, byte c0, byte c1, char[] characters, int S)\n        {\n            for (int dy = 0; dy < MY; dy++) for (int dx = 0; dx < MX; dx++)\n                {\n                    byte b = sample[dx + dy * MX] ? c1 : c0;\n                    drawSquare(x + dx * S, y + dy * S, S, characters[b]);\n                }\n        };\n\n        int y = fonts[1].FY / 2;\n        write(name, 8, y, ACTIVE, 1);\n        y += (int)(AFTERFONT * fonts[1].FY / 2);\n\n        void draw(Node node, int level)\n        {\n            lh.Add(node, (level, y));\n            int x = level * HINDENT;\n            char[] characters = node.grid.characters;\n\n            if (node is Branch branch)\n            {\n                int LINECOLOR = branch == current && branch.n < 0 ? ACTIVE : INACTIVE;\n                if (branch is WFCNode wfcnode)\n                {\n                    write($\"wfc {wfcnode.name}\", x, y, LINECOLOR);\n                    y += fonts[0].FY + VSKIP;\n                }\n                else if (branch is MapNode mapnode)\n                {\n                    for (int r = 0; r < mapnode.rules.Length; r++)\n                    {\n                        Rule rule = mapnode.rules[r];\n                        if (!rule.original) continue;\n                        int s = rule.IMX * rule.IMY > MAXWIDTH ? SMALL : S;\n\n                        x += drawArray(rule.binput, x, y, rule.IMX, rule.IMY, rule.IMZ, characters, s) + HGAP;\n                        drawHLine(x, y + S / 2, HARROW, LINECOLOR, true);\n                        x += HARROW + HGAP;\n                        x += drawArray(rule.output, x, y, rule.OMX, rule.OMY, rule.OMZ, mapnode.newgrid.characters, s) + HGAP;\n\n                        y += Math.Max(rule.IMY, rule.OMY) * s + (Math.Max(rule.IMZ, rule.OMZ) - 1) * ZSHIFT + SMALLVSKIP;\n                        x = level * HINDENT;\n                    }\n                    y += VSKIP;\n                }\n\n                bool markov = branch is MarkovNode;\n                bool sequence = branch is SequenceNode;\n                foreach (var child in branch.nodes)\n                {\n                    draw(child, markov || sequence ? level + 1 : level);\n                    drawDash(child, markov, false);\n                }\n            }\n            else\n            {\n                bool active = current != null && current.n >= 0 && current.nodes[current.n] == node;\n                int NODECOLOR = active ? ACTIVE : INACTIVE;\n                if (node is RuleNode rulenode)\n                {\n                    for (int r = 0; r < rulenode.rules.Length && r < 40; r++)\n                    {\n                        Rule rule = rulenode.rules[r];\n                        if (!rule.original) continue;\n                        int s = rule.IMX * rule.IMY > MAXWIDTH ? SMALL : S;\n\n                        int LINECOLOR = (active && IsActive(rulenode, r)) ? ACTIVE : INACTIVE;\n                        x += drawArray(rule.binput, x, y, rule.IMX, rule.IMY, rule.IMZ, characters, s) + HGAP;\n\n                        drawHLine(x, y + S / 2, HARROW, LINECOLOR, rulenode is not OneNode);\n                        x += HARROW + HGAP;\n                        x += drawArray(rule.output, x, y, rule.OMX, rule.OMY, rule.OMZ, characters, s) + HGAP;\n\n                        if (rulenode.steps > 0) write($\" {rulenode.counter}/{rulenode.steps}\", x, y, LINECOLOR);\n                        y += rule.IMY * s + (rule.IMZ - 1) * ZSHIFT + SMALLVSKIP;\n                        x = level * HINDENT;\n                    }\n                    if (rulenode.fields != null)\n                    {\n                        y += SMALLVSKIP;\n                        for (int c = 0; c < rulenode.fields.Length; c++)\n                        {\n                            Field field = rulenode.fields[c];\n                            if (field == null) continue;\n\n                            if (!DENSE)\n                            {\n                                x += write(\"field for \", x, y, NODECOLOR);\n                                drawSquare(x, y, S, characters[c]);\n                                x += S;\n\n                                x += write(field.inversed ? \" from \" : \" to \", x, y, NODECOLOR);\n                                byte[] zero = Helper.NonZeroPositions(field.zero);\n                                for (int k = 0; k < zero.Length; k++, x += S) drawSquare(x, y, S, characters[zero[k]]);\n\n                                x += write(\" on \", x, y, NODECOLOR);\n                                byte[] substrate = Helper.NonZeroPositions(field.substrate);\n                                for (int k = 0; k < substrate.Length; k++, x += S) drawSquare(x, y, S, characters[substrate[k]]);\n                            }\n                            else\n                            {\n                                x += write(\"field \", x, y, NODECOLOR);\n                                drawSquare(x, y, S, characters[c]);\n                                x += S + HGAP;\n                                byte[] zero = Helper.NonZeroPositions(field.zero);\n                                for (int k = 0; k < zero.Length; k++, x += S) drawSquare(x, y, S, characters[zero[k]]);\n                                x += HGAP;\n                                byte[] substrate = Helper.NonZeroPositions(field.substrate);\n                                for (int k = 0; k < substrate.Length; k++, x += S) drawSquare(x, y, S, characters[substrate[k]]);\n                            }\n                            \n                            x = level * HINDENT;\n                            y += fonts[0].FY;\n                        }\n                    }\n                    y += VSKIP;\n                }\n                else if (node is PathNode path)\n                {\n                    int VSHIFT = (fonts[0].FY - FONTSHIFT - S) / 2;\n                    if (!DENSE)\n                    {\n                        x += write(\"path from \", x, y, NODECOLOR);\n                        byte[] start = Helper.NonZeroPositions(path.start);\n                        for (int k = 0; k < start.Length; k++, x += S) drawSquare(x, y + VSHIFT, S, characters[start[k]]);\n\n                        x += write(\" to \", x, y, NODECOLOR);\n                        byte[] finish = Helper.NonZeroPositions(path.finish);\n                        for (int k = 0; k < finish.Length; k++, x += S) drawSquare(x, y + VSHIFT, S, characters[finish[k]]);\n\n                        x += write(\" on \", x, y, NODECOLOR);\n                        byte[] on = Helper.NonZeroPositions(path.substrate);\n                        for (int k = 0; k < on.Length; k++, x += S) drawSquare(x, y + VSHIFT, S, characters[on[k]]);\n\n                        x += write(\" colored \", x, y, NODECOLOR);\n                        drawSquare(x, y + VSHIFT, S, characters[path.value]);\n                        y += fonts[0].FY + VSKIP;\n                    }\n                    else\n                    {\n                        x += write(\"path \", x, y, NODECOLOR);\n                        byte[] start = Helper.NonZeroPositions(path.start);\n                        for (int k = 0; k < start.Length; k++, x += S) drawSquare(x, y + VSHIFT, S, characters[start[k]]);\n                        x += HGAP;\n                        byte[] finish = Helper.NonZeroPositions(path.finish);\n                        for (int k = 0; k < finish.Length; k++, x += S) drawSquare(x, y + VSHIFT, S, characters[finish[k]]);\n                        x += HGAP;\n                        byte[] on = Helper.NonZeroPositions(path.substrate);\n                        for (int k = 0; k < on.Length; k++, x += S) drawSquare(x, y + VSHIFT, S, characters[on[k]]);\n                        x += HGAP;\n                        drawSquare(x, y + VSHIFT, S, characters[path.value]);\n                        y += fonts[0].FY + VSKIP;\n                    }\n                }\n                else if (node is ConvolutionNode convnode)\n                {\n                    string s = \"convolution\";\n                    if (convnode.steps > 0) s += $\" {convnode.counter}/{convnode.steps}\";\n                    write(s, x, y, NODECOLOR);\n                    y += fonts[0].FY + VSKIP;\n                }\n                else if (node is ConvChainNode chainnode)\n                {\n                    x += write($\"convchain \", x, y, NODECOLOR);\n                    drawSample(x, y, chainnode.sample, chainnode.SMX, chainnode.SMY, chainnode.c0, chainnode.c1, characters, 7);\n                    y += fonts[0].FY + VSKIP;\n                }\n            }\n        };\n\n        void drawLine(Branch branch)\n        {\n            if (branch.nodes.Length == 0) return;\n            int childLevel = lh[branch.nodes[0]].level;\n            drawBracket(branch, childLevel - 1, branch.nodes.Length - 1, false);\n            foreach (Node child in branch.nodes) if (child is Branch childBranch) drawLine(childBranch);\n        };\n\n        draw(root, 0);\n        drawLine(root);\n        for (Branch b = current; b != null; b = b.parent)\n        {\n            if (b.n >= 0)\n            {\n                drawDash(b.nodes[b.n], b is MarkovNode, true);\n                drawBracket(b, lh[b.nodes[0]].level - 1, b.n, true);\n            }\n        }\n    }\n\n    static bool IsActive(RuleNode node, int index)\n    {\n        if (node.last[index]) return true;\n        for (int r = index + 1; r < node.rules.Length; r++)\n        {\n            Rule rule = node.rules[r];\n            if (rule.original) break;\n            if (node.last[r]) return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "source/Graphics.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.PixelFormats;\n\nstatic class Graphics\n{\n    public static (int[], int, int, int) LoadBitmap(string filename)\n    {\n        try\n        {\n            using var image = Image.Load<Bgra32>(filename);\n            int width = image.Width, height = image.Height;\n            int[] result = new int[width * height];\n            image.CopyPixelDataTo(MemoryMarshal.Cast<int, Bgra32>(result.AsSpan()));\n            return (result, width, height, 1);\n        }\n        catch (Exception) { return (null, -1, -1, -1); }\n    }\n\n    unsafe public static void SaveBitmap(int[] data, int width, int height, string filename)\n    {\n        if (width <= 0 || height <= 0 || data.Length != width * height)\n        {\n            Console.WriteLine($\"ERROR: wrong image width * height = {width} * {height}\");\n            return;\n        }\n\n        Span<Bgra32> pixelSpan = MemoryMarshal.Cast<int, Bgra32>(data.AsSpan());\n        using var image = Image.LoadPixelData<Bgra32>(pixelSpan.ToArray(), width, height);\n        image.SaveAsPng(filename);\n    }\n\n    public static (int[], int, int) Render(byte[] state, int MX, int MY, int MZ, int[] colors, int pixelsize, int MARGIN) => MZ == 1 ? BitmapRender(state, MX, MY, colors, pixelsize, MARGIN) : IsometricRender(state, MX, MY, MZ, colors, pixelsize, MARGIN);\n\n    public static (int[], int, int) BitmapRender(byte[] state, int MX, int MY, int[] colors, int pixelsize, int MARGIN)\n    {\n        int WIDTH = MARGIN + MX * pixelsize, HEIGHT = MY * pixelsize;\n        int TOTALWIDTH = WIDTH, TOTALHEIGHT = HEIGHT;\n        //int TOTALWIDTH = 189 + MARGIN, TOTALHEIGHT = 189;\n        int[] bitmap = new int[TOTALWIDTH * TOTALHEIGHT];\n        for (int i = 0; i < bitmap.Length; i++) bitmap[i] = GUI.BACKGROUND;\n        //for (int i = 0; i < bitmap.Length; i++) bitmap[i] = 255 << 24;\n\n        int DX = (TOTALWIDTH - WIDTH) / 2;\n        int DY = (TOTALHEIGHT - HEIGHT) / 2;\n\n        for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++)\n            {\n                int c = colors[state[x + y * MX]];\n                for (int dy = 0; dy < pixelsize; dy++) for (int dx = 0; dx < pixelsize; dx++)\n                    {\n                        int SX = DX + x * pixelsize + dx;\n                        int SY = DY + y * pixelsize + dy;\n                        if (SX < 0 || SX >= TOTALWIDTH - MARGIN || SY < 0 || SY >= TOTALHEIGHT) continue;\n                        bitmap[MARGIN + SX + SY * TOTALWIDTH] = c;\n                    }\n            }\n        return (bitmap, TOTALWIDTH, TOTALHEIGHT);\n    }\n\n    static readonly Dictionary<int, Sprite> sprites = new();\n    public static (int[], int, int) IsometricRender(byte[] state, int MX, int MY, int MZ, int[] colors, int blocksize, int MARGIN)\n    {\n        var voxels = new List<Voxel>[MX + MY + MZ - 2];\n        var visibleVoxels = new List<Voxel>[MX + MY + MZ - 2];\n        for (int i = 0; i < voxels.Length; i++)\n        {\n            voxels[i] = new List<Voxel>();\n            visibleVoxels[i] = new List<Voxel>();\n        }\n        bool[] visible = new bool[MX * MY * MZ]; //нужен для быстрой работы с transparent\n\n        for (int z = 0; z < MZ; z++) for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++)\n                {\n                    int i = x + y * MX + z * MX * MY;\n                    byte value = state[i];\n                    visible[i] = value != 0;\n                    if (value != 0) voxels[x + y + z].Add(new Voxel(colors[value], x, y, z));\n                }\n\n        bool[][] hash = AH.Array2D(MX + MY - 1, MX + MY + 2 * MZ - 3, false);\n        for (int i = voxels.Length - 1; i >= 0; i--)\n        {\n            List<Voxel> voxelsi = voxels[i];\n            for (int j = 0; j < voxelsi.Count; j++)\n            {\n                Voxel s = voxelsi[j];\n                int u = s.x - s.y + MY - 1, v = s.x + s.y - 2 * s.z + 2 * MZ - 2;\n                if (!hash[u][v])\n                {\n                    bool X = s.x == 0 || !visible[(s.x - 1) + s.y * MX + s.z * MX * MY];\n                    bool Y = s.y == 0 || !visible[s.x + (s.y - 1) * MX + s.z * MX * MY];\n                    bool Z = s.z == 0 || !visible[s.x + s.y * MX + (s.z - 1) * MX * MY];\n\n                    s.edges[0] = s.y == MY - 1 || !visible[s.x + (s.y + 1) * MX + s.z * MX * MY];\n                    s.edges[1] = s.x == MX - 1 || !visible[s.x + 1 + s.y * MX + s.z * MX * MY];\n                    s.edges[2] = X || (s.y != MY - 1 && visible[s.x - 1 + (s.y + 1) * MX + s.z * MX * MY]);\n                    s.edges[3] = X || (s.z != MZ - 1 && visible[s.x - 1 + s.y * MX + (s.z + 1) * MX * MY]);\n                    s.edges[4] = Y || (s.x != MX - 1 && visible[s.x + 1 + (s.y - 1) * MX + s.z * MX * MY]);\n                    s.edges[5] = Y || (s.z != MZ - 1 && visible[s.x + (s.y - 1) * MX + (s.z + 1) * MX * MY]);\n                    s.edges[6] = Z || (s.x != MX - 1 && visible[s.x + 1 + s.y * MX + (s.z - 1) * MX * MY]);\n                    s.edges[7] = Z || (s.y != MY - 1 && visible[s.x + (s.y + 1) * MX + (s.z - 1) * MX * MY]);\n\n                    visibleVoxels[i].Add(s);\n                    hash[u][v] = true;\n                }\n            }\n        }\n\n        int FITWIDTH = (MX + MY) * blocksize, FITHEIGHT = ((MX + MY) / 2 + MZ) * blocksize;\n        int WIDTH = FITWIDTH + 2 * blocksize, HEIGHT = FITHEIGHT + 2 * blocksize;\n        //const int WIDTH = 330, HEIGHT = 330;\n\n        int[] screen = new int[(MARGIN + WIDTH) * HEIGHT];\n        for (int i = 0; i < screen.Length; i++) screen[i] = GUI.BACKGROUND;\n\n        void Blit(int[] sprite, int SX, int SY, int x, int y, int r, int g, int b)\n        {\n            for (int dy = 0; dy < SY; dy++) for (int dx = 0; dx < SX; dx++)\n                {\n                    int grayscale = sprite[dx + dy * SX];\n                    if (grayscale < 0) continue;\n                    byte R = (byte)((float)r * (float)grayscale / 256.0f);\n                    byte G = (byte)((float)g * (float)grayscale / 256.0f);\n                    byte B = (byte)((float)b * (float)grayscale / 256.0f);\n                    int X = x + dx;\n                    int Y = y + dy;\n                    if (MARGIN + X >= 0 && X < WIDTH && Y >= 0 && Y < HEIGHT) screen[MARGIN + X + Y * (MARGIN + WIDTH)] = Int(R, G, B);\n                }\n        };\n\n        bool success = sprites.TryGetValue(blocksize, out Sprite sprite);\n        if (!success)\n        {\n            sprite = new Sprite(blocksize);\n            sprites.Add(blocksize, sprite);\n        }       \n\n        for (int i = 0; i < visibleVoxels.Length; i++) foreach (Voxel s in visibleVoxels[i])\n            {\n                int u = blocksize * (s.x - s.y);\n                int v = (blocksize * (s.x + s.y) / 2 - blocksize * s.z);\n                int positionx = WIDTH / 2 + u - blocksize;\n                //int positionx = WIDTH / 2 + u - 5 * blocksize;\n                int positiony = (HEIGHT - FITHEIGHT) / 2 + (MZ - 1) * blocksize + v;\n                var (r, g, b) = RGB(s.color);\n                Blit(sprite.cube, sprite.width, sprite.height, positionx, positiony, r, g, b);\n                for (int j = 0; j < 8; j++) if (s.edges[j]) Blit(sprite.edges[j], sprite.width, sprite.height, positionx, positiony, r, g, b);\n            }\n\n        return (screen, MARGIN + WIDTH, HEIGHT);\n    }\n\n    static int Int(byte r, byte g, byte b) => (0xff << 24) + (r << 16) + (g << 8) + b;\n    static (byte, byte, byte) RGB(int i)\n    {\n        byte r = (byte)((i & 0xff0000) >> 16);\n        byte g = (byte)((i & 0xff00) >> 8);\n        byte b = (byte)(i & 0xff);\n        return (r, g, b);\n    }\n}\n\nclass Sprite\n{\n    public int[] cube;\n    public int[][] edges;\n    public int width, height;\n\n    const int c1 = 215, c2 = 143, c3 = 71, black = 0, transparent = -1;\n\n    public Sprite(int size)\n    {\n        width = 2 * size;\n        height = 2 * size - 1;\n\n        int[] texture(Func<int, int, int> f)\n        {\n            int[] result = new int[width * height];\n            for (int j = 0; j < height; j++) for (int i = 0; i < width; i++) result[i + j * width] = f(i - size + 1, size - j - 1);\n            return result;\n        };\n\n        int f(int x, int y)\n        {\n            if (2 * y - x >= 2 * size || 2 * y + x > 2 * size || 2 * y - x < -2 * size || 2 * y + x <= -2 * size) return transparent;\n            else if (x > 0 && 2 * y < x) return c3;\n            else if (x <= 0 && 2 * y <= -x) return c2;\n            else return c1;\n        };\n\n        cube = texture(f);\n        edges = new int[8][];\n        edges[0] = texture((x, y) => x == 1 && y <= 0 ? c1 : transparent);\n        edges[1] = texture((x, y) => x == 0 && y <= 0 ? c1 : transparent);\n        edges[2] = texture((x, y) => x == 1 - size && 2 * y < size && 2 * y >= -size ? black : transparent);\n        edges[3] = texture((x, y) => x <= 0 && y == x / 2 + size - 1 ? black : transparent);\n        edges[4] = texture((x, y) => x == size && 2 * y < size && 2 * y >= -size ? black : transparent);\n        edges[5] = texture((x, y) => x > 0 && y == -(x + 1) / 2 + size ? black : transparent);\n        edges[6] = texture((x, y) => x > 0 && y == (x + 1) / 2 - size ? black : transparent);\n        edges[7] = texture((x, y) => x <= 0 && y == -x / 2 - size + 1 ? black : transparent);\n    }\n}\n\nstruct Voxel\n{\n    public int color;\n    public int x, y, z;\n    public bool[] edges;\n\n    public Voxel(int color, int x, int y, int z)\n    {\n        this.color = color;\n        this.x = x;\n        this.y = y;\n        this.z = z;\n        edges = new bool[8];\n    }\n}\n"
  },
  {
    "path": "source/Grid.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass Grid\n{\n    public byte[] state;\n    public bool[] mask;\n    public int MX, MY, MZ;\n\n    public byte C;\n    public char[] characters;\n    public Dictionary<char, byte> values;\n    public Dictionary<char, int> waves;\n    public string folder;\n\n    int transparent;\n    byte[] statebuffer;\n\n    public static Grid Load(XElement xelem, int MX, int MY, int MZ)\n    {\n        Grid g = new();\n        g.MX = MX;\n        g.MY = MY;\n        g.MZ = MZ;\n        string valueString = xelem.Get<string>(\"values\", null)?.Replace(\" \", \"\");\n        if (valueString == null)\n        {\n            Interpreter.WriteLine(\"no values specified\");\n            return null;\n        }\n\n        g.C = (byte)valueString.Length;\n        g.values = new Dictionary<char, byte>();\n        g.waves = new Dictionary<char, int>();\n        g.characters = new char[g.C];\n        for (byte i = 0; i < g.C; i++)\n        {\n            char symbol = valueString[i];\n            if (g.values.ContainsKey(symbol))\n            {\n                Interpreter.WriteLine($\"repeating value {symbol} at line {xelem.LineNumber()}\");\n                return null;\n            }\n            else\n            {\n                g.characters[i] = symbol;\n                g.values.Add(symbol, i);\n                g.waves.Add(symbol, 1 << i);\n            }\n        }\n\n        string transparentString = xelem.Get<string>(\"transparent\", null);\n        if (transparentString != null) g.transparent = g.Wave(transparentString);\n\n        var xunions = xelem.MyDescendants(\"markov\", \"sequence\", \"union\").Where(x => x.Name == \"union\");\n        g.waves.Add('*', (1 << g.C) - 1);\n        foreach (XElement xunion in xunions)\n        {\n            char symbol = xunion.Get<char>(\"symbol\");\n            if (g.waves.ContainsKey(symbol))\n            {\n                Interpreter.WriteLine($\"repeating union type {symbol} at line {xunion.LineNumber()}\");\n                return null;\n            }\n            else\n            {\n                int w = g.Wave(xunion.Get<string>(\"values\"));\n                g.waves.Add(symbol, w);\n            }\n        }\n\n        g.state = new byte[MX * MY * MZ];\n        g.statebuffer = new byte[MX * MY * MZ];\n        g.mask = new bool[MX * MY * MZ];\n        g.folder = xelem.Get<string>(\"folder\", null);\n        return g;\n    }\n\n    public void Clear()\n    {\n        for (int i = 0; i < state.Length; i++) state[i] = 0;\n    }\n\n    public int Wave(string values)\n    {\n        int sum = 0;\n        for (int k = 0; k < values.Length; k++) sum += 1 << this.values[values[k]];\n        return sum;\n    }\n\n    /*static readonly int[] DX = { 1, 0, -1, 0, 0, 0 };\n    static readonly int[] DY = { 0, 1, 0, -1, 0, 0 };\n    static readonly int[] DZ = { 0, 0, 0, 0, 1, -1 };\n    public byte[] State()\n    {\n        int neighbors(int x, int y, int z)\n        {\n            int sum = 0;\n            for (int d = 0; d < 6; d++)\n            {\n                int X = x + DX[d], Y = y + DY[d], Z = z + DZ[d];\n                if (X < 0 || X >= MX || Y < 0 || Y >= MY || Z < 0 || Z >= MZ) continue;\n                if (state[X + Y * MX + Z * MX * MY] != 0) sum++;\n            }\n            return sum;\n        };\n\n        Array.Copy(state, statebuffer, state.Length);\n        for (int z = 0; z < MZ; z++) for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++)\n                {\n                    int i = x + y * MX + z * MX * MY;\n                    byte v = state[i];\n                    int n = neighbors(x, y, z);\n                    if (v == 0 || ((1 << v) & transparent) != 0 || n == 0 || (n == 1 && z == 1 && (state[i - MX * MY] == 11 || state[i - MX * MY] == 4))) statebuffer[i] = 0;\n                }\n        return statebuffer;\n    }*/\n\n    public bool Matches(Rule rule, int x, int y, int z)\n    {\n        int dz = 0, dy = 0, dx = 0;\n        for (int di = 0; di < rule.input.Length; di++)\n        {\n            if ((rule.input[di] & (1 << state[x + dx + (y + dy) * MX + (z + dz) * MX * MY])) == 0) return false;\n\n            dx++;\n            if (dx == rule.IMX)\n            {\n                dx = 0; dy++;\n                if (dy == rule.IMY) { dy = 0; dz++; }\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "source/Helper.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Collections.Generic;\n\nstatic class Helper\n{\n    public static (byte[], int) Ords(this int[] data, List<int> uniques = null)\n    {\n        byte[] result = new byte[data.Length];\n        if (uniques == null) uniques = new List<int>();\n        for (int i = 0; i < data.Length; i++)\n        {\n            int d = data[i];\n            int ord = uniques.IndexOf(d);\n            if (ord == -1)\n            {\n                ord = uniques.Count;\n                uniques.Add(d);\n            }\n            result[i] = (byte)ord;\n        }\n        return (result, uniques.Count);\n    }\n\n    public static string[][] Split(this string s, char S1, char S2)\n    {\n        string[] split = s.Split(S1);\n        string[][] result = new string[split.Length][];\n        for (int k = 0; k < result.Length; k++) result[k] = split[k].Split(S2);\n        return result;\n    }\n\n    public static int Power(int a, int n)\n    {\n        int product = 1;\n        for (int i = 0; i < n; i++) product *= a;\n        return product;\n    }\n\n    public static int Index(this bool[] array)\n    {\n        int result = 0, power = 1;\n        for (int i = 0; i < array.Length; i++, power *= 2) if (array[i]) result += power;\n        return result;\n    }\n    public static long Index(this byte[] p, int C)\n    {\n        long result = 0, power = 1;\n        for (int i = 0; i < p.Length; i++, power *= C) result += p[p.Length - 1 - i] * power;\n        return result;\n    }\n\n    public static byte[] NonZeroPositions(int w)\n    {\n        int amount = 0, wcopy = w;\n        for (byte p = 0; p < 32; p++, w >>= 1) if ((w & 1) == 1) amount++;\n        byte[] result = new byte[amount];\n        amount = 0;\n        for (byte p = 0; p < 32; p++, wcopy >>= 1) if ((wcopy & 1) == 1)\n            {\n                result[amount] = p;\n                amount++;\n            }\n        return result;\n    }\n\n    public static int MaxPositiveIndex(this int[] amounts)\n    {\n        int max = -1, argmax = -1;\n        for (int i = 0; i < amounts.Length; i++)\n        {\n            int amount = amounts[i];\n            if (amount > 0 && amount > max)\n            {\n                max = amount;\n                argmax = i;\n            }\n        }\n        return argmax;\n    }\n\n    public static T[] Pattern<T>(Func<int, int, T> f, int N)\n    {\n        T[] result = new T[N * N];\n        for (int y = 0; y < N; y++) for (int x = 0; x < N; x++) result[x + y * N] = f(x, y);\n        return result;\n    }\n    public static T[] Rotated<T>(T[] p, int N) => Pattern((x, y) => p[N - 1 - y + x * N], N);\n    public static T[] Reflected<T>(T[] p, int N) => Pattern((x, y) => p[N - 1 - x + y * N], N);\n}\n\nstatic class RandomHelper\n{\n    public static T Random<T>(this List<T> list, Random random) => list[random.Next(list.Count)];\n    \n    public static int Random(this double[] weights, double r)\n    {\n        double sum = 0;\n        for (int i = 0; i < weights.Length; i++) sum += weights[i];\n        double threshold = r * sum;\n\n        double partialSum = 0;\n        for (int i = 0; i < weights.Length; i++)\n        {\n            partialSum += weights[i];\n            if (partialSum >= threshold) return i;\n        }\n        return 0;\n    }\n\n    public static void Shuffle(this int[] array, Random random)\n    {\n        for (int i = 0; i < array.Length; i++)\n        {\n            int j = random.Next(i + 1);\n            array[i] = array[j];\n            array[j] = i;\n        }\n    }\n}\n"
  },
  {
    "path": "source/Interpreter.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass Interpreter\n{\n    public Branch root, current;\n    public Grid grid;\n    Grid startgrid;\n\n    bool origin;\n    public Random random;\n\n    public List<(int, int, int)> changes;\n    public List<int> first;\n    public int counter;\n    \n    public bool gif;\n\n    Interpreter() { }\n    public static Interpreter Load(XElement xelem, int MX, int MY, int MZ)\n    {\n        Interpreter ip = new();\n        ip.origin = xelem.Get(\"origin\", false);\n        ip.grid = Grid.Load(xelem, MX, MY, MZ);\n        if (ip.grid == null)\n        {\n            Console.WriteLine(\"failed to load grid\");\n            return null;\n        }\n        ip.startgrid = ip.grid;\n\n        string symmetryString = xelem.Get<string>(\"symmetry\", null);\n        bool[] symmetry = SymmetryHelper.GetSymmetry(ip.grid.MZ == 1, symmetryString, AH.Array1D(ip.grid.MZ == 1 ? 8 : 48, true));\n        if (symmetry == null)\n        {\n            WriteLine($\"unknown symmetry {symmetryString} at line {xelem.LineNumber()}\");\n            return null;\n        }\n\n        Node topnode = Node.Factory(xelem, symmetry, ip, ip.grid);\n        if (topnode == null) return null;\n        ip.root = topnode is Branch ? topnode as Branch : new MarkovNode(topnode, ip);\n\n        ip.changes = new List<(int, int, int)>();\n        ip.first = new List<int>();\n        return ip;\n    }\n\n    public IEnumerable<(byte[], char[], int, int, int)> Run(int seed, int steps, bool gif)\n    {\n        random = new Random(seed);\n        grid = startgrid;\n        grid.Clear();\n        if (origin) grid.state[grid.MX / 2 + (grid.MY / 2) * grid.MX + (grid.MZ / 2) * grid.MX * grid.MY] = 1;\n\n        changes.Clear();\n        first.Clear();\n        first.Add(0);\n\n        root.Reset();\n        current = root;\n\n        this.gif = gif;\n        counter = 0;\n        while (current != null && (steps <= 0 || counter < steps))\n        {\n            if (gif)\n            {\n                Console.WriteLine($\"[{counter}]\");\n                yield return (grid.state, grid.characters, grid.MX, grid.MY, grid.MZ);\n            }\n\n            current.Go();\n            counter++;\n            first.Add(changes.Count);\n        }\n\n        yield return (grid.state, grid.characters, grid.MX, grid.MY, grid.MZ);\n    }\n\n    public static void WriteLine(string s) => Console.WriteLine(s);\n    public static void Write(string s) => Console.Write(s);\n}\n"
  },
  {
    "path": "source/Map.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass MapNode : Branch\n{\n    public Grid newgrid;\n    public Rule[] rules;\n    int NX, NY, NZ, DX, DY, DZ;\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        string scalestring = xelem.Get<string>(\"scale\", null);\n        if (scalestring == null)\n        {\n            Interpreter.WriteLine($\"scale should be specified in map node\");\n            return false;\n        }\n        string[] scales = scalestring.Split(' ');\n        if (scales.Length != 3)\n        {\n            Interpreter.WriteLine($\"scale attribute \\\"{scalestring}\\\" should have 3 components separated by space\");\n            return false;\n        }\n\n        static (int numerator, int denominator) readScale(string s)\n        {\n            if (!s.Contains('/')) return (int.Parse(s), 1);\n            else\n            {\n                string[] nd = s.Split('/');\n                return (int.Parse(nd[0]), int.Parse(nd[1]));\n            }\n        };\n\n        (NX, DX) = readScale(scales[0]);\n        (NY, DY) = readScale(scales[1]);\n        (NZ, DZ) = readScale(scales[2]);\n\n        newgrid = Grid.Load(xelem, grid.MX * NX / DX, grid.MY * NY / DY, grid.MZ * NZ / DZ);\n        if (newgrid == null) return false;\n\n        if (!base.Load(xelem, parentSymmetry, newgrid)) return false;\n        bool[] symmetry = SymmetryHelper.GetSymmetry(grid.MZ == 1, xelem.Get<string>(\"symmetry\", null), parentSymmetry);\n\n        List<Rule> ruleList = new();\n        foreach (XElement xrule in xelem.Elements(\"rule\"))\n        {\n            Rule rule = Rule.Load(xrule, grid, newgrid);\n            rule.original = true;\n            if (rule == null) return false;\n            foreach (Rule r in rule.Symmetries(symmetry, grid.MZ == 1)) ruleList.Add(r);\n        }\n        rules = ruleList.ToArray();\n        return true;\n    }\n\n    static bool Matches(Rule rule, int x, int y, int z, byte[] state, int MX, int MY, int MZ)\n    {\n        for (int dz = 0; dz < rule.IMZ; dz++) for (int dy = 0; dy < rule.IMY; dy++) for (int dx = 0; dx < rule.IMX; dx++)\n                {\n                    int sx = x + dx;\n                    int sy = y + dy;\n                    int sz = z + dz;\n\n                    if (sx >= MX) sx -= MX;\n                    if (sy >= MY) sy -= MY;\n                    if (sz >= MZ) sz -= MZ;\n\n                    int inputWave = rule.input[dx + dy * rule.IMX + dz * rule.IMX * rule.IMY];\n                    if ((inputWave & (1 << state[sx + sy * MX + sz * MX * MY])) == 0) return false;\n                }\n\n        return true;\n    }\n\n    static void Apply(Rule rule, int x, int y, int z, byte[] state, int MX, int MY, int MZ)\n    {\n        for (int dz = 0; dz < rule.OMZ; dz++) for (int dy = 0; dy < rule.OMY; dy++) for (int dx = 0; dx < rule.OMX; dx++)\n                {\n                    int sx = x + dx;\n                    int sy = y + dy;\n                    int sz = z + dz;\n\n                    if (sx >= MX) sx -= MX;\n                    if (sy >= MY) sy -= MY;\n                    if (sz >= MZ) sz -= MZ;\n\n                    byte output = rule.output[dx + dy * rule.OMX + dz * rule.OMX * rule.OMY];\n                    if (output != 0xff) state[sx + sy * MX + sz * MX * MY] = output;\n                }\n    }\n\n    override public bool Go()\n    {\n        if (n >= 0) return base.Go();\n\n        newgrid.Clear();\n        foreach (Rule rule in rules)\n            for (int z = 0; z < grid.MZ; z++) for (int y = 0; y < grid.MY; y++) for (int x = 0; x < grid.MX; x++)\n                        if (Matches(rule, x, y, z, grid.state, grid.MX, grid.MY, grid.MZ))\n                            Apply(rule, x * NX / DX, y * NY / DY, z * NZ / DZ, newgrid.state, newgrid.MX, newgrid.MY, newgrid.MZ);\n\n        ip.grid = newgrid;\n        n++;\n        return true;\n    }\n\n    override public void Reset()\n    {\n        base.Reset();\n        n = -1;\n    }\n}\n"
  },
  {
    "path": "source/Node.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\n\nabstract class Node\n{\n    abstract protected bool Load(XElement xelem, bool[] symmetry, Grid grid);\n    abstract public void Reset();\n    abstract public bool Go();\n\n    protected Interpreter ip;\n    public Grid grid;\n\n    public static Node Factory(XElement xelem, bool[] symmetry, Interpreter ip, Grid grid)\n    {\n        if (!nodenames.Contains(xelem.Name.LocalName))\n        {\n            Interpreter.WriteLine($\"unknown node type \\\"{xelem.Name}\\\" at line {xelem.LineNumber()}\");\n            return null;\n        }\n\n        Node result = xelem.Name.LocalName switch\n        {\n            \"one\" => new OneNode(),\n            \"all\" => new AllNode(),\n            \"prl\" => new ParallelNode(),\n            \"markov\" => new MarkovNode(),\n            \"sequence\" => new SequenceNode(),\n            \"path\" => new PathNode(),\n            \"map\" => new MapNode(),\n            \"convolution\" => new ConvolutionNode(),\n            \"convchain\" => new ConvChainNode(),\n            \"wfc\" when xelem.Get<string>(\"sample\", null) != null => new OverlapNode(),\n            \"wfc\" when xelem.Get<string>(\"tileset\", null) != null => new TileNode(),\n            _ => null\n        };\n\n        result.ip = ip;\n        result.grid = grid;\n        bool success = result.Load(xelem, symmetry, grid);\n\n        if (!success) return null;\n        return result;\n    }\n\n    protected static string[] nodenames = new string[] { \"one\", \"all\", \"prl\", \"markov\", \"sequence\", \"path\", \"map\", \"convolution\", \"convchain\", \"wfc\" };\n}\n\nabstract class Branch : Node\n{\n    public Branch parent;\n    public Node[] nodes;\n    public int n;\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        string symmetryString = xelem.Get<string>(\"symmetry\", null);\n        bool[] symmetry = SymmetryHelper.GetSymmetry(ip.grid.MZ == 1, symmetryString, parentSymmetry);\n        if (symmetry == null)\n        {\n            Interpreter.WriteLine($\"unknown symmetry {symmetryString} at line {xelem.LineNumber()}\");\n            return false;\n        }\n\n        XElement[] xchildren = xelem.Elements(nodenames).ToArray();\n        nodes = new Node[xchildren.Length];\n        for (int c = 0; c < xchildren.Length; c++)\n        {\n            var child = Factory(xchildren[c], symmetry, ip, grid);\n            if (child == null) return false;\n            if (child is Branch branch) branch.parent = branch is MapNode || branch is WFCNode ? null : this;\n            nodes[c] = child;\n        }\n        return true;\n    }\n\n    override public bool Go()\n    {\n        for (; n < nodes.Length; n++)\n        {\n            Node node = nodes[n];\n            if (node is Branch branch) ip.current = branch;\n            if (node.Go()) return true;\n        }\n        ip.current = ip.current.parent;\n        Reset();\n        return false;\n    }\n\n    override public void Reset()\n    {\n        foreach (var node in nodes) node.Reset();\n        n = 0;\n    }\n}\n\nclass SequenceNode : Branch { }\nclass MarkovNode : Branch\n{\n    public MarkovNode() { }\n    public MarkovNode(Node child, Interpreter ip) { nodes = new Node[] { child }; this.ip = ip; grid = ip.grid; }\n\n    public override bool Go()\n    {\n        n = 0;\n        return base.Go();\n    }\n}\n"
  },
  {
    "path": "source/Observation.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System.Linq;\nusing System.Collections.Generic;\n\nclass Observation\n{\n    readonly byte from;\n    readonly int to;\n\n    public Observation(char from, string to, Grid grid)\n    {\n        this.from = grid.values[from];\n        this.to = grid.Wave(to);\n    }\n\n    public static bool ComputeFutureSetPresent(int[] future, byte[] state, Observation[] observations)\n    {\n        bool[] mask = new bool[observations.Length];\n        for (int k = 0; k < observations.Length; k++) if (observations[k] == null) mask[k] = true;\n        \n        for (int i = 0; i < state.Length; i++)\n        {\n            byte value = state[i];\n            Observation obs = observations[value];\n            mask[value] = true;\n            if (obs != null)\n            {\n                future[i] = obs.to;\n                state[i] = obs.from;\n            }\n            else future[i] = 1 << value;\n        }\n\n        for (int k = 0; k < mask.Length; k++) if (!mask[k])\n            {\n                //Console.WriteLine($\"observed value {k} not present on the grid, observe-node returning false\");\n                return false;\n            }\n        return true;\n    }\n\n    public static void ComputeForwardPotentials(int[][] potentials, byte[] state, int MX, int MY, int MZ, Rule[] rules)\n    {\n        potentials.Set2D(-1);\n        for (int i = 0; i < state.Length; i++) potentials[state[i]][i] = 0;\n        ComputePotentials(potentials, MX, MY, MZ, rules, false);\n    }\n    public static void ComputeBackwardPotentials(int[][] potentials, int[] future, int MX, int MY, int MZ, Rule[] rules)\n    {\n        for (int c = 0; c < potentials.Length; c++)\n        {\n            int[] potential = potentials[c];\n            for (int i = 0; i < future.Length; i++) potential[i] = (future[i] & (1 << c)) != 0 ? 0 : -1;\n        }\n        ComputePotentials(potentials, MX, MY, MZ, rules, true);\n    }\n\n    static void ComputePotentials(int[][] potentials, int MX, int MY, int MZ, Rule[] rules, bool backwards)\n    {\n        Queue<(byte c, int x, int y, int z)> queue = new();\n        for (byte c = 0; c < potentials.Length; c++)\n        {\n            int[] potential = potentials[c];\n            for (int i = 0; i < potential.Length; i++) if (potential[i] == 0) queue.Enqueue((c, i % MX, (i % (MX * MY)) / MX, i / (MX * MY)));\n        }\n        bool[][] matchMask = AH.Array2D(rules.Length, potentials[0].Length, false);\n\n        while (queue.Any())\n        {\n            (byte value, int x, int y, int z) = queue.Dequeue();\n            int i = x + y * MX + z * MX * MY;\n            int t = potentials[value][i];\n            for (int r = 0; r < rules.Length; r++)\n            {\n                bool[] maskr = matchMask[r];\n                Rule rule = rules[r];\n                var shifts = backwards ? rule.oshifts[value] : rule.ishifts[value];\n                for (int l = 0; l < shifts.Length; l++)\n                {\n                    var (shiftx, shifty, shiftz) = shifts[l];\n                    int sx = x - shiftx;\n                    int sy = y - shifty;\n                    int sz = z - shiftz;\n\n                    if (sx < 0 || sy < 0 || sz < 0 || sx + rule.IMX > MX || sy + rule.IMY > MY || sz + rule.IMZ > MZ) continue;\n                    int si = sx + sy * MX + sz * MX * MY;\n                    if (!maskr[si] && ForwardMatches(rule, sx, sy, sz, potentials, t, MX, MY, backwards))\n                    {\n                        maskr[si] = true;\n                        ApplyForward(rule, sx, sy, sz, potentials, t, MX, MY, queue, backwards);\n                    }\n                }\n            }\n        }\n    }\n\n    static bool ForwardMatches(Rule rule, int x, int y, int z, int[][] potentials, int t, int MX, int MY, bool backwards)\n    {\n        int dz = 0, dy = 0, dx = 0;\n        byte[] a = backwards ? rule.output : rule.binput;\n        for (int di = 0; di < a.Length; di++)\n        {\n            byte value = a[di];\n            if (value != 0xff)\n            {\n                int current = potentials[value][x + dx + (y + dy) * MX + (z + dz) * MX * MY];\n                if (current > t || current == -1) return false;\n            }\n            dx++;\n            if (dx == rule.IMX)\n            {\n                dx = 0; dy++;\n                if (dy == rule.IMY) { dy = 0; dz++; }\n            }\n        }\n        return true;\n    }\n\n    static void ApplyForward(Rule rule, int x, int y, int z, int[][] potentials, int t, int MX, int MY, Queue<(byte, int, int, int)> q, bool backwards)\n    {\n        byte[] a = backwards ? rule.binput : rule.output;\n        for (int dz = 0; dz < rule.IMZ; dz++)\n        {\n            int zdz = z + dz;\n            for (int dy = 0; dy < rule.IMY; dy++)\n            {\n                int ydy = y + dy;\n                for (int dx = 0; dx < rule.IMX; dx++)\n                {\n                    int xdx = x + dx;\n                    int idi = xdx + ydy * MX + zdz * MX * MY;\n                    int di = dx + dy * rule.IMX + dz * rule.IMX * rule.IMY;\n                    byte o = a[di];\n                    if (o != 0xff && potentials[o][idi] == -1)\n                    {\n                        potentials[o][idi] = t + 1;\n                        q.Enqueue((o, xdx, ydy, zdz));\n                    }\n                }\n            }\n        }\n    }\n\n    public static bool IsGoalReached(byte[] present, int[] future)\n    {\n        for (int i = 0; i < present.Length; i++) if (((1 << present[i]) & future[i]) == 0) return false;\n        return true;\n    }\n\n    public static int ForwardPointwise(int[][] potentials, int[] future)\n    {\n        int sum = 0;\n        for (int i = 0; i < future.Length; i++)\n        {\n            int f = future[i];\n            int min = 1000, argmin = -1;\n            for (int c = 0; c < potentials.Length; c++, f >>= 1)\n            {\n                int potential = potentials[c][i];\n                if ((f & 1) == 1 && potential >= 0 && potential < min)\n                {\n                    min = potential;\n                    argmin = c;\n                }\n            }\n            if (argmin < 0) return -1;\n            sum += min;\n        }\n        return sum;\n    }\n\n    public static int BackwardPointwise(int[][] potentials, byte[] present)\n    {\n        int sum = 0;\n        for (int i = 0; i < present.Length; i++)\n        {\n            int potential = potentials[present[i]][i];\n            if (potential < 0) return -1;\n            sum += potential;\n        }\n        return sum;\n    }\n}\n"
  },
  {
    "path": "source/OneNode.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass OneNode : RuleNode\n{\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        if (!base.Load(xelem, parentSymmetry, grid)) return false;\n        matches = new List<(int, int, int, int)>();\n        matchMask = AH.Array2D(rules.Length, grid.state.Length, false);\n        return true;\n    }\n\n    override public void Reset()\n    {\n        base.Reset();\n        if (matchCount != 0)\n        {\n            matchMask.Set2D(false);\n            matchCount = 0;\n        }\n    }\n\n    void Apply(Rule rule, int x, int y, int z)\n    {\n        int MX = grid.MX, MY = grid.MY;\n        var changes = ip.changes;\n\n        for (int dz = 0; dz < rule.OMZ; dz++) for (int dy = 0; dy < rule.OMY; dy++) for (int dx = 0; dx < rule.OMX; dx++)\n                {\n                    byte newValue = rule.output[dx + dy * rule.OMX + dz * rule.OMX * rule.OMY];\n                    if (newValue != 0xff)\n                    {\n                        int sx = x + dx;\n                        int sy = y + dy;\n                        int sz = z + dz;\n                        int si = sx + sy * MX + sz * MX * MY;\n                        byte oldValue = grid.state[si];\n                        if (newValue != oldValue)\n                        {\n                            grid.state[si] = newValue;\n                            changes.Add((sx, sy, sz));\n                        }\n                    }\n                }\n    }\n\n    override public bool Go()\n    {\n        if (!base.Go()) return false;\n        lastMatchedTurn = ip.counter;\n\n        if (trajectory != null)\n        {\n            if (counter >= trajectory.Length) return false;\n            Array.Copy(trajectory[counter], grid.state, grid.state.Length);\n            counter++;\n            return true;\n        }\n\n        var (R, X, Y, Z) = RandomMatch(ip.random);\n        if (R < 0) return false;\n        else\n        {\n            last[R] = true;\n            Apply(rules[R], X, Y, Z);\n            counter++;\n            return true;\n        }\n    }\n\n    (int r, int x, int y, int z) RandomMatch(Random random)\n    {\n        if (potentials != null)\n        {\n            if (observations != null && Observation.IsGoalReached(grid.state, future))\n            {\n                futureComputed = false;\n                return (-1, -1, -1, -1);\n            }\n            double max = -1000.0;\n            int argmax = -1;\n\n            double firstHeuristic = 0.0;\n            bool firstHeuristicComputed = false;\n\n            for (int k = 0; k < matchCount; k++)\n            {\n                var (r, x, y, z) = matches[k];\n                int i = x + y * grid.MX + z * grid.MX * grid.MY;\n                if (!grid.Matches(rules[r], x, y, z))\n                {\n                    matchMask[r][i] = false;\n                    matches[k] = matches[matchCount - 1];\n                    matchCount--;\n                    k--;\n                }\n                else\n                {\n                    double? heuristic = Field.DeltaPointwise(grid.state, rules[r], x, y, z, fields, potentials, grid.MX, grid.MY);\n                    if (heuristic == null) continue;\n                    double h = (double)heuristic;\n                    if (!firstHeuristicComputed)\n                    {\n                        firstHeuristic = h;\n                        firstHeuristicComputed = true;\n                    }\n                    double u = random.NextDouble();\n                    double key = temperature > 0 ? Math.Pow(u, Math.Exp((h - firstHeuristic) / temperature)) : -h + 0.001 * u;\n                    if (key > max)\n                    {\n                        max = key;\n                        argmax = k;\n                    }\n                }\n            }\n            return argmax >= 0 ? matches[argmax] : (-1, -1, -1, -1);\n        }\n        else\n        {\n            while (matchCount > 0)\n            {\n                int matchIndex = random.Next(matchCount);\n\n                var (r, x, y, z) = matches[matchIndex];\n                int i = x + y * grid.MX + z * grid.MX * grid.MY;\n\n                matchMask[r][i] = false;\n                matches[matchIndex] = matches[matchCount - 1];\n                matchCount--;\n\n                if (grid.Matches(rules[r], x, y, z)) return (r, x, y, z);\n            }\n            return (-1, -1, -1, -1);\n        }\n    }\n}\n"
  },
  {
    "path": "source/OverlapModel.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass OverlapNode : WFCNode\n{\n    byte[][] patterns;\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        if (grid.MZ != 1)\n        {\n            Interpreter.WriteLine(\"overlapping model currently works only for 2d\");\n            return false;\n        }\n        N = xelem.Get(\"n\", 3);\n\n        string symmetryString = xelem.Get<string>(\"symmetry\", null);\n        bool[] symmetry = SymmetryHelper.GetSymmetry(true, symmetryString, parentSymmetry);\n        if (symmetry == null)\n        {\n            Interpreter.WriteLine($\"unknown symmetry {symmetryString} at line {xelem.LineNumber()}\");\n            return false;\n        }\n\n        bool periodicInput = xelem.Get(\"periodicInput\", true);\n\n        newgrid = Grid.Load(xelem, grid.MX, grid.MY, grid.MZ);\n        if (newgrid == null) return false;\n        periodic = true;\n\n        name = xelem.Get<string>(\"sample\");\n        (int[] bitmap, int SMX, int SMY, _) = Graphics.LoadBitmap($\"resources/samples/{name}.png\");\n        if (bitmap == null)\n        {\n            Interpreter.WriteLine($\"couldn't read sample {name}\");\n            return false;\n        }\n        (byte[] sample, int C) = bitmap.Ords();\n        if (C > newgrid.C)\n        {\n            Interpreter.WriteLine($\"there were more than {newgrid.C} colors in the sample\");\n            return false;\n        }\n        long W = Helper.Power(C, N * N);\n\n        byte[] patternFromIndex(long ind)\n        {\n            long residue = ind, power = W;\n            byte[] result = new byte[N * N];\n            for (int i = 0; i < result.Length; i++)\n            {\n                power /= C;\n                int count = 0;\n                while (residue >= power)\n                {\n                    residue -= power;\n                    count++;\n                }\n                result[i] = (byte)count;\n            }\n            return result;\n        };\n\n        Dictionary<long, int> weights = new();\n        List<long> ordering = new();\n\n        int ymax = periodicInput ? grid.MY : grid.MY - N + 1;\n        int xmax = periodicInput ? grid.MX : grid.MX - N + 1;\n        for (int y = 0; y < ymax; y++) for (int x = 0; x < xmax; x++)\n            {\n                byte[] pattern = Helper.Pattern((dx, dy) => sample[(x + dx) % SMX + ((y + dy) % SMY) * SMX], N);\n                var symmetries = SymmetryHelper.SquareSymmetries(pattern, q => Helper.Rotated(q, N), q => Helper.Reflected(q, N), (q1, q2) => false, symmetry);\n\n                foreach (byte[] p in symmetries)\n                {\n                    long ind = p.Index(C);\n                    if (weights.ContainsKey(ind)) weights[ind]++;\n                    else\n                    {\n                        weights.Add(ind, 1);\n                        ordering.Add(ind);\n                    }\n                }\n            }\n\n        P = weights.Count;\n        Console.WriteLine($\"number of patterns P = {P}\");\n\n        patterns = new byte[P][];\n        base.weights = new double[P];\n        int counter = 0;\n        foreach (long w in ordering)\n        {\n            patterns[counter] = patternFromIndex(w);\n            base.weights[counter] = weights[w];\n            counter++;\n        }\n\n        bool agrees(byte[] p1, byte[] p2, int dx, int dy)\n        {\n            int xmin = dx < 0 ? 0 : dx, xmax = dx < 0 ? dx + N : N, ymin = dy < 0 ? 0 : dy, ymax = dy < 0 ? dy + N : N;\n            for (int y = ymin; y < ymax; y++) for (int x = xmin; x < xmax; x++) if (p1[x + N * y] != p2[x - dx + N * (y - dy)]) return false;\n            return true;\n        };\n\n        propagator = new int[4][][];\n        for (int d = 0; d < 4; d++)\n        {\n            propagator[d] = new int[P][];\n            for (int t = 0; t < P; t++)\n            {\n                List<int> list = new();\n                for (int t2 = 0; t2 < P; t2++) if (agrees(patterns[t], patterns[t2], DX[d], DY[d])) list.Add(t2);\n                propagator[d][t] = new int[list.Count];\n                for (int c = 0; c < list.Count; c++) propagator[d][t][c] = list[c];\n            }\n        }\n\n        map = new Dictionary<byte, bool[]>();\n        foreach (XElement xrule in xelem.Elements(\"rule\"))\n        {\n            char input = xrule.Get<char>(\"in\");\n            byte[] outputs = xrule.Get<string>(\"out\").Split('|').Select(s => newgrid.values[s[0]]).ToArray();\n            bool[] position = Enumerable.Range(0, P).Select(t => outputs.Contains(patterns[t][0])).ToArray();\n            map.Add(grid.values[input], position);\n        }\n        if (!map.ContainsKey(0)) map.Add(0, Enumerable.Repeat(true, P).ToArray());\n        \n        return base.Load(xelem, parentSymmetry, grid);\n    }\n\n    protected override void UpdateState()\n    {\n        int MX = newgrid.MX, MY = newgrid.MY;\n        int[][] votes = AH.Array2D(newgrid.state.Length, newgrid.C, 0);\n        for (int i = 0; i < wave.data.Length; i++)\n        {\n            bool[] w = wave.data[i];\n            int x = i % MX, y = i / MX;\n            for (int p = 0; p < P; p++) if (w[p])\n                {\n                    byte[] pattern = patterns[p];\n                    for (int dy = 0; dy < N; dy++)\n                    {\n                        int ydy = y + dy;\n                        if (ydy >= MY) ydy -= MY;\n                        for (int dx = 0; dx < N; dx++)\n                        {\n                            int xdx = x + dx;\n                            if (xdx >= MX) xdx -= MX;\n                            byte value = pattern[dx + dy * N];\n                            votes[xdx + ydy * MX][value]++;\n                        }\n                    }\n                }\n        }\n\n        Random r = new();\n        for (int i = 0; i < votes.Length; i++)\n        {\n            double max = -1.0;\n            byte argmax = 0xff;\n            int[] v = votes[i];\n            for (byte c = 0; c < v.Length; c++)\n            {\n                double value = v[c] + 0.1 * r.NextDouble();\n                if (value > max)\n                {\n                    argmax = c;\n                    max = value;\n                }\n            }\n            newgrid.state[i] = argmax;\n        }\n    }\n}\n"
  },
  {
    "path": "source/ParallelNode.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System.Xml.Linq;\n\nclass ParallelNode : RuleNode\n{\n    byte[] newstate;\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        if (!base.Load(xelem, parentSymmetry, grid)) return false;\n        newstate = new byte[grid.state.Length];\n        return true;\n    }\n\n    override protected void Add(int r, int x, int y, int z, bool[] maskr)\n    {\n        Rule rule = rules[r];\n        if (ip.random.NextDouble() > rule.p) return;\n        last[r] = true;\n        int MX = grid.MX, MY = grid.MY;\n\n        for (int dz = 0; dz < rule.OMZ; dz++) for (int dy = 0; dy < rule.OMY; dy++) for (int dx = 0; dx < rule.OMX; dx++)\n                {\n                    byte newvalue = rule.output[dx + dy * rule.OMX + dz * rule.OMX * rule.OMY];\n                    int idi = x + dx + (y + dy) * MX + (z + dz) * MX * MY;\n                    if (newvalue != 0xff && newvalue != grid.state[idi])\n                    {\n                        newstate[idi] = newvalue;\n                        ip.changes.Add((x + dx, y + dy, z + dz));\n                    }\n                }\n        matchCount++;\n    }\n\n    public override bool Go()\n    {\n        if (!base.Go()) return false;\n\n        for (int n = ip.first[ip.counter]; n < ip.changes.Count; n++)\n        {\n            var (x, y, z) = ip.changes[n];\n            int i = x + y * grid.MX + z * grid.MX * grid.MY;\n            grid.state[i] = newstate[i];\n        }\n\n        counter++;\n        return matchCount > 0;\n    }\n}\n"
  },
  {
    "path": "source/Path.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass PathNode : Node\n{\n    public int start, finish, substrate;\n    public byte value;\n    bool inertia, longest, edges, vertices;\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        string startSymbols = xelem.Get<string>(\"from\");\n        start = grid.Wave(startSymbols);\n        value = grid.values[xelem.Get(\"color\", startSymbols[0])];\n        finish = grid.Wave(xelem.Get<string>(\"to\"));\n        inertia = xelem.Get(\"inertia\", false);\n        longest = xelem.Get(\"longest\", false);\n        edges = xelem.Get(\"edges\", false);\n        vertices = xelem.Get(\"vertices\", false);\n        substrate = grid.Wave(xelem.Get<string>(\"on\"));\n        return true;\n    }\n\n    public override void Reset() { }\n    public override bool Go()\n    {\n        Queue<(int, int, int, int)> frontier = new();\n        List<(int x, int y, int z)> startPositions = new();\n        int[] generations = AH.Array1D(grid.state.Length, -1);\n        int MX = grid.MX, MY = grid.MY, MZ = grid.MZ;\n\n        for (int z = 0; z < MZ; z++) for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++)\n                {\n                    int i = x + y * MX + z * MX * MY;\n                    generations[i] = -1;\n\n                    byte s = grid.state[i];\n                    if ((start & 1 << s) != 0) startPositions.Add((x, y, z));\n                    if ((finish & 1 << s) != 0)\n                    {\n                        generations[i] = 0;\n                        frontier.Enqueue((0, x, y, z));\n                    }\n                }\n\n        if (!startPositions.Any() || !frontier.Any()) return false;\n\n        void push(int t, int x, int y, int z)\n        {\n            int i = x + y * MX + z * MX * MY;\n            byte v = grid.state[i];\n            if (generations[i] == -1 && ((substrate & 1 << v) != 0 || (start & 1 << v) != 0))\n            {\n                if ((substrate & 1 << v) != 0) frontier.Enqueue((t, x, y, z));\n                generations[i] = t;\n            }\n        };\n\n        while (frontier.Any())\n        {\n            var (t, x, y, z) = frontier.Dequeue();\n            foreach (var (dx, dy, dz) in Directions(x, y, z, MX, MY, MZ, edges, vertices)) push(t + 1, x + dx, y + dy, z + dz);\n        }\n\n        if (!startPositions.Where(p => generations[p.x + p.y * MX + p.z * MX * MY] > 0).Any()) return false;\n        \n        Random localRandom = new(ip.random.Next());\n        double min = MX * MY * MZ, max = -2;\n        (int, int, int) argmin = (-1, -1, -1), argmax = (-1, -1, -1);\n\n        foreach (var p in startPositions)\n        {\n            int g = generations[p.x + p.y * MX + p.z * MX * MY];\n            if (g == -1) continue;\n            double dg = g;\n            double noise = 0.1 * localRandom.NextDouble();\n\n            if (dg + noise < min)\n            {\n                min = dg + noise;\n                argmin = p;\n            }\n\n            if (dg + noise > max)\n            {\n                max = dg + noise;\n                argmax = p;\n            }\n        }\n\n        var (penx, peny, penz) = longest ? argmax : argmin;\n        var (dirx, diry, dirz) = Direction(penx, peny, penz, 0, 0, 0, generations, localRandom);\n        penx += dirx;\n        peny += diry;\n        penz += dirz;\n\n        while (generations[penx + peny * MX + penz * MX * MY] != 0)\n        {\n            grid.state[penx + peny * MX + penz * MX * MY] = value;\n            ip.changes.Add((penx, peny, penz));\n            (dirx, diry, dirz) = Direction(penx, peny, penz, dirx, diry, dirz, generations, localRandom);\n            penx += dirx;\n            peny += diry;\n            penz += dirz;\n        }\n        return true;\n    }\n\n    (int, int, int) Direction(int x, int y, int z, int dx, int dy, int dz, int[] generations, Random random)\n    {\n        List<(int x, int y, int z)> candidates = new();\n        int MX = grid.MX, MY = grid.MY, MZ = grid.MZ;\n        int g = generations[x + y * MX + z * MX * MY];\n\n        void add(int DX, int DY, int DZ)\n        {\n            if (generations[x + DX + (y + DY) * MX + (z + DZ) * MX * MY] == g - 1) candidates.Add((DX, DY, DZ));\n        };\n\n        if (!vertices && !edges)\n        {\n            if (dx != 0 || dy != 0 || dz != 0)\n            {\n                int cx = x + dx, cy = y + dy, cz = z + dz;\n                if (inertia && cx >= 0 && cy >= 0 && cz >= 0 && cx < MX && cy < MY && cz < MZ && generations[cx + cy * MX + cz * MX * MY] == g - 1)\n                    return (dx, dy, dz);\n            }\n\n            if (x > 0) add(-1, 0, 0);\n            if (x < MX - 1) add(1, 0, 0);\n            if (y > 0) add(0, -1, 0);\n            if (y < MY - 1) add(0, 1, 0);\n            if (z > 0) add(0, 0, -1);\n            if (z < MZ - 1) add(0, 0, 1);\n\n            return candidates.Random(random);\n        }\n        else\n        {\n            foreach (var p in Directions(x, y, z, MX, MY, MZ, edges, vertices)) add(p.x, p.y, p.z);\n            (int, int, int) result = (-1, -1, -1);\n\n            if (inertia && (dx != 0 || dy != 0 || dz != 0))\n            {\n                double maxScalar = -4;\n                foreach (var c in candidates)\n                {\n                    double noise = 0.1 * random.NextDouble();\n                    double cos = (c.x * dx + c.y * dy + c.z * dz) / Math.Sqrt((c.x * c.x + c.y * c.y + c.z * c.z) * (dx * dx + dy * dy + dz * dz));\n\n                    if (cos + noise > maxScalar)\n                    {\n                        maxScalar = cos + noise;\n                        result = c;\n                    }\n                }\n            }\n            else result = candidates.Random(random);\n\n            return result;\n        }\n    }\n\n    static List<(int x, int y, int z)> Directions(int x, int y, int z, int MX, int MY, int MZ, bool edges, bool vertices)\n    {\n        List<(int, int, int)> result = new();\n        if (MZ == 1)\n        {\n            if (x > 0) result.Add((-1, 0, 0));\n            if (x < MX - 1) result.Add((1, 0, 0));\n            if (y > 0) result.Add((0, -1, 0));\n            if (y < MY - 1) result.Add((0, 1, 0));\n\n            if (edges)\n            {\n                if (x > 0 && y > 0) result.Add((-1, -1, 0));\n                if (x > 0 && y < MY - 1) result.Add((-1, 1, 0));\n                if (x < MX - 1 && y > 0) result.Add((1, -1, 0));\n                if (x < MX - 1 && y < MY - 1) result.Add((1, 1, 0));\n            }\n        }\n        else\n        {\n            if (x > 0) result.Add((-1, 0, 0));\n            if (x < MX - 1) result.Add((1, 0, 0));\n            if (y > 0) result.Add((0, -1, 0));\n            if (y < MY - 1) result.Add((0, 1, 0));\n            if (z > 0) result.Add((0, 0, -1));\n            if (z < MZ - 1) result.Add((0, 0, 1));\n\n            if (edges)\n            {\n                if (x > 0 && y > 0) result.Add((-1, -1, 0));\n                if (x > 0 && y < MY - 1) result.Add((-1, 1, 0));\n                if (x < MX - 1 && y > 0) result.Add((1, -1, 0));\n                if (x < MX - 1 && y < MY - 1) result.Add((1, 1, 0));\n\n                if (x > 0 && z > 0) result.Add((-1, 0, -1));\n                if (x > 0 && z < MZ - 1) result.Add((-1, 0, 1));\n                if (x < MX - 1 && z > 0) result.Add((1, 0, -1));\n                if (x < MX - 1 && z < MZ - 1) result.Add((1, 0, 1));\n\n                if (y > 0 && z > 0) result.Add((0, -1, -1));\n                if (y > 0 && z < MZ - 1) result.Add((0, -1, 1));\n                if (y < MY - 1 && z > 0) result.Add((0, 1, -1));\n                if (y < MY - 1 && z < MZ - 1) result.Add((0, 1, 1));\n            }\n\n            if (vertices)\n            {\n                if (x > 0 && y > 0 && z > 0) result.Add((-1, -1, -1));\n                if (x > 0 && y > 0 && z < MZ - 1) result.Add((-1, -1, 1));\n                if (x > 0 && y < MY - 1 && z > 0) result.Add((-1, 1, -1));\n                if (x > 0 && y < MY - 1 && z < MZ - 1) result.Add((-1, 1, 1));\n                if (x < MX - 1 && y > 0 && z > 0) result.Add((1, -1, -1));\n                if (x < MX - 1 && y > 0 && z < MZ - 1) result.Add((1, -1, 1));\n                if (x < MX - 1 && y < MY - 1 && z > 0) result.Add((1, 1, -1));\n                if (x < MX - 1 && y < MY - 1 && z < MZ - 1) result.Add((1, 1, 1));\n            }\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "source/Program.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Diagnostics;\nusing System.Collections.Generic;\n\nstatic class Program\n{\n    static void Main()\n    {\n        Stopwatch sw = Stopwatch.StartNew();\n        var folder = System.IO.Directory.CreateDirectory(\"output\");\n        foreach (var file in folder.GetFiles()) file.Delete();\n\n        Dictionary<char, int> palette = XDocument.Load(\"resources/palette.xml\").Root.Elements(\"color\").ToDictionary(x => x.Get<char>(\"symbol\"), x => (255 << 24) + Convert.ToInt32(x.Get<string>(\"value\"), 16));\n\n        Random meta = new();\n        XDocument xdoc = XDocument.Load(\"models.xml\", LoadOptions.SetLineInfo);\n        foreach (XElement xmodel in xdoc.Root.Elements(\"model\"))\n        {\n            string name = xmodel.Get<string>(\"name\");\n            int linearSize = xmodel.Get(\"size\", -1);\n            int dimension = xmodel.Get(\"d\", 2);\n            int MX = xmodel.Get(\"length\", linearSize);\n            int MY = xmodel.Get(\"width\", linearSize);\n            int MZ = xmodel.Get(\"height\", dimension == 2 ? 1 : linearSize);\n\n            Console.Write($\"{name} > \");\n            string filename = $\"models/{name}.xml\";\n            XDocument modeldoc;\n            try { modeldoc = XDocument.Load(filename, LoadOptions.SetLineInfo); }\n            catch (Exception)\n            {\n                Console.WriteLine($\"ERROR: couldn't open xml file {filename}\");\n                continue;\n            }\n\n            Interpreter interpreter = Interpreter.Load(modeldoc.Root, MX, MY, MZ);\n            if (interpreter == null)\n            {\n                Console.WriteLine(\"ERROR\");\n                continue;\n            }\n\n            int amount = xmodel.Get(\"amount\", 2);\n            int pixelsize = xmodel.Get(\"pixelsize\", 4);\n            string seedString = xmodel.Get<string>(\"seeds\", null);\n            int[] seeds = seedString?.Split(' ').Select(s => int.Parse(s)).ToArray();\n            bool gif = xmodel.Get(\"gif\", false);\n            bool iso = xmodel.Get(\"iso\", false);\n            int steps = xmodel.Get(\"steps\", gif ? 1000 : 50000);\n            int gui = xmodel.Get(\"gui\", 0);\n            if (gif) amount = 1;\n\n            Dictionary<char, int> customPalette = new(palette);\n            foreach (var x in xmodel.Elements(\"color\")) customPalette[x.Get<char>(\"symbol\")] = (255 << 24) + Convert.ToInt32(x.Get<string>(\"value\"), 16);\n\n            for (int k = 0; k < amount; k++)\n            {\n                int seed = seeds != null && k < seeds.Length ? seeds[k] : meta.Next();\n                foreach ((byte[] result, char[] legend, int FX, int FY, int FZ) in interpreter.Run(seed, steps, gif))\n                {\n                    int[] colors = legend.Select(ch => customPalette[ch]).ToArray();\n                    string outputname = gif ? $\"output/{interpreter.counter}\" : $\"output/{name}_{seed}\";\n                    if (FZ == 1 || iso)\n                    {\n                        var (bitmap, WIDTH, HEIGHT) = Graphics.Render(result, FX, FY, FZ, colors, pixelsize, gui);\n                        if (gui > 0) GUI.Draw(name, interpreter.root, interpreter.current, bitmap, WIDTH, HEIGHT, customPalette);\n                        Graphics.SaveBitmap(bitmap, WIDTH, HEIGHT, outputname + \".png\");\n                    }\n                    else VoxHelper.SaveVox(result, (byte)FX, (byte)FY, (byte)FZ, colors, outputname + \".vox\");\n                }\n                Console.WriteLine(\"DONE\");\n            }\n        }\n        Console.WriteLine($\"time = {sw.ElapsedMilliseconds}\");\n    }\n}\n"
  },
  {
    "path": "source/Rule.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass Rule\n{\n    public int IMX, IMY, IMZ, OMX, OMY, OMZ;\n    public int[] input;\n    public byte[] output, binput;\n\n    public double p;\n    public (int, int, int)[][] ishifts, oshifts;\n\n    public bool original;\n\n    public Rule(int[] input, int IMX, int IMY, int IMZ, byte[] output, int OMX, int OMY, int OMZ, int C, double p)\n    {\n        this.input = input;\n        this.output = output;\n        this.IMX = IMX;\n        this.IMY = IMY;\n        this.IMZ = IMZ;\n        this.OMX = OMX;\n        this.OMY = OMY;\n        this.OMZ = OMZ;\n\n        this.p = p;\n\n        List<(int, int, int)>[] lists = new List<(int, int, int)>[C];\n        for (int c = 0; c < C; c++) lists[c] = new List<(int, int, int)>();\n        for (int z = 0; z < IMZ; z++) for (int y = 0; y < IMY; y++) for (int x = 0; x < IMX; x++)\n                {\n                    int w = input[x + y * IMX + z * IMX * IMY];\n                    for (int c = 0; c < C; c++, w >>= 1) if ((w & 1) == 1) lists[c].Add((x, y, z));\n                }\n        ishifts = new (int, int, int)[C][];\n        for (int c = 0; c < C; c++) ishifts[c] = lists[c].ToArray();\n\n        if (OMX == IMX && OMY == IMY && OMZ == IMZ)\n        {\n            for (int c = 0; c < C; c++) lists[c].Clear();\n            for (int z = 0; z < OMZ; z++) for (int y = 0; y < OMY; y++) for (int x = 0; x < OMX; x++)\n                    {\n                        byte o = output[x + y * OMX + z * OMX * OMY];\n                        if (o != 0xff) lists[o].Add((x, y, z));\n                        else for (int c = 0; c < C; c++) lists[c].Add((x, y, z));\n                    }\n            oshifts = new (int, int, int)[C][];\n            for (int c = 0; c < C; c++) oshifts[c] = lists[c].ToArray();\n        }\n\n        int wildcard = (1 << C) - 1;\n        binput = new byte[input.Length];\n        for (int i = 0; i < input.Length; i++)\n        {\n            int w = input[i];\n            binput[i] = w == wildcard ? (byte)0xff : (byte)System.Numerics.BitOperations.TrailingZeroCount(w);\n        }\n    }\n\n    public Rule ZRotated()\n    {\n        int[] newinput = new int[input.Length];\n        for (int z = 0; z < IMZ; z++) for (int y = 0; y < IMX; y++) for (int x = 0; x < IMY; x++)\n                    newinput[x + y * IMY + z * IMX * IMY] = input[IMX - 1 - y + x * IMX + z * IMX * IMY];\n\n        byte[] newoutput = new byte[output.Length];\n        for (int z = 0; z < OMZ; z++) for (int y = 0; y < OMX; y++) for (int x = 0; x < OMY; x++)\n                    newoutput[x + y * OMY + z * OMX * OMY] = output[OMX - 1 - y + x * OMX + z * OMX * OMY];\n\n        return new Rule(newinput, IMY, IMX, IMZ, newoutput, OMY, OMX, OMZ, ishifts.Length, p);\n    }\n\n    public Rule YRotated()\n    {\n        int[] newinput = new int[input.Length];\n        for (int z = 0; z < IMX; z++) for (int y = 0; y < IMY; y++) for (int x = 0; x < IMZ; x++)\n                    newinput[x + y * IMZ + z * IMZ * IMY] = input[IMX - 1 - z + y * IMX + x * IMX * IMY];\n\n        byte[] newoutput = new byte[output.Length];\n        for (int z = 0; z < OMX; z++) for (int y = 0; y < OMY; y++) for (int x = 0; x < OMZ; x++)\n                    newoutput[x + y * OMZ + z * OMZ * OMY] = output[OMX - 1 - z + y * OMX + x * OMX * OMY];\n\n        return new Rule(newinput, IMZ, IMY, IMX, newoutput, OMZ, OMY, OMX, ishifts.Length, p);\n    }\n\n    public Rule Reflected()\n    {\n        int[] newinput = new int[input.Length];\n        for (int z = 0; z < IMZ; z++) for (int y = 0; y < IMY; y++) for (int x = 0; x < IMX; x++)\n                    newinput[x + y * IMX + z * IMX * IMY] = input[IMX - 1 - x + y * IMX + z * IMX * IMY];\n\n        byte[] newoutput = new byte[output.Length];\n        for (int z = 0; z < OMZ; z++) for (int y = 0; y < OMY; y++) for (int x = 0; x < OMX; x++)\n                    newoutput[x + y * OMX + z * OMX * OMY] = output[OMX - 1 - x + y * OMX + z * OMX * OMY];\n\n        return new Rule(newinput, IMX, IMY, IMZ, newoutput, OMX, OMY, OMZ, ishifts.Length, p);\n    }\n\n    public static bool Same(Rule a1, Rule a2)\n    {\n        if (a1.IMX != a2.IMX || a1.IMY != a2.IMY || a1.IMZ != a2.IMZ || a1.OMX != a2.OMX || a1.OMY != a2.OMY || a1.OMZ != a2.OMZ) return false;\n        for (int i = 0; i < a1.IMX * a1.IMY * a1.IMZ; i++) if (a1.input[i] != a2.input[i]) return false;\n        for (int i = 0; i < a1.OMX * a1.OMY * a1.OMZ; i++) if (a1.output[i] != a2.output[i]) return false;\n        return true;\n    }\n\n    public IEnumerable<Rule> Symmetries(bool[] symmetry, bool d2)\n    {\n        if (d2) return SymmetryHelper.SquareSymmetries(this, r => r.ZRotated(), r => r.Reflected(), Same, symmetry);\n        else return SymmetryHelper.CubeSymmetries(this, r => r.ZRotated(), r => r.YRotated(), r => r.Reflected(), Same, symmetry);\n    }\n\n    public static (char[] data, int MX, int MY, int MZ) LoadResource(string filename, string legend, bool d2)\n    {\n        if (legend == null)\n        {\n            Interpreter.WriteLine($\"no legend for {filename}\");\n            return (null, -1, -1, -1);\n        }\n        (int[] data, int MX, int MY, int MZ) = d2 ? Graphics.LoadBitmap(filename) : VoxHelper.LoadVox(filename);\n        if (data == null)\n        {\n            Interpreter.WriteLine($\"couldn't read {filename}\");\n            return (null, MX, MY, MZ);\n        }\n        (byte[] ords, int amount) = data.Ords();\n        if (amount > legend.Length)\n        {\n            Interpreter.WriteLine($\"the amount of colors {amount} in {filename} is more than {legend.Length}\");\n            return (null, MX, MY, MZ);\n        }\n        return (ords.Select(o => legend[o]).ToArray(), MX, MY, MZ);\n    }\n\n    static (char[], int, int, int) Parse(string s)\n    {\n        string[][] lines = Helper.Split(s, ' ', '/');\n        int MX = lines[0][0].Length;\n        int MY = lines[0].Length;\n        int MZ = lines.Length;\n        char[] result = new char[MX * MY * MZ];\n\n        for (int z = 0; z < MZ; z++)\n        {\n            string[] linesz = lines[MZ - 1 - z];\n            if (linesz.Length != MY)\n            {\n                Interpreter.Write(\"non-rectangular pattern\");\n                return (null, -1, -1, -1);\n            }\n            for (int y = 0; y < MY; y++)\n            {\n                string lineszy = linesz[y];\n                if (lineszy.Length != MX)\n                {\n                    Interpreter.Write(\"non-rectangular pattern\");\n                    return (null, -1, -1, -1);\n                }\n                for (int x = 0; x < MX; x++) result[x + y * MX + z * MX * MY] = lineszy[x];\n            }\n        }\n        \n        return (result, MX, MY, MZ);\n    }\n\n    public static Rule Load(XElement xelem, Grid gin, Grid gout)\n    {\n        int lineNumber = xelem.LineNumber();\n        string filepath(string name)\n        {\n            string result = \"resources/rules/\";\n            if (gout.folder != null) result += gout.folder + \"/\";\n            result += name;\n            result += gin.MZ == 1 ? \".png\" : \".vox\";\n            return result;\n        };\n\n        string inString = xelem.Get<string>(\"in\", null);\n        string outString = xelem.Get<string>(\"out\", null);\n        string finString = xelem.Get<string>(\"fin\", null);\n        string foutString = xelem.Get<string>(\"fout\", null);\n        string fileString = xelem.Get<string>(\"file\", null);\n        string legend = xelem.Get<string>(\"legend\", null);\n\n        char[] inRect, outRect;\n        int IMX = -1, IMY = -1, IMZ = -1, OMX = -1, OMY = -1, OMZ = -1;\n        if (fileString == null)\n        {\n            if (inString == null && finString == null)\n            {\n                Interpreter.WriteLine($\"no input in a rule at line {lineNumber}\");\n                return null;\n            }\n            if (outString == null && foutString == null)\n            {\n                Interpreter.WriteLine($\"no output in a rule at line {lineNumber}\");\n                return null;\n            }\n\n            (inRect, IMX, IMY, IMZ) = inString != null ? Parse(inString) : LoadResource(filepath(finString), legend, gin.MZ == 1);\n            if (inRect == null)\n            {\n                Interpreter.WriteLine($\" in input at line {lineNumber}\");\n                return null;\n            }\n\n            (outRect, OMX, OMY, OMZ) = outString != null ? Parse(outString) : LoadResource(filepath(foutString), legend, gin.MZ == 1);\n            if (outRect == null)\n            {\n                Interpreter.WriteLine($\" in output at line {lineNumber}\");\n                return null;\n            }\n\n            if (gin == gout && (OMZ != IMZ || OMY != IMY || OMX != IMX))\n            {\n                Interpreter.WriteLine($\"non-matching pattern sizes at line {lineNumber}\");\n                return null;\n            }\n        }\n        else\n        {\n            if (inString != null || finString != null || outString != null || foutString != null)\n            {\n                Interpreter.WriteLine($\"rule at line {lineNumber} already contains a file attribute\");\n                return null;\n            }\n            (char[] rect, int FX, int FY, int FZ) = LoadResource(filepath(fileString), legend, gin.MZ == 1);\n            if (rect == null)\n            {\n                Interpreter.WriteLine($\" in a rule at line {lineNumber}\");\n                return null;\n            }\n            if (FX % 2 != 0)\n            {\n                Interpreter.WriteLine($\"odd width {FX} in {fileString}\");\n                return null;\n            }\n            \n            IMX = OMX = FX / 2;\n            IMY = OMY = FY;\n            IMZ = OMZ = FZ;\n\n            inRect = AH.FlatArray3D(FX / 2, FY, FZ, (x, y, z) => rect[x + y * FX + z * FX * FY]);\n            outRect = AH.FlatArray3D(FX / 2, FY, FZ, (x, y, z) => rect[x + FX / 2 + y * FX + z * FX * FY]);\n        }\n\n        int[] input = new int[inRect.Length];\n        for (int i = 0; i < inRect.Length; i++)\n        {\n            char c = inRect[i];\n            bool success = gin.waves.TryGetValue(c, out int value);\n            if (!success)\n            {\n                Interpreter.WriteLine($\"input code {c} at line {lineNumber} is not found in codes\");\n                return null;\n            }\n            input[i] = value;\n        }\n\n        byte[] output = new byte[outRect.Length];\n        for (int o = 0; o < outRect.Length; o++)\n        {\n            char c = outRect[o];\n            if (c == '*') output[o] = 0xff;\n            else\n            {\n                bool success = gout.values.TryGetValue(c, out byte value);\n                if (!success)\n                {\n                    Interpreter.WriteLine($\"output code {c} at line {lineNumber} is not found in codes\");\n                    return null;\n                }\n                output[o] = value;\n            }\n        }\n\n        double p = xelem.Get(\"p\", 1.0);\n        return new Rule(input, IMX, IMY, IMZ, output, OMX, OMY, OMZ, gin.C, p);\n    }\n}\n"
  },
  {
    "path": "source/RuleNode.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nabstract class RuleNode : Node\n{\n    public Rule[] rules;\n    public int counter, steps;\n\n    protected List<(int, int, int, int)> matches;\n    protected int matchCount, lastMatchedTurn;\n    protected bool[][] matchMask;\n\n    protected int[][] potentials;\n    public Field[] fields;\n    protected Observation[] observations;\n    protected double temperature;\n\n    protected bool search, futureComputed;\n    protected int[] future;\n    protected byte[][] trajectory;\n\n    int limit;\n    double depthCoefficient;\n\n    public bool[] last;\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        string symmetryString = xelem.Get<string>(\"symmetry\", null);\n        bool[] symmetry = SymmetryHelper.GetSymmetry(grid.MZ == 1, symmetryString, parentSymmetry);\n        if (symmetry == null)\n        {\n            Interpreter.WriteLine($\"unknown symmetry {symmetryString} at line {xelem.LineNumber()}\");\n            return false;\n        }\n\n        List<Rule> ruleList = new();\n        XElement[] xrules = xelem.Elements(\"rule\").ToArray();\n        XElement[] ruleElements = xrules.Length > 0 ? xrules : new XElement[] { xelem };\n        foreach (XElement xrule in ruleElements)\n        {\n            Rule rule = Rule.Load(xrule, grid, grid);\n            if (rule == null) return false;\n            rule.original = true;\n\n            string ruleSymmetryString = xrule.Get<string>(\"symmetry\", null);\n            bool[] ruleSymmetry = SymmetryHelper.GetSymmetry(grid.MZ == 1, ruleSymmetryString, symmetry);\n            if (ruleSymmetry == null)\n            {\n                Interpreter.WriteLine($\"unknown symmetry {ruleSymmetryString} at line {xrule.LineNumber()}\");\n                return false;\n            }\n            foreach (Rule r in rule.Symmetries(ruleSymmetry, grid.MZ == 1)) ruleList.Add(r);\n        }\n        rules = ruleList.ToArray();\n        last = new bool[rules.Length];\n\n        steps = xelem.Get(\"steps\", 0);\n\n        temperature = xelem.Get(\"temperature\", 0.0);\n        var xfields = xelem.Elements(\"field\");\n        if (xfields.Any())\n        {\n            fields = new Field[grid.C];\n            foreach (XElement xfield in xfields)\n            {\n                char c = xfield.Get<char>(\"for\");\n                if (grid.values.TryGetValue(c, out byte value)) fields[value] = new Field(xfield, grid);\n                else\n                {\n                    Interpreter.WriteLine($\"unknown field value {c} at line {xfield.LineNumber()}\");\n                    return false;\n                }\n            }\n            potentials = AH.Array2D(grid.C, grid.state.Length, 0);\n        }\n\n        var xobservations = xelem.Elements(\"observe\");\n        if (xobservations.Any())\n        {\n            observations = new Observation[grid.C];\n            foreach (var x in xobservations)\n            {\n                byte value = grid.values[x.Get<char>(\"value\")];\n                observations[value] = new Observation(x.Get(\"from\", grid.characters[value]), x.Get<string>(\"to\"), grid);\n            }\n\n            search = xelem.Get(\"search\", false);\n            if (search)\n            {\n                limit = xelem.Get(\"limit\", -1);\n                depthCoefficient = xelem.Get(\"depthCoefficient\", 0.5);\n            }\n            else potentials = AH.Array2D(grid.C, grid.state.Length, 0);\n            future = new int[grid.state.Length];\n        }\n\n        return true;\n    }\n\n    override public void Reset()\n    {\n        lastMatchedTurn = -1;\n        counter = 0;\n        futureComputed = false;\n\n        for (int r = 0; r < last.Length; r++) last[r] = false;\n    }\n\n    protected virtual void Add(int r, int x, int y, int z, bool[] maskr)\n    {\n        maskr[x + y * grid.MX + z * grid.MX * grid.MY] = true;\n\n        var match = (r, x, y, z);\n        if (matchCount < matches.Count) matches[matchCount] = match;\n        else matches.Add(match);\n        matchCount++;\n    }\n\n    public override bool Go()\n    {\n        for (int r = 0; r < last.Length; r++) last[r] = false;\n\n        if (steps > 0 && counter >= steps) return false; //есть вариант вернуть false на том же ходу, на котором мы достигли предела\n\n        int MX = grid.MX, MY = grid.MY, MZ = grid.MZ;\n        if (observations != null && !futureComputed)\n        {\n            if (!Observation.ComputeFutureSetPresent(future, grid.state, observations)) return false;\n            else\n            {\n                futureComputed = true;\n                if (search)\n                {\n                    trajectory = null;\n                    int TRIES = limit < 0 ? 1 : 20;\n                    for (int k = 0; k < TRIES && trajectory == null; k++) trajectory = Search.Run(grid.state, future, rules, grid.MX, grid.MY, grid.MZ, grid.C, this is AllNode, limit, depthCoefficient, ip.random.Next());\n                    if (trajectory == null) Console.WriteLine(\"SEARCH RETURNED NULL\");\n                }\n                else Observation.ComputeBackwardPotentials(potentials, future, MX, MY, MZ, rules);\n            }\n        }\n\n        if (lastMatchedTurn >= 0)\n        {\n            for (int n = ip.first[lastMatchedTurn]; n < ip.changes.Count; n++)\n            {\n                var (x, y, z) = ip.changes[n];\n                byte value = grid.state[x + y * MX + z * MX * MY];\n                for (int r = 0; r < rules.Length; r++)\n                {\n                    Rule rule = rules[r];\n                    bool[] maskr = matchMask[r];\n                    (int x, int y, int z)[] shifts = rule.ishifts[value];\n                    for (int l = 0; l < shifts.Length; l++)\n                    {\n                        var (shiftx, shifty, shiftz) = shifts[l];\n                        int sx = x - shiftx;\n                        int sy = y - shifty;\n                        int sz = z - shiftz;\n\n                        if (sx < 0 || sy < 0 || sz < 0 || sx + rule.IMX > MX || sy + rule.IMY > MY || sz + rule.IMZ > MZ) continue;\n                        int si = sx + sy * MX + sz * MX * MY;\n\n                        if (!maskr[si] && grid.Matches(rule, sx, sy, sz)) Add(r, sx, sy, sz, maskr);\n                    }\n                }\n            }\n        }\n        else\n        {\n            matchCount = 0;\n            for (int r = 0; r < rules.Length; r++)\n            {\n                Rule rule = rules[r];\n                bool[] maskr = matchMask?[r];\n                for (int z = rule.IMZ - 1; z < MZ; z += rule.IMZ)\n                    for (int y = rule.IMY - 1; y < MY; y += rule.IMY)\n                        for (int x = rule.IMX - 1; x < MX; x += rule.IMX)\n                        {\n                            var shifts = rule.ishifts[grid.state[x + y * MX + z * MX * MY]];\n                            for (int l = 0; l < shifts.Length; l++)\n                            {\n                                var (shiftx, shifty, shiftz) = shifts[l];\n                                int sx = x - shiftx;\n                                int sy = y - shifty;\n                                int sz = z - shiftz;\n                                if (sx < 0 || sy < 0 || sz < 0 || sx + rule.IMX > MX || sy + rule.IMY > MY || sz + rule.IMZ > MZ) continue;\n\n                                if (grid.Matches(rule, sx, sy, sz)) Add(r, sx, sy, sz, maskr);\n                            }\n                        }\n            }\n        }\n\n        if (fields != null)\n        {\n            bool anysuccess = false, anycomputation = false;\n            for (int c = 0; c < fields.Length; c++)\n            {\n                Field field = fields[c];\n                if (field != null && (counter == 0 || field.recompute))\n                {\n                    bool success = field.Compute(potentials[c], grid);\n                    if (!success && field.essential) return false;\n                    anysuccess |= success;\n                    anycomputation = true;\n                }\n            }\n            if (anycomputation && !anysuccess) return false;\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "source/Search.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Collections.Generic;\n\nstatic class Search\n{\n    public static byte[][] Run(byte[] present, int[] future, Rule[] rules, int MX, int MY, int MZ, int C, bool all, int limit, double depthCoefficient, int seed)\n    {\n        //Console.WriteLine(\"START SEARCH\");\n        //present.Print(MX, MY);\n        int[][] bpotentials = AH.Array2D(C, present.Length, -1);\n        int[][] fpotentials = AH.Array2D(C, present.Length, -1);\n\n        Observation.ComputeBackwardPotentials(bpotentials, future, MX, MY, MZ, rules);\n        int rootBackwardEstimate = Observation.BackwardPointwise(bpotentials, present);\n        Observation.ComputeForwardPotentials(fpotentials, present, MX, MY, MZ, rules);\n        int rootForwardEstimate = Observation.ForwardPointwise(fpotentials, future);\n\n        if (rootBackwardEstimate < 0 || rootForwardEstimate < 0)\n        {\n            Console.WriteLine(\"INCORRECT PROBLEM\");\n            return null;\n        }\n        Console.WriteLine($\"root estimate = ({rootBackwardEstimate}, {rootForwardEstimate})\");\n        if (rootBackwardEstimate == 0) return Array.Empty<byte[]>();\n        Board rootBoard = new(present, -1, 0, rootBackwardEstimate, rootForwardEstimate);\n\n        List<Board> database = new();\n        database.Add(rootBoard);\n        Dictionary<byte[], int> visited = new(new StateComparer());\n        visited.Add(present, 0);\n\n        PriorityQueue<int, double> frontier = new();\n        Random random = new(seed);\n        frontier.Enqueue(0, rootBoard.Rank(random, depthCoefficient));\n        int frontierLength = 1;\n\n        int record = rootBackwardEstimate + rootForwardEstimate;\n        while (frontierLength > 0 && (limit < 0 || database.Count < limit))\n        {\n            int parentIndex = frontier.Dequeue();\n            frontierLength--;\n            Board parentBoard = database[parentIndex];\n            //Console.WriteLine(\"-----------------------------------------------------------------------------------\");\n            //Console.WriteLine($\"extracting board at depth {parentBoard.depth} and estimate ({parentBoard.backwardEstimate}, {parentBoard.forwardEstimate}):\");\n            //parentBoard.state.Print(MX, MY);\n\n            var children = all ? parentBoard.state.AllChildStates(MX, MY, rules) : parentBoard.state.OneChildStates(MX, MY, rules);\n            //Console.WriteLine($\"this board has {children.Length} children\");\n            foreach (var childState in children)\n            //for (int c = 0; c < children.Length; c++)\n            {\n                //byte[] childState = children[c];\n                bool success = visited.TryGetValue(childState, out int childIndex);\n                if (success)\n                {\n                    Board oldBoard = database[childIndex];\n                    if (parentBoard.depth + 1 < oldBoard.depth)\n                    {\n                        //Console.WriteLine($\"found a shorter {parentBoard.depth + 1}-route to an existing {oldBoard.depth}-board of estimate ({oldBoard.backwardEstimate}, {oldBoard.forwardEstimate})\");\n                        oldBoard.depth = parentBoard.depth + 1;\n                        oldBoard.parentIndex = parentIndex;\n\n                        if (oldBoard.backwardEstimate >= 0 && oldBoard.forwardEstimate >= 0)\n                        {\n                            frontier.Enqueue(childIndex, oldBoard.Rank(random, depthCoefficient));\n                            frontierLength++;\n                        }\n                    }\n                    //else Console.WriteLine($\"found a longer {parentBoard.depth + 1}-route to an existing {oldBoard.depth}-board of estimate ({oldBoard.backwardEstimate}, {oldBoard.forwardEstimate})\");\n                }\n                else\n                {\n                    int childBackwardEstimate = Observation.BackwardPointwise(bpotentials, childState);\n                    Observation.ComputeForwardPotentials(fpotentials, childState, MX, MY, MZ, rules);\n                    int childForwardEstimate = Observation.ForwardPointwise(fpotentials, future);\n\n                    //Console.WriteLine($\"child {c} has estimate ({childBackwardEstimate}, {childForwardEstimate}):\");\n                    //childState.Print(MX, MY);\n                    if (childBackwardEstimate < 0 || childForwardEstimate < 0) continue;\n\n                    Board childBoard = new(childState, parentIndex, parentBoard.depth + 1, childBackwardEstimate, childForwardEstimate);\n                    database.Add(childBoard);\n                    childIndex = database.Count - 1;\n                    visited.Add(childBoard.state, childIndex);\n\n                    if (childBoard.forwardEstimate == 0)\n                    {\n                        Console.WriteLine($\"found a trajectory of length {parentBoard.depth + 1}, visited {visited.Count} states\");\n                        List<Board> trajectory = Board.Trajectory(childIndex, database);\n                        trajectory.Reverse();\n                        return trajectory.Select(b => b.state).ToArray();\n                    }\n                    else\n                    {\n                        if (limit < 0 && childBackwardEstimate + childForwardEstimate <= record)\n                        {\n                            record = childBackwardEstimate + childForwardEstimate;\n                            Console.WriteLine($\"found a state of record estimate {record} = {childBackwardEstimate} + {childForwardEstimate}\");\n                            childState.Print(MX, MY);\n                        }\n                        frontier.Enqueue(childIndex, childBoard.Rank(random, depthCoefficient));\n                        frontierLength++;\n                    }\n                }\n            }\n        }\n\n        return null;\n    }\n\n    static List<byte[]> OneChildStates(this byte[] state, int MX, int MY, Rule[] rules)\n    {\n        List<byte[]> result = new();\n        foreach (Rule rule in rules)\n            for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++)\n                    if (Matches(rule, x, y, state, MX, MY)) result.Add(Applied(rule, x, y, state, MX));\n        return result;\n    }\n\n    static bool Matches(this Rule rule, int x, int y, byte[] state, int MX, int MY)\n    {\n        if (x + rule.IMX > MX || y + rule.IMY > MY) return false;\n\n        int dy = 0, dx = 0;\n        for (int di = 0; di < rule.input.Length; di++) //попробовать binput, но в этот раз здесь тоже заменить!\n        {\n            if ((rule.input[di] & (1 << state[x + dx + (y + dy) * MX])) == 0) return false;\n            dx++;\n            if (dx == rule.IMX) { dx = 0; dy++; }\n        }\n        return true;\n    }\n\n    static byte[] Applied(Rule rule, int x, int y, byte[] state, int MX)\n    {\n        byte[] result = new byte[state.Length];\n        Array.Copy(state, result, state.Length);\n        for (int dz = 0; dz < rule.OMZ; dz++) for (int dy = 0; dy < rule.OMY; dy++) for (int dx = 0; dx < rule.OMX; dx++)\n                {\n                    byte newValue = rule.output[dx + dy * rule.OMX + dz * rule.OMX * rule.OMY];\n                    if (newValue != 0xff) result[x + dx + (y + dy) * MX] = newValue;\n                }\n        return result;\n    }\n\n    static void Print(this byte[] state, int MX, int MY)\n    {\n        char[] characters = new[] { '.', 'R', 'W', '#', 'a', '!', '?', '%', '0', '1', '2', '3', '4', '5' };\n        for (int y = 0; y < MY; y++)\n        {\n            for (int x = 0; x < MX; x++) Console.Write($\"{characters[state[x + y * MX]]} \");\n            Console.WriteLine();\n        }\n    }\n\n    public static bool IsInside(this (int x, int y) p, Rule rule, int x, int y) =>\n        x <= p.x && p.x < x + rule.IMX && y <= p.y && p.y < y + rule.IMY;\n    public static bool Overlap(Rule rule0, int x0, int y0, Rule rule1, int x1, int y1)\n    {\n        for (int dy = 0; dy < rule0.IMY; dy++) for (int dx = 0; dx < rule0.IMX; dx++)\n                if ((x0 + dx, y0 + dy).IsInside(rule1, x1, y1)) return true;\n        return false;\n    }\n\n    public static List<byte[]> AllChildStates(this byte[] state, int MX, int MY, Rule[] rules)\n    {\n        var list = new List<(Rule, int)>();\n        int[] amounts = new int[state.Length];\n        for (int i = 0; i < state.Length; i++)\n        {\n            int x = i % MX, y = i / MX;\n            for (int r = 0; r < rules.Length; r++)\n            {\n                Rule rule = rules[r];\n                if (rule.Matches(x, y, state, MX, MY))\n                {\n                    list.Add((rule, i));\n                    for (int dy = 0; dy < rule.IMY; dy++) for (int dx = 0; dx < rule.IMX; dx++) amounts[x + dx + (y + dy) * MX]++;\n                }\n            }\n        }\n        (Rule, int)[] tiles = list.ToArray();\n        bool[] mask = AH.Array1D(tiles.Length, true);\n        List<(Rule, int)> solution = new();\n\n        List<byte[]> result = new();\n        Enumerate(result, solution, tiles, amounts, mask, state, MX);\n        return result;\n    }\n\n    static void Enumerate(List<byte[]> children, List<(Rule, int)> solution, (Rule, int)[] tiles, int[] amounts, bool[] mask, byte[] state, int MX)\n    {\n        int I = amounts.MaxPositiveIndex();\n        int X = I % MX, Y = I / MX;\n        if (I < 0)\n        {\n            children.Add(state.Apply(solution, MX));\n            return;\n        }\n\n        List<(Rule, int)> cover = new();\n        for (int l = 0; l < tiles.Length; l++)\n        {\n            var (rule, i) = tiles[l];\n            if (mask[l] && (X, Y).IsInside(rule, i % MX, i / MX)) cover.Add((rule, i));\n        }\n\n        foreach (var (rule, i) in cover)\n        {\n            solution.Add((rule, i));\n\n            List<int> intersecting = new();\n            for (int l = 0; l < tiles.Length; l++) if (mask[l])\n                {\n                    var (rule1, i1) = tiles[l];\n                    if (Overlap(rule, i % MX, i / MX, rule1, i1 % MX, i1 / MX)) intersecting.Add(l);\n                }\n\n            foreach (int l in intersecting) Hide(l, false, tiles, amounts, mask, MX);\n            Enumerate(children, solution, tiles, amounts, mask, state, MX);\n            foreach (int l in intersecting) Hide(l, true, tiles, amounts, mask, MX);\n\n            solution.RemoveAt(solution.Count - 1);\n        }\n    }\n\n    static void Hide(int l, bool unhide, (Rule, int)[] tiles, int[] amounts, bool[] mask, int MX)\n    {\n        mask[l] = unhide;\n        var (rule, i) = tiles[l];\n        int x = i % MX, y = i / MX;\n        int incr = unhide ? 1 : -1;\n        for (int dy = 0; dy < rule.IMY; dy++) for (int dx = 0; dx < rule.IMX; dx++) amounts[x + dx + (y + dy) * MX] += incr;\n    }\n\n    static void Apply(this Rule rule, int x, int y, byte[] state, int MX)\n    {\n        for (int dy = 0; dy < rule.OMY; dy++) for (int dx = 0; dx < rule.OMX; dx++) state[x + dx + (y + dy) * MX] = rule.output[dx + dy * rule.OMX];\n    }\n    static byte[] Apply(this byte[] state, List<(Rule, int)> solution, int MX)\n    {\n        byte[] result = new byte[state.Length];\n        Array.Copy(state, result, state.Length);\n        foreach (var (rule, i) in solution) Apply(rule, i % MX, i / MX, result, MX);\n        return result;\n    }\n}\n\nclass Board\n{\n    public byte[] state;\n    public int parentIndex, depth, backwardEstimate, forwardEstimate;\n\n    public Board(byte[] state, int parentIndex, int depth, int backwardEstimate, int forwardEstimate)\n    {\n        this.state = state;\n        this.parentIndex = parentIndex;\n        this.depth = depth;\n        this.backwardEstimate = backwardEstimate;\n        this.forwardEstimate = forwardEstimate;\n    }\n\n    public double Rank(Random random, double depthCoefficient)\n    {\n        double result = depthCoefficient < 0.0 ? 1000 - depth : forwardEstimate + backwardEstimate + 2.0 * depthCoefficient * depth;\n        return result + 0.0001 * random.NextDouble();\n    }\n\n    public static List<Board> Trajectory(int index, List<Board> database)\n    {\n        List<Board> result = new();\n        for (Board board = database[index]; board.parentIndex >= 0; board = database[board.parentIndex]) result.Add(board);\n        return result;\n    }\n}\n\nclass StateComparer : IEqualityComparer<byte[]>\n{\n    public bool Equals(byte[] a, byte[] b)\n    {\n        for (int i = 0; i < a.Length; i++) if (a[i] != b[i]) return false;\n        return true;\n    }\n\n    public int GetHashCode(byte[] a)\n    {\n        int result = 17;\n        for (int i = 0; i < a.Length; i++) unchecked { result = result * 29 + a[i]; }\n        return result;\n    }\n}\n"
  },
  {
    "path": "source/SymmetryHelper.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Collections.Generic;\n\nstatic class SymmetryHelper\n{\n    public static Dictionary<string, bool[]> squareSubgroups = new()\n    {\n        [\"()\"] = new bool[8] { true, false, false, false, false, false, false, false },\n        [\"(x)\"] = new bool[8] { true, true, false, false, false, false, false, false },\n        [\"(y)\"] = new bool[8] { true, false, false, false, false, true, false, false },\n        [\"(x)(y)\"] = new bool[8] { true, true, false, false, true, true, false, false },\n        [\"(xy+)\"] = new bool[8] { true, false, true, false, true, false, true, false },\n        [\"(xy)\"] = new bool[8] { true, true, true, true, true, true, true, true }\n    };\n\n    public static IEnumerable<T> SquareSymmetries<T>(T thing, Func<T, T> rotation, Func<T, T> reflection, Func<T, T, bool> same, bool[] subgroup = null)\n    {\n        T[] things = new T[8];\n\n        things[0] = thing;                  // e\n        things[1] = reflection(things[0]);  // b\n        things[2] = rotation(things[0]);    // a\n        things[3] = reflection(things[2]);  // ba\n        things[4] = rotation(things[2]);    // a2\n        things[5] = reflection(things[4]);  // ba2\n        things[6] = rotation(things[4]);    // a3\n        things[7] = reflection(things[6]);  // ba3\n\n        List<T> result = new();\n        for (int i = 0; i < 8; i++) if ((subgroup == null || subgroup[i]) && !result.Where(s => same(s, things[i])).Any()) result.Add(things[i]);\n        return result;\n    }\n\n    public static Dictionary<string, bool[]> cubeSubgroups = new()\n    {\n        [\"()\"] = AH.Array1D(48, l => l == 0),\n        [\"(x)\"] = AH.Array1D(48, l => l == 0 || l == 1),\n        [\"(z)\"] = AH.Array1D(48, l => l == 0 || l == 17),\n        [\"(xy)\"] = AH.Array1D(48, l => l < 8),\n        [\"(xyz+)\"] = AH.Array1D(48, l => l % 2 == 0),\n        [\"(xyz)\"] = AH.Array1D(48, true),\n        //[\"(xy)(z)\"] = AH.Array1D(48, l => l < 8 || l == 17 || ...),\n    };\n\n    public static IEnumerable<T> CubeSymmetries<T>(T thing, Func<T, T> a, Func<T, T> b, Func<T, T> r, Func<T, T, bool> same, bool[] subgroup = null)\n    {\n        T[] s = new T[48];\n\n        s[0] = thing;        // e\n        s[1] = r(s[0]);\n        s[2] = a(s[0]);      // a\n        s[3] = r(s[2]);\n        s[4] = a(s[2]);      // a2\n        s[5] = r(s[4]);\n        s[6] = a(s[4]);      // a3\n        s[7] = r(s[6]);\n        s[8] = b(s[0]);      // b\n        s[9] = r(s[8]);\n        s[10] = b(s[2]);     // b a\n        s[11] = r(s[10]);\n        s[12] = b(s[4]);     // b a2\n        s[13] = r(s[12]);\n        s[14] = b(s[6]);     // b a3\n        s[15] = r(s[14]);\n        s[16] = b(s[8]);     // b2\n        s[17] = r(s[16]);\n        s[18] = b(s[10]);    // b2 a\n        s[19] = r(s[18]);\n        s[20] = b(s[12]);    // b2 a2\n        s[21] = r(s[20]);\n        s[22] = b(s[14]);    // b2 a3\n        s[23] = r(s[22]);\n        s[24] = b(s[16]);    // b3\n        s[25] = r(s[24]);\n        s[26] = b(s[18]);    // b3 a\n        s[27] = r(s[26]);\n        s[28] = b(s[20]);    // b3 a2\n        s[29] = r(s[28]);\n        s[30] = b(s[22]);    // b3 a3\n        s[31] = r(s[30]);\n        s[32] = a(s[8]);     // a b\n        s[33] = r(s[32]);\n        s[34] = a(s[10]);    // a b a\n        s[35] = r(s[34]);\n        s[36] = a(s[12]);    // a b a2\n        s[37] = r(s[36]);\n        s[38] = a(s[14]);    // a b a3\n        s[39] = r(s[38]);\n        s[40] = a(s[24]);    // a3 b a2 = a b3\n        s[41] = r(s[40]);\n        s[42] = a(s[26]);    // a3 b a3 = a b3 a\n        s[43] = r(s[42]);\n        s[44] = a(s[28]);    // a3 b = a b3 a2\n        s[45] = r(s[44]);\n        s[46] = a(s[30]);    // a3 b a = a b3 a3\n        s[47] = r(s[46]);\n\n        List<T> result = new();\n        for (int i = 0; i < 48; i++) if ((subgroup == null || subgroup[i]) && !result.Where(t => same(t, s[i])).Any()) result.Add(s[i]);\n        return result;\n    }\n\n    public static bool[] GetSymmetry(bool d2, string s, bool[] dflt)\n    {\n        if (s == null) return dflt;\n        bool success = d2 ? squareSubgroups.TryGetValue(s, out bool[] result) : cubeSubgroups.TryGetValue(s, out result);\n        return success ? result : null;\n    }\n}\n"
  },
  {
    "path": "source/TileModel.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nclass TileNode : WFCNode\n{\n    List<byte[]> tiledata;\n\n    int S, SZ;\n    int overlap, overlapz;\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        periodic = xelem.Get(\"periodic\", false);\n        /*string*/ name = xelem.Get<string>(\"tileset\");\n        string tilesname = xelem.Get(\"tiles\", name);\n        overlap = xelem.Get(\"overlap\", 0);\n        overlapz = xelem.Get(\"overlapz\", 0);\n\n        XDocument xdoc;\n        string filepath = $\"resources/tilesets/{name}.xml\";\n        try { xdoc = XDocument.Load(filepath, LoadOptions.SetLineInfo); }\n        catch (Exception)\n        {\n            Interpreter.WriteLine($\"couldn't open tileset {filepath}\");\n            return false;\n        }\n        XElement xroot = xdoc.Root;\n\n        bool fullSymmetry = xroot.Get(\"fullSymmetry\", false);\n        XElement xfirsttile = xroot.Element(\"tiles\").Element(\"tile\");\n        string firstFileName = $\"{tilesname}/{xfirsttile.Get<string>(\"name\")}.vox\";\n        int[] firstData;\n        int SY;\n        (firstData, S, SY, SZ) = VoxHelper.LoadVox($\"resources/tilesets/{firstFileName}\");\n        if (firstData == null)\n        {\n            Interpreter.WriteLine($\"couldn't read {firstFileName}\");\n            return false;\n        }\n        if (S != SY)\n        {\n            Interpreter.WriteLine($\"tiles should be square shaped: {S} != {SY}\");\n            return false;\n        }\n        if (fullSymmetry && S != SZ)\n        {\n            Interpreter.WriteLine($\"tiles should be cubes for the full symmetry option: {S} != {SZ}\");\n            return false;\n        }\n\n        newgrid = Grid.Load(xelem, (S - overlap) * grid.MX + overlap, (S - overlap) * grid.MY + overlap, (SZ - overlapz) * grid.MZ + overlapz);\n        if (newgrid == null) return false;\n\n        tiledata = new List<byte[]>();\n        Dictionary<string, bool[]> positions = new();\n        byte[] newtile(Func<int, int, int, byte> f) => AH.FlatArray3D(S, S, SZ, f);\n\n        byte[] zRotate(byte[] p) => newtile((x, y, z) => p[y + (S - 1 - x) * S + z * S * S]);\n        byte[] yRotate(byte[] p) => newtile((x, y, z) => p[z + y * S + (S - 1 - x) * S * S]);\n        byte[] xRotate(byte[] p) => newtile((x, y, z) => p[x + z * S + (S - 1 - y) * S * S]);\n        byte[] xReflect(byte[] p) => newtile((x, y, z) => p[(S - 1 - x) + y * S + z * S * S]);\n        byte[] yReflect(byte[] p) => newtile((x, y, z) => p[x + (S - 1 - y) * S + z * S * S]);\n        byte[] zReflect(byte[] p) => newtile((x, y, z) => p[x + y * S + (S - 1 - z) * S * S]);\n\n        var namedTileData = new Dictionary<string, List<byte[]>>();\n        var tempStationary = new List<double>();\n\n        var uniques = new List<int>();\n        var xtiles = xroot.Element(\"tiles\").Elements(\"tile\");\n        int ind = 0;\n        foreach (XElement xtile in xtiles)\n        {\n            string tilename = xtile.Get<string>(\"name\");\n            double weight = xtile.Get(\"weight\", 1.0);\n\n            string filename = $\"resources/tilesets/{tilesname}/{tilename}.vox\";\n            int[] vox = VoxHelper.LoadVox(filename).Item1;\n            if (vox == null)\n            {\n                Interpreter.WriteLine($\"couldn't read tile {filename}\");\n                return false;\n            }\n            (byte[] flatTile, int C) = vox.Ords(uniques);\n            if (C > newgrid.C)\n            {\n                Interpreter.WriteLine($\"there were more than {newgrid.C} colors in vox files\");\n                return false;\n            }\n\n            List<byte[]> localdata = fullSymmetry ? SymmetryHelper.CubeSymmetries(flatTile, zRotate, yRotate, xReflect, AH.Same).ToList()\n                : SymmetryHelper.SquareSymmetries(flatTile, zRotate, xReflect, AH.Same).ToList();\n\n            bool[] position = new bool[128];\n            namedTileData.Add(tilename, localdata);\n            foreach (byte[] p in localdata)\n            {\n                tiledata.Add(p);\n                tempStationary.Add(weight);\n                position[ind] = true;\n                ind++;\n            }\n            positions.Add(tilename, position);\n        }\n\n        P = tiledata.Count;\n        Console.WriteLine($\"P = {P}\");\n        weights = tempStationary.ToArray();\n\n        map = new Dictionary<byte, bool[]>();\n        foreach (XElement xrule in xelem.Elements(\"rule\"))\n        {\n            char input = xrule.Get<char>(\"in\");\n            string[] outputs = xrule.Get<string>(\"out\").Split('|');\n            bool[] position = new bool[P];\n            foreach (string s in outputs)\n            {\n                bool success = positions.TryGetValue(s, out bool[] array);\n                if (!success)\n                {\n                    Interpreter.WriteLine($\"unknown tilename {s} at line {xrule.LineNumber()}\");\n                    return false;\n                }\n                for (int p = 0; p < P; p++) if (array[p]) position[p] = true;\n            }\n            map.Add(grid.values[input], position);\n        }\n        if (!map.ContainsKey(0)) map.Add(0, AH.Array1D(P, true));\n\n        bool[][][] tempPropagator = AH.Array3D(6, P, P, false);\n\n        int index(byte[] p)\n        {\n            for (int i = 0; i < tiledata.Count; i++) if (AH.Same(p, tiledata[i])) return i;\n            return -1;\n        };\n\n        static string last(string attribute) => attribute?.Split(' ').Last();\n        byte[] tile(string attribute)\n        {\n            string[] code = attribute.Split(' ');\n            string action = code.Length == 2 ? code[0] : \"\";\n            byte[] starttile = namedTileData[last(attribute)][0];\n            for (int i = action.Length - 1; i >= 0; i--)\n            {\n                char sym = action[i];\n                if (sym == 'x') starttile = xRotate(starttile);\n                else if (sym == 'y') starttile = yRotate(starttile);\n                else if (sym == 'z') starttile = zRotate(starttile);\n                else\n                {\n                    Interpreter.WriteLine($\"unknown symmetry {sym}\");\n                    return null;\n                }\n            }\n            return starttile;\n        };\n\n        List<string> tilenames = xtiles.Select(x => x.Get<string>(\"name\")).ToList();\n        tilenames.Add(null);\n\n        foreach (XElement xneighbor in xroot.Element(\"neighbors\").Elements(\"neighbor\"))\n        {\n            if (fullSymmetry)\n            {\n                string left = xneighbor.Get<string>(\"left\"), right = xneighbor.Get<string>(\"right\");\n                if (!tilenames.Contains(last(left)) || !tilenames.Contains(last(right)))\n                {\n                    Interpreter.WriteLine($\"unknown tile {last(left)} or {last(right)} at line {xneighbor.LineNumber()}\");\n                    return false;\n                }\n\n                byte[] ltile = tile(left), rtile = tile(right);\n                if (ltile == null || rtile == null) return false;\n\n                var lsym = SymmetryHelper.SquareSymmetries(ltile, xRotate, yReflect, (p1, p2) => false).ToArray();\n                var rsym = SymmetryHelper.SquareSymmetries(rtile, xRotate, yReflect, (p1, p2) => false).ToArray();\n\n                for (int i = 0; i < lsym.Length; i++)\n                {\n                    tempPropagator[0][index(lsym[i])][index(rsym[i])] = true;\n                    tempPropagator[0][index(xReflect(rsym[i]))][index(xReflect(lsym[i]))] = true;\n                }\n\n                byte[] dtile = zRotate(ltile);\n                byte[] utile = zRotate(rtile);\n\n                var dsym = SymmetryHelper.SquareSymmetries(dtile, yRotate, zReflect, (p1, p2) => false).ToArray();\n                var usym = SymmetryHelper.SquareSymmetries(utile, yRotate, zReflect, (p1, p2) => false).ToArray();\n\n                for (int i = 0; i < dsym.Length; i++)\n                {\n                    tempPropagator[1][index(dsym[i])][index(usym[i])] = true;\n                    tempPropagator[1][index(yReflect(usym[i]))][index(yReflect(dsym[i]))] = true;\n                }\n\n                byte[] btile = yRotate(ltile);\n                byte[] ttile = yRotate(rtile);\n\n                var bsym = SymmetryHelper.SquareSymmetries(btile, zRotate, xReflect, (p1, p2) => false).ToArray();\n                var tsym = SymmetryHelper.SquareSymmetries(ttile, zRotate, xReflect, (p1, p2) => false).ToArray();\n\n                for (int i = 0; i < bsym.Length; i++)\n                {\n                    tempPropagator[4][index(bsym[i])][index(tsym[i])] = true;\n                    tempPropagator[4][index(zReflect(tsym[i]))][index(zReflect(bsym[i]))] = true;\n                }\n            }\n            else if (xneighbor.Get<string>(\"left\", null) != null)\n            {\n                string left = xneighbor.Get<string>(\"left\"), right = xneighbor.Get<string>(\"right\");\n                if (!tilenames.Contains(last(left)) || !tilenames.Contains(last(right)))\n                {\n                    Interpreter.WriteLine($\"unknown tile {last(left)} or {last(right)} at line {xneighbor.LineNumber()}\");\n                    return false;\n                }\n\n                byte[] ltile = tile(left), rtile = tile(right);\n                if (ltile == null || rtile == null) return false;\n\n                tempPropagator[0][index(ltile)][index(rtile)] = true;\n                tempPropagator[0][index(yReflect(ltile))][index(yReflect(rtile))] = true;\n                tempPropagator[0][index(xReflect(rtile))][index(xReflect(ltile))] = true;\n                tempPropagator[0][index(yReflect(xReflect(rtile)))][index(yReflect(xReflect(ltile)))] = true;\n\n                byte[] dtile = zRotate(ltile);\n                byte[] utile = zRotate(rtile);\n\n                tempPropagator[1][index(dtile)][index(utile)] = true;\n                tempPropagator[1][index(xReflect(dtile))][index(xReflect(utile))] = true;\n                tempPropagator[1][index(yReflect(utile))][index(yReflect(dtile))] = true;\n                tempPropagator[1][index(xReflect(yReflect(utile)))][index(xReflect(yReflect(dtile)))] = true;\n            }\n            else\n            {\n                string top = xneighbor.Get<string>(\"top\", null), bottom = xneighbor.Get<string>(\"bottom\", null);\n                if (!tilenames.Contains(last(top)) || !tilenames.Contains(last(bottom)))\n                {\n                    Interpreter.WriteLine($\"unknown tile {last(top)} or {last(bottom)} at line {xneighbor.LineNumber()}\");\n                    return false;\n                }\n\n                byte[] ttile = tile(top), btile = tile(bottom);\n                if (ttile == null || btile == null) return false;\n\n                var tsym = SymmetryHelper.SquareSymmetries(ttile, zRotate, xReflect, (p1, p2) => false).ToArray();\n                var bsym = SymmetryHelper.SquareSymmetries(btile, zRotate, xReflect, (p1, p2) => false).ToArray();\n\n                for (int i = 0; i < tsym.Length; i++) tempPropagator[4][index(bsym[i])][index(tsym[i])] = true;\n            }\n        }\n\n        for (int p2 = 0; p2 < P; p2++) for (int p1 = 0; p1 < P; p1++)\n            {\n                tempPropagator[2][p2][p1] = tempPropagator[0][p1][p2];\n                tempPropagator[3][p2][p1] = tempPropagator[1][p1][p2];\n                tempPropagator[5][p2][p1] = tempPropagator[4][p1][p2];\n            }\n\n        List<int>[][] sparsePropagator = new List<int>[6][];\n        for (int d = 0; d < 6; d++)\n        {\n            sparsePropagator[d] = new List<int>[P];\n            for (int t = 0; t < P; t++) sparsePropagator[d][t] = new List<int>();\n        }\n\n        propagator = new int[6][][];\n        for (int d = 0; d < 6; d++)\n        {\n            propagator[d] = new int[P][];\n            for (int p1 = 0; p1 < P; p1++)\n            {\n                List<int> sp = sparsePropagator[d][p1];\n                bool[] tp = tempPropagator[d][p1];\n\n                for (int p2 = 0; p2 < P; p2++) if (tp[p2]) sp.Add(p2);\n\n                int ST = sp.Count;\n                propagator[d][p1] = new int[ST];\n                for (int st = 0; st < ST; st++) propagator[d][p1][st] = sp[st];\n            }\n        }\n\n        return base.Load(xelem, parentSymmetry, grid);\n    }\n\n    protected override void UpdateState()\n    {\n        Random r = new();\n        for (int z = 0; z < grid.MZ; z++) for (int y = 0; y < grid.MY; y++) for (int x = 0; x < grid.MX; x++)\n                {\n                    bool[] w = wave.data[x + y * grid.MX + z * grid.MX * grid.MY];\n                    int[][] votes = AH.Array2D(S * S * SZ, newgrid.C, 0);\n\n                    for (int t = 0; t < P; t++) if (w[t])\n                        {\n                            byte[] tile = tiledata[t];\n                            for (int dz = 0; dz < SZ; dz++) for (int dy = 0; dy < S; dy++) for (int dx = 0; dx < S; dx++)\n                                    {\n                                        int di = dx + dy * S + dz * S * S;\n                                        votes[di][tile[di]]++;\n                                    }\n                        }\n\n                    for (int dz = 0; dz < SZ; dz++) for (int dy = 0; dy < S; dy++) for (int dx = 0; dx < S; dx++)\n                            {\n                                int[] v = votes[dx + dy * S + dz * S * S];\n                                double max = -1.0;\n                                //int max = -1;\n                                byte argmax = 0xff;\n                                for (byte c = 0; c < v.Length; c++)\n                                {\n                                    //int vote = v[c];\n                                    double vote = v[c] + 0.1 * r.NextDouble();\n                                    if (vote > max)\n                                    {\n                                        argmax = c;\n                                        max = vote;\n                                    }\n                                }\n                                int sx = x * (S - overlap) + dx;\n                                int sy = y * (S - overlap) + dy;\n                                int sz = z * (SZ - overlapz) + dz;\n                                newgrid.state[sx + sy * newgrid.MX + sz * newgrid.MX * newgrid.MY] = argmax;\n                            }\n                }\n    }\n}\n"
  },
  {
    "path": "source/VoxHelper.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.IO;\nusing System.Text;\nusing System.Collections.Generic;\n\nstatic class VoxHelper\n{\n    public static (int[], int, int, int) LoadVox(string filename)\n    {\n        try\n        {\n            using FileStream file = File.Open(filename, FileMode.Open);\n            var stream = new BinaryReader(file);\n\n            int[] result = null;\n            int MX = -1, MY = -1, MZ = -1;\n\n            string magic = new(stream.ReadChars(4));\n            int version = stream.ReadInt32();\n\n            while (stream.BaseStream.Position < stream.BaseStream.Length)\n            {\n                byte[] bt = stream.ReadBytes(1);\n                char head = Encoding.ASCII.GetChars(bt)[0];\n\n                if (head == 'S')\n                {\n                    string tail = Encoding.ASCII.GetString(stream.ReadBytes(3));\n                    if (tail != \"IZE\") continue;\n\n                    int chunkSize = stream.ReadInt32();\n                    stream.ReadBytes(4);\n                    //Console.WriteLine(\"found SIZE chunk\");\n                    MX = stream.ReadInt32();\n                    MY = stream.ReadInt32();\n                    MZ = stream.ReadInt32();\n                    stream.ReadBytes(chunkSize - 4 * 3);\n                    //Console.WriteLine($\"size = ({MX}, {MY}, {MZ})\");\n                }\n                else if (head == 'X')\n                {\n                    string tail = Encoding.ASCII.GetString(stream.ReadBytes(3));\n                    if (tail != \"YZI\") continue;\n\n                    if (MX <= 0 || MY <= 0 || MZ <= 0) return (null, MX, MY, MZ);\n                    result = new int[MX * MY * MZ];\n                    for (int i = 0; i < result.Length; i++) result[i] = -1;\n\n                    //Console.WriteLine(\"found XYZI chunk\");\n                    stream.ReadBytes(8);\n                    int numVoxels = stream.ReadInt32();\n                    //Console.WriteLine($\"number of voxels = {numVoxels}\");\n                    for (int i = 0; i < numVoxels; i++)\n                    {\n                        byte x = stream.ReadByte();\n                        byte y = stream.ReadByte();\n                        byte z = stream.ReadByte();\n                        byte color = stream.ReadByte();\n                        result[x + y * MX + z * MX * MY] = color;\n                        //Console.WriteLine($\"adding voxel {x} {y} {z} of color {color}\");\n                    }\n                }\n            }\n            file.Close();\n            return (result, MX, MY, MZ);\n        }\n        catch (Exception) { return (null, -1, -1, -1); }\n    }\n\n    static void WriteString(this BinaryWriter stream, string s) { foreach (char c in s) stream.Write(c); }\n    public static void SaveVox(byte[] state, byte MX, byte MY, byte MZ, int[] palette, string filename)\n    {\n        List<(byte, byte, byte, byte)> voxels = new();\n        for (byte z = 0; z < MZ; z++) for (byte y = 0; y < MY; y++) for (byte x = 0; x < MX; x++)\n                {\n                    int i = x + y * MX + z * MX * MY;\n                    byte v = state[i];\n                    if (v != 0) voxels.Add((x, y, z, (byte)(v + 1)));\n                }\n\n        FileStream file = File.Open(filename, FileMode.Create);\n        using BinaryWriter stream = new(file);\n\n        stream.WriteString(\"VOX \");\n        stream.Write(150);\n\n        stream.WriteString(\"MAIN\");\n        stream.Write(0);\n        stream.Write(1092 + voxels.Count * 4);\n\n        stream.WriteString(\"PACK\");\n        stream.Write(4);\n        stream.Write(0);\n        stream.Write(1);\n\n        stream.WriteString(\"SIZE\");\n        stream.Write(12);\n        stream.Write(0);\n        stream.Write((int)MX);\n        stream.Write((int)MY);\n        stream.Write((int)MZ);\n\n        stream.WriteString(\"XYZI\");\n        stream.Write(4 + voxels.Count * 4);\n        stream.Write(0);\n        stream.Write(voxels.Count);\n\n        foreach (var (x, y, z, color) in voxels)\n        {\n            stream.Write(x);\n            //stream.Write((byte)(size.y - v.y - 1));\n            stream.Write(y);\n            stream.Write(z);\n            stream.Write(color);\n        }\n\n        stream.WriteString(\"RGBA\");\n        stream.Write(1024);\n        stream.Write(0);\n\n        foreach (int c in palette)\n        {\n            //(byte R, byte G, byte B) = c.ToTuple();\n            stream.Write((byte)((c & 0xff0000) >> 16));\n            stream.Write((byte)((c & 0xff00) >> 8));\n            stream.Write((byte)(c & 0xff));\n            stream.Write((byte)0);\n        }\n        for (int i = palette.Length; i < 255; i++)\n        {\n            stream.Write((byte)(0xff - i - 1));\n            stream.Write((byte)(0xff - i - 1));\n            stream.Write((byte)(0xff - i - 1));\n            stream.Write((byte)(0xff));\n        }\n        stream.Write(0);\n        file.Close();\n    }\n}\n"
  },
  {
    "path": "source/WaveFunctionCollapse.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Xml.Linq;\nusing System.Collections.Generic;\n\nabstract class WFCNode : Branch\n{\n    protected Wave wave;\n    protected int[][][] propagator;\n    protected int P, N = 1;\n\n    (int, int)[] stack;\n    int stacksize;\n\n    protected double[] weights;\n    double[] weightLogWeights;\n    double sumOfWeights, sumOfWeightLogWeights, startingEntropy;\n\n    protected Grid newgrid;\n    Wave startwave;\n\n    protected Dictionary<byte, bool[]> map;\n    protected bool periodic, shannon;\n\n    double[] distribution;\n    int tries;\n\n    public string name;\n\n    override protected bool Load(XElement xelem, bool[] parentSymmetry, Grid grid)\n    {\n        shannon = xelem.Get(\"shannon\", false);\n        tries = xelem.Get(\"tries\", 1000);\n\n        wave = new Wave(grid.state.Length, P, propagator.Length, shannon);\n        startwave = new Wave(grid.state.Length, P, propagator.Length, shannon);\n        stack = new (int, int)[wave.data.Length * P];\n\n        sumOfWeights = sumOfWeightLogWeights = startingEntropy = 0;\n\n        if (shannon)\n        {\n            weightLogWeights = new double[P];\n\n            for (int t = 0; t < P; t++)\n            {\n                weightLogWeights[t] = weights[t] * Math.Log(weights[t]);\n                sumOfWeights += weights[t];\n                sumOfWeightLogWeights += weightLogWeights[t];\n            }\n\n            startingEntropy = Math.Log(sumOfWeights) - sumOfWeightLogWeights / sumOfWeights;\n        }\n\n        distribution = new double[P];\n        return base.Load(xelem, parentSymmetry, newgrid);\n    }\n\n    override public void Reset()\n    {\n        base.Reset();\n        n = -1;\n        firstgo = true;\n    }\n\n    bool firstgo = true;\n    Random random;\n    public override bool Go()\n    {\n        if (n >= 0) return base.Go();\n\n        if (firstgo)\n        {\n            wave.Init(propagator, sumOfWeights, sumOfWeightLogWeights, startingEntropy, shannon);\n\n            for (int i = 0; i < wave.data.Length; i++)\n            {\n                byte value = grid.state[i];\n                if (map.ContainsKey(value))\n                {\n                    bool[] startWave = map[value];\n                    for (int t = 0; t < P; t++) if (!startWave[t]) Ban(i, t);\n                }\n            }\n\n            bool firstSuccess = Propagate();\n            if (!firstSuccess)\n            {\n                Console.WriteLine(\"initial conditions are contradictive\");\n                return false;\n            }\n            startwave.CopyFrom(wave, propagator.Length, shannon);\n            int? goodseed = GoodSeed();\n            if (goodseed == null) return false;\n\n            random = new Random((int)goodseed);\n            stacksize = 0;\n            wave.CopyFrom(startwave, propagator.Length, shannon);\n            firstgo = false;\n\n            newgrid.Clear();\n            ip.grid = newgrid;\n            return true;\n        }\n        else\n        {\n            int node = NextUnobservedNode(random);\n            if (node >= 0)\n            {\n                Observe(node, random);\n                Propagate();\n            }\n            else n++;\n\n            if (n >= 0 || ip.gif) UpdateState();\n            return true;\n        }\n    }\n\n    int? GoodSeed()\n    {\n        for (int k = 0; k < tries; k++)\n        {\n            int observationsSoFar = 0;\n            int seed = ip.random.Next();\n            random = new Random(seed);\n            stacksize = 0;\n            wave.CopyFrom(startwave, propagator.Length, shannon);\n\n            while (true)\n            {\n                int node = NextUnobservedNode(random);\n                if (node >= 0)\n                {\n                    Observe(node, random);\n                    observationsSoFar++;\n                    bool success = Propagate();\n                    if (!success)\n                    {\n                        Console.WriteLine($\"CONTRADICTION on try {k} with {observationsSoFar} observations\");\n                        break;\n                    }\n                }\n                else\n                {\n                    Console.WriteLine($\"wfc found a good seed {seed} on try {k} with {observationsSoFar} observations\");\n                    return seed;\n                }\n            }\n        }\n\n        Console.WriteLine($\"wfc failed to find a good seed in {tries} tries\");\n        return null;\n    }\n\n    int NextUnobservedNode(Random random)\n    {\n        int MX = grid.MX, MY = grid.MY, MZ = grid.MZ;\n        double min = 1E+4;\n        int argmin = -1;\n        for (int z = 0; z < MZ; z++) for (int y = 0; y < MY; y++) for (int x = 0; x < MX; x++)\n                {\n                    if (!periodic && (x + N > MX || y + N > MY || z + 1 > MZ)) continue;\n                    int i = x + y * MX + z * MX * MY;\n                    int remainingValues = wave.sumsOfOnes[i];\n                    double entropy = shannon ? wave.entropies[i] : remainingValues;\n                    if (remainingValues > 1 && entropy <= min)\n                    {\n                        double noise = 1E-6 * random.NextDouble();\n                        if (entropy + noise < min)\n                        {\n                            min = entropy + noise;\n                            argmin = i;\n                        }\n                    }\n                }\n        return argmin;\n    }\n\n    void Observe(int node, Random random)\n    {\n        bool[] w = wave.data[node];\n        for (int t = 0; t < P; t++) distribution[t] = w[t] ? weights[t] : 0.0;\n        int r = distribution.Random(random.NextDouble());\n        for (int t = 0; t < P; t++) if (w[t] != (t == r)) Ban(node, t);\n    }\n\n    bool Propagate()\n    {\n        int MX = grid.MX, MY = grid.MY, MZ = grid.MZ;\n\n        while (stacksize > 0)\n        {\n            (int i1, int p1) = stack[stacksize - 1];\n            stacksize--;\n\n            int x1 = i1 % MX, y1 = (i1 % (MX * MY)) / MX, z1 = i1 / (MX * MY);\n\n            for (int d = 0; d < propagator.Length; d++)\n            {\n                int dx = DX[d], dy = DY[d], dz = DZ[d];\n                int x2 = x1 + dx, y2 = y1 + dy, z2 = z1 + dz;\n                if (!periodic && (x2 < 0 || y2 < 0 || z2 < 0 || x2 + N > MX || y2 + N > MY || z2 + 1 > MZ)) continue;\n\n                if (x2 < 0) x2 += MX;\n                else if (x2 >= MX) x2 -= MX;\n                if (y2 < 0) y2 += MY;\n                else if (y2 >= MY) y2 -= MY;\n                if (z2 < 0) z2 += MZ;\n                else if (z2 >= MZ) z2 -= MZ;\n\n                int i2 = x2 + y2 * MX + z2 * MX * MY;\n                int[] p = propagator[d][p1];\n                int[][] compat = wave.compatible[i2];\n\n                for (int l = 0; l < p.Length; l++)\n                {\n                    int t2 = p[l];\n                    int[] comp = compat[t2];\n\n                    comp[d]--;\n                    if (comp[d] == 0) Ban(i2, t2);\n                }\n            }\n        }\n\n        return wave.sumsOfOnes[0] > 0;\n    }\n\n    void Ban(int i, int t)\n    {\n        wave.data[i][t] = false;\n\n        int[] comp = wave.compatible[i][t];\n        for (int d = 0; d < propagator.Length; d++) comp[d] = 0;\n        stack[stacksize] = (i, t);\n        stacksize++;\n\n        wave.sumsOfOnes[i] -= 1;\n        if (shannon)\n        {\n            double sum = wave.sumsOfWeights[i];\n            wave.entropies[i] += wave.sumsOfWeightLogWeights[i] / sum - Math.Log(sum);\n\n            wave.sumsOfWeights[i] -= weights[t];\n            wave.sumsOfWeightLogWeights[i] -= weightLogWeights[t];\n\n            sum = wave.sumsOfWeights[i];\n            wave.entropies[i] -= wave.sumsOfWeightLogWeights[i] / sum - Math.Log(sum);\n        }\n    }\n\n    protected abstract void UpdateState();\n\n    protected static int[] DX = { 1, 0, -1, 0, 0, 0 };\n    protected static int[] DY = { 0, 1, 0, -1, 0, 0 };\n    protected static int[] DZ = { 0, 0, 0, 0, 1, -1 };\n}\n\nclass Wave\n{\n    public bool[][] data;\n    public int[][][] compatible;\n\n    public int[] sumsOfOnes;\n    public double[] sumsOfWeights, sumsOfWeightLogWeights, entropies;\n\n    public Wave(int length, int P, int D, bool shannon)\n    {\n        data = AH.Array2D(length, P, true);\n        compatible = AH.Array3D(length, P, D, -1);\n        sumsOfOnes = new int[length];\n\n        if (shannon)\n        {\n            sumsOfWeights = new double[length];\n            sumsOfWeightLogWeights = new double[length];\n            entropies = new double[length];\n        }\n    }\n\n    public void Init(int[][][] propagator, double sumOfWeights, double sumOfWeightLogWeights, double startingEntropy, bool shannon)\n    {\n        int P = data[0].Length;\n        for (int i = 0; i < data.Length; i++)\n        {\n            for (int p = 0; p < P; p++)\n            {\n                data[i][p] = true;\n                for (int d = 0; d < propagator.Length; d++) compatible[i][p][d] = propagator[opposite[d]][p].Length;\n            }\n\n            sumsOfOnes[i] = P;\n            if (shannon)\n            {\n                sumsOfWeights[i] = sumOfWeights;\n                sumsOfWeightLogWeights[i] = sumOfWeightLogWeights;\n                entropies[i] = startingEntropy;\n            }\n        }\n    }\n\n    public void CopyFrom(Wave wave, int D, bool shannon)\n    {\n        for (int i = 0; i < data.Length; i++)\n        {\n            bool[] datai = data[i], wavedatai = wave.data[i];\n            for (int t = 0; t < datai.Length; t++)\n            {\n                datai[t] = wavedatai[t];\n                for (int d = 0; d < D; d++) compatible[i][t][d] = wave.compatible[i][t][d];\n            }\n\n            sumsOfOnes[i] = wave.sumsOfOnes[i];\n\n            if (shannon)\n            {\n                sumsOfWeights[i] = wave.sumsOfWeights[i];\n                sumsOfWeightLogWeights[i] = wave.sumsOfWeightLogWeights[i];\n                entropies[i] = wave.entropies[i];\n            }\n        }\n    }\n\n    static readonly int[] opposite = { 2, 3, 0, 1, 5, 4 };\n}\n"
  },
  {
    "path": "source/XMLHelper.cs",
    "content": "﻿// Copyright (C) 2022 Maxim Gumin, The MIT License (MIT)\n\nusing System;\nusing System.Linq;\nusing System.Xml.Linq;\nusing System.ComponentModel;\nusing System.Collections.Generic;\n\nstatic class XMLHelper\n{\n    public static T Get<T>(this XElement xelem, string attribute)\n    {\n        XAttribute a = xelem.Attribute(attribute);\n        if (a == null) throw new Exception($\"xelement {xelem.Name} didn't have attribute {attribute}\");\n        return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(a.Value);\n    }\n\n    public static T Get<T>(this XElement xelem, string attribute, T dflt)\n    {\n        XAttribute a = xelem.Attribute(attribute);\n        return a == null ? dflt : (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(a.Value);\n    }\n\n    public static int LineNumber(this XElement xelem) => ((System.Xml.IXmlLineInfo)xelem).LineNumber;\n\n    public static IEnumerable<XElement> Elements(this XElement xelement, params string[] names) => xelement.Elements().Where(e => names.Any(n => n == e.Name));\n    public static IEnumerable<XElement> MyDescendants(this XElement xelem, params string[] tags)\n    {\n        Queue<XElement> q = new();\n        q.Enqueue(xelem);\n\n        while (q.Any())\n        {\n            XElement e = q.Dequeue();\n            if (e != xelem) yield return e;\n            foreach (XElement x in e.Elements(tags)) q.Enqueue(x);\n        }\n    }\n}\n"
  },
  {
    "path": "syntax.md",
    "content": "## Grid\nNew grids are created either at root, or with `wfc` or `map` nodes. Example: [MazeMap](models/MazeMap.xml) has 2 grids. Grids have one required attribute `values` and one optional attribute `folder`.\n\n`values=\"BRGUY\"` says that black, red, green, blue and yellow values are possible on the grid, and the starting value is black.\n\n`folder=\"DungeonGrowth\"` says that the interpreter should look for rule files in the [DungeonGrowth](resources/rules/DungeonGrowth/) folder.\n\nFor root grids, there is one more optional boolean attribute called `origin`, equal `False` by default. If origin is set `True`, the interpreter creates a pixel at the center of the grid with the value equal to the second value in `values`. For example, `values=\"YRBAN\" origin=\"True\"` creates a yellow screen with a red dot in the center.\n\n\n\n## Symmetry\nWith most nodes, you can specify the `symmetry` attribute. Possible symmetry values are listed in [SymmetryHelper.cs](source/SymmetryHelper.cs). By default, the largest symmetry group is used. Example: in [Flowers](models/Flowers.xml) we need flowers growing vertically and not side-to-side, so we specify that rules should only be mirrored along the x axis.\n\n\n\n## Rules\nRule attributes:\n* `in=\"BBB/BWB\"` - specifies input part of a rule.\n* `out=\"RR DA FR\"` - specifies output part of a rule.\n* `fin=\"filename\"` - loads input from `filename.png` or `filename.vox`.\n* `fout=\"filename\"` - loads output from `filename.png` or `filename.vox`.\n* `file=\"filename\"` - loads a glued input + output box from file. Example: [Circuit](models/Circuit.xml).\n* `p` - the probability that the rule will be applied. Equals `1.0` by default. Example: in [Apartemazements](models/Apartemazements.xml) only 25% of ceiling locations are converted into light sources.\n\nSlashes `/` are y-separators, spaces ` ` are z-separators.\n\nIf a file is referenced, the `legend` should be specified. Legend lists used values in a scanline order. See example in [DungeonGrowth](models/DungeonGrowth.xml).\n\n\n\n## Rulenodes\nThere are 3 kinds of rulenodes:\n1. `one` nodes, also called `(exists)` nodes.\n2. `all` nodes, also called `{forall}` nodes.\n3. `prl` (parallel) nodes are similar to `all` nodes, but they are applied independently of each other, they don't care about rule overlaps. Often executing a `prl` node leads to exactly the same result as with an `all` node, but `prl` is more performant.\n\nRulenode attributes:\n* `steps=\"60\"` - limits node execution to 60 steps. See an example in [River](models/River.xml).\n\n\n\n## Unions\nSee examples of union use in [DungeonGrowth](models/DungeonGrowth.xml).\n\n\n\n## Inference\nInference in MarkovJunior allows to impose constraints on the future state, and generate only those runs that lead to the constrained future. Inference is triggered by putting `observe` elements inside rulenodes (inside `one` or `all` nodes, to be precise). Observe elements have 3 attributes: `value`, `from`, `to`.\n\nFor example, `<observe value=\"W\" to=\"BR\"/>` means that squares that are currently white should become black or red after a chain of rule applications. `<observe value=\"I\" from=\"B\" to=\"W\"/>` means that squares that are currently indigo are turned black immediately, and then should become white after a chain of rule applications.\n\nIn [SokobanLevel1](models/SokobanLevel1.xml) we say that the goal `I`-squares should become white - this would mean that the puzzle is solved. We also help the inference engine by explicitly saying that the current black, white and red squares should *not* be white in the end. Since we don't have `I` in the ruleset, we say that current indigo squares should be treated as black by setting `from=\"B\"`.\n\nRulenodes with inference have a boolean `search` attribute, false by default. If search is set false, the interpreter follows the rule propagation field greedily. If search is set true, the interpreter searches the state graph using the rule propagation field as a heuristic.\n\nIf search is set false, the interpreter can be made to follow the goal more strictly or less strictly by varying the floating point `temperature` parameter. If search is set true, the attributes are:\n1. Integer `limit` attribute sets the maximum number of states searched. By default, the number of states is not limited.\n2. Floating point `depthCoefficient` attribute [interpolates](https://github.com/mxgmn/MarkovJunior/blob/4e64162f00203f5b5753af100af0dab8d72ce805/source/Search.cs#L269) between breadth-first search and depth-first search.\n\nSee 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).\n\n\n\n## Map\nSee examples of `map` node use in [MazeMap](models/MazeMap.xml), [MarchingSquares](models/MarchingSquares.xml), [OddScale](models/OddScale.xml), [OddScale3D](models/OddScale3D.xml), [SeaVilla](models/SeaVilla.xml), [ModernHouse](models/ModernHouse.xml), [CarmaTower](models/CarmaTower.xml).\n\n\n\n## Path\nSee examples of `path` node use in [BasicDijkstraFill](models/BasicDijkstraFill.xml), [BasicDijkstraDungeon](models/BasicDijkstraDungeon.xml), [BernoulliPercolation](models/BernoulliPercolation.xml), [Percolation](models/Percolation.xml), [Circuit](models/Circuit.xml), [DungeonGrowth](models/DungeonGrowth.xml).\n\n\n\n## Convolution\nSee examples of `convolution` node use in [GameOfLife](models/GameOfLife.xml), [Cave](models/Cave.xml), [ConnectedCaves](models/ConnectedCaves.xml), [ConstrainedCaves](models/ConstrainedCaves.xml), [OpenCave](models/OpenCave.xml), [OpenCave3D](models/OpenCave3D.xml), [Counting](models/Counting.xml), [CarmaTower](models/CarmaTower.xml).\n\n\n\n## WaveFunctionCollapse\nSee examples of tile WFC in [TileDungeon](models/TileDungeon.xml), [Knots2D](models/Knots2D.xml), [Knots3D](models/Knots3D.xml), [Surface](models/Surface.xml), [EscherSurface](models/EscherSurface.xml), [PillarsOfEternity](models/PillarsOfEternity.xml), [Apartemazements](models/Apartemazements.xml), [SeaVilla](models/SeaVilla.xml), [ModernHouse](models/ModernHouse.xml).\n\nSee examples of overlap WFC in [WaveFlowers](models/WaveFlowers.xml), [WaveBrickWall](models/WaveBrickWall.xml), [WaveDungeon](models/WaveDungeon.xml).\n\n\n\n## ConvChain\nSee examples of `convchain` node use in [ChainMaze](models/ChainMaze.xml), [ChainDungeon](models/ChainDungeon.xml), [ChainDungeonMaze](models/ChainDungeonMaze.xml).\n\n\n\n## Questions and Answers\n**Q:** How to make a loop? How to make a sequence repeat?<br/>\n**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.\n"
  }
]