[
  {
    "path": ".github/workflows/python-package.yml",
    "content": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions\nname: Python package\n\non:\n  push:\n  pull_request:\n\ndefaults:\n  run:\n    shell: bash\n\njobs:\n  mypy:\n    runs-on: ubuntu-20.04\n    steps:\n      - uses: actions/checkout@v2\n      - name: Install Python dependencies\n        run: pip install mypy -r requirements.txt\n      - name: Mypy\n        uses: liskin/gh-problem-matcher-wrap@v1\n        with:\n          linters: mypy\n          run: mypy --show-column-numbers .\n\n  pytest:\n    runs-on: ubuntu-20.04\n    steps:\n      - uses: actions/checkout@v2\n      - name: Install Python dependencies\n        run: pip install -r requirements.txt\n      - name: Test with pytest\n        run: pytest\n\n  wfc_run:\n    runs-on: ubuntu-20.04\n    steps:\n      - uses: actions/checkout@v2\n      - name: Set up Python\n        uses: actions/setup-python@v2\n        with:\n          python-version: 3.x\n      - name: Build package\n        run: pip install --editable .\n      - name: Run all experiments\n        run: |\n          python ./wfc_run.py -e simple -s samples.xml\n          python ./wfc_run.py -e choice -s samples.xml\n          python ./wfc_run.py -e choices -s samples.xml\n          python ./wfc_run.py -e heuristic -s samples.xml\n          python ./wfc_run.py -e backtracking -s samples.xml\n          python ./wfc_run.py -e backtracking_heuristic -s samples.xml\n      - name: Package output folder\n        run: tar -cf wfc-output.tar --format=ustar output/\n      - uses: actions/upload-artifact@v2\n        with:\n          name: wfc-output\n          path: wfc-output.tar\n          retention-days: 7\n"
  },
  {
    "path": ".gitignore",
    "content": "__pycache__\r\n/output/*\r\n/build\r\nlogs/\r\n*.egg-info/\r\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Isaac Karth\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": "MANIFEST.in",
    "content": "include wfc/py.typed\n"
  },
  {
    "path": "README.md",
    "content": "# wfc_2019f\n\nThis is my research implementation of WaveFunctionCollapse in Python. It has two goals:\n\n* Make it easier to understand how the algorithm operates\n* Provide a testbed for experimenting with alternate heuristics and features\n\nFor more general-purpose WFC information, the original reference repository remains the best resource: https://github.com/mxgmn/WaveFunctionCollapse\n\n## Installing\n\n```\ngit clone https://github.com/ikarth/wfc_2019f.git\ncd wfc_2019f\nconda create -n wfc2019 python=3.10\nconda activate wfc2019\npip install -r requirements.txt\npython wfc_run.py -s samples_reference.xml\n```\n\n## Running WFC\n\nIf you want direct control over running WFC, call `wfc_control.execute_wfc()`.\n\nThe arguments it accepts are:\n\n- `filename=None`: path to the input image file, this is mostly for internal use and should be left as `None`, set `image` instead.\n- `tile_size=1`: size of the tiles it uses (1 is fine for pixel images, larger is for things like a Super Metroid map)\n- `pattern_width=2`: size of the patterns; usually 2 or 3 because bigger gets slower and\n- `rotations=8`: how many reflections and/or rotations to use with the patterns\n- `output_size=[48,48]`: how big the output image is\n- `ground=None`: which patterns should be placed along the bottom-most line\n- `attempt_limit=10`: stop after this many tries\n- `output_periodic=True`: the output wraps at the edges\n- `input_periodic=True`: the input wraps at the edges\n- `loc_heuristic=\"entropy\"`: what location heuristic to use; `entropy` is the original WFC behavior. The heuristics that are implemented are `lexical`, `hilbert`, `spiral`, `entropy`, `anti-entropy`, `simple`, `random`, but when in doubt stick with `entropy`.\n- `choice_heuristic=\"weighted\"`: what choice heuristic to use; `weighted` is the original WFC behavior, other options are `random`, `rarest`, and `lexical`.\n- `visualize=False`: write intermediate images to disk?  requires `filename`.\n- `global_constraint=False`: what global constraint to use. Currently the only one implemented is `allpatterns`\n- `backtracking=False`: do we use backtracking if we run into a contradiction?\n- `log_filename=\"log\"`: what should the log file be named?\n- `logging=False`: should we write to a log file?  requires `filename`.\n- `log_stats_to_output=None`\n- `image`: an array of pixel data, typically in the shape: (height, width, rgb)\n\n## Test\n\n```\npytest\n```\n\n## Documentation\n\n```\npython setup.py build_sphinx\n```\n\nWith linux the documentation can be displayed with:\n\n```\nxdg-open build/sphinx/index.html\n```\n"
  },
  {
    "path": "doc/conf.py",
    "content": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common options. For a full\n# list see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\n# import os\n# import sys\n# sys.path.insert(0, os.path.abspath('.'))\n\n\n# -- Project information -----------------------------------------------------\n\nproject = 'wfc_python'\ncopyright = '2020, Isaac Karth'\nauthor = 'Isaac Karth'\n\n# The full version, including alpha/beta/rc tags\nrelease = '0.1'\n\n\n# -- General configuration ---------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.graphviz',\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['templates']\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path.\nexclude_patterns = []  # type: ignore[var-annotated]\n\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'alabaster'\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['static']"
  },
  {
    "path": "doc/dot/chain.dot",
    "content": "digraph {\r\n        read_xml_command -> import_image -> make_tile_catalog -> make_pattern_catalog -> make_adjacency_matrix -> solve_constraint_problem -> output_solution_image\r\n        make_tile_catalog -> output_solution_image\r\n        make_tile_catalog -> instrumentation [color=gray]\r\n        make_pattern_catalog -> instrumentation [color=gray]\r\n        make_adjacency_matrix -> instrumentation [color=gray]\r\n        solve_constraint_problem -> instrumentation [color=gray]\r\n        output_solution_image -> visualization [color=gray]\r\n        make_tile_catalog -> visualization [color=gray]\r\n        make_pattern_catalog -> visualization [color=gray]\r\n        make_adjacency_matrix -> visualization [color=gray]\r\n        solve_constraint_problem -> visualization [color=gray]\r\n        visualization [color=gray, fontcolor=gray]\r\n        instrumentation [color=gray, fontcolor=gray]\r\n        visualization -> make_tile_catalog [color=magenta]\r\n}"
  },
  {
    "path": "doc/dot/dependency.dot",
    "content": "digraph {\r\n        wfc_run -> wfc_control\r\n        wfc_control -> wfc_utilities\r\n        wfc_control -> wfc_solver\r\n        wfc_solver -> numpy\r\n        wfc_tiles -> numpy\r\n        wfc_patterns -> numpy\r\n        wfc_tiles -> wfc_utilities\r\n        wfc_control -> wfc_tiles\r\n        wfc_control -> wfc_patterns\r\n        wfc_patterns -> wfc_utilities\r\n        wfc_tiles -> imageio\r\n        wfc_control -> wfc_adjacency\r\n        wfc_control -> wfc_visualize\r\n        wfc_visualize -> matplotlib\r\n        wfc_visualize -> wfc_utilities\r\n        wfc_adjacency -> wfc_utilities\r\n        wfc_adjacency -> numpy\r\n        wfc_control -> wfc_instrumentation\r\n\r\n        implemented [style=filled, fillcolor=gray]\r\n        partial [style=filled, fillcolor=cyan]\r\n        unimplemented [style=filled, fillcolor=firebrick]\r\n        wfc_run\r\n        wfc_control []\r\n        wfc_solver\r\n        numpy [color=gray, fontcolor=gray]\r\n        wfc_tiles\r\n        wfc_patterns [style=filled, fillcolor=cyan]\r\n        wfc_utilities\r\n        imageio [color=gray, fontcolor=gray]\r\n        wfc_adjacency \r\n        wfc_visualize [style=filled, fillcolor=cyan]\r\n        matplotlib [color=gray, fontcolor=gray]\r\n        wfc_instrumentation [style=filled, fillcolor=firebrick]\r\n        label=\"Modules in WFC 19f\"\r\n}"
  },
  {
    "path": "doc/dot/design.dot",
    "content": "digraph {\r\n        things_to_implement [label=\"{Things that aren't implemented yet|Intermediate visualization|timing and profiling|performance statistics|outputting images|most heuristics|removing ground patterns|rotated patterns}\", shape=record, fillcolor=\"cyan\", style=filled]\r\n        read_data [label=\"Read data from XML\", fillcolor=\"cyan\", shape=box, style=filled]\r\n        read_data -> input_data\r\n        input_data [shape=record, label=\"XML\"]\r\n        input_data -> execute_wfc\r\n        solver [label=\"Solver\", shape=house]\r\n        solver -> make_wave\r\n        make_wave -> remove_patterns\r\n        remove_patterns [label=\"Remove ground patterns\", fillcolor=\"cyan\", style=filled]\r\n        input_data -> remove_patterns\r\n        input_data -> solver\r\n        remove_patterns -> solver_run [headport=n]\r\n        subgraph cluster_solver_run {\r\n                 label=\"wfc_solver.py\"\r\n                 \r\n                 solver_run [label=\"solver.run()\"]\r\n                 solver_observe [label=\"solver.observe()\"]\r\n                 solver_propagate [label=\"solver.propagate()\"]\r\n                 solver_on_backtrack [label=\"solver.onBacktrack()\", shape=invhouse]\r\n                 solver_on_choice [label=\"solver.onChoice()\", shape=invhouse]\r\n                 on_choice [label=\"onChoice()\", shape=note]\r\n                 on_backtrack [label=\"onBacktrack()\", shape=note, fillcolor=\"cyan\", style=filled]\r\n                 solver_if_backtracking [label=\"if backtracking\", shape=diamond]\r\n                 pattern_heuristic [label=\"pattern heuristic\", shape=note]\r\n                 location_heuristic [label=\"location heuristic\", shape=note]\r\n\r\n\r\n                 {rank=same pattern_heuristic location_heuristic}\r\n                 solver_run -> solver_check_feasible\r\n                 solver_check_feasible -> solver_propagate\r\n                 solver_propagate -> solver_observe\r\n                 solver_observe -> pattern_heuristic\r\n                 solver_observe -> location_heuristic\r\n                 solver_observe -> solver_on_choice\r\n                 solver_on_choice -> on_choice\r\n                 solver_recurse -> except_contradictions [color=red]\r\n                 solver_on_choice -> solver_if_finished\r\n                 solver_recurse -> solver_run [headport=n, tailport=w]\r\n                 solver_if_finished [shape=diamond]\r\n                 solver_if_finished -> solver_recurse [splines=polyline, dir=both, arrowhead=dotvee, arrowtail=dot, tailport=s, headport=n, color=\"black:green:black\"]\r\n                 except_contradictions -> solver_if_backtracking\r\n                 solver_if_backtracking -> solver_on_backtrack [label=\"Yes\"]\r\n                 solver_on_backtrack -> on_backtrack\r\n                 on_backtrack -> solver_run [headport=n]\r\n                 solver_if_backtracking -> cant_solve [splines=curved, label=\"No\", dir=both, arrowhead=dotvee, arrowtail=dot, tailport=e, headport=ne, color=\"grey\"]\r\n        }\r\n        solver_if_finished -> solver_solution [tailport=w, color=\"black:blue:black\"]\r\n\r\n        execute_wfc [shape=invhouse, fillcolor=\"cyan\", style=filled]\r\n        execute_wfc -> import_image\r\n        import_image [shape=box]\r\n        import_image -> make_tile_catalog\r\n        subgraph cluster_tile_py {\r\n                 label=\"wfc_tiles.py\"\r\n                 make_tile_catalog -> image_to_tiles\r\n        }\r\n        image_to_tiles -> tile_catalog\r\n        tile_catalog [label=\"Tile Catalog|{dictionary of tiles|image in tile IDs|set of tiles|frequency of tile occurance}\", shape=record]\r\n        subgraph cluster_patterns {\r\n                 label=\"wfc_patterns.py\"\r\n                 tile_catalog -> make_pattern_catalog\r\n                 {rank=same unique_patterns_2d rotate_or_reflect}\r\n                 make_pattern_catalog -> unique_patterns_2d -> rotate_or_reflect -> unique_patterns_2d\r\n                 make_pattern_catalog [fillcolor=\"cyan\", style=filled]\r\n                 rotate_or_reflect [fillcolor=\"cyan\", style=filled]\r\n        }\r\n        unique_patterns_2d -> pattern_catalog\r\n        pattern_catalog [label=\"Pattern Catalog|{dictionary of patterns|ordered list of pattern weights|ordered list of pattern contents}\", shape=record]\r\n        pattern_catalog -> extract_adjacency\r\n        subgraph cluster_adjacency {\r\n                 extract_adjacency -> is_valid_overlap\r\n        }\r\n        extract_adjacency -> adjacency_relations\r\n        adjacency_relations [label=\"{Adjacency Relations|tuples of (edge,pattern,pattern)}\", shape=record]\r\n        adjacency_relations -> combine_inputs\r\n        combine_inputs -> adjacency_matrix\r\n        adjacency_matrix [label=\"{Adjacency Matrix|boolean matrix of pattern x pattern x direction}\", shape=record]\r\n        adjacency_matrix -> solver\r\n        pattern_catalog -> solver\r\n        cant_solve [label=\"Can't Solve\", shape=box]\r\n        solver_solution [shape=record, label=\"Solution|grid of pattern IDs\"]\r\n        solver_solution -> visualizer\r\n        visualizer -> output_image\r\n        output_image [shape=box, label=\"Output Image\", style=filled, fillcolor=cyan]\r\n        pattern_catalog -> visualizer\r\n        tile_catalog -> visualizer\r\n        visualizer [fillcolor=cyan, style=filled]\r\n}\r\n"
  },
  {
    "path": "doc/index.rst",
    "content": "Documentation\n=============\n\nModule dependencies\n-------------------\n\n.. graphviz:: dot/dependency.dot\n\nDesign\n------\n\n.. graphviz:: dot/design.dot\n\nChain\n-----\n\n.. graphviz:: dot/chain.dot\n"
  },
  {
    "path": "images/samples/Castle/data.xml",
    "content": "<<<<<<< HEAD\n<set size=\"7\">\r\n\t<tiles>\r\n\t\t<tile name=\"bridge\" symmetry=\"I\"/>\r\n\t\t<tile name=\"ground\" symmetry=\"X\"/>\r\n\t\t<tile name=\"river\" symmetry=\"I\"/>\r\n\t\t<tile name=\"riverturn\" symmetry=\"L\"/>\r\n\t\t<tile name=\"road\" symmetry=\"I\"/>\r\n\t\t<tile name=\"roadturn\" symmetry=\"L\"/>\r\n\t\t<tile name=\"t\" symmetry=\"T\"/>\r\n\t\t<tile name=\"tower\" symmetry=\"L\"/>\r\n\t\t<tile name=\"wall\" symmetry=\"I\"/>\r\n\t\t<tile name=\"wallriver\" symmetry=\"I\"/>\r\n\t\t<tile name=\"wallroad\" symmetry=\"I\"/>\r\n\t</tiles>\r\n\t<neighbors>\r\n\t\t<neighbor left=\"bridge 1\" right=\"river 1\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"riverturn 1\"/>\r\n\t\t<neighbor left=\"bridge\" right=\"road 1\"/>\r\n\t\t<neighbor left=\"bridge\" right=\"roadturn 1\"/>\r\n\t\t<neighbor left=\"bridge\" right=\"t\"/>\r\n\t\t<neighbor left=\"bridge\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"bridge\" right=\"wallroad\"/>\r\n\t\t<neighbor left=\"ground\" right=\"ground\"/>\r\n\t\t<neighbor left=\"ground\" right=\"river\"/>\r\n\t\t<neighbor left=\"ground\" right=\"riverturn\"/>\r\n\t\t<neighbor left=\"ground\" right=\"road\"/>\r\n\t\t<neighbor left=\"ground\" right=\"roadturn\"/>\r\n\t\t<neighbor left=\"ground\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"ground\" right=\"tower\"/>\r\n\t\t<neighbor left=\"ground\" right=\"wall\"/>\r\n\t\t<neighbor left=\"river 1\" right=\"river 1\"/>\r\n\t\t<neighbor left=\"river 1\" right=\"riverturn 1\"/>\r\n\t\t<neighbor left=\"river\" right=\"road\"/>\r\n\t\t<neighbor left=\"river\" right=\"roadturn\"/>\r\n\t\t<neighbor left=\"river\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"river\" right=\"tower\"/>\r\n\t\t<neighbor left=\"river\" right=\"wall\"/>\r\n\t\t<neighbor left=\"river 1\" right=\"wallriver\"/>\r\n\t\t<neighbor left=\"riverturn\" right=\"riverturn 2\"/>\r\n\t\t<neighbor left=\"road\" right=\"riverturn\"/>\r\n\t\t<neighbor left=\"roadturn 1\" right=\"riverturn\"/>\r\n\t\t<neighbor left=\"roadturn 2\" right=\"riverturn\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"riverturn\"/>\r\n\t\t<neighbor left=\"tower 1\" right=\"riverturn\"/>\r\n\t\t<neighbor left=\"tower 2\" right=\"riverturn\"/>\r\n\t\t<neighbor left=\"wall\" right=\"riverturn\"/>\r\n\t\t<neighbor left=\"riverturn\" right=\"wallriver\"/>\r\n\t\t<neighbor left=\"road 1\" right=\"road 1\"/>\r\n\t\t<neighbor left=\"roadturn\" right=\"road 1\"/>\r\n\t\t<neighbor left=\"road 1\" right=\"t\"/>\r\n\t\t<neighbor left=\"road 1\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"road\" right=\"tower\"/>\r\n\t\t<neighbor left=\"road\" right=\"wall\"/>\r\n\t\t<neighbor left=\"road 1\" right=\"wallroad\"/>\r\n\t\t<neighbor left=\"roadturn\" right=\"roadturn 2\"/>\r\n\t\t<neighbor left=\"roadturn\" right=\"t\"/>\r\n\t\t<neighbor left=\"roadturn 1\" right=\"tower\"/>\r\n\t\t<neighbor left=\"roadturn 2\" right=\"tower\"/>\r\n\t\t<neighbor left=\"roadturn 1\" right=\"wall\"/>\r\n\t\t<neighbor left=\"roadturn\" right=\"wallroad\"/>\r\n\t\t<neighbor left=\"t\" right=\"t 2\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"tower\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"wall\"/>\r\n\t\t<neighbor left=\"t\" right=\"wallroad\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"wallroad\"/>\r\n\t\t<neighbor left=\"tower\" right=\"wall 1\"/>\r\n\t\t<neighbor left=\"tower\" right=\"wallriver 1\"/>\r\n\t\t<neighbor left=\"tower\" right=\"wallroad 1\"/>\r\n\t\t<neighbor left=\"wall 1\" right=\"wall 1\"/>\r\n\t\t<neighbor left=\"wall 1\" right=\"wallriver 1\"/>\r\n\t\t<neighbor left=\"wall 1\" right=\"wallroad 1\"/>\r\n\t\t<neighbor left=\"wallriver 1\" right=\"wallroad 1\"/>\r\n\t</neighbors>\r\n=======\n<set size=\"7\">\n\t<tiles>\n\t\t<tile name=\"bridge\" symmetry=\"I\"/>\n\t\t<tile name=\"ground\" symmetry=\"X\"/>\n\t\t<tile name=\"river\" symmetry=\"I\"/>\n\t\t<tile name=\"riverturn\" symmetry=\"L\"/>\n\t\t<tile name=\"road\" symmetry=\"I\"/>\n\t\t<tile name=\"roadturn\" symmetry=\"L\"/>\n\t\t<tile name=\"t\" symmetry=\"T\"/>\n\t\t<tile name=\"tower\" symmetry=\"L\"/>\n\t\t<tile name=\"wall\" symmetry=\"I\"/>\n\t\t<tile name=\"wallriver\" symmetry=\"I\"/>\n\t\t<tile name=\"wallroad\" symmetry=\"I\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"bridge 1\" right=\"river 1\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"riverturn 1\"/>\n\t\t<neighbor left=\"bridge\" right=\"road 1\"/>\n\t\t<neighbor left=\"bridge\" right=\"roadturn 1\"/>\n\t\t<neighbor left=\"bridge\" right=\"t\"/>\n\t\t<neighbor left=\"bridge\" right=\"t 3\"/>\n\t\t<neighbor left=\"bridge\" right=\"wallroad\"/>\n\t\t<neighbor left=\"ground\" right=\"ground\"/>\n\t\t<neighbor left=\"ground\" right=\"river\"/>\n\t\t<neighbor left=\"ground\" right=\"riverturn\"/>\n\t\t<neighbor left=\"ground\" right=\"road\"/>\n\t\t<neighbor left=\"ground\" right=\"roadturn\"/>\n\t\t<neighbor left=\"ground\" right=\"t 1\"/>\n\t\t<neighbor left=\"ground\" right=\"tower\"/>\n\t\t<neighbor left=\"ground\" right=\"wall\"/>\n\t\t<neighbor left=\"river 1\" right=\"river 1\"/>\n\t\t<neighbor left=\"river 1\" right=\"riverturn 1\"/>\n\t\t<neighbor left=\"river\" right=\"road\"/>\n\t\t<neighbor left=\"river\" right=\"roadturn\"/>\n\t\t<neighbor left=\"river\" right=\"t 1\"/>\n\t\t<neighbor left=\"river\" right=\"tower\"/>\n\t\t<neighbor left=\"river\" right=\"wall\"/>\n\t\t<neighbor left=\"river 1\" right=\"wallriver\"/>\n\t\t<neighbor left=\"riverturn\" right=\"riverturn 2\"/>\n\t\t<neighbor left=\"road\" right=\"riverturn\"/>\n\t\t<neighbor left=\"roadturn 1\" right=\"riverturn\"/>\n\t\t<neighbor left=\"roadturn 2\" right=\"riverturn\"/>\n\t\t<neighbor left=\"t 3\" right=\"riverturn\"/>\n\t\t<neighbor left=\"tower 1\" right=\"riverturn\"/>\n\t\t<neighbor left=\"tower 2\" right=\"riverturn\"/>\n\t\t<neighbor left=\"wall\" right=\"riverturn\"/>\n\t\t<neighbor left=\"riverturn\" right=\"wallriver\"/>\n\t\t<neighbor left=\"road 1\" right=\"road 1\"/>\n\t\t<neighbor left=\"roadturn\" right=\"road 1\"/>\n\t\t<neighbor left=\"road 1\" right=\"t\"/>\n\t\t<neighbor left=\"road 1\" right=\"t 3\"/>\n\t\t<neighbor left=\"road\" right=\"tower\"/>\n\t\t<neighbor left=\"road\" right=\"wall\"/>\n\t\t<neighbor left=\"road 1\" right=\"wallroad\"/>\n\t\t<neighbor left=\"roadturn\" right=\"roadturn 2\"/>\n\t\t<neighbor left=\"roadturn\" right=\"t\"/>\n\t\t<neighbor left=\"roadturn 1\" right=\"tower\"/>\n\t\t<neighbor left=\"roadturn 2\" right=\"tower\"/>\n\t\t<neighbor left=\"roadturn 1\" right=\"wall\"/>\n\t\t<neighbor left=\"roadturn\" right=\"wallroad\"/>\n\t\t<neighbor left=\"t\" right=\"t 2\"/>\n\t\t<neighbor left=\"t 3\" right=\"tower\"/>\n\t\t<neighbor left=\"t 3\" right=\"wall\"/>\n\t\t<neighbor left=\"t\" right=\"wallroad\"/>\n\t\t<neighbor left=\"t 1\" right=\"wallroad\"/>\n\t\t<neighbor left=\"tower\" right=\"wall 1\"/>\n\t\t<neighbor left=\"tower\" right=\"wallriver 1\"/>\n\t\t<neighbor left=\"tower\" right=\"wallroad 1\"/>\n\t\t<neighbor left=\"wall 1\" right=\"wall 1\"/>\n\t\t<neighbor left=\"wall 1\" right=\"wallriver 1\"/>\n\t\t<neighbor left=\"wall 1\" right=\"wallroad 1\"/>\n\t\t<neighbor left=\"wallriver 1\" right=\"wallroad 1\"/>\n\t</neighbors>\n>>>>>>> 1fe4f1f60ebd57b99ca7148fb003edefa7979d94\n</set>"
  },
  {
    "path": "images/samples/Circles/data.xml",
    "content": "<<<<<<< HEAD\n<set size=\"32\">\r\n\t<tiles>\r\n\t\t<tile name=\"b_half\" symmetry=\"T\"/>\r\n\t\t<tile name=\"b_i\" symmetry=\"I\"/>\r\n\t\t<tile name=\"b_quarter\" symmetry=\"L\"/>\r\n\t\t<tile name=\"w_half\" symmetry=\"T\"/>\r\n\t\t<tile name=\"w_i\" symmetry=\"I\"/>\r\n\t\t<tile name=\"w_quarter\" symmetry=\"L\"/>\r\n\t\t<tile name=\"b\" symmetry=\"X\"/>\r\n\t\t<tile name=\"w\" symmetry=\"X\"/>\r\n\t</tiles>\r\n\t<neighbors>\r\n\t\t<neighbor left=\"b_half\" right=\"b_half\"/>\r\n\t\t<neighbor left=\"b_half 1\" right=\"b_half 3\"/>\r\n\t\t<neighbor left=\"b_half 3\" right=\"b_half 1\"/>\r\n\t\t<neighbor left=\"b_half\" right=\"b_half 3\"/>\r\n\t\t<neighbor left=\"b_half\" right=\"b_half 2\"/>\r\n\t\t<neighbor left=\"b_half\" right=\"b_i\"/>\r\n\t\t<neighbor left=\"b_half 3\" right=\"b_i 3\"/>\r\n\t\t<neighbor left=\"b_half 1\" right=\"b_i\"/>\r\n\t\t<neighbor left=\"b_half\" right=\"b_quarter\"/>\r\n\t\t<neighbor left=\"b_half 1\" right=\"b_quarter\"/>\r\n\t\t<neighbor left=\"b_half 2\" right=\"b_quarter\"/>\r\n\t\t<neighbor left=\"b_half 3\" right=\"b_quarter 1\"/>\r\n\t\t<neighbor left=\"b_i\" right=\"b_i\"/>\r\n\t\t<neighbor left=\"b_i 1\" right=\"b_i 1\"/>\r\n\t\t<neighbor left=\"b_i\" right=\"b_quarter\"/>\r\n\t\t<neighbor left=\"b_i 1\" right=\"b_quarter 1\"/>\r\n\t\t<neighbor left=\"b_quarter\" right=\"b_quarter 1\"/>\r\n\t\t<neighbor left=\"b_quarter 1\" right=\"b_quarter\"/>\r\n\t\t<neighbor left=\"b_quarter 2\" right=\"b_quarter\"/>\r\n\t\t<neighbor left=\"b_quarter\" right=\"b_quarter 2\"/>\r\n\t\t<neighbor left=\"b_half 1\" right=\"w_half 1\"/>\r\n\t\t<neighbor left=\"b_half\" right=\"w_half 1\"/>\r\n\t\t<neighbor left=\"b_half 3\" right=\"w_half\"/>\r\n\t\t<neighbor left=\"b_half 3\" right=\"w_half 3\"/>\r\n\t\t<neighbor left=\"b_half\" right=\"w_i 1\"/>\r\n\t\t<neighbor left=\"b_half 1\" right=\"w_i 1\"/>\r\n\t\t<neighbor left=\"b_half 3\" right=\"w_i\"/>\r\n\t\t<neighbor left=\"b_half\" right=\"w_quarter 1\"/>\r\n\t\t<neighbor left=\"b_half\" right=\"w_quarter 2\"/>\r\n\t\t<neighbor left=\"b_half 1\" right=\"w_quarter 1\"/>\r\n\t\t<neighbor left=\"b_half 3\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"b_i\" right=\"w_half 1\"/>\r\n\t\t<neighbor left=\"b_i 1\" right=\"w_half\"/>\r\n\t\t<neighbor left=\"b_i 1\" right=\"w_half 3\"/>\r\n\t\t<neighbor left=\"b_i\" right=\"w_i 1\"/>\r\n\t\t<neighbor left=\"b_i 1\" right=\"w_i\"/>\r\n\t\t<neighbor left=\"b_i\" right=\"w_quarter 1\"/>\r\n\t\t<neighbor left=\"b_i 1\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"b_quarter\" right=\"w_half\"/>\r\n\t\t<neighbor left=\"b_quarter\" right=\"w_half 3\"/>\r\n\t\t<neighbor left=\"b_quarter\" right=\"w_half 2\"/>\r\n\t\t<neighbor left=\"b_quarter 1\" right=\"w_half 1\"/>\r\n\t\t<neighbor left=\"b_quarter\" right=\"w_i\"/>\r\n\t\t<neighbor left=\"b_quarter 1\" right=\"w_i 1\"/>\r\n\t\t<neighbor left=\"b_quarter\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"b_quarter\" right=\"w_quarter 3\"/>\r\n\t\t<neighbor left=\"b_quarter 1\" right=\"w_quarter 1\"/>\r\n\t\t<neighbor left=\"b_quarter 1\" right=\"w_quarter 2\"/>\r\n\t\t<neighbor left=\"w_half\" right=\"w_half\"/>\r\n\t\t<neighbor left=\"w_half 1\" right=\"w_half 3\"/>\r\n\t\t<neighbor left=\"w_half 3\" right=\"w_half 1\"/>\r\n\t\t<neighbor left=\"w_half\" right=\"w_half 3\"/>\r\n\t\t<neighbor left=\"w_half\" right=\"w_half 2\"/>\r\n\t\t<neighbor left=\"w_half\" right=\"w_i\"/>\r\n\t\t<neighbor left=\"w_half 3\" right=\"w_i 3\"/>\r\n\t\t<neighbor left=\"w_half 1\" right=\"w_i\"/>\r\n\t\t<neighbor left=\"w_half\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"w_half 1\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"w_half 2\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"w_half 3\" right=\"w_quarter 1\"/>\r\n\t\t<neighbor left=\"w_i\" right=\"w_i\"/>\r\n\t\t<neighbor left=\"w_i 1\" right=\"w_i 1\"/>\r\n\t\t<neighbor left=\"w_i\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"w_i 1\" right=\"w_quarter 1\"/>\r\n\t\t<neighbor left=\"w_quarter\" right=\"w_quarter 1\"/>\r\n\t\t<neighbor left=\"w_quarter 1\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"w_quarter 2\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"w_quarter\" right=\"w_quarter 2\"/>\r\n\t\t<neighbor left=\"b\" right=\"b\"/>\r\n\t\t<neighbor left=\"b\" right=\"b_half 1\"/>\r\n\t\t<neighbor left=\"b\" right=\"b_i 1\"/>\r\n\t\t<neighbor left=\"b\" right=\"b_quarter 1\"/>\r\n\t\t<neighbor left=\"b\" right=\"w_half\"/>\r\n\t\t<neighbor left=\"b\" right=\"w_half 3\"/>\r\n\t\t<neighbor left=\"b\" right=\"w_i\"/>\r\n\t\t<neighbor left=\"b\" right=\"w_quarter\"/>\r\n\t\t<neighbor left=\"w\" right=\"w\"/>\r\n\t\t<neighbor left=\"w\" right=\"w_half 1\"/>\r\n\t\t<neighbor left=\"w\" right=\"w_i 1\"/>\r\n\t\t<neighbor left=\"w\" right=\"w_quarter 1\"/>\r\n\t\t<neighbor left=\"w\" right=\"b_half\"/>\r\n\t\t<neighbor left=\"w\" right=\"b_half 3\"/>\r\n\t\t<neighbor left=\"w\" right=\"b_i\"/>\r\n\t\t<neighbor left=\"w\" right=\"b_quarter\"/>\r\n\t</neighbors>\r\n\t<subsets>\r\n\t\t<subset name=\"Large Circles\">\r\n\t\t\t<tile name=\"b_quarter\"/>\r\n\t\t\t<tile name=\"w_quarter\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"Large Circles and Solid\">\r\n\t\t\t<tile name=\"b_quarter\"/>\r\n\t\t\t<tile name=\"w_quarter\"/>\r\n\t\t\t<tile name=\"b\"/>\r\n\t\t\t<tile name=\"w\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"No Solid\">\r\n\t\t\t<tile name=\"b_half\" symmetry=\"T\"/>\r\n\t\t\t<tile name=\"b_i\" symmetry=\"I\"/>\r\n\t\t\t<tile name=\"b_quarter\" symmetry=\"L\"/>\r\n\t\t\t<tile name=\"w_half\" symmetry=\"T\"/>\r\n\t\t\t<tile name=\"w_i\" symmetry=\"I\"/>\r\n\t\t\t<tile name=\"w_quarter\" symmetry=\"L\"/>\r\n\t\t</subset>\r\n\t</subsets>\r\n=======\n<set size=\"32\">\n\t<tiles>\n\t\t<tile name=\"b_half\" symmetry=\"T\"/>\n\t\t<tile name=\"b_i\" symmetry=\"I\"/>\n\t\t<tile name=\"b_quarter\" symmetry=\"L\"/>\n\t\t<tile name=\"w_half\" symmetry=\"T\"/>\n\t\t<tile name=\"w_i\" symmetry=\"I\"/>\n\t\t<tile name=\"w_quarter\" symmetry=\"L\"/>\n\t\t<tile name=\"b\" symmetry=\"X\"/>\n\t\t<tile name=\"w\" symmetry=\"X\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"b_half\" right=\"b_half\"/>\n\t\t<neighbor left=\"b_half 1\" right=\"b_half 3\"/>\n\t\t<neighbor left=\"b_half 3\" right=\"b_half 1\"/>\n\t\t<neighbor left=\"b_half\" right=\"b_half 3\"/>\n\t\t<neighbor left=\"b_half\" right=\"b_half 2\"/>\n\t\t<neighbor left=\"b_half\" right=\"b_i\"/>\n\t\t<neighbor left=\"b_half 3\" right=\"b_i 3\"/>\n\t\t<neighbor left=\"b_half 1\" right=\"b_i\"/>\n\t\t<neighbor left=\"b_half\" right=\"b_quarter\"/>\n\t\t<neighbor left=\"b_half 1\" right=\"b_quarter\"/>\n\t\t<neighbor left=\"b_half 2\" right=\"b_quarter\"/>\n\t\t<neighbor left=\"b_half 3\" right=\"b_quarter 1\"/>\n\t\t<neighbor left=\"b_i\" right=\"b_i\"/>\n\t\t<neighbor left=\"b_i 1\" right=\"b_i 1\"/>\n\t\t<neighbor left=\"b_i\" right=\"b_quarter\"/>\n\t\t<neighbor left=\"b_i 1\" right=\"b_quarter 1\"/>\n\t\t<neighbor left=\"b_quarter\" right=\"b_quarter 1\"/>\n\t\t<neighbor left=\"b_quarter 1\" right=\"b_quarter\"/>\n\t\t<neighbor left=\"b_quarter 2\" right=\"b_quarter\"/>\n\t\t<neighbor left=\"b_quarter\" right=\"b_quarter 2\"/>\n\t\t<neighbor left=\"b_half 1\" right=\"w_half 1\"/>\n\t\t<neighbor left=\"b_half\" right=\"w_half 1\"/>\n\t\t<neighbor left=\"b_half 3\" right=\"w_half\"/>\n\t\t<neighbor left=\"b_half 3\" right=\"w_half 3\"/>\n\t\t<neighbor left=\"b_half\" right=\"w_i 1\"/>\n\t\t<neighbor left=\"b_half 1\" right=\"w_i 1\"/>\n\t\t<neighbor left=\"b_half 3\" right=\"w_i\"/>\n\t\t<neighbor left=\"b_half\" right=\"w_quarter 1\"/>\n\t\t<neighbor left=\"b_half\" right=\"w_quarter 2\"/>\n\t\t<neighbor left=\"b_half 1\" right=\"w_quarter 1\"/>\n\t\t<neighbor left=\"b_half 3\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"b_i\" right=\"w_half 1\"/>\n\t\t<neighbor left=\"b_i 1\" right=\"w_half\"/>\n\t\t<neighbor left=\"b_i 1\" right=\"w_half 3\"/>\n\t\t<neighbor left=\"b_i\" right=\"w_i 1\"/>\n\t\t<neighbor left=\"b_i 1\" right=\"w_i\"/>\n\t\t<neighbor left=\"b_i\" right=\"w_quarter 1\"/>\n\t\t<neighbor left=\"b_i 1\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"b_quarter\" right=\"w_half\"/>\n\t\t<neighbor left=\"b_quarter\" right=\"w_half 3\"/>\n\t\t<neighbor left=\"b_quarter\" right=\"w_half 2\"/>\n\t\t<neighbor left=\"b_quarter 1\" right=\"w_half 1\"/>\n\t\t<neighbor left=\"b_quarter\" right=\"w_i\"/>\n\t\t<neighbor left=\"b_quarter 1\" right=\"w_i 1\"/>\n\t\t<neighbor left=\"b_quarter\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"b_quarter\" right=\"w_quarter 3\"/>\n\t\t<neighbor left=\"b_quarter 1\" right=\"w_quarter 1\"/>\n\t\t<neighbor left=\"b_quarter 1\" right=\"w_quarter 2\"/>\n\t\t<neighbor left=\"w_half\" right=\"w_half\"/>\n\t\t<neighbor left=\"w_half 1\" right=\"w_half 3\"/>\n\t\t<neighbor left=\"w_half 3\" right=\"w_half 1\"/>\n\t\t<neighbor left=\"w_half\" right=\"w_half 3\"/>\n\t\t<neighbor left=\"w_half\" right=\"w_half 2\"/>\n\t\t<neighbor left=\"w_half\" right=\"w_i\"/>\n\t\t<neighbor left=\"w_half 3\" right=\"w_i 3\"/>\n\t\t<neighbor left=\"w_half 1\" right=\"w_i\"/>\n\t\t<neighbor left=\"w_half\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"w_half 1\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"w_half 2\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"w_half 3\" right=\"w_quarter 1\"/>\n\t\t<neighbor left=\"w_i\" right=\"w_i\"/>\n\t\t<neighbor left=\"w_i 1\" right=\"w_i 1\"/>\n\t\t<neighbor left=\"w_i\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"w_i 1\" right=\"w_quarter 1\"/>\n\t\t<neighbor left=\"w_quarter\" right=\"w_quarter 1\"/>\n\t\t<neighbor left=\"w_quarter 1\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"w_quarter 2\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"w_quarter\" right=\"w_quarter 2\"/>\n\t\t<neighbor left=\"b\" right=\"b\"/>\n\t\t<neighbor left=\"b\" right=\"b_half 1\"/>\n\t\t<neighbor left=\"b\" right=\"b_i 1\"/>\n\t\t<neighbor left=\"b\" right=\"b_quarter 1\"/>\n\t\t<neighbor left=\"b\" right=\"w_half\"/>\n\t\t<neighbor left=\"b\" right=\"w_half 3\"/>\n\t\t<neighbor left=\"b\" right=\"w_i\"/>\n\t\t<neighbor left=\"b\" right=\"w_quarter\"/>\n\t\t<neighbor left=\"w\" right=\"w\"/>\n\t\t<neighbor left=\"w\" right=\"w_half 1\"/>\n\t\t<neighbor left=\"w\" right=\"w_i 1\"/>\n\t\t<neighbor left=\"w\" right=\"w_quarter 1\"/>\n\t\t<neighbor left=\"w\" right=\"b_half\"/>\n\t\t<neighbor left=\"w\" right=\"b_half 3\"/>\n\t\t<neighbor left=\"w\" right=\"b_i\"/>\n\t\t<neighbor left=\"w\" right=\"b_quarter\"/>\n\t</neighbors>\n\t<subsets>\n\t\t<subset name=\"Large Circles\">\n\t\t\t<tile name=\"b_quarter\"/>\n\t\t\t<tile name=\"w_quarter\"/>\n\t\t</subset>\n\t\t<subset name=\"Large Circles and Solid\">\n\t\t\t<tile name=\"b_quarter\"/>\n\t\t\t<tile name=\"w_quarter\"/>\n\t\t\t<tile name=\"b\"/>\n\t\t\t<tile name=\"w\"/>\n\t\t</subset>\n\t\t<subset name=\"No Solid\">\n\t\t\t<tile name=\"b_half\" symmetry=\"T\"/>\n\t\t\t<tile name=\"b_i\" symmetry=\"I\"/>\n\t\t\t<tile name=\"b_quarter\" symmetry=\"L\"/>\n\t\t\t<tile name=\"w_half\" symmetry=\"T\"/>\n\t\t\t<tile name=\"w_i\" symmetry=\"I\"/>\n\t\t\t<tile name=\"w_quarter\" symmetry=\"L\"/>\n\t\t</subset>\n\t</subsets>\n>>>>>>> 1fe4f1f60ebd57b99ca7148fb003edefa7979d94\n</set>"
  },
  {
    "path": "images/samples/Circuit/data.xml",
    "content": "<<<<<<< HEAD\n<set size=\"14\">\r\n\t<tiles>\r\n\t\t<tile name=\"bridge\" symmetry=\"I\" weight=\"1.0\"/>\r\n\t\t<tile name=\"component\" symmetry=\"X\" weight=\"20.0\"/>\r\n\t\t<tile name=\"connection\" symmetry=\"T\" weight=\"10.0\"/>\r\n\t\t<tile name=\"corner\" symmetry=\"L\" weight=\"10.0\"/>\r\n\t\t<tile name=\"substrate\" symmetry=\"X\" weight=\"2.0\"/>\r\n\t\t<tile name=\"t\" symmetry=\"T\" weight=\"0.1\"/>\r\n\t\t<tile name=\"track\" symmetry=\"I\" weight=\"2.0\"/>\r\n\t\t<tile name=\"transition\" symmetry=\"T\" weight=\"0.4\"/>\r\n\t\t<tile name=\"turn\" symmetry=\"L\" weight=\"1.0\"/>\r\n\t\t<tile name=\"viad\" symmetry=\"I\" weight=\"0.1\"/>\r\n\t\t<tile name=\"vias\" symmetry=\"T\" weight=\"0.3\"/>\r\n\t\t<tile name=\"wire\" symmetry=\"I\" weight=\"0.5\"/>\r\n\t\t<tile name=\"skew\" symmetry=\"L\" weight=\"2.0\"/>\r\n\t\t<tile name=\"dskew\" symmetry=\"\\\" weight=\"2.0\"/>\r\n\t</tiles>\r\n\t<neighbors>\r\n\t\t<neighbor left=\"bridge\" right=\"bridge\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"bridge 1\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"connection 1\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"t 2\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"track 1\"/>\r\n\t\t<neighbor left=\"bridge\" right=\"transition 1\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"viad\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"vias 1\"/>\r\n\t\t<neighbor left=\"bridge\" right=\"wire\"/>\r\n\t\t<neighbor left=\"component\" right=\"component\"/>\r\n\t\t<neighbor left=\"connection 1\" right=\"component\"/>\r\n\t\t<neighbor left=\"connection\" right=\"connection\"/>\r\n\t\t<neighbor left=\"connection\" right=\"corner\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"connection 1\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"connection 1\"/>\r\n\t\t<neighbor left=\"track 1\" right=\"connection 1\"/>\r\n\t\t<neighbor left=\"turn\" right=\"connection 1\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"track\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"transition 2\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"transition\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"turn 2\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"viad 1\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"vias 1\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"vias 2\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"vias\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"wire 1\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"substrate\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"track\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"transition 2\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"turn\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"viad 1\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"vias 2\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"vias 3\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"wire 1\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"t 2\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"t 2\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"t\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"track\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"track 1\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"track 1\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"transition 3\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"transition 2\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"transition 3\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"turn\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"turn 2\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"viad 1\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"viad\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"viad\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"vias 1\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"vias 1\"/>\r\n\t\t<neighbor left=\"vias 1\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"vias 2\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"wire 1\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"track\" right=\"track\"/>\r\n\t\t<neighbor left=\"track 1\" right=\"track 1\"/>\r\n\t\t<neighbor left=\"track 1\" right=\"transition 3\"/>\r\n\t\t<neighbor left=\"track\" right=\"transition 2\"/>\r\n\t\t<neighbor left=\"track\" right=\"turn\"/>\r\n\t\t<neighbor left=\"track 1\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"track\" right=\"viad 1\"/>\r\n\t\t<neighbor left=\"track 1\" right=\"viad\"/>\r\n\t\t<neighbor left=\"track\" right=\"vias 2\"/>\r\n\t\t<neighbor left=\"track\" right=\"vias 3\"/>\r\n\t\t<neighbor left=\"track 1\" right=\"vias 1\"/>\r\n\t\t<neighbor left=\"track\" right=\"wire 1\"/>\r\n\t\t<neighbor left=\"transition 2\" right=\"turn\"/>\r\n\t\t<neighbor left=\"transition\" right=\"turn\"/>\r\n\t\t<neighbor left=\"transition 1\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"transition 2\" right=\"viad 1\"/>\r\n\t\t<neighbor left=\"transition 2\" right=\"vias 2\"/>\r\n\t\t<neighbor left=\"transition 2\" right=\"vias 3\"/>\r\n\t\t<neighbor left=\"transition 2\" right=\"vias\"/>\r\n\t\t<neighbor left=\"wire\" right=\"transition 1\"/>\r\n\t\t<neighbor left=\"transition 2\" right=\"wire 1\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"turn\"/>\r\n\t\t<neighbor left=\"turn 2\" right=\"turn\"/>\r\n\t\t<neighbor left=\"turn\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"turn\" right=\"turn 2\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"viad 1\"/>\r\n\t\t<neighbor left=\"turn\" right=\"viad\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"vias 2\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"vias 3\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"vias\"/>\r\n\t\t<neighbor left=\"turn\" right=\"vias 1\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"wire 1\"/>\r\n\t\t<neighbor left=\"viad 1\" right=\"viad 1\"/>\r\n\t\t<neighbor left=\"viad 1\" right=\"vias 2\"/>\r\n\t\t<neighbor left=\"viad 1\" right=\"vias 3\"/>\r\n\t\t<neighbor left=\"viad 1\" right=\"wire 1\"/>\r\n\t\t<neighbor left=\"vias 1\" right=\"wire 1\"/>\r\n\t\t<neighbor left=\"vias 2\" right=\"wire 1\"/>\r\n\t\t<neighbor left=\"vias 1\" right=\"vias 3\"/>\r\n\t\t<neighbor left=\"vias 2\" right=\"vias 2\"/>\r\n\t\t<neighbor left=\"vias 2\" right=\"vias\"/>\r\n\t\t<neighbor left=\"wire\" right=\"wire\"/>\r\n\t\t<neighbor left=\"wire 1\" right=\"wire 1\"/>\r\n\t\t<neighbor left=\"bridge 1\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"connection 3\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"dskew\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"skew\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"t\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"track 1\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"transition 1\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"turn 3\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"viad\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"vias 3\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"skew\" right=\"bridge 1\"/>\r\n\t\t<neighbor left=\"skew\" right=\"connection 1\"/>\r\n\t\t<neighbor left=\"corner\" right=\"skew\"/>\r\n\t\t<neighbor left=\"corner 3\" right=\"skew\"/>\r\n\t\t<neighbor left=\"skew\" right=\"dskew\"/>\r\n\t\t<neighbor left=\"skew\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"skew 1\" right=\"skew\"/>\r\n\t\t<neighbor left=\"skew 1\" right=\"skew 3\"/>\r\n\t\t<neighbor left=\"substrate\" right=\"skew\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"skew\"/>\r\n\t\t<neighbor left=\"t\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"track\" right=\"skew\"/>\r\n\t\t<neighbor left=\"track 1\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"transition\" right=\"skew\"/>\r\n\t\t<neighbor left=\"transition 1\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"skew\"/>\r\n\t\t<neighbor left=\"turn 2\" right=\"skew\"/>\r\n\t\t<neighbor left=\"turn 3\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"viad 1\" right=\"skew\"/>\r\n\t\t<neighbor left=\"viad\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"vias\" right=\"skew\"/>\r\n\t\t<neighbor left=\"vias 1\" right=\"skew\"/>\r\n\t\t<neighbor left=\"vias 2\" right=\"skew\"/>\r\n\t\t<neighbor left=\"vias 3\" right=\"skew 2\"/>\r\n\t\t<neighbor left=\"wire 1\" right=\"skew\"/>\r\n\t</neighbors>\r\n\t<subsets>\r\n\t\t<subset name=\"Turnless\">\r\n\t\t\t<tile name=\"bridge\"/>\r\n\t\t\t<tile name=\"component\"/>\r\n\t\t\t<tile name=\"connection\"/>\r\n\t\t\t<tile name=\"corner\"/>\r\n\t\t\t<tile name=\"substrate\"/>\r\n\t\t\t<tile name=\"t\"/>\r\n\t\t\t<tile name=\"track\"/>\r\n\t\t\t<tile name=\"transition\"/>\r\n\t\t\t<tile name=\"viad\"/>\r\n\t\t\t<tile name=\"vias\"/>\r\n\t\t\t<tile name=\"wire\"/>\r\n\t\t\t<tile name=\"skew\"/>\r\n\t\t\t<tile name=\"dskew\"/>\r\n\t\t</subset>\r\n\t</subsets>\r\n=======\n<set size=\"14\">\n\t<tiles>\n\t\t<tile name=\"bridge\" symmetry=\"I\" weight=\"1.0\"/>\n\t\t<tile name=\"component\" symmetry=\"X\" weight=\"20.0\"/>\n\t\t<tile name=\"connection\" symmetry=\"T\" weight=\"10.0\"/>\n\t\t<tile name=\"corner\" symmetry=\"L\" weight=\"10.0\"/>\n\t\t<tile name=\"substrate\" symmetry=\"X\" weight=\"2.0\"/>\n\t\t<tile name=\"t\" symmetry=\"T\" weight=\"0.1\"/>\n\t\t<tile name=\"track\" symmetry=\"I\" weight=\"2.0\"/>\n\t\t<tile name=\"transition\" symmetry=\"T\" weight=\"0.4\"/>\n\t\t<tile name=\"turn\" symmetry=\"L\" weight=\"1.0\"/>\n\t\t<tile name=\"viad\" symmetry=\"I\" weight=\"0.1\"/>\n\t\t<tile name=\"vias\" symmetry=\"T\" weight=\"0.3\"/>\n\t\t<tile name=\"wire\" symmetry=\"I\" weight=\"0.5\"/>\n\t\t<tile name=\"skew\" symmetry=\"L\" weight=\"2.0\"/>\n\t\t<tile name=\"dskew\" symmetry=\"\\\" weight=\"2.0\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"bridge\" right=\"bridge\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"bridge 1\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"connection 1\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"t 2\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"t 3\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"track 1\"/>\n\t\t<neighbor left=\"bridge\" right=\"transition 1\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"turn 1\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"viad\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"vias 1\"/>\n\t\t<neighbor left=\"bridge\" right=\"wire\"/>\n\t\t<neighbor left=\"component\" right=\"component\"/>\n\t\t<neighbor left=\"connection 1\" right=\"component\"/>\n\t\t<neighbor left=\"connection\" right=\"connection\"/>\n\t\t<neighbor left=\"connection\" right=\"corner\"/>\n\t\t<neighbor left=\"t 1\" right=\"connection 1\"/>\n\t\t<neighbor left=\"t 2\" right=\"connection 1\"/>\n\t\t<neighbor left=\"track 1\" right=\"connection 1\"/>\n\t\t<neighbor left=\"turn\" right=\"connection 1\"/>\n\t\t<neighbor left=\"substrate\" right=\"corner 1\"/>\n\t\t<neighbor left=\"t 3\" right=\"corner 1\"/>\n\t\t<neighbor left=\"track\" right=\"corner 1\"/>\n\t\t<neighbor left=\"transition 2\" right=\"corner 1\"/>\n\t\t<neighbor left=\"transition\" right=\"corner 1\"/>\n\t\t<neighbor left=\"turn 1\" right=\"corner 1\"/>\n\t\t<neighbor left=\"turn 2\" right=\"corner 1\"/>\n\t\t<neighbor left=\"viad 1\" right=\"corner 1\"/>\n\t\t<neighbor left=\"vias 1\" right=\"corner 1\"/>\n\t\t<neighbor left=\"vias 2\" right=\"corner 1\"/>\n\t\t<neighbor left=\"vias\" right=\"corner 1\"/>\n\t\t<neighbor left=\"wire 1\" right=\"corner 1\"/>\n\t\t<neighbor left=\"substrate\" right=\"substrate\"/>\n\t\t<neighbor left=\"substrate\" right=\"t 1\"/>\n\t\t<neighbor left=\"substrate\" right=\"track\"/>\n\t\t<neighbor left=\"substrate\" right=\"transition 2\"/>\n\t\t<neighbor left=\"substrate\" right=\"turn\"/>\n\t\t<neighbor left=\"substrate\" right=\"viad 1\"/>\n\t\t<neighbor left=\"substrate\" right=\"vias 2\"/>\n\t\t<neighbor left=\"substrate\" right=\"vias 3\"/>\n\t\t<neighbor left=\"substrate\" right=\"wire 1\"/>\n\t\t<neighbor left=\"t 1\" right=\"t 3\"/>\n\t\t<neighbor left=\"t 3\" right=\"t 1\"/>\n\t\t<neighbor left=\"t 1\" right=\"t 2\"/>\n\t\t<neighbor left=\"t 2\" right=\"t 2\"/>\n\t\t<neighbor left=\"t 2\" right=\"t\"/>\n\t\t<neighbor left=\"t 3\" right=\"track\"/>\n\t\t<neighbor left=\"t 1\" right=\"track 1\"/>\n\t\t<neighbor left=\"t 2\" right=\"track 1\"/>\n\t\t<neighbor left=\"t 1\" right=\"transition 3\"/>\n\t\t<neighbor left=\"t 3\" right=\"transition 2\"/>\n\t\t<neighbor left=\"t 2\" right=\"transition 3\"/>\n\t\t<neighbor left=\"t 3\" right=\"turn\"/>\n\t\t<neighbor left=\"t 1\" right=\"turn 1\"/>\n\t\t<neighbor left=\"t 2\" right=\"turn 1\"/>\n\t\t<neighbor left=\"t 2\" right=\"turn 2\"/>\n\t\t<neighbor left=\"t 3\" right=\"viad 1\"/>\n\t\t<neighbor left=\"t 1\" right=\"viad\"/>\n\t\t<neighbor left=\"t 2\" right=\"viad\"/>\n\t\t<neighbor left=\"t 2\" right=\"vias 1\"/>\n\t\t<neighbor left=\"t 1\" right=\"vias 1\"/>\n\t\t<neighbor left=\"vias 1\" right=\"t 1\"/>\n\t\t<neighbor left=\"vias 2\" right=\"t 1\"/>\n\t\t<neighbor left=\"wire 1\" right=\"t 1\"/>\n\t\t<neighbor left=\"track\" right=\"track\"/>\n\t\t<neighbor left=\"track 1\" right=\"track 1\"/>\n\t\t<neighbor left=\"track 1\" right=\"transition 3\"/>\n\t\t<neighbor left=\"track\" right=\"transition 2\"/>\n\t\t<neighbor left=\"track\" right=\"turn\"/>\n\t\t<neighbor left=\"track 1\" right=\"turn 1\"/>\n\t\t<neighbor left=\"track\" right=\"viad 1\"/>\n\t\t<neighbor left=\"track 1\" right=\"viad\"/>\n\t\t<neighbor left=\"track\" right=\"vias 2\"/>\n\t\t<neighbor left=\"track\" right=\"vias 3\"/>\n\t\t<neighbor left=\"track 1\" right=\"vias 1\"/>\n\t\t<neighbor left=\"track\" right=\"wire 1\"/>\n\t\t<neighbor left=\"transition 2\" right=\"turn\"/>\n\t\t<neighbor left=\"transition\" right=\"turn\"/>\n\t\t<neighbor left=\"transition 1\" right=\"turn 1\"/>\n\t\t<neighbor left=\"transition 2\" right=\"viad 1\"/>\n\t\t<neighbor left=\"transition 2\" right=\"vias 2\"/>\n\t\t<neighbor left=\"transition 2\" right=\"vias 3\"/>\n\t\t<neighbor left=\"transition 2\" right=\"vias\"/>\n\t\t<neighbor left=\"wire\" right=\"transition 1\"/>\n\t\t<neighbor left=\"transition 2\" right=\"wire 1\"/>\n\t\t<neighbor left=\"turn 1\" right=\"turn\"/>\n\t\t<neighbor left=\"turn 2\" right=\"turn\"/>\n\t\t<neighbor left=\"turn\" right=\"turn 1\"/>\n\t\t<neighbor left=\"turn\" right=\"turn 2\"/>\n\t\t<neighbor left=\"turn 1\" right=\"viad 1\"/>\n\t\t<neighbor left=\"turn\" right=\"viad\"/>\n\t\t<neighbor left=\"turn 1\" right=\"vias 2\"/>\n\t\t<neighbor left=\"turn 1\" right=\"vias 3\"/>\n\t\t<neighbor left=\"turn 1\" right=\"vias\"/>\n\t\t<neighbor left=\"turn\" right=\"vias 1\"/>\n\t\t<neighbor left=\"turn 1\" right=\"wire 1\"/>\n\t\t<neighbor left=\"viad 1\" right=\"viad 1\"/>\n\t\t<neighbor left=\"viad 1\" right=\"vias 2\"/>\n\t\t<neighbor left=\"viad 1\" right=\"vias 3\"/>\n\t\t<neighbor left=\"viad 1\" right=\"wire 1\"/>\n\t\t<neighbor left=\"vias 1\" right=\"wire 1\"/>\n\t\t<neighbor left=\"vias 2\" right=\"wire 1\"/>\n\t\t<neighbor left=\"vias 1\" right=\"vias 3\"/>\n\t\t<neighbor left=\"vias 2\" right=\"vias 2\"/>\n\t\t<neighbor left=\"vias 2\" right=\"vias\"/>\n\t\t<neighbor left=\"wire\" right=\"wire\"/>\n\t\t<neighbor left=\"wire 1\" right=\"wire 1\"/>\n\t\t<neighbor left=\"bridge 1\" right=\"dskew\"/>\n\t\t<neighbor left=\"connection 3\" right=\"dskew\"/>\n\t\t<neighbor left=\"dskew\" right=\"dskew\"/>\n\t\t<neighbor left=\"skew\" right=\"dskew\"/>\n\t\t<neighbor left=\"t\" right=\"dskew\"/>\n\t\t<neighbor left=\"t 2\" right=\"dskew\"/>\n\t\t<neighbor left=\"t 1\" right=\"dskew\"/>\n\t\t<neighbor left=\"track 1\" right=\"dskew\"/>\n\t\t<neighbor left=\"transition 1\" right=\"dskew\"/>\n\t\t<neighbor left=\"turn 3\" right=\"dskew\"/>\n\t\t<neighbor left=\"viad\" right=\"dskew\"/>\n\t\t<neighbor left=\"vias 3\" right=\"dskew\"/>\n\t\t<neighbor left=\"skew\" right=\"bridge 1\"/>\n\t\t<neighbor left=\"skew\" right=\"connection 1\"/>\n\t\t<neighbor left=\"corner\" right=\"skew\"/>\n\t\t<neighbor left=\"corner 3\" right=\"skew\"/>\n\t\t<neighbor left=\"skew\" right=\"dskew\"/>\n\t\t<neighbor left=\"skew\" right=\"skew 2\"/>\n\t\t<neighbor left=\"skew 1\" right=\"skew\"/>\n\t\t<neighbor left=\"skew 1\" right=\"skew 3\"/>\n\t\t<neighbor left=\"substrate\" right=\"skew\"/>\n\t\t<neighbor left=\"t 3\" right=\"skew\"/>\n\t\t<neighbor left=\"t\" right=\"skew 2\"/>\n\t\t<neighbor left=\"t 2\" right=\"skew 2\"/>\n\t\t<neighbor left=\"t 1\" right=\"skew 2\"/>\n\t\t<neighbor left=\"track\" right=\"skew\"/>\n\t\t<neighbor left=\"track 1\" right=\"skew 2\"/>\n\t\t<neighbor left=\"transition\" right=\"skew\"/>\n\t\t<neighbor left=\"transition 1\" right=\"skew 2\"/>\n\t\t<neighbor left=\"turn 1\" right=\"skew\"/>\n\t\t<neighbor left=\"turn 2\" right=\"skew\"/>\n\t\t<neighbor left=\"turn 3\" right=\"skew 2\"/>\n\t\t<neighbor left=\"viad 1\" right=\"skew\"/>\n\t\t<neighbor left=\"viad\" right=\"skew 2\"/>\n\t\t<neighbor left=\"vias\" right=\"skew\"/>\n\t\t<neighbor left=\"vias 1\" right=\"skew\"/>\n\t\t<neighbor left=\"vias 2\" right=\"skew\"/>\n\t\t<neighbor left=\"vias 3\" right=\"skew 2\"/>\n\t\t<neighbor left=\"wire 1\" right=\"skew\"/>\n\t</neighbors>\n\t<subsets>\n\t\t<subset name=\"Turnless\">\n\t\t\t<tile name=\"bridge\"/>\n\t\t\t<tile name=\"component\"/>\n\t\t\t<tile name=\"connection\"/>\n\t\t\t<tile name=\"corner\"/>\n\t\t\t<tile name=\"substrate\"/>\n\t\t\t<tile name=\"t\"/>\n\t\t\t<tile name=\"track\"/>\n\t\t\t<tile name=\"transition\"/>\n\t\t\t<tile name=\"viad\"/>\n\t\t\t<tile name=\"vias\"/>\n\t\t\t<tile name=\"wire\"/>\n\t\t\t<tile name=\"skew\"/>\n\t\t\t<tile name=\"dskew\"/>\n\t\t</subset>\n\t</subsets>\n>>>>>>> 1fe4f1f60ebd57b99ca7148fb003edefa7979d94\n</set>"
  },
  {
    "path": "images/samples/Knots/data.xml",
    "content": "<<<<<<< HEAD\n<set size=\"10\">\r\n\t<tiles>\r\n\t\t<tile name=\"corner\" symmetry=\"L\"/>\r\n\t\t<tile name=\"cross\" symmetry=\"I\"/>\r\n\t\t<tile name=\"empty\" symmetry=\"X\"/>\r\n\t\t<tile name=\"line\" symmetry=\"I\"/>\r\n\t\t<tile name=\"t\" symmetry=\"T\"/>\r\n\t</tiles>\r\n\t<neighbors>\r\n\t\t<neighbor left=\"corner 1\" right=\"empty\"/>\r\n\t\t<neighbor left=\"corner\" right=\"cross\"/>\r\n\t\t<neighbor left=\"corner\" right=\"cross 1\"/>\r\n\t\t<neighbor left=\"corner\" right=\"line\"/>\r\n\t\t<neighbor left=\"corner 1\" right=\"line 1\"/>\r\n\t\t<neighbor left=\"corner\" right=\"t 2\"/>\r\n\t\t<neighbor left=\"corner\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"corner\" right=\"t\"/>\r\n\t\t<neighbor left=\"corner 1\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"corner 1\" right=\"corner 3\"/>\r\n\t\t<neighbor left=\"corner 1\" right=\"corner\"/>\r\n\t\t<neighbor left=\"corner\" right=\"corner 1\"/>\r\n\t\t<neighbor left=\"corner\" right=\"corner 2\"/>\r\n\t\t<neighbor left=\"cross\" right=\"cross\"/>\r\n\t\t<neighbor left=\"cross\" right=\"cross 1\"/>\r\n\t\t<neighbor left=\"cross 1\" right=\"cross 1\"/>\r\n\t\t<neighbor left=\"cross\" right=\"line\"/>\r\n\t\t<neighbor left=\"cross 1\" right=\"line\"/>\r\n\t\t<neighbor left=\"cross\" right=\"t\"/>\r\n\t\t<neighbor left=\"cross\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"cross 1\" right=\"t\"/>\r\n\t\t<neighbor left=\"cross 1\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"empty\" right=\"empty\"/>\r\n\t\t<neighbor left=\"empty\" right=\"line 1\"/>\r\n\t\t<neighbor left=\"empty\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"line\" right=\"line\"/>\r\n\t\t<neighbor left=\"line 1\" right=\"line 1\"/>\r\n\t\t<neighbor left=\"line\" right=\"t\"/>\r\n\t\t<neighbor left=\"line 1\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"line\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"t\" right=\"t\"/>\r\n\t\t<neighbor left=\"t 2\" right=\"t\"/>\r\n\t\t<neighbor left=\"t 1\" right=\"t\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"t 1\"/>\r\n\t</neighbors>\r\n\t<subsets>\r\n\t\t<subset name=\"Standard\">\r\n\t\t\t<tile name=\"corner\"/>\r\n\t\t\t<tile name=\"cross\"/>\r\n\t\t\t<tile name=\"empty\"/>\r\n\t\t\t<tile name=\"line\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"Dense\">\r\n\t\t\t<tile name=\"corner\"/>\r\n\t\t\t<tile name=\"cross\"/>\r\n\t\t\t<tile name=\"line\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"Crossless\">\r\n\t\t\t<tile name=\"corner\"/>\r\n\t\t\t<tile name=\"empty\"/>\r\n\t\t\t<tile name=\"line\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"TE\">\r\n\t\t\t<tile name=\"t\"/>\r\n\t\t\t<tile name=\"empty\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"T\">\r\n\t\t\t<tile name=\"t\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"CL\">\r\n\t\t\t<tile name=\"corner\"/>\r\n\t\t\t<tile name=\"line\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"CE\">\r\n\t\t\t<tile name=\"corner\"/>\r\n\t\t\t<tile name=\"empty\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"C\">\r\n\t\t\t<tile name=\"corner\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"Fabric\">\r\n\t\t\t<tile name=\"cross\"/>\r\n\t\t\t<tile name=\"line\"/>\r\n\t\t</subset>\r\n\t\t<subset name=\"Dense Fabric\">\r\n\t\t\t<tile name=\"cross\"/>\r\n\t\t</subset>\r\n\t</subsets>\r\n=======\n<set size=\"10\">\n\t<tiles>\n\t\t<tile name=\"corner\" symmetry=\"L\"/>\n\t\t<tile name=\"cross\" symmetry=\"I\"/>\n\t\t<tile name=\"empty\" symmetry=\"X\"/>\n\t\t<tile name=\"line\" symmetry=\"I\"/>\n\t\t<tile name=\"t\" symmetry=\"T\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"corner 1\" right=\"empty\"/>\n\t\t<neighbor left=\"corner\" right=\"cross\"/>\n\t\t<neighbor left=\"corner\" right=\"cross 1\"/>\n\t\t<neighbor left=\"corner\" right=\"line\"/>\n\t\t<neighbor left=\"corner 1\" right=\"line 1\"/>\n\t\t<neighbor left=\"corner\" right=\"t 2\"/>\n\t\t<neighbor left=\"corner\" right=\"t 3\"/>\n\t\t<neighbor left=\"corner\" right=\"t\"/>\n\t\t<neighbor left=\"corner 1\" right=\"t 1\"/>\n\t\t<neighbor left=\"corner 1\" right=\"corner 3\"/>\n\t\t<neighbor left=\"corner 1\" right=\"corner\"/>\n\t\t<neighbor left=\"corner\" right=\"corner 1\"/>\n\t\t<neighbor left=\"corner\" right=\"corner 2\"/>\n\t\t<neighbor left=\"cross\" right=\"cross\"/>\n\t\t<neighbor left=\"cross\" right=\"cross 1\"/>\n\t\t<neighbor left=\"cross 1\" right=\"cross 1\"/>\n\t\t<neighbor left=\"cross\" right=\"line\"/>\n\t\t<neighbor left=\"cross 1\" right=\"line\"/>\n\t\t<neighbor left=\"cross\" right=\"t\"/>\n\t\t<neighbor left=\"cross\" right=\"t 3\"/>\n\t\t<neighbor left=\"cross 1\" right=\"t\"/>\n\t\t<neighbor left=\"cross 1\" right=\"t 3\"/>\n\t\t<neighbor left=\"empty\" right=\"empty\"/>\n\t\t<neighbor left=\"empty\" right=\"line 1\"/>\n\t\t<neighbor left=\"empty\" right=\"t 1\"/>\n\t\t<neighbor left=\"line\" right=\"line\"/>\n\t\t<neighbor left=\"line 1\" right=\"line 1\"/>\n\t\t<neighbor left=\"line\" right=\"t\"/>\n\t\t<neighbor left=\"line 1\" right=\"t 1\"/>\n\t\t<neighbor left=\"line\" right=\"t 3\"/>\n\t\t<neighbor left=\"t 1\" right=\"t 3\"/>\n\t\t<neighbor left=\"t\" right=\"t\"/>\n\t\t<neighbor left=\"t 2\" right=\"t\"/>\n\t\t<neighbor left=\"t 1\" right=\"t\"/>\n\t\t<neighbor left=\"t 3\" right=\"t 1\"/>\n\t</neighbors>\n\t<subsets>\n\t\t<subset name=\"Standard\">\n\t\t\t<tile name=\"corner\"/>\n\t\t\t<tile name=\"cross\"/>\n\t\t\t<tile name=\"empty\"/>\n\t\t\t<tile name=\"line\"/>\n\t\t</subset>\n\t\t<subset name=\"Dense\">\n\t\t\t<tile name=\"corner\"/>\n\t\t\t<tile name=\"cross\"/>\n\t\t\t<tile name=\"line\"/>\n\t\t</subset>\n\t\t<subset name=\"Crossless\">\n\t\t\t<tile name=\"corner\"/>\n\t\t\t<tile name=\"empty\"/>\n\t\t\t<tile name=\"line\"/>\n\t\t</subset>\n\t\t<subset name=\"TE\">\n\t\t\t<tile name=\"t\"/>\n\t\t\t<tile name=\"empty\"/>\n\t\t</subset>\n\t\t<subset name=\"T\">\n\t\t\t<tile name=\"t\"/>\n\t\t</subset>\n\t\t<subset name=\"CL\">\n\t\t\t<tile name=\"corner\"/>\n\t\t\t<tile name=\"line\"/>\n\t\t</subset>\n\t\t<subset name=\"CE\">\n\t\t\t<tile name=\"corner\"/>\n\t\t\t<tile name=\"empty\"/>\n\t\t</subset>\n\t\t<subset name=\"C\">\n\t\t\t<tile name=\"corner\"/>\n\t\t</subset>\n\t\t<subset name=\"Fabric\">\n\t\t\t<tile name=\"cross\"/>\n\t\t\t<tile name=\"line\"/>\n\t\t</subset>\n\t\t<subset name=\"Dense Fabric\">\n\t\t\t<tile name=\"cross\"/>\n\t\t</subset>\n\t</subsets>\n>>>>>>> 1fe4f1f60ebd57b99ca7148fb003edefa7979d94\n</set>"
  },
  {
    "path": "images/samples/Rooms/data.xml",
    "content": "<<<<<<< HEAD\n<set size=\"3\">\r\n\t<tiles>\r\n\t\t<tile name=\"bend\" symmetry=\"L\" weight=\"0.5\"/>\r\n\t\t<tile name=\"corner\" symmetry=\"L\" weight=\"0.5\"/>\r\n\t\t<tile name=\"corridor\" symmetry=\"I\" weight=\"1.0\"/>\r\n\t\t<tile name=\"door\" symmetry=\"T\" weight=\"0.5\"/>\r\n\t\t<tile name=\"empty\" symmetry=\"X\"/>\r\n\t\t<tile name=\"side\" symmetry=\"T\" weight=\"2.0\"/>\r\n\t\t<tile name=\"t\" symmetry=\"T\" weight=\"0.5\"/>\r\n\t\t<tile name=\"turn\" symmetry=\"L\" weight=\"0.25\"/>\r\n\t\t<tile name=\"wall\" symmetry=\"X\"/>\r\n\t</tiles>\r\n\t<neighbors>\r\n\t\t<neighbor left=\"corner 1\" right=\"corner\"/>\r\n\t\t<neighbor left=\"corner 2\" right=\"corner\"/>\r\n\t\t<neighbor left=\"corner\" right=\"door\"/>\r\n\t\t<neighbor left=\"corner\" right=\"side 2\"/>\r\n\t\t<neighbor left=\"corner 1\" right=\"side 1\"/>\r\n\t\t<neighbor left=\"corner 1\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"corner 1\" right=\"turn\"/>\r\n\t\t<neighbor left=\"corner 2\" right=\"turn\"/>\r\n\t\t<neighbor left=\"wall\" right=\"corner\"/>\r\n\t\t<neighbor left=\"corridor 1\" right=\"corridor 1\"/>\r\n\t\t<neighbor left=\"corridor 1\" right=\"door 3\"/>\r\n\t\t<neighbor left=\"corridor\" right=\"side 1\"/>\r\n\t\t<neighbor left=\"corridor 1\" right=\"t\"/>\r\n\t\t<neighbor left=\"corridor 1\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"corridor 1\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"corridor\" right=\"wall\"/>\r\n\t\t<neighbor left=\"door 1\" right=\"door 3\"/>\r\n\t\t<neighbor left=\"door 3\" right=\"empty\"/>\r\n\t\t<neighbor left=\"door\" right=\"side 2\"/>\r\n\t\t<neighbor left=\"door 1\" right=\"t\"/>\r\n\t\t<neighbor left=\"door 1\" right=\"t 3\"/>\r\n\t\t<neighbor left=\"door 1\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"empty\" right=\"empty\"/>\r\n\t\t<neighbor left=\"empty\" right=\"side 3\"/>\r\n\t\t<neighbor left=\"side\" right=\"side\"/>\r\n\t\t<neighbor left=\"side 3\" right=\"side 1\"/>\r\n\t\t<neighbor left=\"side 3\" right=\"t 1\"/>\r\n\t\t<neighbor left=\"side 3\" right=\"turn\"/>\r\n\t\t<neighbor left=\"side 3\" right=\"wall\"/>\r\n\t\t<neighbor left=\"t\" right=\"t 2\"/>\r\n\t\t<neighbor left=\"t\" right=\"turn 1\"/>\r\n\t\t<neighbor left=\"t 3\" right=\"wall\"/>\r\n\t\t<neighbor left=\"turn\" right=\"turn 2\"/>\r\n\t\t<neighbor left=\"turn 1\" right=\"wall\"/>\r\n\t\t<neighbor left=\"wall\" right=\"wall\"/>\r\n\t\t<neighbor left=\"bend\" right=\"bend 1\"/>\r\n\t\t<neighbor left=\"corner\" right=\"bend 2\"/>\r\n\t\t<neighbor left=\"door\" right=\"bend 2\"/>\r\n\t\t<neighbor left=\"empty\" right=\"bend\"/>\r\n\t\t<neighbor left=\"side\" right=\"bend 1\"/>\r\n\t</neighbors>\r\n=======\n<set size=\"3\">\n\t<tiles>\n\t\t<tile name=\"bend\" symmetry=\"L\" weight=\"0.5\"/>\n\t\t<tile name=\"corner\" symmetry=\"L\" weight=\"0.5\"/>\n\t\t<tile name=\"corridor\" symmetry=\"I\" weight=\"1.0\"/>\n\t\t<tile name=\"door\" symmetry=\"T\" weight=\"0.5\"/>\n\t\t<tile name=\"empty\" symmetry=\"X\"/>\n\t\t<tile name=\"side\" symmetry=\"T\" weight=\"2.0\"/>\n\t\t<tile name=\"t\" symmetry=\"T\" weight=\"0.5\"/>\n\t\t<tile name=\"turn\" symmetry=\"L\" weight=\"0.25\"/>\n\t\t<tile name=\"wall\" symmetry=\"X\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"corner 1\" right=\"corner\"/>\n\t\t<neighbor left=\"corner 2\" right=\"corner\"/>\n\t\t<neighbor left=\"corner\" right=\"door\"/>\n\t\t<neighbor left=\"corner\" right=\"side 2\"/>\n\t\t<neighbor left=\"corner 1\" right=\"side 1\"/>\n\t\t<neighbor left=\"corner 1\" right=\"t 1\"/>\n\t\t<neighbor left=\"corner 1\" right=\"turn\"/>\n\t\t<neighbor left=\"corner 2\" right=\"turn\"/>\n\t\t<neighbor left=\"wall\" right=\"corner\"/>\n\t\t<neighbor left=\"corridor 1\" right=\"corridor 1\"/>\n\t\t<neighbor left=\"corridor 1\" right=\"door 3\"/>\n\t\t<neighbor left=\"corridor\" right=\"side 1\"/>\n\t\t<neighbor left=\"corridor 1\" right=\"t\"/>\n\t\t<neighbor left=\"corridor 1\" right=\"t 3\"/>\n\t\t<neighbor left=\"corridor 1\" right=\"turn 1\"/>\n\t\t<neighbor left=\"corridor\" right=\"wall\"/>\n\t\t<neighbor left=\"door 1\" right=\"door 3\"/>\n\t\t<neighbor left=\"door 3\" right=\"empty\"/>\n\t\t<neighbor left=\"door\" right=\"side 2\"/>\n\t\t<neighbor left=\"door 1\" right=\"t\"/>\n\t\t<neighbor left=\"door 1\" right=\"t 3\"/>\n\t\t<neighbor left=\"door 1\" right=\"turn 1\"/>\n\t\t<neighbor left=\"empty\" right=\"empty\"/>\n\t\t<neighbor left=\"empty\" right=\"side 3\"/>\n\t\t<neighbor left=\"side\" right=\"side\"/>\n\t\t<neighbor left=\"side 3\" right=\"side 1\"/>\n\t\t<neighbor left=\"side 3\" right=\"t 1\"/>\n\t\t<neighbor left=\"side 3\" right=\"turn\"/>\n\t\t<neighbor left=\"side 3\" right=\"wall\"/>\n\t\t<neighbor left=\"t\" right=\"t 2\"/>\n\t\t<neighbor left=\"t\" right=\"turn 1\"/>\n\t\t<neighbor left=\"t 3\" right=\"wall\"/>\n\t\t<neighbor left=\"turn\" right=\"turn 2\"/>\n\t\t<neighbor left=\"turn 1\" right=\"wall\"/>\n\t\t<neighbor left=\"wall\" right=\"wall\"/>\n\t\t<neighbor left=\"bend\" right=\"bend 1\"/>\n\t\t<neighbor left=\"corner\" right=\"bend 2\"/>\n\t\t<neighbor left=\"door\" right=\"bend 2\"/>\n\t\t<neighbor left=\"empty\" right=\"bend\"/>\n\t\t<neighbor left=\"side\" right=\"bend 1\"/>\n\t</neighbors>\n>>>>>>> 1fe4f1f60ebd57b99ca7148fb003edefa7979d94\n</set>"
  },
  {
    "path": "images/samples/Summer/data.xml",
    "content": "<<<<<<< HEAD\n<set size=\"48\" unique=\"True\">\r\n\t<tiles>\r\n\t\t<tile name=\"cliff\" symmetry=\"T\"/>\r\n\t\t<tile name=\"cliffcorner\" symmetry=\"L\"/>\r\n\t\t<tile name=\"cliffturn\" symmetry=\"L\"/>\r\n\t\t<tile name=\"grass\" symmetry=\"X\"/>\r\n\t\t<tile name=\"grasscorner\" symmetry=\"L\" weight=\"0.0001\"/>\r\n\t\t<tile name=\"road\" symmetry=\"T\" weight=\"0.1\"/>\r\n\t\t<tile name=\"roadturn\" symmetry=\"L\" weight=\"0.1\"/>\r\n\t\t<tile name=\"water_a\" symmetry=\"X\"/>\r\n\t\t<tile name=\"water_b\" symmetry=\"X\"/>\r\n\t\t<tile name=\"water_c\" symmetry=\"X\"/>\r\n\t\t<tile name=\"watercorner\" symmetry=\"L\"/>\r\n\t\t<tile name=\"waterside\" symmetry=\"T\"/>\r\n\t\t<tile name=\"waterturn\" symmetry=\"L\"/>\r\n\t</tiles>\r\n\t<neighbors>\r\n\t\t<neighbor left=\"cliff 0\" right=\"cliff 0\"/>\r\n\t\t<neighbor left=\"cliff 2\" right=\"cliffcorner 1\"/>\r\n\t\t<neighbor left=\"cliff 2\" right=\"cliffturn 2\"/>\r\n\t\t<neighbor left=\"cliff 1\" right=\"grass 0\"/>\r\n\t\t<neighbor left=\"grass 0\" right=\"cliff 1\"/>\r\n\t\t<neighbor left=\"cliff 1\" right=\"road 3\"/>\r\n\t\t<neighbor left=\"road 1\" right=\"cliff 1\"/>\r\n\t\t<neighbor left=\"cliff 1\" right=\"roadturn 0\"/>\r\n\t\t<neighbor left=\"roadturn 1\" right=\"cliff 1\"/>\r\n\t\t<neighbor left=\"cliffcorner 0\" right=\"cliffturn 2\"/>\r\n\t\t<neighbor left=\"cliffcorner 1\" right=\"road 3\"/>\r\n\t\t<neighbor left=\"cliffcorner 1\" right=\"roadturn 0\"/>\r\n\t\t<neighbor left=\"cliffcorner 1\" right=\"roadturn 3\"/>\r\n\t\t<neighbor left=\"cliffcorner 1\" right=\"grass 0\"/>\r\n\t\t<neighbor left=\"cliffturn 1\" right=\"grass 0\"/>\r\n\t\t<neighbor left=\"cliffturn 1\" right=\"road 3\"/>\r\n\t\t<neighbor left=\"cliffturn 1\" right=\"roadturn 0\"/>\r\n\t\t<neighbor left=\"cliffturn 1\" right=\"roadturn 3\"/>\r\n\t\t<neighbor left=\"grass 0\" right=\"grass 0\"/>\r\n\t\t<neighbor left=\"grass 0\" right=\"road 3\"/>\r\n\t\t<neighbor left=\"grass 0\" right=\"roadturn 0\"/>\r\n\t\t<neighbor left=\"grass 0\" right=\"watercorner 0\"/>\r\n\t\t<neighbor left=\"grass 0\" right=\"waterside 3\"/>\r\n\t\t<neighbor left=\"grasscorner 1\" right=\"grasscorner 0\"/>\r\n\t\t<neighbor left=\"grasscorner 1\" right=\"grasscorner 3\"/>\r\n\t\t<neighbor left=\"grasscorner 1\" right=\"road 1\"/>\r\n\t\t<neighbor left=\"grasscorner 3\" right=\"road 0\"/>\r\n\t\t<neighbor left=\"grasscorner 3\" right=\"roadturn 1\"/>\r\n\t\t<neighbor left=\"road 3\" right=\"road 1\"/>\r\n\t\t<neighbor left=\"road 0\" right=\"road 0\"/>\r\n\t\t<neighbor left=\"road 0\" right=\"roadturn 1\"/>\r\n\t\t<neighbor left=\"road 1\" right=\"watercorner 0\"/>\r\n\t\t<neighbor left=\"road 1\" right=\"waterside 3\"/>\r\n\t\t<neighbor left=\"roadturn 1\" right=\"watercorner 0\"/>\r\n\t\t<neighbor left=\"roadturn 1\" right=\"watercorner 3\"/>\r\n\t\t<neighbor left=\"roadturn 1\" right=\"waterside 3\"/>\r\n\t\t<neighbor left=\"water_a 0\" right=\"water_a 0\"/>\r\n\t\t<neighbor left=\"water_a 0\" right=\"water_b 0\"/>\r\n\t\t<neighbor left=\"water_a 0\" right=\"water_c 0\"/>\r\n\t\t<neighbor left=\"water_a 0\" right=\"waterside 1\"/>\r\n\t\t<neighbor left=\"water_a 0\" right=\"waterturn 1\"/>\r\n\t\t<neighbor left=\"water_b 0\" right=\"waterside 1\"/>\r\n\t\t<neighbor left=\"water_b 0\" right=\"waterturn 1\"/>\r\n\t\t<neighbor left=\"water_c 0\" right=\"waterside 1\"/>\r\n\t\t<neighbor left=\"water_c 0\" right=\"waterturn 1\"/>\r\n\t\t<neighbor left=\"watercorner 0\" right=\"waterside 0\"/>\r\n\t\t<neighbor left=\"watercorner 0\" right=\"waterturn 0\"/>\r\n\t\t<neighbor left=\"waterside 0\" right=\"waterside 0\"/>\r\n\t\t<neighbor left=\"waterside 0\" right=\"waterturn 0\"/>\r\n\t</neighbors>\r\n=======\n<set size=\"48\" unique=\"True\">\n\t<tiles>\n\t\t<tile name=\"cliff\" symmetry=\"T\"/>\n\t\t<tile name=\"cliffcorner\" symmetry=\"L\"/>\n\t\t<tile name=\"cliffturn\" symmetry=\"L\"/>\n\t\t<tile name=\"grass\" symmetry=\"X\"/>\n\t\t<tile name=\"grasscorner\" symmetry=\"L\" weight=\"0.0001\"/>\n\t\t<tile name=\"road\" symmetry=\"T\" weight=\"0.1\"/>\n\t\t<tile name=\"roadturn\" symmetry=\"L\" weight=\"0.1\"/>\n\t\t<tile name=\"water_a\" symmetry=\"X\"/>\n\t\t<tile name=\"water_b\" symmetry=\"X\"/>\n\t\t<tile name=\"water_c\" symmetry=\"X\"/>\n\t\t<tile name=\"watercorner\" symmetry=\"L\"/>\n\t\t<tile name=\"waterside\" symmetry=\"T\"/>\n\t\t<tile name=\"waterturn\" symmetry=\"L\"/>\n\t</tiles>\n\t<neighbors>\n\t\t<neighbor left=\"cliff 0\" right=\"cliff 0\"/>\n\t\t<neighbor left=\"cliff 2\" right=\"cliffcorner 1\"/>\n\t\t<neighbor left=\"cliff 2\" right=\"cliffturn 2\"/>\n\t\t<neighbor left=\"cliff 1\" right=\"grass 0\"/>\n\t\t<neighbor left=\"grass 0\" right=\"cliff 1\"/>\n\t\t<neighbor left=\"cliff 1\" right=\"road 3\"/>\n\t\t<neighbor left=\"road 1\" right=\"cliff 1\"/>\n\t\t<neighbor left=\"cliff 1\" right=\"roadturn 0\"/>\n\t\t<neighbor left=\"roadturn 1\" right=\"cliff 1\"/>\n\t\t<neighbor left=\"cliffcorner 0\" right=\"cliffturn 2\"/>\n\t\t<neighbor left=\"cliffcorner 1\" right=\"road 3\"/>\n\t\t<neighbor left=\"cliffcorner 1\" right=\"roadturn 0\"/>\n\t\t<neighbor left=\"cliffcorner 1\" right=\"roadturn 3\"/>\n\t\t<neighbor left=\"cliffcorner 1\" right=\"grass 0\"/>\n\t\t<neighbor left=\"cliffturn 1\" right=\"grass 0\"/>\n\t\t<neighbor left=\"cliffturn 1\" right=\"road 3\"/>\n\t\t<neighbor left=\"cliffturn 1\" right=\"roadturn 0\"/>\n\t\t<neighbor left=\"cliffturn 1\" right=\"roadturn 3\"/>\n\t\t<neighbor left=\"grass 0\" right=\"grass 0\"/>\n\t\t<neighbor left=\"grass 0\" right=\"road 3\"/>\n\t\t<neighbor left=\"grass 0\" right=\"roadturn 0\"/>\n\t\t<neighbor left=\"grass 0\" right=\"watercorner 0\"/>\n\t\t<neighbor left=\"grass 0\" right=\"waterside 3\"/>\n\t\t<neighbor left=\"grasscorner 1\" right=\"grasscorner 0\"/>\n\t\t<neighbor left=\"grasscorner 1\" right=\"grasscorner 3\"/>\n\t\t<neighbor left=\"grasscorner 1\" right=\"road 1\"/>\n\t\t<neighbor left=\"grasscorner 3\" right=\"road 0\"/>\n\t\t<neighbor left=\"grasscorner 3\" right=\"roadturn 1\"/>\n\t\t<neighbor left=\"road 3\" right=\"road 1\"/>\n\t\t<neighbor left=\"road 0\" right=\"road 0\"/>\n\t\t<neighbor left=\"road 0\" right=\"roadturn 1\"/>\n\t\t<neighbor left=\"road 1\" right=\"watercorner 0\"/>\n\t\t<neighbor left=\"road 1\" right=\"waterside 3\"/>\n\t\t<neighbor left=\"roadturn 1\" right=\"watercorner 0\"/>\n\t\t<neighbor left=\"roadturn 1\" right=\"watercorner 3\"/>\n\t\t<neighbor left=\"roadturn 1\" right=\"waterside 3\"/>\n\t\t<neighbor left=\"water_a 0\" right=\"water_a 0\"/>\n\t\t<neighbor left=\"water_a 0\" right=\"water_b 0\"/>\n\t\t<neighbor left=\"water_a 0\" right=\"water_c 0\"/>\n\t\t<neighbor left=\"water_a 0\" right=\"waterside 1\"/>\n\t\t<neighbor left=\"water_a 0\" right=\"waterturn 1\"/>\n\t\t<neighbor left=\"water_b 0\" right=\"waterside 1\"/>\n\t\t<neighbor left=\"water_b 0\" right=\"waterturn 1\"/>\n\t\t<neighbor left=\"water_c 0\" right=\"waterside 1\"/>\n\t\t<neighbor left=\"water_c 0\" right=\"waterturn 1\"/>\n\t\t<neighbor left=\"watercorner 0\" right=\"waterside 0\"/>\n\t\t<neighbor left=\"watercorner 0\" right=\"waterturn 0\"/>\n\t\t<neighbor left=\"waterside 0\" right=\"waterside 0\"/>\n\t\t<neighbor left=\"waterside 0\" right=\"waterturn 0\"/>\n\t</neighbors>\n>>>>>>> 1fe4f1f60ebd57b99ca7148fb003edefa7979d94\n</set>"
  },
  {
    "path": "pyproject.toml",
    "content": "[project]\nname = \"wfc_python\"\nversion = \"0.0.0\"\ndescription = \"Implementation of wave function collapse in Python.\"\nreadme = \"README.md\"\nrequires-python = \">=3.5\"\nlicense = {file = \"LICENSE\"}\nkeywords = [\"sample\", \"wfc\", \"wave function collapse\"]\nauthors = [{name = \"Isaac Karth\", email = \"isaac@isaackarth.com\"}]\nclassifiers = [\n    \"Development Status :: 3 - Alpha\",\n    \"Topic :: Utilities\",\n    \"License :: OSI Approved :: MIT License\",\n]\n\ndependencies  = [\n    \"hilbertcurve\",\n    \"imageio\",\n    \"matplotlib\",\n    \"numpy\",\n    \"scipy\"\n]\n\n[project.optional-dependencies]\ntests = [\"pytest\"]\ndocs = [\"sphinx\"]\n\n[project.urls]\nhomepage = \"https://github.com/ikarth/wfc_python\"\n\n[build-system]\nrequires = [\"setuptools\", \"wheel\"]\nbuild-backend = \"setuptools.build_meta\"\n"
  },
  {
    "path": "requirements.txt",
    "content": "numpy\nhilbertcurve>=2\nimageio\nmatplotlib\nscipy\n\n# Testing\npytest\ntypes-setuptools\n\n# Documentation.\nsphinx\n"
  },
  {
    "path": "samples.xml",
    "content": "<samples>\n\t<overlapping name=\"Red Maze\" N=\"2\" periodic=\"True\" width=\"8\" height=\"6\"/>\n</samples>\n"
  },
  {
    "path": "samples_cats.xml",
    "content": "<samples>\n\t<overlapping name=\"Cat\" N=\"3\" symmetry=\"2\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Cats\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Skyline 2\" N=\"3\" symmetry=\"2\" periodic=\"True\" ground=\"-1\"/>\n\t<overlapping name=\"Angular\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"City\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Colored City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Dungeon\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Lake\" N=\"3\" periodic=\"True\" width=\"60\" height=\"60\"/>\n\t<overlapping name=\"Link\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Link 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mazelike\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Nested\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Magic Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Office 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Qud\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Red Dot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Scaled Maze\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Sewers\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 1\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Smile City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Spirals\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Town\" N=\"3\" periodic=\"True\"/>\n\n\t<overlapping name=\"3Bricks\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" limit=\"50\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\" periodic=\"False\" limit=\"15\"/>\n\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"5\" height=\"5\" periodic=\"True\" textOutput=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"6\" height=\"6\" periodic=\"False\" textOutput=\"True\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"7\" height=\"7\" periodic=\"True\" textOutput=\"True\"/>\n</samples>\n"
  },
  {
    "path": "samples_original.xml",
    "content": "<samples>\n\t<overlapping name=\"Chess\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Chess\" N=\"2\" width=\"47\" height=\"47\" periodic=\"True\" screenshots=\"1\"/>\n\t<overlapping name=\"Hogs\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Hogs\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Less Rooms\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mountains\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Paths\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"2\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Red Maze\" N=\"2\"/>\n\t<overlapping name=\"Rooms\" N=\"3\" screenshots=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Rule 126\" N=\"3\" symmetry=\"2\" periodicInput=\"False\" periodic=\"False\"/>\n\t<overlapping name=\"Simple Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Maze\" N=\"2\"/>\n\t<overlapping name=\"Simple Wall\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"2\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Trick Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Water\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\"/>\n\t<simpletiled name=\"Castle\" width=\"20\" height=\"20\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"34\" height=\"34\" periodic=\"True\" screenshots=\"3\"/>\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Dense\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Crossless\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"TE\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"T\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"CL\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"CE\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"C\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Fabric\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Dense Fabric\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Rooms\" width=\"30\" height=\"30\"/>\n\t<simpletiled name=\"Circles\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"Large Circles\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"Large Circles and Solid\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"No Solid\" width=\"24\" height=\"24\"/>\n\n\t<overlapping name=\"Cat\" N=\"3\" symmetry=\"2\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Cats\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Skyline 2\" N=\"3\" symmetry=\"2\" periodic=\"True\" ground=\"-1\"/>\n\t<overlapping name=\"Angular\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"City\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Colored City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Dungeon\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Lake\" N=\"3\" periodic=\"True\" width=\"60\" height=\"60\"/>\n\t<overlapping name=\"Link\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Link 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mazelike\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Nested\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Magic Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Office 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Qud\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Red Dot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Scaled Maze\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Sewers\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 1\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Smile City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Spirals\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Town\" N=\"3\" periodic=\"True\"/>\n\n\t<overlapping name=\"3Bricks\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" limit=\"50\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\" periodic=\"False\" limit=\"15\"/>\n\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"5\" height=\"5\" periodic=\"True\" textOutput=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"6\" height=\"6\" periodic=\"False\" textOutput=\"True\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"7\" height=\"7\" periodic=\"True\" textOutput=\"True\"/>\n</samples>\n"
  },
  {
    "path": "samples_reference.xml",
    "content": "<samples>\n\t<comment name=\"Skyline\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Chess\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Chess\" N=\"2\" width=\"47\" height=\"47\" periodic=\"True\" screenshots=\"1\"/>\n\t<overlapping name=\"Flowers\" N=\"3\" symmetry=\"2\" ground=\"-4\" periodic=\"True\"/>\n\t<overlapping name=\"Hogs\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Hogs\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Less Rooms\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mountains\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Paths\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"2\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Red Maze\" N=\"2\"/>\n\t<overlapping name=\"Rooms\" N=\"3\" screenshots=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Rule 126\" N=\"3\" symmetry=\"2\" periodicInput=\"False\" periodic=\"False\"/>\n\t<overlapping name=\"Simple Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Maze\" N=\"2\"/>\n\t<overlapping name=\"Simple Wall\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"2\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Trick Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Water\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<overlapping name=\"Skyline\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\"/>\n\t<simpletiled name=\"Castle\" width=\"20\" height=\"20\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"34\" height=\"34\" periodic=\"True\" screenshots=\"3\"/>\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Dense\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Crossless\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"TE\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"T\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"CL\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"CE\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"C\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Fabric\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Dense Fabric\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Rooms\" width=\"30\" height=\"30\"/>\n\t<simpletiled name=\"Circles\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"Large Circles\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"Large Circles and Solid\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"No Solid\" width=\"24\" height=\"24\"/>\n\n\t<overlapping name=\"Cat\" N=\"3\" symmetry=\"2\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Cats\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Skyline 2\" N=\"3\" symmetry=\"2\" periodic=\"True\" ground=\"-1\"/>\n\t<overlapping name=\"Angular\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"City\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Colored City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Dungeon\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Lake\" N=\"3\" periodic=\"True\" width=\"60\" height=\"60\"/>\n\t<overlapping name=\"Link\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Link 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mazelike\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Nested\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Magic Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Office 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Qud\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Red Dot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Scaled Maze\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Sewers\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 1\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Smile City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Spirals\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Town\" N=\"3\" periodic=\"True\"/>\n\n\t<overlapping name=\"3Bricks\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" limit=\"50\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\" periodic=\"False\" limit=\"15\"/>\n\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"5\" height=\"5\" periodic=\"True\" textOutput=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"6\" height=\"6\" periodic=\"False\" textOutput=\"True\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"7\" height=\"7\" periodic=\"True\" textOutput=\"True\"/>\n</samples>\n"
  },
  {
    "path": "samples_reference_continue.xml",
    "content": "<samples>\n\t<overlapping name=\"Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Less Rooms\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mountains\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Paths\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"2\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Red Maze\" N=\"2\"/>\n\t<overlapping name=\"Rooms\" N=\"3\" screenshots=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Rule 126\" N=\"3\" symmetry=\"2\" periodicInput=\"False\" periodic=\"False\"/>\n\t<overlapping name=\"Simple Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Maze\" N=\"2\"/>\n\t<overlapping name=\"Simple Wall\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"2\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Trick Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Water\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\"/>\n\t<simpletiled name=\"Castle\" width=\"20\" height=\"20\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"34\" height=\"34\" periodic=\"True\" screenshots=\"3\"/>\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Dense\" width=\"24\" height=\"24\" periodic=\"True\"/>\t\n\t<simpletiled name=\"Knots\" subset=\"Crossless\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"TE\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"T\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"CL\" width=\"24\" height=\"24\"/>\t\n\t<simpletiled name=\"Knots\" subset=\"CE\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"C\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Fabric\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Dense Fabric\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Rooms\" width=\"30\" height=\"30\"/>\n\t<simpletiled name=\"Circles\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"Large Circles\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"Large Circles and Solid\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"No Solid\" width=\"24\" height=\"24\"/>\n\t\n\t<overlapping name=\"Cat\" N=\"3\" symmetry=\"2\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Cats\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Skyline 2\" N=\"3\" symmetry=\"2\" periodic=\"True\" ground=\"-1\"/>\n\t<overlapping name=\"Angular\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"City\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Colored City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Dungeon\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Lake\" N=\"3\" periodic=\"True\" width=\"60\" height=\"60\"/>\n\t<overlapping name=\"Link\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Link 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mazelike\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Nested\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Magic Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Office 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Qud\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Red Dot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Scaled Maze\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Sewers\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 1\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Smile City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Spirals\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Town\" N=\"3\" periodic=\"True\"/>\n\t\n\t<overlapping name=\"3Bricks\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" limit=\"50\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\" periodic=\"False\" limit=\"15\"/>\n\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"5\" height=\"5\" periodic=\"True\" textOutput=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"6\" height=\"6\" periodic=\"False\" textOutput=\"True\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"7\" height=\"7\" periodic=\"True\" textOutput=\"True\"/>\n</samples>\n"
  },
  {
    "path": "samples_reference_nohogs.xml",
    "content": "<samples>\n\t<overlapping name=\"Chess\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Chess\" N=\"2\" width=\"47\" height=\"47\" periodic=\"True\" screenshots=\"1\"/>\n\t<overlapping name=\"Skyline\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Flowers\" N=\"3\" symmetry=\"2\" ground=\"-4\" periodic=\"True\"/>\n\t<overlapping name=\"Hogs\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Less Rooms\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mountains\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Paths\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"2\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Red Maze\" N=\"2\"/>\n\t<overlapping name=\"Rooms\" N=\"3\" screenshots=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Rule 126\" N=\"3\" symmetry=\"2\" periodicInput=\"False\" periodic=\"False\"/>\n\t<overlapping name=\"Simple Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Maze\" N=\"2\"/>\n\t<overlapping name=\"Simple Wall\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"2\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Simple Wall\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Trick Knot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Water\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\"/>\n\t<simpletiled name=\"Castle\" width=\"20\" height=\"20\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"34\" height=\"34\" periodic=\"True\" screenshots=\"3\"/>\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Dense\" width=\"24\" height=\"24\" periodic=\"True\"/>\t\n\t<simpletiled name=\"Knots\" subset=\"Crossless\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"TE\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"T\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Knots\" subset=\"CL\" width=\"24\" height=\"24\"/>\t\n\t<simpletiled name=\"Knots\" subset=\"CE\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"C\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Fabric\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Knots\" subset=\"Dense Fabric\" width=\"24\" height=\"24\" periodic=\"True\"/>\n\t<simpletiled name=\"Rooms\" width=\"30\" height=\"30\"/>\n\t<simpletiled name=\"Circles\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"Large Circles\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"Large Circles and Solid\" width=\"24\" height=\"24\"/>\n\t<simpletiled name=\"Circles\" subset=\"No Solid\" width=\"24\" height=\"24\"/>\n\t\n\t<overlapping name=\"Cat\" N=\"3\" symmetry=\"2\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Cats\" N=\"3\" symmetry=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Skyline 2\" N=\"3\" symmetry=\"2\" periodic=\"True\" ground=\"-1\"/>\n\t<overlapping name=\"Angular\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"City\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Colored City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Dungeon\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Lake\" N=\"3\" periodic=\"True\" width=\"60\" height=\"60\"/>\n\t<overlapping name=\"Link\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Link 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Mazelike\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Nested\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Magic Office\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Office 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Qud\" N=\"3\" periodic=\"True\" width=\"80\" height=\"80\"/>\n\t<overlapping name=\"Red Dot\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Scaled Maze\" N=\"2\" periodic=\"True\"/>\n\t<overlapping name=\"Sewers\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 1\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Skew 2\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Smile City\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Spirals\" N=\"3\" periodic=\"True\"/>\n\t<overlapping name=\"Town\" N=\"3\" periodic=\"True\"/>\n\t\n\t<overlapping name=\"3Bricks\" N=\"3\" symmetry=\"1\" periodic=\"True\"/>\n\t<overlapping name=\"Village\" N=\"3\" symmetry=\"2\" limit=\"50\" periodic=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"15\" height=\"15\" periodic=\"False\" limit=\"15\"/>\n\n\t<simpletiled name=\"Knots\" subset=\"Standard\" width=\"5\" height=\"5\" periodic=\"True\" textOutput=\"True\"/>\n\t<simpletiled name=\"Summer\" width=\"6\" height=\"6\" periodic=\"False\" textOutput=\"True\"/>\n\t<simpletiled name=\"Circuit\" subset=\"Turnless\" width=\"7\" height=\"7\" periodic=\"True\" textOutput=\"True\"/>\n</samples>\n"
  },
  {
    "path": "samples_test.xml",
    "content": "<samples>\n\t<overlapping name=\"test_pattern\" N=\"2\" symmetry=\"1\" periodic=\"False\" width=\"11\" height=\"9\"/>\n</samples>\n"
  },
  {
    "path": "samples_test_ground.xml",
    "content": "<samples>\n\t<overlapping name=\"Flowers\" N=\"3\" symmetry=\"2\" ground=\"-4\" periodic=\"False\" width=\"8\" height=\"12\"/>\n\t<overlapping name=\"Flowers\" N=\"3\" symmetry=\"2\" ground=\"-4\" periodic=\"False\" width=\"16\" height=\"12\"/>\n\t<overlapping name=\"Platformer\" N=\"2\" symmetry=\"2\" ground=\"-1\" periodic=\"False\" width=\"12\" height=\"8\"/>\n\t<overlapping name=\"Platformer\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"False\" width=\"12\" height=\"8\"/>\n\t<overlapping name=\"Skyline\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"False\" width=\"12\" height=\"8\"/>\n\t<overlapping name=\"Flowers\" N=\"3\" symmetry=\"2\" ground=\"-4\" periodic=\"False\" width=\"12\" height=\"8\"/>\n\t<overlapping name=\"Skyline 2\" N=\"3\" symmetry=\"2\" periodic=\"False\" ground=\"-1\" width=\"12\" height=\"8\"/>        \n  \t<overlapping name=\"Red Maze\" N=\"2\" width=\"16\" height=\"11\"/>\n\n  \t<overlapping name=\"Dungeon\" N=\"3\" periodic=\"True\" width=\"28\" height=\"31\"/>\n\n        <overlapping name=\"Platformer\" N=\"2\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Platformer\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Skyline\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n\t<overlapping name=\"Flowers\" N=\"3\" symmetry=\"2\" ground=\"-4\" periodic=\"True\"/>\n\t<overlapping name=\"Skyline 2\" N=\"3\" symmetry=\"2\" periodic=\"True\" ground=\"-1\"/>\n</samples>\n"
  },
  {
    "path": "samples_test_vis.xml",
    "content": "<samples>\n  <overlapping name=\"Flowers\" N=\"3\" symmetry=\"2\" ground=\"-4\" periodic=\"True\"/>\n  <overlapping name=\"Skyline\" N=\"3\" symmetry=\"2\" ground=\"-1\" periodic=\"True\"/>\n  <overlapping name=\"Red Maze\" N=\"2\" width=\"36\" height=\"48\"/>\n  <overlapping name=\"Red Maze\" N=\"2\" width=\"16\" height=\"13\"/>\n \n\t<overlapping name=\"Flowers\" N=\"3\" symmetry=\"2\" ground=\"-4\" periodic=\"False\" width=\"8\" height=\"12\"/>\n \t<overlapping name=\"Dungeon\" N=\"3\" periodic=\"True\" width=\"18\" height=\"7\"/>\n                <overlapping name=\"f_test\" N=\"2\" width=\"4\" height=\"4\" screenshots=\"3\" symmetry=\"1\"/>\n  \t<overlapping name=\"f_test\" N=\"2\" width=\"16\" height=\"16\" screenshots=\"3\" symmetry=\"1\"/>\n  \t<overlapping name=\"f_test\" N=\"2\" width=\"32\" height=\"32\" screenshots=\"3\" symmetry=\"1\"/>\n  \t<overlapping name=\"tilesize_test\" N=\"2\" width=\"10\" height=\"8\" tile_size=\"2\" screenshots=\"1\"/>\n\n</samples>\n"
  },
  {
    "path": "setup.cfg",
    "content": "[metadata]\nname = wfc_python\nversion = 0.0.0\n\n[options]\npackages = wfc\ninclude_package_data = True\ninstall_requires =\n    hilbertcurve>=2\n    imageio\n    matplotlib\n    numpy\n    scipy\n\n[options.package_data]\nwfc = py.typed\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n\nimport setuptools\n\nif __name__ == \"__main__\":\n    setuptools.setup()\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/conftest.py",
    "content": "from __future__ import annotations\n\nimport os.path\nimport pytest\n\nPROJECT_ROOT = os.path.dirname(os.path.dirname(__file__))\n\nclass Resources:\n    def get_image(self, image: str) -> str:\n        return os.path.join(PROJECT_ROOT, \"images\", image)\n\n\n@pytest.fixture(scope=\"session\")\ndef resources() -> Resources:\n    return Resources()"
  },
  {
    "path": "tests/test_wfc_adjacency.py",
    "content": "\"\"\"Convert input data to adjacency information\"\"\"\nfrom __future__ import annotations\n\nimport imageio  # type: ignore\nfrom tests.conftest import Resources\nfrom wfc import wfc_tiles\nfrom wfc import wfc_patterns\nfrom wfc import wfc_adjacency\n\n\ndef test_adjacency_extraction(resources: Resources) -> None:\n    # TODO: generalize this to more than the four cardinal directions\n    direction_offsets = list(enumerate([(0, -1), (1, 0), (0, 1), (-1, 0)]))\n\n    filename = resources.get_image(\"samples/Red Maze.png\")\n    img = imageio.imread(filename)\n    tile_size = 1\n    pattern_width = 2\n    periodic = False\n    _tile_catalog, tile_grid, _code_list, _unique_tiles = wfc_tiles.make_tile_catalog(img, tile_size)\n    pattern_catalog, _pattern_weights, _pattern_list, pattern_grid = wfc_patterns.make_pattern_catalog(\n        tile_grid, pattern_width, periodic\n    )\n    adjacency_relations = wfc_adjacency.adjacency_extraction(\n        pattern_grid, pattern_catalog, direction_offsets\n    )\n    assert ((0, -1), -6150964001204120324, -4042134092912931260) in adjacency_relations\n    assert ((-1, 0), -4042134092912931260, 3069048847358774683) in adjacency_relations\n    assert ((1, 0), -3950451988873469076, -3950451988873469076) in adjacency_relations\n    assert ((-1, 0), -3950451988873469076, -3950451988873469076) in adjacency_relations\n    assert ((0, 1), -3950451988873469076, 3336256675067683735) in adjacency_relations\n    assert (\n        not ((0, -1), -3950451988873469076, -3950451988873469076) in adjacency_relations\n    )\n    assert (\n        not ((0, 1), -3950451988873469076, -3950451988873469076) in adjacency_relations\n    )\n"
  },
  {
    "path": "tests/test_wfc_patterns.py",
    "content": "from __future__ import annotations\n\nimport imageio  # type: ignore\nimport numpy as np\nfrom tests.conftest import Resources\nfrom wfc import wfc_patterns\nfrom wfc import wfc_tiles\n\n\ndef test_unique_patterns_2d(resources: Resources) -> None:\n    filename = resources.get_image(\"samples/Red Maze.png\")\n    img = imageio.imread(filename)\n    tile_size = 1\n    pattern_width = 2\n    _tile_catalog, tile_grid, _code_list, _unique_tiles = wfc_tiles.make_tile_catalog(img, tile_size)\n\n    _patterns_in_grid, pattern_contents_list, patch_codes = wfc_patterns.unique_patterns_2d(\n        tile_grid, pattern_width, True\n    )\n    assert patch_codes[1][2] == 4867810695119132864\n    assert pattern_contents_list[7][1][1] == 8253868773529191888\n\n\ndef test_make_pattern_catalog(resources: Resources) -> None:\n    filename = resources.get_image(\"samples/Red Maze.png\")\n    img = imageio.imread(filename)\n    tile_size = 1\n    pattern_width = 2\n    _tile_catalog, tile_grid, _code_list, _unique_tiles = wfc_tiles.make_tile_catalog(img, tile_size)\n\n    pattern_catalog, pattern_weights, pattern_list, _pattern_grid = wfc_patterns.make_pattern_catalog(\n        tile_grid, pattern_width\n    )\n    assert pattern_weights[-6150964001204120324] == 1\n    assert pattern_list[3] == 2800765426490226432\n    assert pattern_catalog[5177878755649963747][0][1] == -8754995591521426669\n\n\ndef test_pattern_to_tile(resources: Resources) -> None:\n    filename = resources.get_image(\"samples/Red Maze.png\")\n    img = imageio.imread(filename)\n    tile_size = 1\n    pattern_width = 2\n    _tile_catalog, tile_grid, _code_list, _unique_tiles = wfc_tiles.make_tile_catalog(img, tile_size)\n\n    pattern_catalog, _pattern_weights, _pattern_list, pattern_grid = wfc_patterns.make_pattern_catalog(\n        tile_grid, pattern_width\n    )\n    new_tile_grid = wfc_patterns.pattern_grid_to_tiles(pattern_grid, pattern_catalog)\n    assert np.array_equal(tile_grid, new_tile_grid)\n"
  },
  {
    "path": "tests/test_wfc_solver.py",
    "content": "from __future__ import annotations\r\n\r\nfrom typing import Any, Dict, List, Set, Tuple\r\nfrom numpy.typing import NDArray\r\nimport imageio  # type: ignore\r\nimport numpy\r\nimport numpy as np\r\nfrom tests.conftest import Resources\r\nfrom wfc import wfc_solver\r\nfrom wfc import wfc_tiles\r\nfrom wfc import wfc_patterns\r\nfrom wfc import wfc_adjacency\r\n\r\n\r\ndef test_makeWave() -> None:\r\n    wave = wfc_solver.makeWave(3, 10, 20, ground=[-1])\r\n    # print(wave)\r\n    # print(wave.sum())\r\n    # print((2*10*19) + (1*10*1))\r\n    assert wave.sum() == (2 * 10 * 19) + (1 * 10 * 1)\r\n    assert wave[2, 5, 19] == True\r\n    assert wave[1, 5, 19] == False\r\n\r\n\r\ndef test_entropyLocationHeuristic() -> None:\r\n    wave = numpy.ones((5, 3, 4), dtype=bool)  # everthing is possible\r\n    wave[1:, 0, 0] = False  # first cell is fully observed\r\n    wave[4, :, 2] = False\r\n    preferences: NDArray[np.float_] = numpy.ones((3, 4), dtype=np.float_) * 0.5\r\n    preferences[1, 2] = 0.3\r\n    preferences[1, 1] = 0.1\r\n    heu = wfc_solver.makeEntropyLocationHeuristic(preferences)\r\n    result = heu(wave)\r\n    assert (1, 2) == result\r\n\r\n\r\ndef test_observe() -> None:\r\n\r\n    my_wave = numpy.ones((5, 3, 4), dtype=np.bool_)\r\n    my_wave[0, 1, 2] = False\r\n\r\n    def locHeu(wave: NDArray[np.bool_]) -> Tuple[int, int]:\r\n        assert numpy.array_equal(wave, my_wave)\r\n        return 1, 2\r\n\r\n    def patHeu(weights: NDArray[np.bool_], wave: NDArray[np.bool_]) -> int:\r\n        assert numpy.array_equal(weights, my_wave[:, 1, 2])\r\n        return 3\r\n\r\n    assert wfc_solver.observe(my_wave, locationHeuristic=locHeu, patternHeuristic=patHeu) == (\r\n        3,\r\n        1,\r\n        2,\r\n    )\r\n\r\n\r\ndef test_propagate() -> None:\r\n    wave = numpy.ones((3, 3, 4), dtype=bool)\r\n    adjLists = {}\r\n    # checkerboard #0/#1 or solid fill #2\r\n    adjLists[(+1, 0)] = adjLists[(-1, 0)] = adjLists[(0, +1)] = adjLists[(0, -1)] = [\r\n        [1],\r\n        [0],\r\n        [2],\r\n    ]\r\n    wave[:, 0, 0] = False\r\n    wave[0, 0, 0] = True\r\n    adj = wfc_solver.makeAdj(adjLists)\r\n    wfc_solver.propagate(wave, adj, periodic=False)\r\n    expected_result = numpy.array(\r\n        [\r\n            [\r\n                [True, False, True, False],\r\n                [False, True, False, True],\r\n                [True, False, True, False],\r\n            ],\r\n            [\r\n                [False, True, False, True],\r\n                [True, False, True, False],\r\n                [False, True, False, True],\r\n            ],\r\n            [\r\n                [False, False, False, False],\r\n                [False, False, False, False],\r\n                [False, False, False, False],\r\n            ],\r\n        ]\r\n    )\r\n    assert numpy.array_equal(wave, expected_result)\r\n\r\n\r\ndef test_run() -> None:\r\n    wave = wfc_solver.makeWave(3, 3, 4)\r\n    adjLists = {}\r\n    adjLists[(+1, 0)] = adjLists[(-1, 0)] = adjLists[(0, +1)] = adjLists[(0, -1)] = [\r\n        [1],\r\n        [0],\r\n        [2],\r\n    ]\r\n    adj = wfc_solver.makeAdj(adjLists)\r\n\r\n    first_result = wfc_solver.run(\r\n        wave.copy(),\r\n        adj,\r\n        locationHeuristic=wfc_solver.lexicalLocationHeuristic,\r\n        patternHeuristic=wfc_solver.lexicalPatternHeuristic,\r\n        periodic=False,\r\n    )\r\n\r\n    expected_first_result = numpy.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1]])\r\n\r\n    assert numpy.array_equal(first_result, expected_first_result)\r\n\r\n    event_log: List[Any] = []\r\n\r\n    def onChoice(pattern: int, i: int, j: int) -> None:\r\n        event_log.append((pattern, i, j))\r\n\r\n    def onBacktrack() -> None:\r\n        event_log.append(\"backtrack\")\r\n\r\n    second_result = wfc_solver.run(\r\n        wave.copy(),\r\n        adj,\r\n        locationHeuristic=wfc_solver.lexicalLocationHeuristic,\r\n        patternHeuristic=wfc_solver.lexicalPatternHeuristic,\r\n        periodic=True,\r\n        backtracking=True,\r\n        onChoice=onChoice,\r\n        onBacktrack=onBacktrack,\r\n    )\r\n\r\n    expected_second_result = numpy.array([[2, 2, 2, 2], [2, 2, 2, 2], [2, 2, 2, 2]])\r\n\r\n    assert numpy.array_equal(second_result, expected_second_result)\r\n    print(event_log)\r\n    assert event_log == [(0, 0, 0), \"backtrack\", (2, 0, 0)]\r\n\r\n    class Infeasible(Exception):\r\n        pass\r\n\r\n    def explode(wave: NDArray[np.bool_]) -> bool:\r\n        if wave.sum() < 20:\r\n            raise Infeasible\r\n        return False\r\n\r\n    try:\r\n        result = wfc_solver.run(\r\n            wave.copy(),\r\n            adj,\r\n            locationHeuristic=wfc_solver.lexicalLocationHeuristic,\r\n            patternHeuristic=wfc_solver.lexicalPatternHeuristic,\r\n            periodic=True,\r\n            backtracking=True,\r\n            checkFeasible=explode,\r\n        )\r\n        print(result)\r\n        happy = False\r\n    except wfc_solver.Contradiction:\r\n        happy = True\r\n\r\n    assert happy\r\n"
  },
  {
    "path": "tests/test_wfc_tiles.py",
    "content": "\"\"\"Breaks an image into consituant tiles.\"\"\"\nfrom __future__ import annotations\n\nimport imageio  # type: ignore\nfrom tests.conftest import Resources\nfrom wfc import wfc_tiles\n\n\ndef test_image_to_tile(resources: Resources) -> None:\n    filename = resources.get_image(\"samples/Red Maze.png\")\n    img = imageio.imread(filename)\n    tiles = wfc_tiles.image_to_tiles(img, 1)\n    assert tiles[2][2][0][0][0] == 255\n    assert tiles[2][2][0][0][1] == 0\n\n\ndef test_make_tile_catalog(resources: Resources) -> None:\n    filename = resources.get_image(\"samples/Red Maze.png\")\n    img = imageio.imread(filename)\n    print(img)\n    tc, tg, cl, ut = wfc_tiles.make_tile_catalog(img, 1)\n    print(\"tile catalog\")\n    print(tc)\n    print(\"tile grid\")\n    print(tg)\n    print(\"code list\")\n    print(cl)\n    print(\"unique tiles\")\n    print(ut)\n    assert ut[1][0] == 7\n"
  },
  {
    "path": "wfc/__init__.py",
    "content": ""
  },
  {
    "path": "wfc/py.typed",
    "content": ""
  },
  {
    "path": "wfc/wfc_adjacency.py",
    "content": "\"\"\"Convert input data to adjacency information\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Dict, List, Tuple\nimport numpy as np\nfrom numpy.typing import NDArray\n\ndef adjacency_extraction(\n    pattern_grid: NDArray[np.int64],\n    pattern_catalog: Dict[int, NDArray[np.int64]],\n    direction_offsets: List[Tuple[int, Tuple[int, int]]],\n    pattern_size: Tuple[int, int] = (2, 2),\n) -> List[Tuple[Tuple[int, int], int, int]]:\n    \"\"\"Takes a pattern grid and returns a list of all of the legal adjacencies found in it.\"\"\"\n\n    def is_valid_overlap_xy(adjacency_direction: Tuple[int, int], pattern_1: int, pattern_2: int) -> bool:\n        \"\"\"Given a direction and two patterns, find the overlap of the two patterns \n        and return True if the intersection matches.\"\"\"\n        dimensions = (1, 0)\n        not_a_number = -1\n\n        # TODO: can probably speed this up by using the right slices, rather than rolling the whole pattern...\n        shifted = np.roll(\n            np.pad(\n                pattern_catalog[pattern_2],\n                max(pattern_size),\n                mode=\"constant\",\n                constant_values=not_a_number,\n            ),\n            adjacency_direction,\n            dimensions,\n        )\n        compare = shifted[\n            pattern_size[0] : pattern_size[0] + pattern_size[0],\n            pattern_size[1] : pattern_size[1] + pattern_size[1],\n        ]\n\n        left = max(0, 0, +adjacency_direction[0])\n        right = min(pattern_size[0], pattern_size[0] + adjacency_direction[0])\n        top = max(0, 0 + adjacency_direction[1])\n        bottom = min(pattern_size[1], pattern_size[1] + adjacency_direction[1])\n        a = pattern_catalog[pattern_1][top:bottom, left:right]\n        b = compare[top:bottom, left:right]\n        res = np.array_equal(a, b)\n        return res\n\n    pattern_list = list(pattern_catalog.keys())\n    legal = []\n    for pattern_1 in pattern_list:\n        for pattern_2 in pattern_list:\n            for _direction_index, direction in direction_offsets:\n                if is_valid_overlap_xy(direction, pattern_1, pattern_2):\n                    legal.append((direction, pattern_1, pattern_2))\n    return legal\n"
  },
  {
    "path": "wfc/wfc_control.py",
    "content": "from __future__ import annotations\n\nimport datetime\nfrom typing import Any, Callable, Dict, List, Literal, Optional, Set, Tuple\nfrom .wfc_tiles import make_tile_catalog\nfrom .wfc_patterns import (\n    pattern_grid_to_tiles,\n    make_pattern_catalog_with_rotations,\n)\nfrom .wfc_adjacency import adjacency_extraction\nfrom .wfc_solver import (\n    run,\n    makeWave,\n    makeAdj,\n    lexicalLocationHeuristic,\n    lexicalPatternHeuristic,\n    makeWeightedPatternHeuristic,\n    Contradiction,\n    StopEarly,\n    makeEntropyLocationHeuristic,\n    make_global_use_all_patterns,\n    makeRandomLocationHeuristic,\n    makeRandomPatternHeuristic,\n    TimedOut,\n    simpleLocationHeuristic,\n    makeSpiralLocationHeuristic,\n    makeHilbertLocationHeuristic,\n    makeAntiEntropyLocationHeuristic,\n    makeRarestPatternHeuristic,\n)\nfrom .wfc_visualize import (\n    figure_list_of_tiles,\n    figure_false_color_tile_grid,\n    figure_pattern_catalog,\n    render_tiles_to_output,\n    figure_adjacencies,\n    make_solver_visualizers,\n    make_solver_loggers,\n    tile_grid_to_image,\n)\nimport imageio  # type: ignore\nimport numpy as np\nimport time\nimport logging\nfrom numpy.typing import NDArray\n\nlogger = logging.getLogger(__name__)\n\n\ndef visualize_tiles(unique_tiles, tile_catalog, tile_grid):\n    if False:\n        figure_list_of_tiles(unique_tiles, tile_catalog)\n        figure_false_color_tile_grid(tile_grid)\n\n\ndef visualize_patterns(pattern_catalog, tile_catalog, pattern_weights, pattern_width):\n    if False:\n        figure_pattern_catalog(\n            pattern_catalog, tile_catalog, pattern_weights, pattern_width\n        )\n\n\ndef make_log_stats() -> Callable[[Dict[str, Any], str], None]:\n    log_line = 0\n\n    def log_stats(stats: Dict[str, Any], filename: str) -> None:\n        nonlocal log_line\n        if stats:\n            log_line += 1\n            with open(filename, \"a\", encoding=\"utf_8\") as logf:\n                if log_line < 2:\n                    for s in stats.keys():\n                        print(str(s), end=\"\\t\", file=logf)\n                    print(\"\", file=logf)\n                for s in stats.keys():\n                    print(str(stats[s]), end=\"\\t\", file=logf)\n                print(\"\", file=logf)\n\n    return log_stats\n\n\ndef execute_wfc(\n    filename: Optional[str] = None,\n    tile_size: int = 1,\n    pattern_width: int = 2,\n    rotations: int = 8,\n    output_size: Tuple[int, int] = (48, 48),\n    ground: Optional[int] = None,\n    attempt_limit: int = 10,\n    output_periodic: bool = True,\n    input_periodic: bool = True,\n    loc_heuristic: Literal[\"lexical\", \"hilbert\", \"spiral\", \"entropy\", \"anti-entropy\", \"simple\", \"random\"] = \"entropy\",\n    choice_heuristic: Literal[\"lexical\", \"rarest\", \"weighted\", \"random\"] = \"weighted\",\n    visualize: bool = False,\n    global_constraint: Literal[False, \"allpatterns\"] = False,\n    backtracking: bool = False,\n    log_filename: str = \"log\",\n    logging: bool = False,\n    global_constraints: None = None,\n    log_stats_to_output: Optional[Callable[[Dict[str, Any], str], None]] = None,\n    *,\n    image: Optional[NDArray[np.integer]] = None,\n) -> NDArray[np.integer]:\n    timecode = datetime.datetime.now().isoformat().replace(\":\", \".\")\n    time_begin = time.perf_counter()\n    output_destination = r\"./output/\"\n    input_folder = r\"./images/samples/\"\n\n    rotations -= 1  # change to zero-based\n\n    input_stats = {\n        \"filename\": str(filename),\n        \"tile_size\": tile_size,\n        \"pattern_width\": pattern_width,\n        \"rotations\": rotations,\n        \"output_size\": output_size,\n        \"ground\": ground,\n        \"attempt_limit\": attempt_limit,\n        \"output_periodic\": output_periodic,\n        \"input_periodic\": input_periodic,\n        \"location heuristic\": loc_heuristic,\n        \"choice heuristic\": choice_heuristic,\n        \"global constraint\": global_constraint,\n        \"backtracking\": backtracking,\n    }\n\n    # Load the image\n    if filename:\n        if image is not None:\n            raise TypeError(\"Only filename or image can be provided, not both.\")\n        image = imageio.imread(input_folder + filename + \".png\")[:, :, :3]  # TODO: handle alpha channels\n\n    if image is None:\n        raise TypeError(\"An image must be given.\")\n\n    # TODO: generalize this to more than the four cardinal directions\n    direction_offsets = list(enumerate([(0, -1), (1, 0), (0, 1), (-1, 0)]))\n\n    tile_catalog, tile_grid, _code_list, _unique_tiles = make_tile_catalog(image, tile_size)\n    (\n        pattern_catalog,\n        pattern_weights,\n        pattern_list,\n        pattern_grid,\n    ) = make_pattern_catalog_with_rotations(\n        tile_grid, pattern_width, input_is_periodic=input_periodic, rotations=rotations\n    )\n\n    logger.debug(\"pattern catalog\")\n\n    # visualize_tiles(unique_tiles, tile_catalog, tile_grid)\n    # visualize_patterns(pattern_catalog, tile_catalog, pattern_weights, pattern_width)\n    # figure_list_of_tiles(unique_tiles, tile_catalog, output_filename=f\"visualization/tilelist_{filename}_{timecode}\")\n    # figure_false_color_tile_grid(tile_grid, output_filename=f\"visualization/tile_falsecolor_{filename}_{timecode}\")\n    if visualize and filename:\n        figure_pattern_catalog(\n            pattern_catalog,\n            tile_catalog,\n            pattern_weights,\n            pattern_width,\n            output_filename=f\"visualization/pattern_catalog_{filename}_{timecode}\",\n        )\n\n    logger.debug(\"profiling adjacency relations\")\n    if False:\n        import pprofile  # type: ignore\n        profiler = pprofile.Profile()\n        with profiler:\n            adjacency_relations = adjacency_extraction(\n                pattern_grid,\n                pattern_catalog,\n                direction_offsets,\n                [pattern_width, pattern_width],\n            )\n        profiler.dump_stats(f\"logs/profile_adj_{filename}_{timecode}.txt\")\n    else:\n        adjacency_relations = adjacency_extraction(\n            pattern_grid,\n            pattern_catalog,\n            direction_offsets,\n            (pattern_width, pattern_width),\n        )\n\n    logger.debug(\"adjacency_relations\")\n\n    if visualize:\n        figure_adjacencies(\n            adjacency_relations,\n            direction_offsets,\n            tile_catalog,\n            pattern_catalog,\n            pattern_width,\n            [tile_size, tile_size],\n            output_filename=f\"visualization/adjacency_{filename}_{timecode}_A\",\n        )\n        # figure_adjacencies(adjacency_relations, direction_offsets, tile_catalog, pattern_catalog, pattern_width, [tile_size, tile_size], output_filename=f\"visualization/adjacency_{filename}_{timecode}_B\", render_b_first=True)\n\n    logger.debug(f\"output size: {output_size}\\noutput periodic: {output_periodic}\")\n    number_of_patterns = len(pattern_weights)\n    logger.debug(f\"# patterns: {number_of_patterns}\")\n    decode_patterns = dict(enumerate(pattern_list))\n    encode_patterns = {x: i for i, x in enumerate(pattern_list)}\n    _encode_directions = {j: i for i, j in direction_offsets}\n\n    adjacency_list: Dict[Tuple[int, int], List[Set[int]]] = {}\n    for _, adjacency in direction_offsets:\n        adjacency_list[adjacency] = [set() for _ in pattern_weights]\n    # logger.debug(adjacency_list)\n    for adjacency, pattern1, pattern2 in adjacency_relations:\n        # logger.debug(adjacency)\n        # logger.debug(decode_patterns[pattern1])\n        adjacency_list[adjacency][encode_patterns[pattern1]].add(encode_patterns[pattern2])\n\n    logger.debug(f\"adjacency: {len(adjacency_list)}\")\n\n    time_adjacency = time.perf_counter()\n\n    ### Ground ###\n\n    ground_list: Optional[NDArray[np.int64]] = None\n    if ground:\n        ground_list = np.vectorize(lambda x: encode_patterns[x])(\n            pattern_grid.flat[(ground - 1) :]\n        )\n    if ground_list is None or ground_list.size == 0:\n        ground_list = None\n\n    if ground_list is not None:\n        ground_catalog = {\n            encode_patterns[k]: v\n            for k, v in pattern_catalog.items()\n            if encode_patterns[k] in ground_list\n        }\n        if visualize:\n            figure_pattern_catalog(\n                ground_catalog,\n                tile_catalog,\n                pattern_weights,\n                pattern_width,\n                output_filename=f\"visualization/patterns_ground_{filename}_{timecode}\",\n            )\n\n    wave = makeWave(\n        number_of_patterns, output_size[0], output_size[1], ground=ground_list\n    )\n    adjacency_matrix = makeAdj(adjacency_list)\n\n    ### Heuristics ###\n\n    encoded_weights: NDArray[np.float64] = np.zeros((number_of_patterns), dtype=np.float64)\n    for w_id, w_val in pattern_weights.items():\n        encoded_weights[encode_patterns[w_id]] = w_val\n    choice_random_weighting: NDArray[np.float64] = np.random.random_sample(wave.shape[1:]) * 0.1\n\n    pattern_heuristic: Callable[[NDArray[np.bool_], NDArray[np.bool_]], int] = lexicalPatternHeuristic\n    if choice_heuristic == \"rarest\":\n        pattern_heuristic = makeRarestPatternHeuristic(encoded_weights)\n    if choice_heuristic == \"weighted\":\n        pattern_heuristic = makeWeightedPatternHeuristic(encoded_weights)\n    if choice_heuristic == \"random\":\n        pattern_heuristic = makeRandomPatternHeuristic(encoded_weights)\n\n    logger.debug(loc_heuristic)\n    location_heuristic: Callable[[NDArray[np.bool_]], Tuple[int, int]] = lexicalLocationHeuristic\n    if loc_heuristic == \"anti-entropy\":\n        location_heuristic = makeAntiEntropyLocationHeuristic(choice_random_weighting)\n    if loc_heuristic == \"entropy\":\n        location_heuristic = makeEntropyLocationHeuristic(choice_random_weighting)\n    if loc_heuristic == \"random\":\n        location_heuristic = makeRandomLocationHeuristic(choice_random_weighting)\n    if loc_heuristic == \"simple\":\n        location_heuristic = simpleLocationHeuristic\n    if loc_heuristic == \"spiral\":\n        location_heuristic = makeSpiralLocationHeuristic(choice_random_weighting)\n    if loc_heuristic == \"hilbert\":\n        location_heuristic = makeHilbertLocationHeuristic(choice_random_weighting)\n\n    ### Visualization ###\n\n    (\n        visualize_choice,\n        visualize_wave,\n        visualize_backtracking,\n        visualize_propagate,\n        visualize_final,\n        visualize_after,\n    ) = (None, None, None, None, None, None)\n    if filename and visualize:\n        (\n            visualize_choice,\n            visualize_wave,\n            visualize_backtracking,\n            visualize_propagate,\n            visualize_final,\n            visualize_after,\n        ) = make_solver_visualizers(\n            f\"{filename}_{timecode}\",\n            wave,\n            decode_patterns=decode_patterns,\n            pattern_catalog=pattern_catalog,\n            tile_catalog=tile_catalog,\n            tile_size=[tile_size, tile_size],\n        )\n    if filename and logging:\n        (\n            visualize_choice,\n            visualize_wave,\n            visualize_backtracking,\n            visualize_propagate,\n            visualize_final,\n            visualize_after,\n        ) = make_solver_loggers(f\"{filename}_{timecode}\", input_stats.copy())\n    if filename and logging and visualize:\n        vis = make_solver_visualizers(\n            f\"{filename}_{timecode}\",\n            wave,\n            decode_patterns=decode_patterns,\n            pattern_catalog=pattern_catalog,\n            tile_catalog=tile_catalog,\n            tile_size=[tile_size, tile_size],\n        )\n        log = make_solver_loggers(f\"{filename}_{timecode}\", input_stats.copy())\n\n        def visfunc(idx: int):\n            def vf(*args, **kwargs):\n                if vis[idx]:\n                    vis[idx](*args, **kwargs)\n                if log[idx]:\n                    return log[idx](*args, **kwargs)\n\n            return vf\n\n        (\n            visualize_choice,\n            visualize_wave,\n            visualize_backtracking,\n            visualize_propagate,\n            visualize_final,\n            visualize_after,\n        ) = [visfunc(x) for x in range(len(vis))]\n\n    ### Global Constraints ###\n    active_global_constraint = lambda wave: True\n    if global_constraint == \"allpatterns\":\n        active_global_constraint = make_global_use_all_patterns()\n    logger.debug(active_global_constraint)\n    combined_constraints = [active_global_constraint]\n\n    def combinedConstraints(wave: NDArray[np.bool_]) -> bool:\n        return all(fn(wave) for fn in combined_constraints)\n\n    ### Solving ###\n\n    time_solve_start = None\n    time_solve_end = None\n\n    solution_tile_grid = None\n    logger.debug(\"solving...\")\n    attempts = 0\n    while attempts < attempt_limit:\n        attempts += 1\n        time_solve_start = time.perf_counter()\n        stats = {}\n        # profiler = pprofile.Profile()\n        # with profiler:\n        # with PyCallGraph(output=GraphvizOutput(output_file=f\"visualization/pycallgraph_{filename}_{timecode}.png\")):\n        try:\n            solution = run(\n                wave.copy(),\n                adjacency_matrix,\n                locationHeuristic=location_heuristic,\n                patternHeuristic=pattern_heuristic,\n                periodic=output_periodic,\n                backtracking=backtracking,\n                onChoice=visualize_choice,\n                onBacktrack=visualize_backtracking,\n                onObserve=visualize_wave,\n                onPropagate=visualize_propagate,\n                onFinal=visualize_final,\n                checkFeasible=combinedConstraints,\n            )\n            if visualize_after:\n                stats = visualize_after()\n            # logger.debug(solution)\n            # logger.debug(stats)\n            solution_as_ids = np.vectorize(lambda x: decode_patterns[x])(solution)\n            solution_tile_grid = pattern_grid_to_tiles(\n                solution_as_ids, pattern_catalog\n            )\n\n            logger.debug(\"Solution:\")\n            # logger.debug(solution_tile_grid)\n            if filename:\n                render_tiles_to_output(\n                    solution_tile_grid,\n                    tile_catalog,\n                    (tile_size, tile_size),\n                    output_destination + filename + \"_\" + timecode + \".png\",\n                )\n\n            time_solve_end = time.perf_counter()\n            stats.update({\"outcome\": \"success\"})\n        except StopEarly:\n            logger.debug(\"Skipping...\")\n            stats.update({\"outcome\": \"skipped\"})\n            raise\n        except TimedOut:\n            logger.debug(\"Timed Out\")\n            if visualize_after:\n                stats = visualize_after()\n            stats.update({\"outcome\": \"timed_out\"})\n        except Contradiction as exc:\n            logger.warning(f\"Contradiction: {exc}\")\n            if visualize_after:\n                stats = visualize_after()\n            stats.update({\"outcome\": \"contradiction\"})\n        finally:\n            # profiler.dump_stats(f\"logs/profile_{filename}_{timecode}.txt\")\n            outstats = {}\n            outstats.update(input_stats)\n            solve_duration = time.perf_counter() - time_solve_start\n            if time_solve_end is not None:\n                solve_duration = time_solve_end - time_solve_start\n            adjacency_duration = time_solve_start - time_adjacency\n            outstats.update(\n                {\n                    \"attempts\": attempts,\n                    \"time_start\": time_begin,\n                    \"time_adjacency\": time_adjacency,\n                    \"adjacency_duration\": adjacency_duration,\n                    \"time solve start\": time_solve_start,\n                    \"time solve end\": time_solve_end,\n                    \"solve duration\": solve_duration,\n                    \"pattern count\": number_of_patterns,\n                }\n            )\n            outstats.update(stats)\n            if log_stats_to_output is not None:\n                log_stats_to_output(outstats, output_destination + log_filename + \".tsv\")\n        if solution_tile_grid is not None:\n            return tile_grid_to_image(solution_tile_grid, tile_catalog, (tile_size, tile_size))\n\n    raise TimedOut(\"Attempt limit exceeded.\")\n"
  },
  {
    "path": "wfc/wfc_patterns.py",
    "content": "\"Extract patterns from grids of tiles.\"\nfrom __future__ import annotations\n\nimport logging\nfrom typing import Any, Dict, Mapping, Optional, Tuple\nfrom .wfc_utilities import hash_downto\nfrom collections import Counter\nimport numpy as np\nfrom numpy.typing import NDArray\n\nlogger = logging.getLogger(__name__)\n\n\ndef unique_patterns_2d(agrid: NDArray[np.int64], ksize: int, periodic_input: bool) -> Tuple[NDArray[np.int64], NDArray[np.int64], NDArray[np.int64]]:\n    assert ksize >= 1\n    if periodic_input:\n        agrid = np.pad(\n            agrid,\n            ((0, ksize - 1), (0, ksize - 1), *(((0, 0),) * (len(agrid.shape) - 2))),\n            mode=\"wrap\",\n        )\n    else:\n        # TODO: implement non-wrapped image handling\n        # a = np.pad(a, ((0,k-1),(0,k-1),*(((0,0),)*(len(a.shape)-2))), mode='constant', constant_values=None)\n        agrid = np.pad(\n            agrid,\n            ((0, ksize - 1), (0, ksize - 1), *(((0, 0),) * (len(agrid.shape) - 2))),\n            mode=\"wrap\",\n        )\n\n    patches: NDArray[np.int64] = np.lib.stride_tricks.as_strided(\n        agrid,\n        (\n            agrid.shape[0] - ksize + 1,\n            agrid.shape[1] - ksize + 1,\n            ksize,\n            ksize,\n            *agrid.shape[2:],\n        ),\n        agrid.strides[:2] + agrid.strides[:2] + agrid.strides[2:],\n        writeable=False,\n    )\n    patch_codes = hash_downto(patches, 2)\n    uc, ui = np.unique(patch_codes, return_index=True)\n    locs = np.unravel_index(ui, patch_codes.shape)\n    up: NDArray[np.int64] = patches[locs[0], locs[1]]\n    ids: NDArray[np.int64] = np.vectorize({code: ind for ind, code in enumerate(uc)}.get)(patch_codes)\n    return ids, up, patch_codes\n\n\ndef unique_patterns_brute_force(grid, size, periodic_input):\n    padded_grid = np.pad(\n        grid,\n        ((0, size - 1), (0, size - 1), *(((0, 0),) * (len(grid.shape) - 2))),\n        mode=\"wrap\",\n    )\n    patches = []\n    for x in range(grid.shape[0]):\n        row_patches = []\n        for y in range(grid.shape[1]):\n            row_patches.append(\n                np.ndarray.tolist(padded_grid[x : x + size, y : y + size])\n            )\n        patches.append(row_patches)\n    patches = np.array(patches)\n    patch_codes = hash_downto(patches, 2)\n    uc, ui = np.unique(patch_codes, return_index=True)\n    locs = np.unravel_index(ui, patch_codes.shape)\n    up = patches[locs[0], locs[1]]\n    ids = np.vectorize({c: i for i, c in enumerate(uc)}.get)(patch_codes)\n    return ids, up\n\n\ndef make_pattern_catalog(\n    tile_grid: NDArray[np.int64], pattern_width: int, input_is_periodic: bool = True\n) -> Tuple[Dict[int, NDArray[np.int64]], Counter, NDArray[np.int64], NDArray[np.int64]]:\n    \"\"\"Returns a pattern catalog (dictionary of pattern hashes to consituent tiles), \nan ordered list of pattern weights, and an ordered list of pattern contents.\"\"\"\n    _patterns_in_grid, pattern_contents_list, patch_codes = unique_patterns_2d(\n        tile_grid, pattern_width, input_is_periodic\n    )\n    dict_of_pattern_contents: Dict[int, NDArray[np.int64]] = {}\n    for pat_idx in range(pattern_contents_list.shape[0]):\n        p_hash = hash_downto(pattern_contents_list[pat_idx], 0)\n        dict_of_pattern_contents.update(\n            {p_hash.item(): pattern_contents_list[pat_idx]}\n        )\n    pattern_frequency = Counter(hash_downto(pattern_contents_list, 1))\n    return (\n        dict_of_pattern_contents,\n        pattern_frequency,\n        hash_downto(pattern_contents_list, 1),\n        patch_codes,\n    )\n\n\ndef identity_grid(grid):\n    \"\"\"Do nothing to the grid\"\"\"\n    # return np.array([[7,5,5,5],[5,0,0,0],[5,0,1,0],[5,0,0,0]])\n    return grid\n\n\ndef reflect_grid(grid):\n    \"\"\"Reflect the grid left/right\"\"\"\n    return np.fliplr(grid)\n\n\ndef rotate_grid(grid):\n    \"\"\"Rotate the grid\"\"\"\n    return np.rot90(grid, axes=(1, 0))\n\n\ndef make_pattern_catalog_with_rotations(\n    tile_grid: NDArray[np.int64], pattern_width: int, rotations: int = 7, input_is_periodic: bool = True\n) -> Tuple[Dict[int, NDArray[np.int64]], Counter, NDArray[np.int64], NDArray[np.int64]]:\n    rotated_tile_grid = tile_grid.copy()\n    merged_dict_of_pattern_contents: Dict[int, NDArray[np.int64]] = {}\n    merged_pattern_frequency: Counter = Counter()\n    merged_pattern_contents_list: Optional[NDArray[np.int64]] = None\n    merged_patch_codes: Optional[NDArray[np.int64]]  = None\n\n    def _make_catalog() -> None:\n        nonlocal rotated_tile_grid, merged_dict_of_pattern_contents, merged_pattern_contents_list, merged_pattern_frequency, merged_patch_codes\n        (\n            dict_of_pattern_contents,\n            pattern_frequency,\n            pattern_contents_list,\n            patch_codes,\n        ) = make_pattern_catalog(rotated_tile_grid, pattern_width, input_is_periodic)\n        merged_dict_of_pattern_contents.update(dict_of_pattern_contents)\n        merged_pattern_frequency.update(pattern_frequency)\n        if merged_pattern_contents_list is None:\n            merged_pattern_contents_list = pattern_contents_list.copy()\n        else:\n            merged_pattern_contents_list = np.unique(\n                np.concatenate((merged_pattern_contents_list, pattern_contents_list))\n            )\n        if merged_patch_codes is None:\n            merged_patch_codes = patch_codes.copy()\n\n    counter = 0\n    grid_ops = [\n        identity_grid,\n        reflect_grid,\n        rotate_grid,\n        reflect_grid,\n        rotate_grid,\n        reflect_grid,\n        rotate_grid,\n        reflect_grid,\n    ]\n    while counter <= (rotations):\n        # logger.debug(rotated_tile_grid.shape)\n        # logger.debug(np.array_equiv(reflect_grid(rotated_tile_grid.copy()), rotate_grid(rotated_tile_grid.copy())))\n\n        # logger.debug(counter)\n        # logger.debug(grid_ops[counter].__name__)\n        rotated_tile_grid = grid_ops[counter](rotated_tile_grid.copy())\n        # logger.debug(rotated_tile_grid)\n        # logger.debug(\"---\")\n        _make_catalog()\n        counter += 1\n\n    # assert False\n    assert merged_pattern_contents_list is not None\n    assert merged_patch_codes is not None\n    return (\n        merged_dict_of_pattern_contents,\n        merged_pattern_frequency,\n        merged_pattern_contents_list,\n        merged_patch_codes,\n    )\n\n\ndef pattern_grid_to_tiles(\n    pattern_grid: NDArray[np.int64], pattern_catalog: Mapping[int, NDArray[np.int64]]\n) -> NDArray[np.int64]:\n    anchor_x = 0\n    anchor_y = 0\n\n    def pattern_to_tile(pattern: int) -> Any:\n        # if isinstance(pattern, list):\n        #     ptrns = []\n        #     for p in pattern:\n        #         logger.debug(p)\n        #         ptrns.push(pattern_to_tile(p))\n        #     logger.debug(ptrns)\n        #     assert False\n        #     return ptrns\n        return pattern_catalog[pattern][anchor_x][anchor_y]\n\n    return np.vectorize(pattern_to_tile)(pattern_grid)\n"
  },
  {
    "path": "wfc/wfc_solver.py",
    "content": "from __future__ import annotations\r\n\r\nimport logging\r\nfrom typing import Any, Callable, Collection, Dict, Iterable, Iterator, List, Mapping, Optional, Tuple, TypeVar\r\nfrom scipy import sparse  # type: ignore\r\nimport numpy\r\nimport numpy as np\r\nimport sys\r\nimport math\r\nimport itertools\r\nfrom numpy.typing import NBitBase, NDArray\r\nfrom hilbertcurve.hilbertcurve import HilbertCurve  # type: ignore\r\n\r\nlogger = logging.getLogger(__name__)\r\n\r\nT = TypeVar(\"T\", bound=NBitBase)\r\n\r\n\r\nclass Contradiction(Exception):\r\n    \"\"\"Solving could not proceed without backtracking/restarting.\"\"\"\r\n\r\n    pass\r\n\r\n\r\nclass TimedOut(Exception):\r\n    \"\"\"Solve timed out.\"\"\"\r\n\r\n    pass\r\n\r\n\r\nclass StopEarly(Exception):\r\n    \"\"\"Aborting solve early.\"\"\"\r\n\r\n    pass\r\n\r\n\r\nclass Solver:\r\n    \"\"\"WFC Solver which can hold wave and backtracking state.\"\"\"\r\n\r\n    def __init__(\r\n        self,\r\n        *,\r\n        wave: NDArray[np.bool_],\r\n        adj: Mapping[Tuple[int, int], NDArray[numpy.bool_]],\r\n        periodic: bool = False,\r\n        backtracking: bool = False,\r\n        on_backtrack: Optional[Callable[[], None]] = None,\r\n        on_choice: Optional[Callable[[int, int, int], None]] = None,\r\n        on_observe: Optional[Callable[[NDArray[numpy.bool_]], None]] = None,\r\n        on_propagate: Optional[Callable[[NDArray[numpy.bool_]], None]] = None,\r\n        check_feasible: Optional[Callable[[NDArray[numpy.bool_]], bool]] = None\r\n    ) -> None:\r\n        self.wave = wave\r\n        self.adj = adj\r\n        self.periodic = periodic\r\n        self.backtracking = backtracking\r\n        self.history: List[NDArray[np.bool_]] = []  # An undo history for backtracking.\r\n        self.on_backtrack = on_backtrack\r\n        self.on_choice = on_choice\r\n        self.on_observe = on_observe\r\n        self.on_propagate = on_propagate\r\n        self.check_feasible = check_feasible\r\n\r\n    @property\r\n    def is_solved(self) -> bool:\r\n        \"\"\"Is True if the wave has been fully resolved.\"\"\"\r\n        return self.wave.sum() == self.wave.shape[1] * self.wave.shape[2] and (self.wave.sum(axis=0) == 1).all()\r\n\r\n    def solve_next(\r\n        self,\r\n        location_heuristic: Callable[[NDArray[numpy.bool_]], Tuple[int, int]],\r\n        pattern_heuristic: Callable[[NDArray[np.bool_], NDArray[np.bool_]], int],\r\n    ) -> bool:\r\n        \"\"\"Attempt to collapse one wave.  Returns True if no more steps remain.\"\"\"\r\n        if self.is_solved:\r\n            return True\r\n        if self.check_feasible and not self.check_feasible(self.wave):\r\n            raise Contradiction(\"Not feasible.\")\r\n        if self.backtracking:\r\n            self.history.append(self.wave.copy())\r\n        propagate(self.wave, self.adj, periodic=self.periodic, onPropagate=self.on_propagate)\r\n        try:\r\n            pattern, i, j = observe(self.wave, location_heuristic, pattern_heuristic)\r\n            if self.on_choice:\r\n                self.on_choice(pattern, i, j)\r\n            self.wave[:, i, j] = False\r\n            self.wave[pattern, i, j] = True\r\n            if self.on_observe:\r\n                self.on_observe(self.wave)\r\n            propagate(self.wave, self.adj, periodic=self.periodic, onPropagate=self.on_propagate)\r\n            return False  # Assume there is remaining steps, if not then the next call will return True.\r\n        except Contradiction:\r\n            if not self.backtracking:\r\n                raise\r\n            if not self.history:\r\n                raise Contradiction(\"Every permutation has been attempted.\")\r\n            if self.on_backtrack:\r\n                self.on_backtrack()\r\n            self.wave = self.history.pop()\r\n            self.wave[pattern, i, j] = False\r\n            return False\r\n\r\n    def solve(\r\n        self,\r\n        location_heuristic: Callable[[NDArray[numpy.bool_]], Tuple[int, int]],\r\n        pattern_heuristic: Callable[[NDArray[np.bool_], NDArray[np.bool_]], int],\r\n    ) -> NDArray[np.int64]:\r\n        \"\"\"Attempts to solve all waves and returns the solution.\"\"\"\r\n        while not self.solve_next(location_heuristic=location_heuristic, pattern_heuristic=pattern_heuristic):\r\n            pass\r\n        return numpy.argmax(self.wave, axis=0)\r\n\r\n\r\ndef makeWave(n: int, w: int, h: int, ground: Optional[Iterable[int]] = None) -> NDArray[numpy.bool_]:\r\n    wave: NDArray[numpy.bool_] = numpy.ones((n, w, h), dtype=numpy.bool_)\r\n    if ground is not None:\r\n        wave[:, :, h - 1] = False\r\n        for g in ground:\r\n            wave[g, :,] = False\r\n            wave[g, :, h - 1] = True\r\n    # logger.debug(wave)\r\n    # for i in range(wave.shape[0]):\r\n    #  logger.debug(wave[i])\r\n    return wave\r\n\r\n\r\ndef makeAdj(\r\n    adjLists: Mapping[Tuple[int, int], Collection[Iterable[int]]]\r\n) -> Dict[Tuple[int, int], NDArray[numpy.bool_]]:\r\n    adjMatrices = {}\r\n    # logger.debug(adjLists)\r\n    num_patterns = len(list(adjLists.values())[0])\r\n    for d in adjLists:\r\n        m = numpy.zeros((num_patterns, num_patterns), dtype=bool)\r\n        for i, js in enumerate(adjLists[d]):\r\n            # logger.debug(js)\r\n            for j in js:\r\n                m[i, j] = 1\r\n        adjMatrices[d] = sparse.csr_matrix(m)\r\n    return adjMatrices\r\n\r\n\r\n######################################\r\n# Location Heuristics\r\n\r\n\r\ndef makeRandomLocationHeuristic(preferences: NDArray[np.floating[Any]]) -> Callable[[NDArray[np.bool_]], Tuple[int, int]]:\r\n    def randomLocationHeuristic(wave: NDArray[np.bool_]) -> Tuple[int, int]:\r\n        unresolved_cell_mask = numpy.count_nonzero(wave, axis=0) > 1\r\n        cell_weights = numpy.where(unresolved_cell_mask, preferences, numpy.inf)\r\n        row, col = numpy.unravel_index(numpy.argmin(cell_weights), cell_weights.shape)\r\n        return row.item(), col.item()\r\n\r\n    return randomLocationHeuristic\r\n\r\n\r\ndef makeEntropyLocationHeuristic(preferences: NDArray[np.floating[Any]]) -> Callable[[NDArray[np.bool_]], Tuple[int, int]]:\r\n    def entropyLocationHeuristic(wave: NDArray[np.bool_]) -> Tuple[int, int]:\r\n        unresolved_cell_mask = numpy.count_nonzero(wave, axis=0) > 1\r\n        cell_weights = numpy.where(\r\n            unresolved_cell_mask,\r\n            preferences + numpy.count_nonzero(wave, axis=0),\r\n            numpy.inf,\r\n        )\r\n        row, col = numpy.unravel_index(numpy.argmin(cell_weights), cell_weights.shape)\r\n        return row.item(), col.item()\r\n\r\n    return entropyLocationHeuristic\r\n\r\n\r\ndef makeAntiEntropyLocationHeuristic(\r\n    preferences: NDArray[np.floating[Any]]\r\n) -> Callable[[NDArray[np.bool_]], Tuple[int, int]]:\r\n    def antiEntropyLocationHeuristic(wave: NDArray[np.bool_]) -> Tuple[int, int]:\r\n        unresolved_cell_mask = numpy.count_nonzero(wave, axis=0) > 1\r\n        cell_weights = numpy.where(\r\n            unresolved_cell_mask,\r\n            preferences + numpy.count_nonzero(wave, axis=0),\r\n            -numpy.inf,\r\n        )\r\n        row, col = numpy.unravel_index(numpy.argmax(cell_weights), cell_weights.shape)\r\n        return row.item(), col.item()\r\n\r\n    return antiEntropyLocationHeuristic\r\n\r\n\r\ndef spiral_transforms() -> Iterator[Tuple[int, int]]:\r\n    for N in itertools.count(start=1):\r\n        if N % 2 == 0:\r\n            yield (0, 1)  # right\r\n            for _ in range(N):\r\n                yield (1, 0)  # down\r\n            for _ in range(N):\r\n                yield (0, -1)  # left\r\n        else:\r\n            yield (0, -1)  # left\r\n            for _ in range(N):\r\n                yield (-1, 0)  # up\r\n            for _ in range(N):\r\n                yield (0, 1)  # right\r\n\r\n\r\ndef spiral_coords(x: int, y: int) -> Iterator[Tuple[int, int]]:\r\n    yield x, y\r\n    for transform in spiral_transforms():\r\n        x += transform[0]\r\n        y += transform[1]\r\n        yield x, y\r\n\r\ndef fill_with_curve(arr: NDArray[np.floating[T]], curve_gen: Iterable[Iterable[int]]) -> NDArray[np.floating[T]]:\r\n    arr_len = numpy.prod(arr.shape)\r\n    fill = 0\r\n    for coord in curve_gen:\r\n        # logger.debug(fill, idx, coord)\r\n        if fill < arr_len:\r\n            try:\r\n                arr[tuple(coord)] = fill / arr_len\r\n                fill += 1\r\n            except IndexError:\r\n                pass\r\n        else:\r\n            break\r\n    # logger.debug(arr)\r\n    return arr\r\n\r\n\r\ndef makeSpiralLocationHeuristic(preferences: NDArray[np.floating[Any]]) -> Callable[[NDArray[np.bool_]], Tuple[int, int]]:\r\n    # https://stackoverflow.com/a/23707273/5562922\r\n\r\n    spiral_gen = (\r\n        sc for sc in spiral_coords(preferences.shape[0] // 2, preferences.shape[1] // 2)\r\n    )\r\n\r\n    cell_order = fill_with_curve(preferences, spiral_gen)\r\n\r\n    def spiralLocationHeuristic(wave: NDArray[np.bool_]) -> Tuple[int, int]:\r\n        unresolved_cell_mask = numpy.count_nonzero(wave, axis=0) > 1\r\n        cell_weights = numpy.where(unresolved_cell_mask, cell_order, numpy.inf)\r\n        row, col = numpy.unravel_index(numpy.argmin(cell_weights), cell_weights.shape)\r\n        return row.item(), col.item()\r\n\r\n    return spiralLocationHeuristic\r\n\r\n\r\ndef makeHilbertLocationHeuristic(preferences: NDArray[np.floating[Any]]) -> Callable[[NDArray[np.bool_]], Tuple[int, int]]:\r\n    curve_size = math.ceil(math.sqrt(max(preferences.shape[0], preferences.shape[1])))\r\n    logger.debug(curve_size)\r\n    curve_size = 4\r\n    h_curve = HilbertCurve(curve_size, 2)\r\n    h_coords = (h_curve.point_from_distance(i) for i in itertools.count())\r\n    cell_order = fill_with_curve(preferences, h_coords)\r\n    # logger.debug(cell_order)\r\n\r\n    def hilbertLocationHeuristic(wave: NDArray[np.bool_]) -> Tuple[int, int]:\r\n        unresolved_cell_mask = numpy.count_nonzero(wave, axis=0) > 1\r\n        cell_weights = numpy.where(unresolved_cell_mask, cell_order, numpy.inf)\r\n        row, col = numpy.unravel_index(numpy.argmin(cell_weights), cell_weights.shape)\r\n        return row.item(), col.item()\r\n\r\n    return hilbertLocationHeuristic\r\n\r\n\r\ndef simpleLocationHeuristic(wave: NDArray[np.bool_]) -> Tuple[int, int]:\r\n    unresolved_cell_mask = numpy.count_nonzero(wave, axis=0) > 1\r\n    cell_weights = numpy.where(\r\n        unresolved_cell_mask, numpy.count_nonzero(wave, axis=0), numpy.inf\r\n    )\r\n    row, col = numpy.unravel_index(numpy.argmin(cell_weights), cell_weights.shape)\r\n    return row.item(), col.item()\r\n\r\n\r\ndef lexicalLocationHeuristic(wave: NDArray[np.bool_]) -> Tuple[int, int]:\r\n    unresolved_cell_mask = numpy.count_nonzero(wave, axis=0) > 1\r\n    cell_weights = numpy.where(unresolved_cell_mask, 1.0, numpy.inf)\r\n    row, col = numpy.unravel_index(numpy.argmin(cell_weights), cell_weights.shape)\r\n    return row.item(), col.item()\r\n\r\n\r\n#####################################\r\n# Pattern Heuristics\r\n\r\n\r\ndef lexicalPatternHeuristic(weights: NDArray[np.bool_], wave: NDArray[np.bool_]) -> int:\r\n    return numpy.nonzero(weights)[0][0].item()\r\n\r\n\r\ndef makeWeightedPatternHeuristic(weights: NDArray[np.floating[Any]]):\r\n    num_of_patterns = len(weights)\r\n\r\n    def weightedPatternHeuristic(wave: NDArray[np.bool_], _: NDArray[np.bool_]) -> int:\r\n        # TODO: there's maybe a faster, more controlled way to do this sampling...\r\n        weighted_wave: NDArray[np.floating[Any]] = weights * wave\r\n        weighted_wave /= weighted_wave.sum()\r\n        result = numpy.random.choice(num_of_patterns, p=weighted_wave)\r\n        return result\r\n\r\n    return weightedPatternHeuristic\r\n\r\n\r\ndef makeRarestPatternHeuristic(weights: NDArray[np.floating[Any]]) -> Callable[[NDArray[np.bool_], NDArray[np.bool_]], int]:\r\n    \"\"\"Return a function that chooses the rarest (currently least-used) pattern.\"\"\"\r\n    def weightedPatternHeuristic(wave: NDArray[np.bool_], total_wave: NDArray[np.bool_]) -> int:\r\n        logger.debug(total_wave.shape)\r\n        # [logger.debug(e) for e in wave]\r\n        wave_sums = numpy.sum(total_wave, (1, 2))\r\n        # logger.debug(wave_sums)\r\n        selected_pattern = numpy.random.choice(\r\n            numpy.where(wave_sums == wave_sums.max())[0]\r\n        )\r\n        return selected_pattern\r\n\r\n    return weightedPatternHeuristic\r\n\r\n\r\ndef makeMostCommonPatternHeuristic(\r\n    weights: NDArray[np.floating[Any]]\r\n) -> Callable[[NDArray[np.bool_], NDArray[np.bool_]], int]:\r\n    \"\"\"Return a function that chooses the most common (currently most-used) pattern.\"\"\"\r\n    def weightedPatternHeuristic(wave: NDArray[np.bool_], total_wave: NDArray[np.bool_]) -> int:\r\n        logger.debug(total_wave.shape)\r\n        # [logger.debug(e) for e in wave]\r\n        wave_sums = numpy.sum(total_wave, (1, 2))\r\n        selected_pattern = numpy.random.choice(\r\n            numpy.where(wave_sums == wave_sums.min())[0]\r\n        )\r\n        return selected_pattern\r\n\r\n    return weightedPatternHeuristic\r\n\r\n\r\ndef makeRandomPatternHeuristic(weights: NDArray[np.floating[Any]]) -> Callable[[NDArray[np.bool_], NDArray[np.bool_]], int]:\r\n    num_of_patterns = len(weights)\r\n\r\n    def randomPatternHeuristic(wave: NDArray[np.bool_], _: NDArray[np.bool_]) -> int:\r\n        # TODO: there's maybe a faster, more controlled way to do this sampling...\r\n        weighted_wave = 1.0 * wave\r\n        weighted_wave /= weighted_wave.sum()\r\n        result = numpy.random.choice(num_of_patterns, p=weighted_wave)\r\n        return result\r\n\r\n    return randomPatternHeuristic\r\n\r\n\r\n######################################\r\n# Global Constraints\r\n\r\n\r\ndef make_global_use_all_patterns() -> Callable[[NDArray[np.bool_]], bool]:\r\n    def global_use_all_patterns(wave: NDArray[np.bool_]) -> bool:\r\n        \"\"\"Returns true if at least one instance of each pattern is still possible.\"\"\"\r\n        return numpy.all(numpy.any(wave, axis=(1, 2))).item()\r\n\r\n    return global_use_all_patterns\r\n\r\n\r\n#####################################\r\n# Solver\r\n\r\n\r\ndef propagate(\r\n    wave: NDArray[np.bool_],\r\n    adj: Mapping[Tuple[int, int], NDArray[numpy.bool_]],\r\n    periodic: bool = False,\r\n    onPropagate: Optional[Callable[[NDArray[numpy.bool_]], None]] = None,\r\n) -> None:\r\n    \"\"\"Completely probagate any newly collapsed waves to all areas.\"\"\"\r\n    last_count = wave.sum()\r\n\r\n    while True:\r\n        supports = {}\r\n        if periodic:\r\n            padded = numpy.pad(wave, ((0, 0), (1, 1), (1, 1)), mode=\"wrap\")\r\n        else:\r\n            padded = numpy.pad(\r\n                wave, ((0, 0), (1, 1), (1, 1)), mode=\"constant\", constant_values=True\r\n            )\r\n\r\n        # adj is the list of adjacencies. For each direction d in adjacency, \r\n        # check which patterns are still valid... \r\n        for d in adj:\r\n            dx, dy = d\r\n            # padded[] is a version of the adjacency matrix with the values wrapped around\r\n            # shifted[] is the padded version with the values shifted over in one direction\r\n            # because my code stores the directions as relative (x,y) coordinates, we can find\r\n            # the adjacent cell for each direction by simply shifting the matrix in that direction,\r\n            # which allows for arbitrary adjacency directions. This is somewhat excessive, but elegant.\r\n\r\n            shifted = padded[\r\n                :, 1 + dx : 1 + wave.shape[1] + dx, 1 + dy : 1 + wave.shape[2] + dy\r\n            ]\r\n            # logger.debug(f\"shifted: {shifted.shape} | adj[d]: {adj[d].shape} | d: {d}\")\r\n            # raise StopEarly\r\n            # supports[d] = numpy.einsum('pwh,pq->qwh', shifted, adj[d]) > 0\r\n\r\n            # The adjacency matrix is a boolean matrix, indexed by the direction and the two patterns.\r\n            # If the value for (direction, pattern1, pattern2) is True, then this is a valid adjacency.\r\n            # This gives us a rapid way to compare: True is 1, False is 0, so multiplying the matrices\r\n            # gives us the adjacency compatibility.\r\n            supports[d] = (adj[d] @ shifted.reshape(shifted.shape[0], -1)).reshape(\r\n                shifted.shape\r\n            ) > 0\r\n            # supports[d] = ( <- for each cell in the matrix\r\n            # adj[d]  <- the adjacency matrix [sliced by the direction d]\r\n            # @       <- Matrix multiplication\r\n            # shifted.reshape(shifted.shape[0], -1)) <- change the shape of the shifted matrix to 2-dimensions, to make the matrix multiplication easier\r\n            # .reshape(           <- reshape our matrix-multiplied result...\r\n            #   shifted.shape)   <- ...to match the original shape of the shifted matrix\r\n            # > 0    <- is not false\r\n\r\n        # multiply the wave matrix by the support matrix to find which patterns are still in the domain\r\n        for d in adj:\r\n            wave *= supports[d]\r\n\r\n        if wave.sum() == last_count:\r\n            break  # No changes since the last loop, changed waves have been fully propagated.\r\n        last_count = wave.sum()\r\n\r\n    if onPropagate:\r\n        onPropagate(wave)\r\n\r\n    if (wave.sum(axis=0) == 0).any():\r\n        raise Contradiction(\"Wave is in a contradictory state and can not be solved.\")\r\n\r\n\r\ndef observe(\r\n    wave: NDArray[np.bool_],\r\n    locationHeuristic: Callable[[NDArray[np.bool_]], Tuple[int, int]],\r\n    patternHeuristic: Callable[[NDArray[np.bool_], NDArray[np.bool_]], int],\r\n) -> Tuple[int, int, int]:\r\n    \"\"\"Return the next best wave to collapse based on the provided heuristics.\"\"\"\r\n    i, j = locationHeuristic(wave)\r\n    pattern = patternHeuristic(wave[:, i, j], wave)\r\n    return pattern, i, j\r\n\r\n\r\ndef run(\r\n    wave: NDArray[np.bool_],\r\n    adj: Mapping[Tuple[int, int], NDArray[numpy.bool_]],\r\n    locationHeuristic: Callable[[NDArray[numpy.bool_]], Tuple[int, int]],\r\n    patternHeuristic: Callable[[NDArray[np.bool_], NDArray[np.bool_]], int],\r\n    periodic: bool = False,\r\n    backtracking: bool = False,\r\n    onBacktrack: Optional[Callable[[], None]] = None,\r\n    onChoice: Optional[Callable[[int, int, int], None]] = None,\r\n    onObserve: Optional[Callable[[NDArray[numpy.bool_]], None]] = None,\r\n    onPropagate: Optional[Callable[[NDArray[numpy.bool_]], None]] = None,\r\n    checkFeasible: Optional[Callable[[NDArray[numpy.bool_]], bool]] = None,\r\n    onFinal: Optional[Callable[[NDArray[numpy.bool_]], None]] = None,\r\n    depth: int = 0,\r\n    depth_limit: Optional[int] = None,\r\n) -> NDArray[numpy.int64]:\r\n    solver = Solver(\r\n        wave=wave,\r\n        adj=adj,\r\n        periodic=periodic,\r\n        backtracking=backtracking,\r\n        on_backtrack=onBacktrack,\r\n        on_choice=onChoice,\r\n        on_observe=onObserve,\r\n        on_propagate=onPropagate,\r\n        check_feasible=checkFeasible\r\n    )\r\n    while not solver.solve_next(location_heuristic=locationHeuristic, pattern_heuristic=patternHeuristic):\r\n        pass\r\n    if onFinal:\r\n        onFinal(solver.wave)\r\n    return numpy.argmax(solver.wave, axis=0)\r\n"
  },
  {
    "path": "wfc/wfc_tiles.py",
    "content": "\"\"\"Breaks an image into consituant tiles.\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Dict, Tuple\nimport numpy as np\nfrom numpy.typing import NDArray\nfrom .wfc_utilities import hash_downto\n\n\ndef image_to_tiles(img: NDArray[np.integer], tile_size: int) -> NDArray[np.integer]:\n    \"\"\"\n    Takes an images, divides it into tiles, return an array of tiles.\n    \"\"\"\n    padding_argument = [(0, 0), (0, 0), (0, 0)]\n    for input_dim in [0, 1]:\n        padding_argument[input_dim] = (\n            0,\n            (tile_size - img.shape[input_dim]) % tile_size,\n        )\n    img = np.pad(img, padding_argument, mode=\"constant\")\n    tiles = img.reshape(\n        (\n            img.shape[0] // tile_size,\n            tile_size,\n            img.shape[1] // tile_size,\n            tile_size,\n            img.shape[2],\n        )\n    ).swapaxes(1, 2)\n    return tiles\n\n\ndef make_tile_catalog(\n    image_data: NDArray[np.integer], tile_size: int\n) -> Tuple[Dict[int, NDArray[np.integer]], NDArray[np.int64], NDArray[np.int64], Tuple[NDArray[np.int64], NDArray[np.int64]]]:\n    \"\"\"\n    Takes an image and tile size and returns the following:\n    tile_catalog is a dictionary tiles, with the hashed ID as the key\n    tile_grid is the original image, expressed in terms of hashed tile IDs\n    code_list is the original image, expressed in terms of hashed tile IDs and reduced to one dimension\n    unique_tiles is the set of tiles, plus the frequency of their occurrence\n    \"\"\"\n    channels = image_data.shape[2]  # Number of color channels in the image\n    tiles = image_to_tiles(image_data, tile_size)\n    tile_list: NDArray[np.integer] = tiles.reshape((tiles.shape[0] * tiles.shape[1], tile_size, tile_size, channels))\n    code_list: NDArray[np.int64] = hash_downto(tiles, 2).reshape((tiles.shape[0] * tiles.shape[1]))\n    tile_grid: NDArray[np.int64] = hash_downto(tiles, 2)\n    unique_tiles: Tuple[NDArray[np.int64], NDArray[np.int64]] = np.unique(tile_grid, return_counts=True)\n\n    tile_catalog: Dict[int, NDArray[np.integer]] = {}\n    for i, j in enumerate(code_list):\n        tile_catalog[j] = tile_list[i]\n    return tile_catalog, tile_grid, code_list, unique_tiles\n\n\ndef tiles_to_images(tile_grid, tile_catalog):\n    return\n"
  },
  {
    "path": "wfc/wfc_utilities.py",
    "content": "\"\"\"Utility data and functions for WFC\"\"\"\nfrom __future__ import annotations\n\nimport collections\nimport logging\nfrom typing import Any\nimport numpy as np\nfrom numpy.typing import NDArray\n\nlogger = logging.getLogger(__name__)\n\nCoordXY = collections.namedtuple(\"CoordXY\", [\"x\", \"y\"])\nCoordRC = collections.namedtuple(\"CoordRC\", [\"row\", \"column\"])\n\n\ndef hash_downto(a: NDArray[np.integer], rank: int, seed: Any=0) -> NDArray[np.int64]:\n    state = np.random.RandomState(seed)\n    assert rank < len(a.shape)\n    # logger.debug((np.prod(a.shape[:rank]),-1))\n    # logger.debug(np.array([np.prod(a.shape[:rank]),-1], dtype=np.int64).dtype)\n    u: NDArray[np.integer] = a.reshape((np.prod(a.shape[:rank], dtype=np.int64), -1))\n    # u = a.reshape((np.prod(a.shape[:rank]),-1))\n    v = state.randint(1 - (1 << 63), 1 << 63, np.prod(a.shape[rank:]), dtype=np.int64)\n    return np.asarray(np.inner(u, v).reshape(a.shape[:rank]), dtype=np.int64)\n\n\ntry:\n    import google.colab  # type: ignore\n\n    IN_COLAB = True\nexcept:\n    IN_COLAB = False\n\n\ndef load_visualizer(wfc_ns):\n    if IN_COLAB:\n        from google.colab import files  # type: ignore\n\n        uploaded = files.upload()\n        for fn in uploaded.keys():\n            logger.debug(\n                'User uploaded file \"{name}\" with length {length} bytes'.format(\n                    name=fn, length=len(uploaded[fn])\n                )\n            )\n    else:\n        import matplotlib  # type: ignore\n        import matplotlib.pylab  # type: ignore\n        from matplotlib.pyplot import figure, subplot, title, matshow  # type: ignore\n\n    wfc_ns.img_filename = f\"images/{wfc_ns.img_filename}\"\n    return wfc_ns\n\n\ndef find_pattern_center(wfc_ns):\n    # wfc_ns.pattern_center = (math.floor((wfc_ns.pattern_width - 1) / 2), math.floor((wfc_ns.pattern_width - 1) / 2))\n    wfc_ns.pattern_center = (0, 0)\n    return wfc_ns\n"
  },
  {
    "path": "wfc/wfc_visualize.py",
    "content": "\"Visualize the patterns into tiles and so on.\"\r\nfrom __future__ import annotations\r\n\r\nimport logging\r\nimport math\r\nimport pathlib\r\nimport itertools\r\nfrom typing import Dict, Tuple\r\nimport imageio  # type: ignore\r\nimport matplotlib  # type: ignore\r\nimport struct\r\nimport matplotlib.pyplot as plt  # type: ignore\r\nimport numpy as np\r\nfrom numpy.typing import NDArray\r\nfrom .wfc_patterns import pattern_grid_to_tiles\r\n\r\nlogger = logging.getLogger(__name__)\r\n\r\n## Helper functions\r\nRGB_CHANNELS = 3\r\n\r\n\r\ndef rgb_to_int(rgb_in):\r\n    \"\"\"\"Takes RGB triple, returns integer representation.\"\"\"\r\n    return struct.unpack(\r\n        \"I\", struct.pack(\"<\" + \"B\" * 4, *(rgb_in + [0] * (4 - len(rgb_in))))\r\n    )[0]\r\n\r\n\r\ndef int_to_rgb(val):\r\n    \"\"\"Convert hashed int to RGB values\"\"\"\r\n    return [x for x in val.to_bytes(RGB_CHANNELS, \"little\")]\r\n\r\n\r\nWFC_PARTIAL_BLANK = np.nan\r\n\r\n\r\ndef tile_to_image(tile, tile_catalog, tile_size, visualize=False):\r\n    \"\"\"\r\n    Takes a single tile and returns the pixel image representation.\r\n    \"\"\"\r\n    new_img = np.zeros((tile_size[0], tile_size[1], 3), dtype=np.int64)\r\n    for u in range(tile_size[0]):\r\n        for v in range(tile_size[1]):\r\n            ## If we want to display a partial pattern, it is helpful to\r\n            ## be able to show empty cells. Therefore, in visualize mode,\r\n            ## we use -1 as a magic number for a non-existant tile.\r\n            pixel = [200, 0, 200]\r\n            if (visualize) and ((-1 == tile) or (WFC_PARTIAL_BLANK == tile)):\r\n                if 0 == (u + v) % 2:\r\n                    pixel = [255, 0, 255]\r\n            else:\r\n                if (visualize) and -2 == tile:\r\n                    pixel = [0, 255, 255]\r\n                else:\r\n                    pixel = tile_catalog[tile][u, v]\r\n            new_img[u, v] = pixel\r\n    return new_img\r\n\r\n\r\ndef argmax_unique(arr, axis):\r\n    \"\"\"Return a mask so that we can exclude the nonunique maximums, i.e. the nodes that aren't completely resolved\"\"\"\r\n    arrm = np.argmax(arr, axis)\r\n    arrs = np.sum(arr, axis)\r\n    nonunique_mask = np.ma.make_mask((arrs == 1) is False)\r\n    uni_argmax = np.ma.masked_array(arrm, mask=nonunique_mask, fill_value=-1)\r\n    return uni_argmax, nonunique_mask\r\n\r\n\r\ndef make_solver_loggers(filename, stats={}):\r\n    counter_choices = 0\r\n    counter_wave = 0\r\n    counter_backtracks = 0\r\n    counter_propagate = 0\r\n\r\n    def choice_count(pattern, i, j, wave=None):\r\n        nonlocal counter_choices\r\n        counter_choices += 1\r\n\r\n    def wave_count(wave):\r\n        nonlocal counter_wave\r\n        counter_wave += 1\r\n\r\n    def backtrack_count() -> None:\r\n        nonlocal counter_backtracks\r\n        counter_backtracks += 1\r\n\r\n    def propagate_count(wave):\r\n        nonlocal counter_propagate\r\n        counter_propagate += 1\r\n\r\n    def final_count(wave):\r\n        logger.info(\r\n            f\"{filename}: choices: {counter_choices}, wave:{counter_wave}, backtracks: {counter_backtracks}, propagations: {counter_propagate}\"\r\n        )\r\n        stats.update(\r\n            {\r\n                \"choices\": counter_choices,\r\n                \"wave\": counter_wave,\r\n                \"backtracks\": counter_backtracks,\r\n                \"propagations\": counter_propagate,\r\n            }\r\n        )\r\n        return stats\r\n\r\n    def report_count():\r\n        stats.update(\r\n            {\r\n                \"choices\": counter_choices,\r\n                \"wave\": counter_wave,\r\n                \"backtracks\": counter_backtracks,\r\n                \"propagations\": counter_propagate,\r\n            }\r\n        )\r\n        return stats\r\n\r\n    return (\r\n        choice_count,\r\n        wave_count,\r\n        backtrack_count,\r\n        propagate_count,\r\n        final_count,\r\n        report_count,\r\n    )\r\n\r\n\r\ndef make_solver_visualizers(\r\n    filename: str,\r\n    wave: NDArray[np.bool_],\r\n    decode_patterns=None,\r\n    pattern_catalog=None,\r\n    tile_catalog=None,\r\n    tile_size=[1, 1],\r\n):\r\n    \"\"\"Construct visualizers for displaying the intermediate solver status\"\"\"\r\n    logger.debug(wave.shape)\r\n    pattern_total_count = wave.shape[0]\r\n    resolution_order = np.full(\r\n        wave.shape[1:], np.nan\r\n    )  # pattern_wave = when was this resolved?\r\n    backtracking_order = np.full(\r\n        wave.shape[1:], np.nan\r\n    )  # on which iternation was this resolved?\r\n    pattern_solution = np.full(wave.shape[1:], np.nan)  # what is the resolved result?\r\n    resolution_method = np.zeros(\r\n        wave.shape[1:]\r\n    )  # did we set this via observation or propagation?\r\n    choice_count = 0\r\n    vis_count = 0\r\n    backtracking_count = 0\r\n    max_choices = math.floor((wave.shape[1] * wave.shape[2]) / 3)\r\n    output_individual_visualizations = False\r\n\r\n    tile_wave = np.zeros(wave.shape, dtype=np.int64)\r\n    for i in range(wave.shape[0]):\r\n        local_solution_as_ids = np.full(wave.shape[1:], decode_patterns[i])\r\n        local_solution_tile_grid = pattern_grid_to_tiles(\r\n            local_solution_as_ids, pattern_catalog\r\n        )\r\n        tile_wave[i] = local_solution_tile_grid\r\n\r\n    def choice_vis(pattern, i, j, wave=None):\r\n        nonlocal choice_count\r\n        nonlocal resolution_order\r\n        nonlocal resolution_method\r\n        choice_count += 1\r\n        resolution_order[i][j] = choice_count\r\n        pattern_solution[i][j] = pattern\r\n        resolution_method[i][j] = 2\r\n        if output_individual_visualizations:\r\n            figure_solver_data(\r\n                f\"visualization/{filename}_choice_{choice_count}.png\",\r\n                \"order of resolution\",\r\n                resolution_order,\r\n                0,\r\n                max_choices,\r\n                \"gist_ncar\",\r\n            )\r\n            figure_solver_data(\r\n                f\"visualization/{filename}_solution_{choice_count}.png\",\r\n                \"chosen pattern\",\r\n                pattern_solution,\r\n                0,\r\n                pattern_total_count,\r\n                \"viridis\",\r\n            )\r\n            figure_solver_data(\r\n                f\"visualization/{filename}_resolution_{choice_count}.png\",\r\n                \"resolution method\",\r\n                resolution_method,\r\n                0,\r\n                2,\r\n                \"inferno\",\r\n            )\r\n        if wave:\r\n            _assigned_patterns, nonunique_mask = argmax_unique(wave, 0)\r\n            resolved_by_propagation = (\r\n                np.ma.mask_or(nonunique_mask, resolution_method != 0) == 0\r\n            )\r\n            resolution_method[resolved_by_propagation] = 1\r\n            resolution_order[resolved_by_propagation] = choice_count\r\n            if output_individual_visualizations:\r\n                figure_solver_data(\r\n                    f\"visualization/{filename}_wave_{choice_count}.png\",\r\n                    \"patterns remaining\",\r\n                    np.count_nonzero(wave > 0, axis=0),\r\n                    0,\r\n                    wave.shape[0],\r\n                    \"plasma\",\r\n                )\r\n\r\n    def wave_vis(wave):\r\n        nonlocal vis_count\r\n        nonlocal resolution_method\r\n        nonlocal resolution_order\r\n        vis_count += 1\r\n        pattern_left_count = np.count_nonzero(wave > 0, axis=0)\r\n        # assigned_patterns, nonunique_mask = argmax_unique(wave, 0)\r\n        resolved_by_propagation = (\r\n            np.ma.mask_or(pattern_left_count > 1, resolution_method != 0) != 1\r\n        )\r\n        # logger.debug(resolved_by_propagation)\r\n        resolution_method[resolved_by_propagation] = 1\r\n        resolution_order[resolved_by_propagation] = choice_count\r\n        backtracking_order[resolved_by_propagation] = backtracking_count\r\n        if output_individual_visualizations:\r\n            figure_wave_patterns(filename, pattern_left_count, pattern_total_count)\r\n            figure_solver_data(\r\n                f\"visualization/{filename}_wave_patterns_{choice_count}.png\",\r\n                \"patterns remaining\",\r\n                pattern_left_count,\r\n                0,\r\n                pattern_total_count,\r\n                \"magma\",\r\n            )\r\n        if decode_patterns and pattern_catalog and tile_catalog:\r\n            solution_as_ids = np.vectorize(lambda x: decode_patterns[x])(\r\n                np.argmax(wave, 0)\r\n            )\r\n            solution_tile_grid = pattern_grid_to_tiles(solution_as_ids, pattern_catalog)\r\n            if output_individual_visualizations:\r\n                figure_solver_data(\r\n                    f\"visualization/{filename}_tiles_assigned_{choice_count}.png\",\r\n                    \"tiles assigned\",\r\n                    solution_tile_grid,\r\n                    0,\r\n                    pattern_total_count,\r\n                    \"plasma\",\r\n                )\r\n            img = tile_grid_to_image(solution_tile_grid.T, tile_catalog, tile_size)\r\n\r\n            masked_tile_wave: np.ma.MaskedArray = np.ma.MaskedArray(\r\n                data=tile_wave, mask=(wave == False), dtype=np.int64\r\n            )\r\n            masked_img = tile_grid_to_average(\r\n                np.transpose(masked_tile_wave, (0, 2, 1)), tile_catalog, tile_size\r\n            )\r\n\r\n            if output_individual_visualizations:\r\n                figure_solver_image(\r\n                    f\"visualization/{filename}_solution_partial_{choice_count}.png\",\r\n                    \"solved_tiles\",\r\n                    img.astype(np.uint8),\r\n                )\r\n                imageio.imwrite(\r\n                    f\"visualization/{filename}_solution_partial_img_{choice_count}.png\",\r\n                    img.astype(np.uint8),\r\n                )\r\n            fig_list = [\r\n                # {\"title\": \"resolved by propagation\", \"data\": resolved_by_propagation.T, \"vmin\": 0, \"vmax\": 2, \"cmap\": \"inferno\", \"datatype\":\"figure\"},\r\n                {\r\n                    \"title\": \"order of resolution\",\r\n                    \"data\": resolution_order.T,\r\n                    \"vmin\": 0,\r\n                    \"vmax\": max_choices / 4,\r\n                    \"cmap\": \"hsv\",\r\n                    \"datatype\": \"figure\",\r\n                },\r\n                {\r\n                    \"title\": \"chosen pattern\",\r\n                    \"data\": pattern_solution.T,\r\n                    \"vmin\": 0,\r\n                    \"vmax\": pattern_total_count,\r\n                    \"cmap\": \"viridis\",\r\n                    \"datatype\": \"figure\",\r\n                },\r\n                {\r\n                    \"title\": \"resolution method\",\r\n                    \"data\": resolution_method.T,\r\n                    \"vmin\": 0,\r\n                    \"vmax\": 2,\r\n                    \"cmap\": \"magma\",\r\n                    \"datatype\": \"figure\",\r\n                },\r\n                {\r\n                    \"title\": \"patterns remaining\",\r\n                    \"data\": pattern_left_count.T,\r\n                    \"vmin\": 0,\r\n                    \"vmax\": pattern_total_count,\r\n                    \"cmap\": \"viridis\",\r\n                    \"datatype\": \"figure\",\r\n                },\r\n                {\r\n                    \"title\": \"tiles assigned\",\r\n                    \"data\": solution_tile_grid.T,\r\n                    \"vmin\": None,\r\n                    \"vmax\": None,\r\n                    \"cmap\": \"prism\",\r\n                    \"datatype\": \"figure\",\r\n                },\r\n                {\r\n                    \"title\": \"solved tiles\",\r\n                    \"data\": masked_img.astype(np.uint8),\r\n                    \"datatype\": \"image\",\r\n                },\r\n            ]\r\n            figure_unified(\r\n                \"Solver Readout\",\r\n                f\"visualization/{filename}_readout_{choice_count:03}_{vis_count:03}.png\",\r\n                fig_list,\r\n            )\r\n\r\n    def backtrack_vis() -> None:\r\n        nonlocal vis_count\r\n        nonlocal pattern_solution\r\n        nonlocal backtracking_count\r\n        backtracking_count += 1\r\n        vis_count += 1\r\n        pattern_solution = np.full(wave.shape[1:], -1)\r\n\r\n    return choice_vis, wave_vis, backtrack_vis, None, wave_vis, None\r\n\r\n\r\ndef figure_unified(figure_name_overall, filename, data):\r\n    matfig, axs = plt.subplots(\r\n        1, len(data), sharey=\"row\", gridspec_kw={\"hspace\": 0, \"wspace\": 0}\r\n    )\r\n\r\n    for idx, _data_obj in enumerate(data):\r\n        if \"image\" == data[idx][\"datatype\"]:\r\n            axs[idx].imshow(data[idx][\"data\"], interpolation=\"nearest\")\r\n        else:\r\n            axs[idx].matshow(\r\n                data[idx][\"data\"],\r\n                vmin=data[idx][\"vmin\"],\r\n                vmax=data[idx][\"vmax\"],\r\n                cmap=data[idx][\"cmap\"],\r\n            )\r\n        axs[idx].get_xaxis().set_visible(False)\r\n        axs[idx].get_yaxis().set_visible(False)\r\n        axs[idx].label_outer()\r\n\r\n    plt.savefig(filename, bbox_inches=\"tight\", pad_inches=0, dpi=600)\r\n    plt.close(fig=matfig)\r\n    plt.close(\"all\")\r\n\r\n\r\nvis_count = 0\r\n\r\n\r\ndef visualize_solver(wave):\r\n    pattern_left_count = np.count_nonzero(wave > 0, axis=0)\r\n    pattern_total_count = wave.shape[0]\r\n    figure_wave_patterns(pattern_left_count, pattern_total_count)\r\n\r\n\r\ndef make_figure_solver_image(plot_title, img):\r\n    visfig = plt.figure(figsize=(4, 4), edgecolor=\"k\", frameon=True)\r\n    plt.imshow(img, interpolation=\"nearest\")\r\n    plt.title(plot_title)\r\n    plt.grid(None)\r\n    plt.grid(None)\r\n    an_ax = plt.gca()\r\n    an_ax.get_xaxis().set_visible(False)\r\n    an_ax.get_yaxis().set_visible(False)\r\n    return visfig\r\n\r\n\r\ndef figure_solver_image(filename, plot_title, img):\r\n    visfig = make_figure_solver_image(plot_title, img)\r\n    plt.savefig(filename, bbox_inches=\"tight\", pad_inches=0)\r\n    plt.close(fig=visfig)\r\n    plt.close(\"all\")\r\n\r\n\r\ndef make_figure_solver_data(plot_title, data, min_count, max_count, cmap_name):\r\n    visfig = plt.figure(figsize=(4, 4), edgecolor=\"k\", frameon=True)\r\n    plt.title(plot_title)\r\n    plt.matshow(data, vmin=min_count, vmax=max_count, cmap=cmap_name)\r\n    plt.grid(None)\r\n    plt.grid(None)\r\n    ax = plt.gca()\r\n    ax.get_xaxis().set_visible(False)\r\n    ax.get_yaxis().set_visible(False)\r\n    return visfig\r\n\r\n\r\ndef figure_solver_data(filename, plot_title, data, min_count, max_count, cmap_name):\r\n    visfig = make_figure_solver_data(plot_title, data, min_count, max_count, cmap_name)\r\n    plt.savefig(filename, bbox_inches=\"tight\", pad_inches=0)\r\n    plt.close(fig=visfig)\r\n    plt.close(\"all\")\r\n\r\n\r\ndef figure_wave_patterns(filename, pattern_left_count, max_count):\r\n    global vis_count\r\n    vis_count += 1\r\n    visfig = plt.figure(figsize=(4, 4), edgecolor=\"k\", frameon=True)\r\n\r\n    plt.title(\"wave\")\r\n    plt.matshow(pattern_left_count, vmin=0, vmax=max_count, cmap=\"plasma\")\r\n    plt.grid(None)\r\n\r\n    plt.grid(None)\r\n    plt.savefig(f\"{filename}_wave_patterns_{vis_count}.png\")\r\n    plt.close(fig=visfig)\r\n\r\n\r\ndef tile_grid_to_average(\r\n    tile_grid: np.ma.MaskedArray,\r\n    tile_catalog: Dict[int, NDArray[np.int64]],\r\n    tile_size: Tuple[int, int],\r\n    color_channels: int = 3,\r\n) -> NDArray[np.int64]:\r\n    \"\"\"\r\n  Takes a masked array of tile grid stacks and transforms it into an image, taking\r\n  the average colors of the tiles in tile_catalog.\r\n  \"\"\"\r\n    new_img = np.zeros(\r\n        (\r\n            tile_grid.shape[1] * tile_size[0],\r\n            tile_grid.shape[2] * tile_size[1],\r\n            color_channels,\r\n        ),\r\n        dtype=np.int64,\r\n    )\r\n    for i in range(tile_grid.shape[1]):\r\n        for j in range(tile_grid.shape[2]):\r\n            tile_stack = tile_grid[:, i, j]\r\n            for u in range(tile_size[0]):\r\n                for v in range(tile_size[1]):\r\n                    pixel = [200, 0, 200]\r\n                    pixel_list = np.array(\r\n                        [\r\n                            tile_catalog[t][u, v]\r\n                            for t in tile_stack[tile_stack.mask == False]\r\n                        ],\r\n                        dtype=np.int64,\r\n                    )\r\n                    pixel = np.mean(pixel_list, axis=0)\r\n                    # TODO: will need to change if using an image with more than 3 channels\r\n                    new_img[(i * tile_size[0]) + u, (j * tile_size[1]) + v] = np.resize(\r\n                        pixel,\r\n                        new_img[(i * tile_size[0]) + u, (j * tile_size[1]) + v].shape,\r\n                    )\r\n    return new_img\r\n\r\n\r\ndef tile_grid_to_image(\r\n    tile_grid: NDArray[np.int64],\r\n    tile_catalog: Dict[int, NDArray[np.integer]],\r\n    tile_size: Tuple[int, int],\r\n    visualize: bool = False,\r\n    partial: bool = False,\r\n    color_channels: int = 3,\r\n) -> NDArray[np.integer]:\r\n    \"\"\"\r\n    Takes a tile_grid and transforms it into an image, using the information\r\n    in tile_catalog. We use tile_size to figure out the size the new image\r\n    should be, and visualize for displaying partial tile patterns.\r\n    \"\"\"\r\n    tile_dtype = next(iter(tile_catalog.values())).dtype\r\n    new_img = np.zeros(\r\n        (\r\n            tile_grid.shape[0] * tile_size[0],\r\n            tile_grid.shape[1] * tile_size[1],\r\n            color_channels,\r\n        ),\r\n        dtype=tile_dtype,\r\n    )\r\n    if partial and (len(tile_grid.shape)) > 2:\r\n        # TODO: implement rendering partially completed solution\r\n        # Call tile_grid_to_average() instead.\r\n        assert False\r\n    else:\r\n        for i in range(tile_grid.shape[0]):\r\n            for j in range(tile_grid.shape[1]):\r\n                tile = tile_grid[i, j]\r\n                for u in range(tile_size[0]):\r\n                    for v in range(tile_size[1]):\r\n                        pixel = [200, 0, 200]\r\n                        ## If we want to display a partial pattern, it is helpful to\r\n                        ## be able to show empty cells. Therefore, in visualize mode,\r\n                        ## we use -1 as a magic number for a non-existant tile.\r\n                        if visualize and ((-1 == tile) or (-2 == tile)):\r\n                            if -1 == tile:\r\n                                if 0 == (i + j) % 2:\r\n                                    pixel = [255, 0, 255]\r\n                            if -2 == tile:\r\n                                pixel = [0, 255, 255]\r\n                        else:\r\n                            pixel = tile_catalog[tile][u, v]\r\n                        # TODO: will need to change if using an image with more than 3 channels\r\n                        new_img[\r\n                            (i * tile_size[0]) + u, (j * tile_size[1]) + v\r\n                        ] = np.resize(\r\n                            pixel,\r\n                            new_img[\r\n                                (i * tile_size[0]) + u, (j * tile_size[1]) + v\r\n                            ].shape,\r\n                        )\r\n    return new_img\r\n\r\n\r\ndef figure_list_of_tiles(unique_tiles, tile_catalog, output_filename=\"list_of_tiles\"):\r\n    plt.figure(figsize=(4, 4), edgecolor=\"k\", frameon=True)\r\n    plt.title(\"Extracted Tiles\")\r\n    s = math.ceil(math.sqrt(len(unique_tiles))) + 1\r\n    for i, tcode in enumerate(unique_tiles[0]):\r\n        sp = plt.subplot(s, s, i + 1).imshow(tile_catalog[tcode])\r\n        sp.axes.tick_params(labelleft=False, labelbottom=False, length=0)\r\n        plt.title(f\"{i}\\n{tcode}\", fontsize=10)\r\n        sp.axes.grid(False)\r\n    fp = pathlib.Path(output_filename + \".pdf\")\r\n    plt.savefig(fp, bbox_inches=\"tight\")\r\n    plt.close()\r\n\r\n\r\ndef figure_false_color_tile_grid(tile_grid, output_filename=\"./false_color_tiles\"):\r\n    figure_plot = plt.matshow(\r\n        tile_grid,\r\n        cmap=\"gist_ncar\",\r\n        extent=(0, tile_grid.shape[1], tile_grid.shape[0], 0),\r\n    )\r\n    plt.title(\"False Color Map of Tiles in Input Image\")\r\n    figure_plot.axes.grid(None)\r\n    plt.savefig(output_filename + \".png\", bbox_inches=\"tight\")\r\n    plt.close()\r\n\r\n\r\ndef figure_tile_grid(tile_grid, tile_catalog, tile_size):\r\n    tile_grid_to_image(tile_grid, tile_catalog, tile_size)\r\n\r\n\r\ndef render_pattern(render_pattern, tile_catalog):\r\n    \"\"\"Turn a pattern into an image\"\"\"\r\n    rp_iter = np.nditer(render_pattern, flags=[\"multi_index\"])\r\n    output = np.zeros(render_pattern.shape + (3,), dtype=np.uint32)\r\n    while not rp_iter.finished:\r\n        # Note that this truncates images with more than 3 channels down to just the channels in the output.\r\n        # If we want to have alpha channels, we'll need a different way to handle this.\r\n        output[rp_iter.multi_index] = np.resize(\r\n            tile_catalog[render_pattern[rp_iter.multi_index]],\r\n            output[rp_iter.multi_index].shape,\r\n        )\r\n        rp_iter.iternext()\r\n    return output\r\n\r\n\r\ndef figure_pattern_catalog(\r\n    pattern_catalog,\r\n    tile_catalog,\r\n    pattern_weights,\r\n    pattern_width,\r\n    output_filename=\"pattern_catalog\",\r\n):\r\n    s_columns = 24 // min(24, pattern_width)\r\n    s_rows = 1 + (int(len(pattern_catalog)) // s_columns)\r\n    _fig = plt.figure(figsize=(s_columns, s_rows * 1.5))\r\n    plt.title(\"Extracted Patterns\")\r\n    counter = 0\r\n    for i, _tcode in pattern_catalog.items():\r\n        pat_cat = pattern_catalog[i]\r\n        ptr = render_pattern(pat_cat, tile_catalog).astype(np.uint8)\r\n        sp = plt.subplot(s_rows, s_columns, counter + 1)\r\n        spi = sp.imshow(ptr)\r\n        spi.axes.xaxis.set_label_text(f\"({pattern_weights[i]})\")\r\n        sp.set_title(f\"{counter}\\n{i}\", fontsize=3)\r\n        spi.axes.tick_params(\r\n            labelleft=False, labelbottom=False, left=False, bottom=False\r\n        )\r\n        spi.axes.grid(False)\r\n        counter += 1\r\n    plt.savefig(output_filename + \"_patterns.pdf\", bbox_inches=\"tight\")\r\n    plt.close()\r\n\r\n\r\ndef render_tiles_to_output(\r\n    tile_grid: NDArray[np.int64],\r\n    tile_catalog: Dict[int, NDArray[np.integer]],\r\n    tile_size: Tuple[int, int],\r\n    output_filename: str,\r\n) -> None:\r\n    img = tile_grid_to_image(tile_grid.T, tile_catalog, tile_size)\r\n    imageio.imwrite(output_filename, img.astype(np.uint8))\r\n\r\n\r\ndef blit(destination, sprite, upper_left, layer=False, check=False):\r\n    \"\"\"\r\n    Blits one multidimensional array into another numpy array.\r\n    \"\"\"\r\n    lower_right = [\r\n        ((a + b) if ((a + b) < c) else c)\r\n        for a, b, c in zip(upper_left, sprite.shape, destination.shape)\r\n    ]\r\n    if min(lower_right) < 0:\r\n        return\r\n\r\n    for i_index, i in enumerate(range(upper_left[0], lower_right[0])):\r\n        for j_index, j in enumerate(range(upper_left[1], lower_right[1])):\r\n            if (i >= 0) and (j >= 0):\r\n                if len(destination.shape) > 2:\r\n                    destination[i, j, layer] = sprite[i_index, j_index]\r\n                else:\r\n                    if check:\r\n                        if (\r\n                            (destination[i, j] == sprite[i_index, j_index])\r\n                            or (destination[i, j] == -1)\r\n                            or {sprite[i_index, j_index] == -1}\r\n                        ):\r\n                            destination[i, j] = sprite[i_index, j_index]\r\n                        else:\r\n                            logger.error(\r\n                                \"mismatch: destination[{i},{j}] = {destination[i, j]}, sprite[{i_index}, {j_index}] = {sprite[i_index, j_index]}\"\r\n                            )\r\n                    else:\r\n                        destination[i, j] = sprite[i_index, j_index]\r\n    return destination\r\n\r\n\r\nclass InvalidAdjacency(Exception):\r\n    \"\"\"The combination of patterns and offsets results in pattern combinations that don't match.\"\"\"\r\n\r\n    pass\r\n\r\n\r\ndef validate_adjacency(\r\n    pattern_a, pattern_b, preview_size, upper_left_of_center, adj_rel\r\n):\r\n    preview_adj_a_first = np.full((preview_size, preview_size), -1, dtype=np.int64)\r\n    preview_adj_b_first = np.full((preview_size, preview_size), -1, dtype=np.int64)\r\n    blit(\r\n        preview_adj_b_first,\r\n        pattern_b,\r\n        (\r\n            upper_left_of_center[1] + adj_rel[0][1],\r\n            upper_left_of_center[0] + adj_rel[0][0],\r\n        ),\r\n        check=True,\r\n    )\r\n    blit(preview_adj_b_first, pattern_a, upper_left_of_center, check=True)\r\n\r\n    blit(preview_adj_a_first, pattern_a, upper_left_of_center, check=True)\r\n    blit(\r\n        preview_adj_a_first,\r\n        pattern_b,\r\n        (\r\n            upper_left_of_center[1] + adj_rel[0][1],\r\n            upper_left_of_center[0] + adj_rel[0][0],\r\n        ),\r\n        check=True,\r\n    )\r\n    if not np.array_equiv(preview_adj_a_first, preview_adj_b_first):\r\n        logger.debug(adj_rel)\r\n        logger.debug(pattern_a)\r\n        logger.debug(pattern_b)\r\n        logger.debug(preview_adj_a_first)\r\n        logger.debug(preview_adj_b_first)\r\n        raise InvalidAdjacency\r\n\r\n\r\ndef figure_adjacencies(\r\n    adjacency_relations_list,\r\n    adjacency_directions,\r\n    tile_catalog,\r\n    patterns,\r\n    pattern_width,\r\n    tile_size,\r\n    output_filename=\"adjacency\",\r\n    render_b_first=False,\r\n):\r\n    #    try:\r\n    adjacency_directions_list = list(dict(adjacency_directions).values())\r\n    _figadj = plt.figure(\r\n        figsize=(12, 1 + len(adjacency_relations_list[:64])), edgecolor=\"b\"\r\n    )\r\n    plt.title(\"Adjacencies\")\r\n    max_offset = max(\r\n        [abs(x) for x in list(itertools.chain.from_iterable(adjacency_directions_list))]\r\n    )\r\n\r\n    for i, adj_rel in enumerate(adjacency_relations_list[:64]):\r\n        preview_size = pattern_width + max_offset * 2\r\n        preview_adj = np.full((preview_size, preview_size), -1, dtype=np.int64)\r\n        upper_left_of_center = [max_offset, max_offset]\r\n\r\n        pattern_a = patterns[adj_rel[1]]\r\n        pattern_b = patterns[adj_rel[2]]\r\n        validate_adjacency(\r\n            pattern_a, pattern_b, preview_size, upper_left_of_center, adj_rel\r\n        )\r\n        if render_b_first:\r\n            blit(\r\n                preview_adj,\r\n                pattern_b,\r\n                (\r\n                    upper_left_of_center[1] + adj_rel[0][1],\r\n                    upper_left_of_center[0] + adj_rel[0][0],\r\n                ),\r\n                check=True,\r\n            )\r\n            blit(preview_adj, pattern_a, upper_left_of_center, check=True)\r\n        else:\r\n            blit(preview_adj, pattern_a, upper_left_of_center, check=True)\r\n            blit(\r\n                preview_adj,\r\n                pattern_b,\r\n                (\r\n                    upper_left_of_center[1] + adj_rel[0][1],\r\n                    upper_left_of_center[0] + adj_rel[0][0],\r\n                ),\r\n                check=True,\r\n            )\r\n\r\n        ptr = tile_grid_to_image(\r\n            preview_adj, tile_catalog, tile_size, visualize=True\r\n        ).astype(np.uint8)\r\n\r\n        subp = plt.subplot(math.ceil(len(adjacency_relations_list[:64]) / 4), 4, i + 1)\r\n        spi = subp.imshow(ptr)\r\n        spi.axes.tick_params(\r\n            left=False, bottom=False, labelleft=False, labelbottom=False\r\n        )\r\n        plt.title(\r\n            f\"{i}:\\n({adj_rel[1]} +\\n{adj_rel[2]})\\n by {adj_rel[0]}\", fontsize=10\r\n        )\r\n\r\n        indicator_rect = matplotlib.patches.Rectangle(\r\n            (upper_left_of_center[1] - 0.51, upper_left_of_center[0] - 0.51),\r\n            pattern_width,\r\n            pattern_width,\r\n            Fill=False,\r\n            edgecolor=\"b\",\r\n            linewidth=3.0,\r\n            linestyle=\":\",\r\n        )\r\n\r\n        spi.axes.add_artist(indicator_rect)\r\n        spi.axes.grid(False)\r\n    plt.savefig(output_filename + \"_adjacency.pdf\", bbox_inches=\"tight\")\r\n    plt.close()\r\n\r\n\r\n#    except ValueError as e:\r\n#        logger.exception(e)\r\n"
  },
  {
    "path": "wfc_run.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"Base code to load commands from xml and run them.\"\"\"\nfrom __future__ import annotations\n\nimport argparse\nimport datetime\nimport logging\nfrom typing import List, Literal, TypedDict, Union\nimport wfc.wfc_control as wfc_control\nimport xml.etree.ElementTree as ET\nimport os\n\n\nclass RunInstructions(TypedDict):\n    loc: Literal[\"lexical\", \"hilbert\", \"spiral\", \"entropy\", \"anti-entropy\", \"simple\", \"random\"]\n    choice: Literal[\"lexical\", \"rarest\", \"weighted\", \"random\"]\n    backtracking: bool\n    global_constraint: Literal[False, \"allpatterns\"]\n\n\ndef string2bool(strn: Union[bool, str]) -> bool:\n    if isinstance(strn, bool):\n        return strn\n    return strn.lower() in [\"true\"]\n\n\ndef run_default(run_experiment: str = \"simple\", samples: str = \"samples_reference.xml\") -> None:\n    log_filename = f\"log_{datetime.datetime.now().isoformat()}\".replace(\":\", \".\")\n    xdoc = ET.ElementTree(file=samples)\n    default_allowed_attempts = 10\n    default_backtracking = str(False)\n    log_stats_to_output = wfc_control.make_log_stats()\n\n    for xnode in xdoc.getroot():\n        name = xnode.get(\"name\", \"NAME\")\n        if \"overlapping\" == xnode.tag:\n            # seed = 3262\n            tile_size = int(xnode.get(\"tile_size\", 1))\n            # seed for random generation, can be any number\n            tile_size = int(xnode.get(\"tile_size\", 1))  # size of tile, in pixels\n            pattern_width = int(xnode.get(\"N\", 2))  # Size of the patterns we want.\n            # 2x2 is the minimum, larger scales get slower fast.\n\n            symmetry = int(xnode.get(\"symmetry\", 8))\n            ground = int(xnode.get(\"ground\", 0))\n            periodic_input = string2bool(\n                xnode.get(\"periodic\", \"False\")\n            )  # Does the input wrap?\n            periodic_output = string2bool(\n                xnode.get(\"periodic\", \"False\")\n            )  # Do we want the output to wrap?\n            generated_size = (int(xnode.get(\"width\", 48)), int(xnode.get(\"height\", 48)))\n            screenshots = int(\n                xnode.get(\"screenshots\", 1)\n            )  # Number of times to run the algorithm, will produce this many distinct outputs\n            iteration_limit = int(\n                xnode.get(\"iteration_limit\", 0)\n            )  # After this many iterations, time out. 0 = never time out.\n            allowed_attempts = int(\n                xnode.get(\"allowed_attempts\", default_allowed_attempts)\n            )  # Give up after this many contradictions\n            backtracking = string2bool(xnode.get(\"backtracking\", default_backtracking))\n            visualize_experiment = False\n\n            run_instructions: List[RunInstructions] = [  # simple\n                {\n                    \"loc\": \"entropy\",\n                    \"choice\": \"weighted\",\n                    \"backtracking\": backtracking,\n                    \"global_constraint\": False,\n                }\n            ]\n            # run_instructions = [{\"loc\": \"entropy\", \"choice\": \"weighted\", \"backtracking\": True, \"global_constraint\": \"allpatterns\"}]\n            if run_experiment == \"choice\":\n                run_instructions = [\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"random\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"random\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"random\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"random\",\n                        \"choice\": \"random\",\n                        \"backtracking\": False,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                ]\n            if run_experiment == \"heuristic\":\n                run_instructions = [\n                    {\n                        \"loc\": \"hilbert\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"spiral\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"anti-entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"simple\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"random\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": backtracking,\n                        \"global_constraint\": False,\n                    },\n                ]\n            if run_experiment == \"backtracking\":\n                run_instructions = [\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": False,\n                    },\n                ]\n            if run_experiment == \"backtracking_heuristic\":\n                run_instructions = [\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"lexical\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"random\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"random\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": \"allpatterns\",\n                    },\n                    {\n                        \"loc\": \"random\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": True,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"random\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": False,\n                    },\n                ]\n            if run_experiment == \"choices\":\n                run_instructions = [\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"rarest\",\n                        \"backtracking\": False,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"weighted\",\n                        \"backtracking\": False,\n                        \"global_constraint\": False,\n                    },\n                    {\n                        \"loc\": \"entropy\",\n                        \"choice\": \"random\",\n                        \"backtracking\": False,\n                        \"global_constraint\": False,\n                    },\n                ]\n\n            for experiment in run_instructions:\n                for x in range(screenshots):\n                    print(f\"-: {name} > {x}\")\n                    try:\n                        solution = wfc_control.execute_wfc(\n                            name,\n                            tile_size=tile_size,\n                            pattern_width=pattern_width,\n                            rotations=symmetry,\n                            output_size=generated_size,\n                            ground=ground,\n                            attempt_limit=allowed_attempts,\n                            output_periodic=periodic_output,\n                            input_periodic=periodic_input,\n                            loc_heuristic=experiment[\"loc\"],\n                            choice_heuristic=experiment[\"choice\"],\n                            backtracking=experiment[\"backtracking\"],\n                            global_constraint=experiment[\"global_constraint\"],\n                            log_filename=log_filename,\n                            log_stats_to_output=log_stats_to_output,\n                            visualize=visualize_experiment,\n                            logging=True,\n                        )\n                        print(solution)\n                    except Exception as exc:\n                        print(f\"Skipped because: {exc}\")\n\n            if False:  # These are included for my colab experiments, remove them if you're not me\n                os.system(\n                    'cp -rf \"/content/wfc/output/*.tsv\" \"/content/drive/My Drive/wfc_exper/2\"'\n                )\n                os.system(\n                    'cp -r \"/content/wfc/output\" \"/content/drive/My Drive/wfc_exper/2\"'\n                )\n\ndef main() -> None:\n    logging.basicConfig(level=logging.DEBUG)\n    parser = argparse.ArgumentParser(\n        description=\"Geneates examples from bundled samples which will be saved to the output/ directory.\",\n    )\n    parser.add_argument(\n        \"-e\", \"--experiment\",\n        type=str,\n        default=\"simple\",\n        choices=[\"simple\", \"choice\", \"choices\", \"heuristic\", \"backtracking\", \"backtracking_heuristic\"],\n        help=\"Which experiment to run, defaults to simple.\",\n    )\n    parser.add_argument(\n        \"-s\", \"--samples\",\n        type=str,\n        required=True,\n        metavar=\"XML_FILE\",\n        default=\"samples_reference.xml\",\n        help=\"An XML file with input data.  If unsure then use '-s samples_reference.xml'\",\n    )\n    args = parser.parse_args()\n    run_default(run_experiment=args.experiment, samples=args.samples)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  }
]