From: Axy Date: Mon, 23 Mar 2026 02:16:10 +0000 (+0100) Subject: Minor refactor X-Git-Url: https://git.uwuaxy.net/animations_scrolling.mp4?a=commitdiff_plain;h=f4fc87f0c31b56180c8c62b446894f7fa0ad5a23;p=axy%2Fft%2Fa-maze-ing.git Minor refactor --- diff --git a/__main__.py b/__main__.py index 4e15931..963a1b1 100644 --- a/__main__.py +++ b/__main__.py @@ -10,12 +10,14 @@ import random from amazeing.config.config_parser import Config -from amazeing.maze_class.maze_network_tracker import MazeNetworkTracker -from amazeing.maze_class.maze_coords import CellCoord -from amazeing.maze_class.maze_dirty_tracker import MazeDirtyTracker -from amazeing.maze_class.maze_pacman_tracker import MazePacmanTracker +from amazeing.maze import ( + MazeNetworkTracker, + CellCoord, + MazeDirtyTracker, + MazePacmanTracker, +) from amazeing.maze_display.TTYdisplay import TileCycle, TileMaps, extract_pairs -from amazeing.maze_display.backend import CloseRequested, IVec2 +from amazeing.utils import IVec2 config = Config.parse(open("./example.conf").read()) @@ -98,7 +100,7 @@ def poll_events(timeout_ms: int = -1) -> None: if timeout() == 0 and not event: return continue - if isinstance(event, CloseRequested) or event.sym == "q": + if event.sym == "q": exit(0) if event.sym == "c": filler.cycle() diff --git a/amazeing/__init__.py b/amazeing/__init__.py index a68e0eb..b0d3918 100644 --- a/amazeing/__init__.py +++ b/amazeing/__init__.py @@ -1,19 +1,21 @@ __version__ = "0.0.0" __author__ = "luflores & agilliar" -from amazeing.maze_class import WallCoord, Maze, Pattern -from amazeing.maze_display import IVec2, TTYBackend -from .maze_make_pacman import maze_make_pacman -from .maze_make_perfect import maze_make_perfect + +from .maze.maze_coords import WallCoord +from .maze.maze import Maze +from .maze.maze_pattern import Pattern +from .maze_display.TTYdisplay import TTYBackend from .maze_make_empty import maze_make_empty +from .maze_make_perfect import maze_make_perfect +from .maze_make_pacman import maze_make_pacman __all__ = [ - "WallCoord", "Maze", - "Pattern", - "IVec2", + "WallCoord", "TTYBackend", - "maze_make_pacman", - "maze_make_perfect", + "Pattern", "maze_make_empty", + "maze_make_perfect", + "maze_make_pacman", ] diff --git a/amazeing/config/config_parser.py b/amazeing/config/config_parser.py index a18e305..f868827 100644 --- a/amazeing/config/config_parser.py +++ b/amazeing/config/config_parser.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from collections.abc import Callable from typing import Any, Type, cast -from amazeing.maze_display.backend import IVec2 +from amazeing.utils import IVec2 from .parser_combinator import ( ParseResult, Parser, @@ -390,7 +390,7 @@ class Config: @staticmethod def parse(s: str) -> "Config": - from amazeing.maze_class import maze_pattern + from amazeing.maze import Pattern fields = parser_complete( fields_parser( @@ -431,7 +431,7 @@ class Config: ['"{BLACK:BLACK} "', '"{BLACK:BLACK} "'], ), "MAZE_PATTERN": DefaultedField( - PatternField, maze_pattern.Pattern.FT_PATTERN + PatternField, Pattern.FT_PATTERN ), } ) diff --git a/amazeing/maze_class/__init__.py b/amazeing/maze/__init__.py similarity index 54% rename from amazeing/maze_class/__init__.py rename to amazeing/maze/__init__.py index f7d4856..59f7cbf 100644 --- a/amazeing/maze_class/__init__.py +++ b/amazeing/maze/__init__.py @@ -3,6 +3,9 @@ __author__ = "agilliar & luflores" from .maze import Maze from .maze_pattern import Pattern from .maze_coords import Cardinal, Orientation, WallCoord, CellCoord +from .maze_dirty_tracker import MazeDirtyTracker +from .maze_pacman_tracker import MazePacmanTracker +from .maze_network_tracker import MazeNetworkTracker __all__ = [ "Maze", @@ -11,4 +14,7 @@ __all__ = [ "Orientation", "WallCoord", "CellCoord", + "MazeDirtyTracker", + "MazePacmanTracker", + "MazeNetworkTracker", ] diff --git a/amazeing/maze_class/maze.py b/amazeing/maze/maze.py similarity index 98% rename from amazeing/maze_class/maze.py rename to amazeing/maze/maze.py index 4acb454..1e75f00 100644 --- a/amazeing/maze_class/maze.py +++ b/amazeing/maze/maze.py @@ -1,5 +1,5 @@ from typing import Callable, Generator, Iterable -from amazeing.maze_display.backend import IVec2 +from amazeing.utils import IVec2 from .maze_coords import ( CellCoord, Orientation, diff --git a/amazeing/maze_class/maze_coords.py b/amazeing/maze/maze_coords.py similarity index 99% rename from amazeing/maze_class/maze_coords.py rename to amazeing/maze/maze_coords.py index 30c32ea..07fc6a8 100644 --- a/amazeing/maze_class/maze_coords.py +++ b/amazeing/maze/maze_coords.py @@ -1,6 +1,6 @@ from enum import Enum, auto from typing import Iterable, cast, overload -from ..maze_display import IVec2 +from amazeing.utils import IVec2 class Orientation(Enum): diff --git a/amazeing/maze_class/maze_dirty_tracker.py b/amazeing/maze/maze_dirty_tracker.py similarity index 88% rename from amazeing/maze_class/maze_dirty_tracker.py rename to amazeing/maze/maze_dirty_tracker.py index ed8c91a..0608799 100644 --- a/amazeing/maze_class/maze_dirty_tracker.py +++ b/amazeing/maze/maze_dirty_tracker.py @@ -1,6 +1,6 @@ from collections.abc import Iterable -from amazeing.maze_class.maze import Maze -from amazeing.maze_class.maze_coords import WallCoord +from amazeing.maze import Maze +from amazeing.maze import WallCoord class MazeDirtyTracker: diff --git a/amazeing/maze_class/maze_network_tracker.py b/amazeing/maze/maze_network_tracker.py similarity index 98% rename from amazeing/maze_class/maze_network_tracker.py rename to amazeing/maze/maze_network_tracker.py index 8b0297b..dc5d4f5 100644 --- a/amazeing/maze_class/maze_network_tracker.py +++ b/amazeing/maze/maze_network_tracker.py @@ -1,5 +1,5 @@ -from amazeing.maze_class.maze import Maze -from amazeing.maze_class.maze_coords import ( +from amazeing.maze import Maze +from amazeing.maze.maze_coords import ( SplitWall, WallCoord, split_wall_ccw, diff --git a/amazeing/maze_class/maze_pacman_tracker.py b/amazeing/maze/maze_pacman_tracker.py similarity index 90% rename from amazeing/maze_class/maze_pacman_tracker.py rename to amazeing/maze/maze_pacman_tracker.py index 559f8d0..b33a3b9 100644 --- a/amazeing/maze_class/maze_pacman_tracker.py +++ b/amazeing/maze/maze_pacman_tracker.py @@ -1,6 +1,6 @@ from collections.abc import Iterable -from amazeing.maze_class.maze import Maze -from amazeing.maze_class.maze_coords import WallCoord +from amazeing.maze import Maze +from amazeing.maze import WallCoord from amazeing.utils.randset import Randset diff --git a/amazeing/maze_class/maze_pattern.py b/amazeing/maze/maze_pattern.py similarity index 98% rename from amazeing/maze_class/maze_pattern.py rename to amazeing/maze/maze_pattern.py index 62cd09f..ee73ac6 100644 --- a/amazeing/maze_class/maze_pattern.py +++ b/amazeing/maze/maze_pattern.py @@ -1,5 +1,5 @@ from collections.abc import Iterable, Generator, Callable -from amazeing.maze_display.backend import IVec2 +from amazeing.utils import IVec2 from .maze import Maze from .maze_coords import CellCoord diff --git a/amazeing/maze_display/TTYdisplay.py b/amazeing/maze_display/TTYdisplay.py index 5dead97..ad6b08a 100644 --- a/amazeing/maze_display/TTYdisplay.py +++ b/amazeing/maze_display/TTYdisplay.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod from collections.abc import Callable, Generator, Iterable +from dataclasses import dataclass from amazeing.utils import BiMap from amazeing.config.config_parser import Color, Config, ColoredLine, ColorPair from amazeing.maze_display.layout import ( @@ -14,8 +15,7 @@ from amazeing.maze_display.layout import ( layout_sort_chunked, layout_split, ) -from amazeing.utils import Rect, QuadTree -from .backend import IVec2, BackendEvent, KeyboardInput +from amazeing.utils import Rect, QuadTree, IVec2 import curses @@ -23,6 +23,11 @@ class BackendException(Exception): pass +@dataclass +class KeyboardInput: + sym: str + + class ITile(ABC): @abstractmethod def size(self) -> IVec2: ... @@ -552,7 +557,7 @@ class TTYBackend: self.__layout.laid_out(IVec2(0, 0), IVec2(x, y)) self.__scratch.overwrite(self.__screen) - def event(self, timeout_ms: int = -1) -> BackendEvent | bool: + def event(self, timeout_ms: int = -1) -> KeyboardInput | bool: self.__screen.timeout(timeout_ms) try: key = self.__screen.getkey() diff --git a/amazeing/maze_display/__init__.py b/amazeing/maze_display/__init__.py index 0964b48..33dd123 100644 --- a/amazeing/maze_display/__init__.py +++ b/amazeing/maze_display/__init__.py @@ -1,10 +1,4 @@ __version__ = "0.0.0" __author__ = "luflores & agilliar" -from .backend import IVec2 -from .TTYdisplay import TTYBackend - -__all__ = [ - "IVec2", - "TTYBackend", -] +__all__ = [] diff --git a/amazeing/maze_display/backend.py b/amazeing/maze_display/backend.py index 184e76f..fc0ee68 100644 --- a/amazeing/maze_display/backend.py +++ b/amazeing/maze_display/backend.py @@ -1,5 +1,4 @@ from collections.abc import Callable -from dataclasses import dataclass from typing import Type, cast @@ -67,15 +66,3 @@ class IVec2[T = int]: def yx(self) -> tuple[T, T]: return (self.y, self.x) - - -@dataclass -class KeyboardInput: - sym: str - - -class CloseRequested: - pass - - -type BackendEvent = KeyboardInput | CloseRequested diff --git a/amazeing/maze_display/layout.py b/amazeing/maze_display/layout.py index 44435a1..7d7155e 100644 --- a/amazeing/maze_display/layout.py +++ b/amazeing/maze_display/layout.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod from collections.abc import Callable -from .backend import IVec2 +from amazeing.utils import IVec2 class BInt: diff --git a/amazeing/maze_make_empty.py b/amazeing/maze_make_empty.py index 336ac5b..aed31e8 100644 --- a/amazeing/maze_make_empty.py +++ b/amazeing/maze_make_empty.py @@ -1,6 +1,6 @@ from collections.abc import Callable -from amazeing.maze_class.maze import Maze -from amazeing.maze_class.maze_coords import WallCoord +from amazeing.maze import Maze +from amazeing.maze import WallCoord import random diff --git a/amazeing/maze_make_pacman.py b/amazeing/maze_make_pacman.py index 8fe2e00..9ee10fd 100644 --- a/amazeing/maze_make_pacman.py +++ b/amazeing/maze_make_pacman.py @@ -1,8 +1,8 @@ from typing import Callable -from amazeing import Maze, WallCoord +from amazeing.maze import Maze, WallCoord import random -from amazeing.maze_class.maze_pacman_tracker import MazePacmanTracker +from amazeing.maze import MazePacmanTracker def maze_make_pacman( diff --git a/amazeing/maze_make_perfect.py b/amazeing/maze_make_perfect.py index bfb629f..7901108 100644 --- a/amazeing/maze_make_perfect.py +++ b/amazeing/maze_make_perfect.py @@ -1,8 +1,8 @@ from typing import Callable -from amazeing import Maze +from amazeing.maze import Maze import random -from amazeing.maze_class.maze_network_tracker import MazeNetworkTracker +from amazeing.maze import MazeNetworkTracker def maze_make_perfect( diff --git a/amazeing/utils/__init__.py b/amazeing/utils/__init__.py index 5db6856..59cd57d 100644 --- a/amazeing/utils/__init__.py +++ b/amazeing/utils/__init__.py @@ -1,5 +1,6 @@ from .bi_map import BiMap from .avl import Tree as AVLTree, Leaf as AVLLeaf from .quadtree import Tree as QuadTree, Rect +from .ivec2 import IVec2 -__all__ = ["BiMap", "AVLTree", "AVLLeaf", "QuadTree", "Rect"] +__all__ = ["BiMap", "AVLTree", "AVLLeaf", "QuadTree", "Rect", "IVec2"] diff --git a/amazeing/utils/ivec2.py b/amazeing/utils/ivec2.py new file mode 100644 index 0000000..fc0ee68 --- /dev/null +++ b/amazeing/utils/ivec2.py @@ -0,0 +1,68 @@ +from collections.abc import Callable +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 + + @staticmethod + def splat(n: T) -> "IVec2[T]": + return IVec2(n, n) + + def __repr__(self) -> str: + return f"{self.x, self.y}" + + @staticmethod + def with_op[T2]( + op: Callable[[T, T], T2], + ) -> Callable[["IVec2[T]", "T | IVec2[T]"], "IVec2[T2]"]: + return lambda self, other: IVec2( + op( + self.x, + ( + other + if isinstance(other, IVec2) + else (other := type(self).splat(other)) + ).x, + ), + op(self.y, cast(IVec2[T], other).y), + ) + + def innertype(self) -> Type[T]: + return type(self.x) + + def __mul__(self, other: "IVec2[T]") -> "IVec2[T]": + return IVec2(self.x * other.x, self.y * other.y) # type:ignore + + def __add__(self, other: "IVec2[T]") -> "IVec2[T]": + return IVec2(self.x + other.x, self.y + other.y) # type:ignore + + def __sub__(self, other: "IVec2[T]") -> "IVec2[T]": + return IVec2(self.x - other.x, self.y - other.y) # type:ignore + + def __floordiv__(self, other: "IVec2[T]") -> "IVec2[T]": + return IVec2(self.x // other.x, self.y // other.y) # type:ignore + + def __mod__(self, other: "IVec2[T]") -> "IVec2[T]": + return IVec2(self.x % other.x, self.y % other.y) # type:ignore + + def __eq__(self, value: object, /) -> bool: + return ( + isinstance(value, IVec2) + and self.x == value.x + and self.y == value.y + ) + + def __hash__(self) -> int: + return hash((self.x, self.y)) + + def xy(self) -> tuple[T, T]: + return (self.x, self.y) + + def yx(self) -> tuple[T, T]: + return (self.y, self.x) diff --git a/amazeing/utils/quadtree.py b/amazeing/utils/quadtree.py index 9a2de7a..9af2829 100644 --- a/amazeing/utils/quadtree.py +++ b/amazeing/utils/quadtree.py @@ -1,5 +1,5 @@ from collections.abc import Callable, Generator -from amazeing.maze_display.backend import IVec2 +from .ivec2 import IVec2 from functools import partial from itertools import chain diff --git a/tmp2 b/tmp2 new file mode 100644 index 0000000..0ec98cf --- /dev/null +++ b/tmp2 @@ -0,0 +1,530 @@ +(10, 2) +(10, 3) +(10, 4) +(14, 6) +(15, 6) +(16, 6) +(10, 8) +(11, 8) +(12, 8) +(12, 14) +(12, 15) +(12, 16) +(2, 12) +(2, 13) +(2, 14) +(0, 2) +(0, 3) +(0, 4) +(12, 0) +(12, 1) +(12, 2) +(14, 10) +(15, 10) +(16, 10) +(16, 12) +(17, 12) +(18, 12) +(12, 4) +(12, 5) +(12, 6) +(8, 14) +(9, 14) +(10, 14) +(2, 2) +(2, 3) +(2, 4) +(0, 14) +(0, 15) +(0, 16) +(6, 0) +(6, 1) +(6, 2) +(14, 8) +(14, 9) +(14, 10) +(8, 8) +(9, 8) +(10, 8) +(10, 16) +(11, 16) +(12, 16) +(18, 6) +(18, 7) +(18, 8) +(4, 10) +(5, 10) +(6, 10) +(2, 10) +(2, 11) +(2, 12) +(8, 6) +(9, 6) +(10, 6) +(6, 4) +(6, 5) +(6, 6) +(18, 10) +(18, 11) +(18, 12) +(6, 14) +(7, 14) +(8, 14) +(0, 16) +(1, 16) +(2, 16) +(10, 8) +(10, 9) +(10, 10) +(0, 12) +(0, 13) +(0, 14) +(10, 12) +(11, 12) +(12, 12) +(2, 8) +(3, 8) +(4, 8) +(4, 16) +(5, 16) +(6, 16) +(18, 0) +(18, 1) +(18, 2) +(2, 4) +(2, 5) +(2, 6) +(4, 12) +(4, 13) +(4, 14) +(6, 2) +(7, 2) +(8, 2) +(0, 6) +(0, 7) +(0, 8) +(14, 0) +(14, 1) +(14, 2) +(8, 10) +(8, 11) +(8, 12) +(2, 16) +(3, 16) +(4, 16) +(2, 2) +(3, 2) +(4, 2) +(10, 0) +(11, 0) +(12, 0) +(4, 6) +(4, 7) +(4, 8) +(0, 10) +(1, 10) +(2, 10) +(10, 4) +(11, 4) +(12, 4) +(12, 8) +(13, 8) +(14, 8) +(4, 6) +(5, 6) +(6, 6) +(14, 12) +(14, 13) +(14, 14) +(0, 0) +(1, 0) +(2, 0) +(12, 12) +(12, 13) +(12, 14) +(12, 6) +(13, 6) +(14, 6) +(8, 0) +(9, 0) +(10, 0) +(16, 8) +(17, 8) +(18, 8) +(6, 8) +(6, 9) +(6, 10) +(18, 14) +(18, 15) +(18, 16) +(6, 12) +(7, 12) +(8, 12) +(14, 2) +(14, 3) +(14, 4) +(4, 0) +(4, 1) +(4, 2) +(16, 0) +(16, 1) +(16, 2) +(12, 10) +(12, 11) +(12, 12) +(14, 14) +(15, 14) +(16, 14) +(8, 16) +(9, 16) +(10, 16) +(16, 4) +(17, 4) +(18, 4) +(2, 8) +(2, 9) +(2, 10) +(14, 14) +(14, 15) +(14, 16) +(8, 2) +(8, 3) +(8, 4) +(16, 16) +(17, 16) +(18, 16) +(2, 4) +(3, 4) +(4, 4) +(0, 0) +(1, 0) +(0, 1) +(1, 1) +(2, 0) +(3, 0) +(2, 1) +(3, 1) +(0, 2) +(1, 2) +(0, 3) +(1, 3) +(2, 2) +(3, 2) +(2, 3) +(3, 3) +(4, 0) +(5, 0) +(4, 1) +(5, 1) +(6, 0) +(7, 0) +(6, 1) +(7, 1) +(4, 2) +(5, 2) +(4, 3) +(5, 3) +(6, 2) +(7, 2) +(6, 3) +(7, 3) +(0, 4) +(1, 4) +(0, 5) +(1, 5) +(2, 4) +(3, 4) +(2, 5) +(3, 5) +(0, 6) +(1, 6) +(0, 7) +(1, 7) +(2, 6) +(3, 6) +(2, 7) +(3, 7) +(4, 4) +(5, 4) +(4, 5) +(5, 5) +(6, 4) +(7, 4) +(6, 5) +(7, 5) +(4, 6) +(5, 6) +(4, 7) +(5, 7) +(6, 6) +(7, 6) +(6, 7) +(7, 7) +(8, 0) +(9, 0) +(8, 1) +(9, 1) +(10, 0) +(11, 0) +(10, 1) +(11, 1) +(8, 2) +(9, 2) +(8, 3) +(9, 3) +(10, 2) +(11, 2) +(10, 3) +(11, 3) +(12, 0) +(13, 0) +(12, 1) +(13, 1) +(14, 0) +(15, 0) +(14, 1) +(15, 1) +(12, 2) +(13, 2) +(12, 3) +(13, 3) +(14, 2) +(15, 2) +(14, 3) +(15, 3) +(8, 4) +(9, 4) +(8, 5) +(9, 5) +(10, 4) +(11, 4) +(10, 5) +(11, 5) +(8, 6) +(9, 6) +(8, 7) +(9, 7) +(10, 6) +(11, 6) +(10, 7) +(11, 7) +(12, 4) +(13, 4) +(12, 5) +(13, 5) +(14, 4) +(15, 4) +(14, 5) +(15, 5) +(12, 6) +(13, 6) +(12, 7) +(13, 7) +(14, 6) +(15, 6) +(14, 7) +(15, 7) +(0, 8) +(1, 8) +(0, 9) +(1, 9) +(2, 8) +(3, 8) +(2, 9) +(3, 9) +(0, 10) +(1, 10) +(0, 11) +(1, 11) +(2, 10) +(3, 10) +(2, 11) +(3, 11) +(4, 8) +(5, 8) +(4, 9) +(5, 9) +(6, 8) +(7, 8) +(6, 9) +(7, 9) +(4, 10) +(5, 10) +(4, 11) +(5, 11) +(6, 10) +(7, 10) +(6, 11) +(7, 11) +(0, 12) +(1, 12) +(0, 13) +(1, 13) +(2, 12) +(3, 12) +(2, 13) +(3, 13) +(0, 14) +(1, 14) +(0, 15) +(1, 15) +(2, 14) +(3, 14) +(2, 15) +(3, 15) +(4, 12) +(5, 12) +(4, 13) +(5, 13) +(6, 12) +(7, 12) +(6, 13) +(7, 13) +(4, 14) +(5, 14) +(4, 15) +(5, 15) +(6, 14) +(7, 14) +(6, 15) +(7, 15) +(8, 8) +(9, 8) +(8, 9) +(9, 9) +(10, 8) +(11, 8) +(10, 9) +(11, 9) +(8, 10) +(9, 10) +(8, 11) +(9, 11) +(10, 10) +(11, 10) +(10, 11) +(11, 11) +(12, 8) +(13, 8) +(12, 9) +(13, 9) +(14, 8) +(15, 8) +(14, 9) +(15, 9) +(12, 10) +(13, 10) +(12, 11) +(13, 11) +(14, 10) +(15, 10) +(14, 11) +(15, 11) +(8, 12) +(9, 12) +(8, 13) +(9, 13) +(10, 12) +(11, 12) +(10, 13) +(11, 13) +(8, 14) +(9, 14) +(8, 15) +(9, 15) +(10, 14) +(11, 14) +(10, 15) +(11, 15) +(12, 12) +(13, 12) +(12, 13) +(13, 13) +(14, 12) +(15, 12) +(14, 13) +(15, 13) +(12, 14) +(13, 14) +(12, 15) +(13, 15) +(14, 14) +(15, 14) +(14, 15) +(15, 15) +(16, 0) +(17, 0) +(16, 1) +(17, 1) +(18, 0) +(18, 1) +(16, 2) +(17, 2) +(16, 3) +(17, 3) +(18, 2) +(18, 3) +(16, 4) +(17, 4) +(16, 5) +(17, 5) +(18, 4) +(18, 5) +(16, 6) +(17, 6) +(16, 7) +(17, 7) +(18, 6) +(18, 7) +(16, 8) +(17, 8) +(16, 9) +(17, 9) +(18, 8) +(18, 9) +(16, 10) +(17, 10) +(16, 11) +(17, 11) +(18, 10) +(18, 11) +(16, 12) +(17, 12) +(16, 13) +(17, 13) +(18, 12) +(18, 13) +(16, 14) +(17, 14) +(16, 15) +(17, 15) +(18, 14) +(18, 15) +(0, 16) +(1, 16) +(2, 16) +(3, 16) +(4, 16) +(5, 16) +(6, 16) +(7, 16) +(8, 16) +(9, 16) +(10, 16) +(11, 16) +(12, 16) +(13, 16) +(14, 16) +(15, 16) +(16, 16) +(17, 16) +(18, 16) +(2, 2) +(3, 2) +(4, 2) +(2, 2) +(2, 3) +(2, 4) +(4, 0) +(4, 1) +(4, 2)