]> Untitled Git - axy/ft/a-maze-ing.git/commitdiff
Background, fixes, python is dumb and the lib too
authorAxy <gilliardmarthey.axel@gmail.com>
Thu, 5 Mar 2026 21:13:18 +0000 (22:13 +0100)
committerAxy <gilliardmarthey.axel@gmail.com>
Thu, 5 Mar 2026 21:13:18 +0000 (22:13 +0100)
__main__.py
amazeing/config/config_parser.py
amazeing/config/parser_combinator.py
amazeing/maze_display/TTYdisplay.py
amazeing/maze_display/backend.py
amazeing/maze_display/layout.py
example.conf

index f7e494f143d490a5cafa8db37873f22f8ed2b1a3..21eed848680b9468761f171a925a0468471a8294 100644 (file)
@@ -26,7 +26,7 @@ maze = Maze(dims)
 
 maze.outline()
 
-pattern = Pattern(Pattern.FT_PATTERN).centered_for(dims)
+pattern = Pattern(config.maze_pattern).centered_for(dims)
 pattern.fill(maze)
 
 walls_const = set(maze.walls_full())
@@ -34,6 +34,7 @@ walls_const = set(maze.walls_full())
 backend = TTYBackend(dims, config.tilemap_wall_size, config.tilemap_cell_size)
 pair_map = extract_pairs(config)
 tilemaps = TileMaps(config, pair_map, backend)
+backend.set_filler(tilemaps.filler)
 
 backend.set_style(tilemaps.empty)
 for wall in maze.all_walls():
index 4a4f37fc071bb79f6173bc9636d21e4448a1f5bf..6ab43872b9cbe780eec041b2ad9d52b905c072bd 100644 (file)
@@ -106,6 +106,7 @@ def parse_colored_line(
     """
     returns a list of a color pair variable associated with its string
     """
+
     color_prefix = delimited(
         tag("{"), cut(spaced(parse_color_pair)), cut(tag("}"))
     )
@@ -124,7 +125,7 @@ def parse_colored_line(
             ),
         ),
         lambda a, b: a + b,
-        "",
+        lambda: "",
     )
 
     return spaced(
@@ -134,6 +135,20 @@ def parse_colored_line(
     )(s)
 
 
+def parse_str_line(s: str) -> ParseResult[str]:
+    return spaced(
+        delimited(
+            tag('"'),
+            recognize(
+                many_count(
+                    none_of('"\n'),
+                )
+            ),
+            tag('"'),
+        )
+    )(s)
+
+
 class ConfigException(Exception):
     pass
 
@@ -237,20 +252,28 @@ def DefaultedStrField[T](
 
 def ListParser[T](parser: Parser[T]) -> Type[ConfigField[list[T]]]:
     class Inner(ConfigField[list[T]]):
+        def __init__(
+            self, name: str, default: Callable[[], list[T]] | None = lambda: []
+        ) -> None:
+            super().__init__(name, default)
+
         def parse(self, s: str) -> ParseResult[list[T]]:
             return parser_map(lambda e: [e], parser)(s)
 
-        def default(self) -> list[T]:
-            return []
-
         def merge(self, vals: list[list[T]]) -> list[T]:
-            return [e for l in vals for e in l]
+            return (
+                [e for l in vals for e in l]
+                if len(vals) > 0
+                else self.default()
+            )
 
     return Inner
 
 
 ColoredLineField = ListParser(parse_colored_line)
 
+PatternField = ListParser(parse_str_line)
+
 
 def line_parser[T](
     fields: dict[str, ConfigField[T]],
@@ -295,7 +318,7 @@ def fields_parser(
             fold(
                 parse_line,
                 fold_fn,
-                {name: [] for name in fields.keys()},
+                lambda: {name: [] for name in fields.keys()},
             ),
         )(s)
 
@@ -317,13 +340,17 @@ class Config:
     tilemap_cell_size: IVec2
     tilemap_full: list[ColoredLine]
     tilemap_empty: list[ColoredLine]
+    tilemap_background_size: IVec2
     tilemap_background: list[ColoredLine]
+    maze_pattern: list[str]
 
     def __init__(self) -> None:
         pass
 
     @staticmethod
     def parse(s: str) -> "Config":
+        from amazeing.maze_class import maze_pattern
+
         fields = parser_complete(
             fields_parser(
                 {
@@ -351,10 +378,16 @@ class Config:
                         ColoredLineField,
                         ['"{BLACK:BLACK}    "', '"{BLACK:BLACK}    "'],
                     ),
+                    "TILEMAP_BACKGROUND_SIZE": DefaultedField(
+                        CoordField, IVec2(4, 2)
+                    ),
                     "TILEMAP_BACKGROUND": DefaultedStrField(
                         ColoredLineField,
                         ['"{BLACK:BLACK}    "', '"{BLACK:BLACK}    "'],
                     ),
+                    "MAZE_PATTERN": DefaultedField(
+                        PatternField, maze_pattern.Pattern.FT_PATTERN
+                    ),
                 }
             )
         )(s)
index a025da8063b4f59f7e5510a88c0f8a9a8e340901..45569b28c8c25eca5c66baf9a0c80b4edf6791d3 100644 (file)
@@ -94,14 +94,14 @@ def alt[T](*choices: Parser[T]) -> Parser[T]:
 def fold[T, R](
     p: Parser[T],
     f: Callable[[R, T], R],
-    acc: R,
+    acc_init: Callable[[], R],
     min_n: int = 0,
     max_n: int | None = None,
     sep: Parser[Any] = null_parser,
 ) -> Parser[R]:
     # no clean way to do this with lambdas i could figure out :<
     def inner(s: str) -> ParseResult[R]:
-        nonlocal acc
+        acc = acc_init()
         count: int = 0
         curr_p: Parser[T] = p
         while max_n is None or count < max_n:
@@ -125,7 +125,12 @@ def many[T](
     sep: Parser[Any] = null_parser,
 ) -> Parser[list[T]]:
     return fold(
-        parser_map(lambda e: [e], p), list.__add__, [], min_n, max_n, sep
+        parser_map(lambda e: [e], p),
+        list.__add__,
+        lambda: [],
+        min_n,
+        max_n,
+        sep,
     )
 
 
@@ -135,7 +140,7 @@ def many_count[T](
     max_n: int | None = None,
     sep: Parser[Any] = null_parser,
 ) -> Parser[int]:
-    return fold(value(1, p), int.__add__, 0, min_n, max_n, sep)
+    return fold(value(1, p), int.__add__, lambda: 0, min_n, max_n, sep)
 
 
 def seq[T](*parsers: Parser[T]) -> Parser[str]:
index 88354dbe200e864bea8aabc56bf230c9d35a3370..d51607be2406367aa6f2ea5bb5760c657e5f5ba2 100644 (file)
@@ -1,4 +1,5 @@
-from collections.abc import Generator, Iterable
+from collections.abc import Callable, Generator, Iterable
+from sys import stderr
 from ..config.config_parser import Color, Config, ColoredLine, ColorPair
 from amazeing.maze_display.layout import (
     BInt,
@@ -7,6 +8,10 @@ from amazeing.maze_display.layout import (
     HBox,
     VBox,
     layout_fair,
+    layout_priority,
+    layout_sort_chunked,
+    layout_sort_shuffled,
+    layout_split,
     vpad_box,
     hpad_box,
 )
@@ -56,6 +61,7 @@ class Tile:
     ) -> None:
         if size.x <= 0 or size.y <= 0:
             return
+        print(src, dst, size, window.getmaxyx(), file=stderr)
         self.__pad.overwrite(
             window, *src.yx(), *dst.yx(), *(dst + size - IVec2.splat(1)).yx()
         )
@@ -87,7 +93,9 @@ class Tile:
             for y_size, y_offset in size_offset_iter(src.y, size.y, dims.y):
                 sub_size = IVec2(x_size, y_size)
                 offset = IVec2(x_offset, y_offset)
-                self.blit(src + offset, dst + offset, sub_size, window)
+                self.blit(
+                    (src + offset) % dims, dst + offset, sub_size, window
+                )
 
 
 class SubTile(Tile):
@@ -129,14 +137,24 @@ class MazeTileMap:
     def tile_size(self, pos: IVec2) -> IVec2:
         return (pos + 1) % 2 * self.__wall_dim + pos % 2 * self.__cell_dim
 
-    def draw_at(self, pos: IVec2, idx: int, window: curses.window) -> None:
+    def draw_at(self, at: IVec2, idx: int, window: curses.window) -> None:
         self.__tiles[idx].blit(
-            self.src_coord(pos),
-            self.dst_coord(pos),
-            self.tile_size(pos),
+            self.src_coord(at),
+            self.dst_coord(at),
+            self.tile_size(at),
             window,
         )
 
+    def draw_at_wrapping(
+        self,
+        start: IVec2,
+        at: IVec2,
+        into: IVec2,
+        idx: int,
+        window: curses.window,
+    ) -> None:
+        self.__tiles[idx].blit_wrapping(start, at, into, window)
+
 
 class ScrollablePad:
     def __init__(
@@ -158,7 +176,7 @@ class ScrollablePad:
             IVec2.with_op(max)(self.__pos, dims - self.dims()), IVec2.splat(0)
         )
 
-    def refresh(self, at: IVec2, into: IVec2) -> None:
+    def present(self, at: IVec2, into: IVec2, window: curses.window) -> None:
         if self.constrained:
             self.clamp(into)
 
@@ -173,7 +191,8 @@ class ScrollablePad:
             return
         draw_start = at + win_start
         draw_end = draw_start + draw_dim - IVec2.splat(1)
-        self.pad.refresh(
+        self.pad.overwrite(
+            window,
             *pad_start.yx(),
             *draw_start.yx(),
             *draw_end.yx(),
@@ -256,17 +275,26 @@ class TileMaps:
     ) -> None:
         mazetile_dims = config.tilemap_wall_size + config.tilemap_cell_size
 
-        def new_tilemap(lines: list[ColoredLine]) -> Tile:
+        def new_tilemap(lines: list[ColoredLine], dim: IVec2) -> Tile:
             return Tile(
                 [
                     [(s, pair_map[color_pair]) for color_pair, s in line]
                     for line in lines
                 ],
-                mazetile_dims,
+                dim,
             )
 
-        self.empty: int = backend.add_style(new_tilemap(config.tilemap_empty))
-        self.full: int = backend.add_style(new_tilemap(config.tilemap_full))
+        self.empty: int = backend.add_style(
+            new_tilemap(config.tilemap_empty, mazetile_dims)
+        )
+        self.full: int = backend.add_style(
+            new_tilemap(config.tilemap_full, mazetile_dims)
+        )
+        self.filler: int = backend.add_style(
+            new_tilemap(
+                config.tilemap_background, config.tilemap_background_size
+            )
+        )
 
 
 class TTYBackend(Backend[int]):
@@ -286,24 +314,47 @@ class TTYBackend(Backend[int]):
         curses.curs_set(0)
         self.__screen.keypad(True)
 
+        self.__scratch: curses.window = curses.newpad(1, 1)
         self.__pad: ScrollablePad = ScrollablePad(dims)
         self.__dims = maze_dims
 
         maze_box = FBox(
             IVec2(BInt(dims.x), BInt(dims.y)),
-            self.__pad.refresh,
+            lambda at, into: self.__pad.present(at, into, self.__scratch),
+        )
+        filler_box = FBox(
+            IVec2(BInt(0, True), BInt(0, True)),
+            lambda at, into: (
+                None
+                if self.__filler is None
+                else self.__tilemap.draw_at_wrapping(
+                    at, at, into, self.__filler, self.__scratch
+                )
+            ),
         )
-        self.__layout: Box = VBox.noassoc(
-            layout_fair,
+        f: Callable[[int], int] = lambda e: e
+        layout = layout_split(
+            layout_fair, layout_sort_chunked(layout_fair, layout_priority, f)
+        )
+        self.__layout: Box = VBox(
+            layout,
             [
-                vpad_box(),
-                HBox.noassoc(layout_fair, [hpad_box(), maze_box, hpad_box()]),
-                vpad_box(),
+                (filler_box, 0),
+                (
+                    HBox(
+                        layout,
+                        [(filler_box, 0), (maze_box, 1), (filler_box, 0)],
+                    ),
+                    1,
+                ),
+                (filler_box, 0),
             ],
         )
 
         self.__resize: bool = False
 
+        self.__filler: None | int = None
+
     def __del__(self):
         curses.curs_set(1)
         curses.nocbreak()
@@ -311,6 +362,9 @@ class TTYBackend(Backend[int]):
         curses.echo()
         curses.endwin()
 
+    def set_filler(self, style: int) -> None:
+        self.__filler = style
+
     def add_style(self, style: Tile) -> int:
         return self.__tilemap.add_tile(style)
 
@@ -329,7 +383,9 @@ class TTYBackend(Backend[int]):
             self.__screen.erase()
         self.__screen.refresh()
         y, x = self.__screen.getmaxyx()
+        self.__scratch.resize(y, x)
         self.__layout.laid_out(IVec2(0, 0), IVec2(x, y))
+        self.__scratch.overwrite(self.__screen)
 
     def event(self, timeout_ms: int = -1) -> BackendEvent | None:
         self.__screen.timeout(timeout_ms)
index e4497f14d871620f4d53c0a753eb8c11bea85bb6..910058f529cc4fd6462a0b0d81bc8ae28ebb884f 100644 (file)
@@ -5,6 +5,9 @@ from typing import Type, cast
 
 
 class IVec2[T = int]:
+    def copy(self, inner_copy: Callable[[T], T] = lambda e: e) -> "IVec2[T]":
+        return IVec2(inner_copy(self.x), inner_copy(self.y))
+
     def __init__(self, x: T, y: T) -> None:
         self.x: T = x
         self.y: T = y
@@ -13,6 +16,9 @@ class IVec2[T = int]:
     def splat(n: T) -> "IVec2[T]":
         return IVec2(n, n)
 
+    def __repr__(self) -> str:
+        return f"{self.x, self.y}"
+
     @staticmethod
     def with_op(
         op: Callable[[T, T], T],
index 9412db1c327654a63cfaaf65e066893234edef20..16fc870ebfac983714fde8b1aefe587654e679a9 100644 (file)
@@ -1,5 +1,6 @@
 from abc import ABC, abstractmethod
 from collections.abc import Callable
+from sys import stderr
 from .backend import IVec2
 
 
@@ -8,18 +9,43 @@ class BInt:
         self.val: int = val
         self.has_flex: bool = has_flex
 
+    def __repr__(self) -> str:
+        return f"{self.val}" + (" flexible" if self.has_flex else "")
+
+    @staticmethod
+    def vector_sum(elems: list["BInt"]) -> "BInt":
+        res = BInt(
+            sum(map(lambda e: e.val, elems)),
+            any(map(lambda e: e.has_flex, elems)),
+        )
+        return res
+
+    @staticmethod
+    def vector_max(elems: list["BInt"]) -> "BInt":
+        res = BInt(
+            max(map(lambda e: e.val, elems), default=0),
+            any(map(lambda e: e.has_flex, elems)),
+        )
+        return res
+
 
 type BVec2 = IVec2[BInt]
 
 type Layout[T] = Callable[[list[tuple[BInt, T]], int], list[int]]
 
 
-def layout_priority(sizes: list[BInt], available: int) -> list[int]:
+def layout_priority[T](
+    sizes: list[tuple[BInt, T]], available: int
+) -> list[int]:
     res = []
-    for size in sizes:
+    for size, _ in sizes:
         size_scaled = min(size.val, available)
         res.append(size_scaled)
         available -= size_scaled
+    for i, (size, _) in enumerate(sizes):
+        if size.has_flex:
+            res[i] += available
+            break
     return res
 
 
@@ -30,14 +56,14 @@ def rdiv(a: int, b: int) -> int:
 def layout_fair[T](sizes: list[tuple[BInt, T]], available: int) -> list[int]:
     res = [0 for _ in sizes]
     count = len(sizes)
-    for idx, size in sorted(enumerate(sizes), key=lambda e: e[1][0].val):
-        size_scaled = min(size[0].val, rdiv(available, count))
+    for idx, (size, _) in sorted(enumerate(sizes), key=lambda e: e[1][0].val):
+        size_scaled = min(size.val, rdiv(available, count))
         res[idx] += size_scaled
         available -= size_scaled
         count -= 1
-    count = sum(1 for e in sizes if e[0].has_flex)
-    for idx, size in enumerate(sizes):
-        if not size[0].has_flex:
+    count = sum(1 for (e, _) in sizes if e.has_flex)
+    for idx, (size, _) in enumerate(sizes):
+        if not size.has_flex:
             continue
         size_scaled = rdiv(available, count)
         res[idx] += size_scaled
@@ -46,6 +72,91 @@ def layout_fair[T](sizes: list[tuple[BInt, T]], available: int) -> list[int]:
     return res
 
 
+def layout_split[T](sized: Layout[T], flexed: Layout[T]) -> Layout[T]:
+    def inner(sizes: list[tuple[BInt, T]], available: int) -> list[int]:
+        flexes = [(BInt(0, e[0].has_flex), e[1]) for e in sizes]
+        sizes = [(BInt(e[0].val), e[1]) for e in sizes]
+        res_sizes = sized(sizes, available)
+        res_flexes = flexed(flexes, available - sum(res_sizes))
+        return [a + b for a, b in zip(res_sizes, res_flexes)]
+
+    return inner
+
+
+def layout_sort_shuffled[T](
+    init: Layout[T], extract: Callable[[T], int]
+) -> Layout[T]:
+    def inner(sizes: list[tuple[BInt, T]], available: int) -> list[int]:
+        mapping = [(i, extract(assoc)) for i, (_, assoc) in enumerate(sizes)]
+        mapping.sort(key=lambda e: e[1])
+        sizes = [e for e in sizes]
+        sizes.sort(key=lambda e: extract(e[1]))
+        res_init = init(sizes, available)
+        res = [0 for _ in res_init]
+        for src, (dst, _) in enumerate(mapping):
+            res[dst] = res_init[src]
+        return res
+
+    return inner
+
+
+def layout_mapped[T, U](init: Layout[T], f: Callable[[U], T]) -> Layout[U]:
+    return lambda sizes, available: init(
+        list(map(lambda e: (e[0], f(e[1])), sizes)), available
+    )
+
+
+def layout_sort_chunked[T](
+    per_chunk: Layout[T],
+    chunk_layout: Layout[list[tuple[BInt, T]]],
+    extract: Callable[[T], int],
+) -> Layout[T]:
+    def layout_chunk_seq(
+        sizes: list[tuple[BInt, T]], available: int
+    ) -> list[int]:
+        chunks: list[tuple[BInt, list[tuple[BInt, T]]]] = []
+        i = 0
+        curr_chunk = None
+
+        def try_add_curr() -> None:
+            nonlocal curr_chunk
+            if curr_chunk is None:
+                return
+            chunk = curr_chunk[0]
+            chunks.append(
+                (
+                    BInt.vector_sum([e[0] for e in chunk]),
+                    chunk,
+                )
+            )
+            curr_chunk = None
+
+        while i < len(sizes):
+            val = sizes[i]
+            _, assoc = val
+            extracted = extract(assoc)
+            if curr_chunk is None:
+                curr_chunk = ([val], extracted)
+            else:
+                if extracted == curr_chunk[1]:
+                    curr_chunk[0].append(val)
+                else:
+                    try_add_curr()
+                    continue
+            i += 1
+        try_add_curr()
+
+        chunk_sizes = chunk_layout(chunks, available)
+        res = [
+            size
+            for (_, chunk), chunk_layout_size in zip(chunks, chunk_sizes)
+            for size in per_chunk(chunk, chunk_layout_size)
+        ]
+        return res
+
+    return layout_sort_shuffled(layout_chunk_seq, extract)
+
+
 class Box(ABC):
     @abstractmethod
     def dims(self) -> BVec2: ...
@@ -67,14 +178,8 @@ class VBox[T](Box):
     def dims(self) -> BVec2:
         dims = [box.dims() for box, _ in self.boxes]
         return IVec2(
-            BInt(
-                max(map(lambda e: e.x.val, dims)),
-                any(map(lambda e: e.x.has_flex, dims)),
-            ),
-            BInt(
-                sum(map(lambda e: e.y.val, dims)),
-                any(map(lambda e: e.y.has_flex, dims)),
-            ),
+            BInt.vector_max([e.x for e in dims]),
+            BInt.vector_sum([e.y for e in dims]),
         )
 
     def laid_out(self, at: IVec2, into: IVec2) -> None:
@@ -87,7 +192,9 @@ class VBox[T](Box):
         widths = [(get_width(dim.x), assoc) for dim, assoc in dims]
 
         for height, (width, _), (box, _) in zip(heights, widths, self.boxes):
-            box.laid_out(at, IVec2(width, height))
+            # that copy cost me half an hour, thank you pass by reference
+            # rust would have prevented that :D
+            box.laid_out(at.copy(), IVec2(width, height))
             at.y += height
 
 
@@ -105,14 +212,8 @@ class HBox[T](Box):
     def dims(self) -> BVec2:
         dims = [box.dims() for box, _ in self.boxes]
         return IVec2(
-            BInt(
-                sum(map(lambda e: e.x.val, dims)),
-                any(map(lambda e: e.x.has_flex, dims)),
-            ),
-            BInt(
-                max(map(lambda e: e.y.val, dims)),
-                any(map(lambda e: e.y.has_flex, dims)),
-            ),
+            BInt.vector_sum([e.x for e in dims]),
+            BInt.vector_max([e.y for e in dims]),
         )
 
     def laid_out(self, at: IVec2, into: IVec2) -> None:
@@ -146,12 +247,12 @@ class FBox(Box):
         self.__cb(at, into)
 
 
-def vpad_box[T](min_pad: int = 0) -> FBox:
-    return FBox(IVec2(BInt(0), BInt(min_pad, True)), lambda _at, _into: None)
+def vpad_box(min_pad: int = 0, cb=lambda _at, _into: None) -> FBox:
+    return FBox(IVec2(BInt(0), BInt(min_pad, True)), cb)
 
 
-def hpad_box(min_pad: int = 0) -> FBox:
-    return FBox(IVec2(BInt(min_pad, True), BInt(0)), lambda _at, _into: None)
+def hpad_box(min_pad: int = 0, cb=lambda _at, _into: None) -> FBox:
+    return FBox(IVec2(BInt(min_pad, True), BInt(0)), cb)
 
 
 def print_cb(at: IVec2, into: IVec2) -> None:
@@ -159,9 +260,15 @@ def print_cb(at: IVec2, into: IVec2) -> None:
 
 
 def example() -> None:
-    a = FBox(IVec2(BInt(8, False), BInt(4, True)), print_cb)
-    b = FBox(IVec2(BInt(4, False), BInt(8, False)), print_cb)
-    c = VBox.noassoc(layout_fair, [a, b])
+    a = FBox(IVec2(BInt(8, False), BInt(4, False)), print_cb)
+    c = HBox.noassoc(
+        layout_fair,
+        [
+            hpad_box(),
+            VBox.noassoc(layout_fair, [vpad_box(), a, vpad_box()]),
+            hpad_box(),
+        ],
+    )
     c.laid_out(IVec2(0, 0), IVec2(3, 4))
     c.laid_out(IVec2(0, 0), IVec2(8, 30))
     c.laid_out(IVec2(0, 0), IVec2(12, 30))
index 50b57d82aa55da5b6dc386b038f86193021f1449..8a415634429ceb2624c5de2c0be46d420e00b9c5 100644 (file)
@@ -1,5 +1,5 @@
-WIDTH=25
-HEIGHT=25
+WIDTH=50
+HEIGHT=50
 ENTRY=2,5
 #EXIT=100,100
 OUTPUT_FILE=test
@@ -7,13 +7,22 @@ PERFECT=False
 SEED=111
 TILEMAP_WALL_SIZE=2,1
 TILEMAP_CELL_SIZE=4,2
-TILEMAP_FULL="{100,100,100:1000,1000,1000}######"
-TILEMAP_FULL="{100,100,100:1000,1000,1000}######"
-TILEMAP_FULL="{100,100,100:1000,1000,1000}######"
+TILEMAP_FULL="{1000,100,100:1000,1000,1000}###{1000,1000,1000:1000,1000,1000}###"
+TILEMAP_FULL="{100,1000,100:1000,1000,1000}###{1000,1000,1000:1000,1000,1000}###"
+TILEMAP_FULL="{100,100,1000:1000,1000,1000}###{1000,1000,1000:1000,1000,1000}###"
 TILEMAP_EMPTY="{0,0,0:0,0,0}      "
 TILEMAP_EMPTY="{0,0,0:0,0,0}      "
 TILEMAP_EMPTY="{0,0,0:0,0,0}      "
+TILEMAP_BACKGROUND_SIZE=4,4
 TILEMAP_BACKGROUND="{100,100,100:0,0,0}#   "
 TILEMAP_BACKGROUND="{100,100,100:0,0,0}### "
 TILEMAP_BACKGROUND="{100,100,100:0,0,0}  # "
 TILEMAP_BACKGROUND="{100,100,100:0,0,0}# # "
+MAZE_PATTERN=" #   # "
+MAZE_PATTERN="  # #  "
+MAZE_PATTERN="   #   "
+MAZE_PATTERN="       "
+MAZE_PATTERN=" #  #  "
+MAZE_PATTERN="       "
+MAZE_PATTERN="#  #  #"
+MAZE_PATTERN=" ## ## "