diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml
index e6c1c97..761f5aa 100644
--- a/.github/workflows/python-app.yml
+++ b/.github/workflows/python-app.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ['3.7', '3.8', '3.9']
+ python-version: ['3.9', '3.10', '3.11', '3.12']
name: Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v4
diff --git a/README.md b/README.md
index 4a47e58..ae7c222 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
pyscroll
========
-For Python 3.7+ and pygame 2.0+
+For Python 3.9+ and pygame 2.0+
__pygame-ce is supported__
@@ -105,7 +105,7 @@ class Sprite(pygame.sprite.Sprite):
Simple Sprite class for on-screen things
"""
- def __init__(self, surface):
+ def __init__(self, surface) -> None:
self.image = surface
self.rect = surface.get_rect()
diff --git a/apps/demo/demo-stitched.py b/apps/demo/demo-stitched.py
index 054eabe..2a2c76a 100644
--- a/apps/demo/demo-stitched.py
+++ b/apps/demo/demo-stitched.py
@@ -9,7 +9,6 @@
from __future__ import annotations
from pathlib import Path
-from typing import List
import pygame
from pygame.locals import (
@@ -57,11 +56,11 @@ def __init__(self) -> None:
self.feet = pygame.Rect(0, 0, self.rect.width * 0.5, 8)
@property
- def position(self) -> List[float]:
+ def position(self) -> list[float]:
return list(self._position)
@position.setter
- def position(self, value: List[float]) -> None:
+ def position(self, value: list[float]) -> None:
self._position = list(value)
def update(self, dt: float) -> None:
@@ -167,14 +166,14 @@ def handle_input(self) -> None:
else:
self.hero.velocity[0] = 0
- def update(self, dt: float):
+ def update(self, dt: float) -> None:
"""
Tasks that occur over time should be handled here
"""
self.group.update(dt)
- def run(self):
+ def run(self) -> None:
clock = pygame.time.Clock()
self.running = True
diff --git a/apps/demo/demo.py b/apps/demo/demo.py
index bc7c6d0..c0a032c 100644
--- a/apps/demo/demo.py
+++ b/apps/demo/demo.py
@@ -36,12 +36,13 @@ def init_screen(width, height):
class ScrollTest:
- """ Test and demo of pyscroll
+ """Test and demo of pyscroll
For normal use, please see the quest demo, not this.
"""
- def __init__(self, filename):
+
+ def __init__(self, filename) -> None:
# load data from pytmx
tmx_data = load_pygame(filename)
@@ -50,19 +51,22 @@ def __init__(self, filename):
map_data = pyscroll.data.TiledMapData(tmx_data)
# create new renderer
- self.map_layer = pyscroll.orthographic.BufferedRenderer(map_data, screen.get_size())
+ self.map_layer = pyscroll.orthographic.BufferedRenderer(
+ map_data, screen.get_size()
+ )
# create a font and pre-render some text to be displayed over the map
f = pygame.font.Font(pygame.font.get_default_font(), 20)
- t = ["scroll demo. press escape to quit",
- "arrow keys move"]
+ t = ["scroll demo. press escape to quit", "arrow keys move"]
# save the rendered text
self.text_overlay = [f.render(i, 1, (180, 180, 0)) for i in t]
# set our initial viewpoint in the center of the map
- self.center = [self.map_layer.map_rect.width / 2,
- self.map_layer.map_rect.height / 2]
+ self.center = [
+ self.map_layer.map_rect.width / 2,
+ self.map_layer.map_rect.height / 2,
+ ]
# the camera vector is used to handle camera movement
self.camera_acc = [0, 0, 0]
@@ -72,7 +76,7 @@ def __init__(self, filename):
# true when running
self.running = False
- def draw(self, surface):
+ def draw(self, surface) -> None:
# tell the map_layer (BufferedRenderer) to draw to the surface
# the draw function requires a rect to draw to.
@@ -81,15 +85,14 @@ def draw(self, surface):
# blit our text over the map
self.draw_text(surface)
- def draw_text(self, surface):
+ def draw_text(self, surface) -> None:
y = 0
for text in self.text_overlay:
surface.blit(text, (0, y))
y += text.get_height()
- def handle_input(self):
- """ Simply handle pygame input events
- """
+ def handle_input(self) -> None:
+ """Simply handle pygame input events"""
for event in pygame.event.get():
if event.type == QUIT:
self.running = False
@@ -126,10 +129,10 @@ def handle_input(self):
else:
self.camera_acc[0] = 0
- def update(self, td):
+ def update(self, td) -> None:
self.last_update_time = td
- friction = pow(.0001, self.last_update_time)
+ friction = pow(0.0001, self.last_update_time)
# update the camera vector
self.camera_vel[0] += self.camera_acc[0] * td
@@ -164,21 +167,21 @@ def update(self, td):
# in a game, you would set center to a playable character
self.map_layer.center(self.center)
- def run(self):
+ def run(self) -> None:
clock = pygame.time.Clock()
self.running = True
- fps = 60.
+ fps = 60.0
fps_log = collections.deque(maxlen=20)
try:
while self.running:
# somewhat smoother way to get fps and limit the framerate
- clock.tick(fps*2)
+ clock.tick(fps * 2)
try:
fps_log.append(clock.get_fps())
- fps = sum(fps_log)/len(fps_log)
- dt = 1/fps
+ fps = sum(fps_log) / len(fps_log)
+ dt = 1 / fps
except ZeroDivisionError:
continue
@@ -197,7 +200,7 @@ def run(self):
pygame.init()
pygame.font.init()
screen = init_screen(800, 600)
- pygame.display.set_caption('pyscroll Test')
+ pygame.display.set_caption("pyscroll Test")
try:
filename = sys.argv[1]
diff --git a/apps/demo/translate.py b/apps/demo/translate.py
index 070bba1..d827fbd 100644
--- a/apps/demo/translate.py
+++ b/apps/demo/translate.py
@@ -7,8 +7,7 @@
class Dummy:
-
- def run(self):
+ def run(self) -> None:
surface = None
for spr in self.sprites():
diff --git a/apps/tutorial/quest.py b/apps/tutorial/quest.py
index 7b0301e..bfb7a29 100644
--- a/apps/tutorial/quest.py
+++ b/apps/tutorial/quest.py
@@ -11,7 +11,6 @@
from __future__ import annotations
from pathlib import Path
-from typing import List
import pygame
from pygame.locals import (
@@ -70,6 +69,7 @@ class Hero(pygame.sprite.Sprite):
it collides with level walls.
"""
+
def __init__(self) -> None:
super().__init__()
self.image = load_image("hero.png").convert_alpha()
@@ -80,11 +80,11 @@ def __init__(self) -> None:
self.feet = pygame.Rect(0, 0, self.rect.width * 0.5, 8)
@property
- def position(self) -> List[float]:
+ def position(self) -> list[float]:
return list(self._position)
@position.setter
- def position(self, value: List[float]) -> None:
+ def position(self, value: list[float]) -> None:
self._position = list(value)
def update(self, dt: float) -> None:
@@ -113,6 +113,7 @@ class QuestGame:
Finally, it uses a pyscroll group to render the map and Hero.
"""
+
map_path = RESOURCES_DIR / "grasslands.tmx"
def __init__(self, screen: pygame.Surface) -> None:
@@ -206,7 +207,7 @@ def handle_input(self) -> None:
else:
self.hero.velocity[0] = 0
- def update(self, dt: float):
+ def update(self, dt: float) -> None:
"""
Tasks that occur over time should be handled here
@@ -220,7 +221,7 @@ def update(self, dt: float):
if sprite.feet.collidelist(self.walls) > -1:
sprite.move_back(dt)
- def run(self):
+ def run(self) -> None:
"""
Run the game loop
diff --git a/docs/_build/html/_sources/index.txt b/docs/_build/html/_sources/index.txt
index 48ebde7..9790aaa 100644
--- a/docs/_build/html/_sources/index.txt
+++ b/docs/_build/html/_sources/index.txt
@@ -6,7 +6,7 @@
pyscroll
========
-for Python 2.7 & 3.3 and Pygame 1.9
+for Python 3.9 and Pygame 1.9
A simple, fast module for adding scrolling maps to your new or existing game.
diff --git a/docs/_build/html/index.html b/docs/_build/html/index.html
index 9d78562..329377f 100644
--- a/docs/_build/html/index.html
+++ b/docs/_build/html/index.html
@@ -46,7 +46,7 @@
Navigation
diff --git a/docs/conf.py b/docs/conf.py
index 5eca40b..23fe35c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,224 +18,218 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.todo',
- 'sphinx.ext.viewcode',
+ "sphinx.ext.autodoc",
+ "sphinx.ext.todo",
+ "sphinx.ext.viewcode",
]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
# The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# General information about the project.
-project = u'pyscroll'
-copyright = u'2014, bitcraft'
+project = "pyscroll"
+copyright = "2014, bitcraft"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = '2.14.2'
+version = "2.14.2"
# The full version, including alpha/beta/rc tags.
-release = '2.14.2'
+release = "2.14.2"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-#language = None
+# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
-#today = ''
+# today = ''
# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
-exclude_patterns = ['_build', 'tests']
+exclude_patterns = ["_build", "tests"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
-#default_role = None
+# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+# keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
+html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
-#html_theme_options = {}
+# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "
v documentation".
-#html_title = None
+# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-#html_logo = None
+# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-#html_favicon = None
+# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
-#html_extra_path = []
+# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
# If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
# If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
# Output file base name for HTML help builder.
-htmlhelp_basename = 'pyscrolldoc'
+htmlhelp_basename = "pyscrolldoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
+ # The paper size ('letterpaper' or 'a4paper').
+ #'papersize': 'letterpaper',
+ # The font size ('10pt', '11pt' or '12pt').
+ #'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ #'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- ('index', 'pyscroll.tex', u'pyscroll Documentation',
- u'bitcraft', 'manual'),
+ ("index", "pyscroll.tex", "pyscroll Documentation", "bitcraft", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-#latex_logo = None
+# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
# If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
# If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
# Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
# If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'pyscroll', u'pyscroll Documentation',
- [u'bitcraft'], 1)
-]
+man_pages = [("index", "pyscroll", "pyscroll Documentation", ["bitcraft"], 1)]
# If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
@@ -244,19 +238,25 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- ('index', 'pyscroll', u'pyscroll Documentation',
- u'bitcraft', 'pyscroll', 'One line description of project.',
- 'Miscellaneous'),
+ (
+ "index",
+ "pyscroll",
+ "pyscroll Documentation",
+ "bitcraft",
+ "pyscroll",
+ "One line description of project.",
+ "Miscellaneous",
+ ),
]
# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
# If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
+# texinfo_no_detailmenu = False
diff --git a/docs/index.rst b/docs/index.rst
index 099b1b8..135c864 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -6,7 +6,7 @@
pyscroll
========
-for Python 2.7 & 3.3+ and Pygame 1.9
+for Python 3.9+ and Pygame 1.9
A simple, fast module for adding scrolling maps to your new or existing game.
Includes support to load and render maps in TMX format from the Tiled map editor.
diff --git a/pyproject.toml b/pyproject.toml
index d8dd816..3d654b9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,14 +15,15 @@ classifiers = [
"Intended Audience :: Developers",
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
"Topic :: Games/Entertainment",
"Topic :: Multimedia :: Graphics",
"Topic :: Software Development :: Libraries :: pygame",
]
-requires-python = ">=3.7"
+requires-python = ">=3.9"
[project.urls]
source = "https://github.com/bitcraft/pyscroll"
@@ -36,7 +37,7 @@ include = ["pyscroll*"]
[tool.black]
line-length = 88
-target-version = ["py37"]
+target-version = ["py39"]
[tool.isort]
line_length = 88
diff --git a/pyscroll/animation.py b/pyscroll/animation.py
index 6864fba..504d1ab 100644
--- a/pyscroll/animation.py
+++ b/pyscroll/animation.py
@@ -1,18 +1,19 @@
from __future__ import annotations
from collections import namedtuple
-from typing import Sequence, Union
+from collections.abc import Sequence
+from typing import Union
AnimationFrame = namedtuple("AnimationFrame", "image duration")
TimeLike = Union[float, int]
-__all__ = ('AnimationFrame', 'AnimationToken')
+__all__ = ("AnimationFrame", "AnimationToken")
class AnimationToken:
- __slots__ = ['next', 'positions', 'frames', 'index']
+ __slots__ = ["next", "positions", "frames", "index"]
- def __init__(self, positions, frames: Sequence, initial_time: int = 0):
+ def __init__(self, positions, frames: Sequence, initial_time: int = 0) -> None:
"""
Constructor
diff --git a/pyscroll/common.py b/pyscroll/common.py
index a2b8f07..a80bf38 100644
--- a/pyscroll/common.py
+++ b/pyscroll/common.py
@@ -1,13 +1,13 @@
from __future__ import annotations
from contextlib import contextmanager
-from typing import Any, List, Tuple, Union
+from typing import Any, Union
from pygame import Rect, Surface, Vector2
-RectLike = Union[Rect, Tuple[Any, Any, Any, Any]]
-Vector2D = Union[Tuple[float, float], Tuple[int, int], Vector2]
-Vector2DInt = Tuple[int, int]
+RectLike = Union[Rect, tuple[Any, Any, Any, Any]]
+Vector2D = Union[tuple[float, float], tuple[int, int], Vector2]
+Vector2DInt = tuple[int, int]
@contextmanager
@@ -18,7 +18,7 @@ def surface_clipping_context(surface: Surface, clip: RectLike):
surface.set_clip(original)
-def rect_difference(a: RectLike, b: RectLike) -> List[Rect]:
+def rect_difference(a: RectLike, b: RectLike) -> list[Rect]:
"""
Compute difference of two rects. Returns up to 4.
@@ -26,6 +26,6 @@ def rect_difference(a: RectLike, b: RectLike) -> List[Rect]:
raise NotImplementedError
-def rect_to_bb(rect: RectLike) -> Tuple[int, int, int, int]:
+def rect_to_bb(rect: RectLike) -> tuple[int, int, int, int]:
x, y, w, h = rect
return x, y, x + w - 1, y + h - 1
diff --git a/pyscroll/data.py b/pyscroll/data.py
index 638344b..860f031 100644
--- a/pyscroll/data.py
+++ b/pyscroll/data.py
@@ -9,7 +9,6 @@
import time
from heapq import heappop, heappush
from itertools import product
-from typing import List, Tuple
import pygame
from pygame import Surface
@@ -39,27 +38,28 @@ class PyscrollDataAdapter:
source, it is only tested using Tiled maps, loaded with pytmx.
"""
+
# the following can be class/instance attributes
# or properties. they are listed here as class
# instances, but use as properties is fine, too.
- tile_size = None # (int, int): size of each tile in pixels
- map_size = None # (int, int): size of map in tiles
- visible_tile_layers = None # list of visible layer integers
+ tile_size = None # (int, int): size of each tile in pixels
+ map_size = None # (int, int): size of map in tiles
+ visible_tile_layers = None # list of visible layer integers
- def __init__(self):
- self._last_time = None # last time map animations were updated
- self._animation_queue = list() # list of animation tokens
- self._animated_tile = dict() # mapping of tile substitutions when animated
- self._tracked_tiles = set() # track the tiles on screen with animations
+ def __init__(self) -> None:
+ self._last_time = None # last time map animations were updated
+ self._animation_queue = list() # list of animation tokens
+ self._animated_tile = dict() # mapping of tile substitutions when animated
+ self._tracked_tiles = set() # track the tiles on screen with animations
- def reload_data(self):
+ def reload_data(self) -> None:
raise NotImplementedError
def process_animation_queue(
- self,
- tile_view: RectLike,
- ) -> List[Tuple[int, int, int, Surface]]:
+ self,
+ tile_view: RectLike,
+ ) -> list[tuple[int, int, int, Surface]]:
"""
Given the time and the tile view, process tile changes and return them
@@ -122,7 +122,7 @@ def process_animation_queue(
return new_tiles
- def _update_time(self):
+ def _update_time(self) -> None:
"""
Update the internal clock.
@@ -147,7 +147,7 @@ def prepare_tiles(self, tiles: RectLike):
"""
pass
- def reload_animations(self):
+ def reload_animations(self) -> None:
"""
Reload animation information.
@@ -233,7 +233,7 @@ def _get_tile_image_by_id(self, id):
"""
raise NotImplementedError
- def get_animations(self):
+ def get_animations(self) -> None:
"""
Get tile animation data.
@@ -277,8 +277,7 @@ def get_tile_images_by_rect(self, rect: RectLike):
"""
x1, y1, x2, y2 = rect_to_bb(rect)
for layer in self.visible_tile_layers:
- for y, x in product(range(y1, y2 + 1),
- range(x1, x2 + 1)):
+ for y, x in product(range(y1, y2 + 1), range(x1, x2 + 1)):
tile = self.get_tile_image(x, y, layer)
if tile:
yield x, y, layer, tile
@@ -289,25 +288,26 @@ class TiledMapData(PyscrollDataAdapter):
For data loaded from pytmx.
"""
- def __init__(self, tmx):
+
+ def __init__(self, tmx) -> None:
super(TiledMapData, self).__init__()
self.tmx = tmx
self.reload_animations()
- def reload_data(self):
+ def reload_data(self) -> None:
self.tmx = pytmx.load_pygame(self.tmx.filename)
def get_animations(self):
for gid, d in self.tmx.tile_properties.items():
try:
- frames = d['frames']
+ frames = d["frames"]
except KeyError:
continue
if frames:
yield gid, frames
- def convert_surfaces(self, parent: Surface, alpha: bool = False):
+ def convert_surfaces(self, parent: Surface, alpha: bool = False) -> None:
images = list()
for i in self.tmx.images:
try:
@@ -333,8 +333,11 @@ def visible_tile_layers(self):
@property
def visible_object_layers(self):
- return (layer for layer in self.tmx.visible_layers
- if isinstance(layer, pytmx.TiledObjectGroup))
+ return (
+ layer
+ for layer in self.tmx.visible_layers
+ if isinstance(layer, pytmx.TiledObjectGroup)
+ )
def _get_tile_image(self, x: int, y: int, l: int):
try:
@@ -349,7 +352,7 @@ def get_tile_images_by_rect(self, rect: RectLike):
def rev(seq, start, stop):
if start < 0:
start = 0
- return enumerate(seq[start:stop + 1], start)
+ return enumerate(seq[start : stop + 1], start)
x1, y1, x2, y2 = rect_to_bb(rect)
images = self.tmx.images
@@ -389,7 +392,8 @@ class MapAggregator(PyscrollDataAdapter):
- Cannot remove maps once added
"""
- def __init__(self, tile_size):
+
+ def __init__(self, tile_size) -> None:
super().__init__()
self.tile_size = tile_size
self.map_size = 0, 0
@@ -404,14 +408,14 @@ def _get_tile_image(self, x: int, y: int, l: int) -> Surface:
"""
pass
- def _get_tile_image_by_id(self, id):
+ def _get_tile_image_by_id(self, id) -> None:
"""
Required for sprite collation - not implemented
"""
pass
- def add_map(self, data: PyscrollDataAdapter, offset: Vector2DInt):
+ def add_map(self, data: PyscrollDataAdapter, offset: Vector2DInt) -> None:
"""
Add map data and position it with an offset
@@ -450,14 +454,14 @@ def remove_map(self, data: PyscrollDataAdapter):
"""
raise NotImplementedError
- def get_animations(self):
+ def get_animations(self) -> None:
"""
Get animations - not implemented
"""
pass
- def reload_data(self):
+ def reload_data(self) -> None:
"""
Reload the tiles - not implemented
diff --git a/pyscroll/group.py b/pyscroll/group.py
index cfca0df..c04a88d 100644
--- a/pyscroll/group.py
+++ b/pyscroll/group.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, List
+from typing import TYPE_CHECKING
import pygame
@@ -16,16 +16,12 @@ class PyscrollGroup(pygame.sprite.LayeredUpdates):
map_layer: Pyscroll Renderer
"""
- def __init__(
- self,
- map_layer: BufferedRenderer,
- *args,
- **kwargs
- ):
+
+ def __init__(self, map_layer: BufferedRenderer, *args, **kwargs) -> None:
pygame.sprite.LayeredUpdates.__init__(self, *args, **kwargs)
self._map_layer = map_layer
- def center(self, value):
+ def center(self, value) -> None:
"""
Center the group/map on a pixel.
@@ -46,10 +42,7 @@ def view(self) -> pygame.Rect:
"""
return self._map_layer.view_rect.copy()
- def draw(
- self,
- surface: pygame.surface.Surface
- ) -> List[pygame.rect.Rect]:
+ def draw(self, surface: pygame.surface.Surface) -> list[pygame.rect.Rect]:
"""
Draw map and all sprites onto the surface.
diff --git a/pyscroll/isometric.py b/pyscroll/isometric.py
index 24fb7a7..fec5065 100644
--- a/pyscroll/isometric.py
+++ b/pyscroll/isometric.py
@@ -7,18 +7,22 @@
def vector3_to_iso(vector3):
offset = 0, 0
- return ((vector3[0] - vector3[1]) + offset[0],
- ((vector3[0] + vector3[1]) >> 1) - vector3[2] + offset[1])
+ return (
+ (vector3[0] - vector3[1]) + offset[0],
+ ((vector3[0] + vector3[1]) >> 1) - vector3[2] + offset[1],
+ )
def vector2_to_iso(vector2):
offset = 0, 0
- return ((vector2[0] - vector2[1]) + offset[0],
- ((vector2[0] + vector2[1]) >> 1) + offset[1])
+ return (
+ (vector2[0] - vector2[1]) + offset[0],
+ ((vector2[0] + vector2[1]) >> 1) + offset[1],
+ )
class IsometricBufferedRenderer(BufferedRenderer):
- """ TEST ISOMETRIC
+ """TEST ISOMETRIC
here be dragons. lots of odd, untested, and unoptimised stuff.
@@ -26,12 +30,12 @@ class IsometricBufferedRenderer(BufferedRenderer):
- drawing may have depth sorting issues
"""
- def _draw_surfaces(self, surface, rect, surfaces):
+ def _draw_surfaces(self, surface, rect, surfaces) -> None:
if surfaces is not None:
[(surface.blit(i[0], i[1]), i[2]) for i in surfaces]
- def _initialize_buffers(self, view_size):
- """ Create the buffers to cache tile drawing
+ def _initialize_buffers(self, view_size) -> None:
+ """Create the buffers to cache tile drawing
:param view_size: (int, int): size of the draw area
:return: None
@@ -58,9 +62,8 @@ def _initialize_buffers(self, view_size):
self.redraw_tiles()
- def _flush_tile_queue(self):
- """ Blits (x, y, layer) tuples to buffer from iterator
- """
+ def _flush_tile_queue(self) -> None:
+ """Blits (x, y, layer) tuples to buffer from iterator"""
iterator = self._tile_queue
surface_blit = self._buffer.blit
map_get = self._animation_map.get
@@ -79,12 +82,11 @@ def _flush_tile_queue(self):
# iso => cart
iso_x = ((x - y) * twh) + bw
- iso_y = ((x + y) * thh)
+ iso_y = (x + y) * thh
surface_blit(tile, (iso_x, iso_y))
- def center(self, coords):
- """ center the map on a "map pixel"
- """
+ def center(self, coords) -> None:
+ """center the map on a "map pixel" """
x, y = [round(i, 0) for i in coords]
self.view_rect.center = x, y
diff --git a/pyscroll/orthographic.py b/pyscroll/orthographic.py
index 84ec2b5..19ae364 100644
--- a/pyscroll/orthographic.py
+++ b/pyscroll/orthographic.py
@@ -3,8 +3,9 @@
import logging
import math
import time
+from collections.abc import Callable
from itertools import chain, product
-from typing import TYPE_CHECKING, Callable, List
+from typing import TYPE_CHECKING
import pygame
from pygame import Rect, Surface
@@ -28,6 +29,7 @@ class BufferedRenderer:
created with Tiled.
"""
+
_rgba_clear_color = 0, 0, 0, 0
_rgb_clear_color = 0, 0, 0
@@ -35,15 +37,15 @@ def __init__(
self,
data: PyscrollDataAdapter,
size: Vector2DInt,
- clamp_camera: bool=True,
+ clamp_camera: bool = True,
colorkey=None,
- alpha: bool=False,
- time_source: Callable=time.time,
- scaling_function: Callable=pygame.transform.scale,
- tall_sprites: int=0,
- sprite_damage_height: int=0,
- zoom: float=1.0,
- ):
+ alpha: bool = False,
+ time_source: Callable = time.time,
+ scaling_function: Callable = pygame.transform.scale,
+ tall_sprites: int = 0,
+ sprite_damage_height: int = 0,
+ zoom: float = 1.0,
+ ) -> None:
"""
Constructor
@@ -75,7 +77,7 @@ def __init__(
# internal private defaults
if colorkey and alpha:
- log.error('cannot select both colorkey and alpha')
+ log.error("cannot select both colorkey and alpha")
raise ValueError
elif colorkey:
self._clear_color = colorkey
@@ -85,31 +87,41 @@ def __init__(
self._clear_color = None
# private attributes
- self._anchored_view = True # if true, map is fixed to upper left corner
- self._previous_blit = None # rect of the previous map blit when map edges are visible
- self._size = None # actual pixel size of the view, as it occupies the screen
- self._redraw_cutoff = None # size of dirty tile edge that will trigger full redraw
- self._x_offset = None # offsets are used to scroll map in sub-tile increments
+ self._anchored_view = True # if true, map is fixed to upper left corner
+ self._previous_blit = (
+ None # rect of the previous map blit when map edges are visible
+ )
+ self._size = None # actual pixel size of the view, as it occupies the screen
+ self._redraw_cutoff = (
+ None # size of dirty tile edge that will trigger full redraw
+ )
+ self._x_offset = None # offsets are used to scroll map in sub-tile increments
self._y_offset = None
- self._buffer = None # complete rendering of tilemap
- self._tile_view = None # this rect represents each tile on the buffer
- self._half_width = None # 'half x' attributes are used to reduce division ops.
+ self._buffer = None # complete rendering of tilemap
+ self._tile_view = None # this rect represents each tile on the buffer
+ self._half_width = None # 'half x' attributes are used to reduce division ops.
self._half_height = None
- self._tile_queue = None # tiles queued to be draw onto buffer
- self._animation_queue = None # heap queue of animation token; schedules tile changes
- self._layer_quadtree = None # used to draw tiles that overlap optional surfaces
- self._zoom_buffer = None # used to speed up zoom operations
+ self._tile_queue = None # tiles queued to be draw onto buffer
+ self._animation_queue = (
+ None # heap queue of animation token; schedules tile changes
+ )
+ self._layer_quadtree = None # used to draw tiles that overlap optional surfaces
+ self._zoom_buffer = None # used to speed up zoom operations
self._zoom_level = zoom
- self._real_ratio_x = 1.0 # zooming slightly changes aspect ratio; this compensates
- self._real_ratio_y = 1.0 # zooming slightly changes aspect ratio; this compensates
+ self._real_ratio_x = (
+ 1.0 # zooming slightly changes aspect ratio; this compensates
+ )
+ self._real_ratio_y = (
+ 1.0 # zooming slightly changes aspect ratio; this compensates
+ )
self.view_rect = Rect(0, 0, 0, 0) # this represents the viewable map pixels
self.set_size(size)
if self.tall_sprites != 0:
- log.warning('using tall_sprites feature is not supported')
+ log.warning("using tall_sprites feature is not supported")
- def reload(self):
+ def reload(self) -> None:
"""
Reload tiles and animations for the data source.
@@ -118,7 +130,7 @@ def reload(self):
self.data.reload_animations()
self.redraw_tiles(self._buffer)
- def scroll(self, vector: Vector2DInt):
+ def scroll(self, vector: Vector2DInt) -> None:
"""
Scroll the background in pixels.
@@ -126,10 +138,11 @@ def scroll(self, vector: Vector2DInt):
vector: x, y
"""
- self.center((vector[0] + self.view_rect.centerx,
- vector[1] + self.view_rect.centery))
+ self.center(
+ (vector[0] + self.view_rect.centerx, vector[1] + self.view_rect.centery)
+ )
- def center(self, coords: Vector2D):
+ def center(self, coords: Vector2D) -> None:
"""
Center the map on a pixel.
@@ -199,17 +212,11 @@ def center(self, coords: Vector2D):
self._flush_tile_queue(self._buffer)
elif view_change > self._redraw_cutoff:
- log.debug('scrolling too quickly. redraw forced')
+ log.debug("scrolling too quickly. redraw forced")
self._tile_view.move_ip(dx, dy)
self.redraw_tiles(self._buffer)
- def draw(
- self,
- surface: Surface,
- rect: RectLike,
- surfaces:
- List[Surface]=None
- ):
+ def draw(self, surface: Surface, rect: RectLike, surfaces: list[Surface] = None):
"""
Draw the map onto a surface.
@@ -238,11 +245,7 @@ def draw(
if self._zoom_level == 1.0:
self._render_map(surface, rect, surfaces)
else:
- self._render_map(
- self._zoom_buffer,
- self._zoom_buffer.get_rect(),
- surfaces
- )
+ self._render_map(self._zoom_buffer, self._zoom_buffer.get_rect(), surfaces)
self.scaling_function(self._zoom_buffer, rect.size, surface)
return self._previous_blit.copy()
@@ -261,7 +264,7 @@ def zoom(self) -> float:
return self._zoom_level
@zoom.setter
- def zoom(self, value: float):
+ def zoom(self, value: float) -> None:
zoom_buffer_size = self._calculate_zoom_buffer_size(self._size, value)
self._zoom_level = value
self._initialize_buffers(zoom_buffer_size)
@@ -270,7 +273,7 @@ def zoom(self, value: float):
self._real_ratio_x = float(self._size[0]) / zoom_buffer_size[0]
self._real_ratio_y = float(self._size[1]) / zoom_buffer_size[1]
- def set_size(self, size: Vector2DInt):
+ def set_size(self, size: Vector2DInt) -> None:
"""
Set the size of the map in pixels.
@@ -284,7 +287,7 @@ def set_size(self, size: Vector2DInt):
self._size = size
self._initialize_buffers(buffer_size)
- def redraw_tiles(self, surface: Surface):
+ def redraw_tiles(self, surface: Surface) -> None:
"""
Redraw the visible portion of the buffer -- it is slow.
@@ -293,7 +296,7 @@ def redraw_tiles(self, surface: Surface):
"""
# TODO/BUG: Animated tiles are getting reset here
- log.debug('pyscroll buffer redraw')
+ log.debug("pyscroll buffer redraw")
self._clear_surface(self._buffer)
self._tile_queue = self.data.get_tile_images_by_rect(self._tile_view)
self._flush_tile_queue(surface)
@@ -303,8 +306,10 @@ def get_center_offset(self) -> Vector2DInt:
Return x, y pair that will change world coords to screen coords.
"""
- return (-self.view_rect.centerx + self._half_width,
- -self.view_rect.centery + self._half_height)
+ return (
+ -self.view_rect.centerx + self._half_width,
+ -self.view_rect.centery + self._half_height,
+ )
def translate_point(self, point: Vector2D) -> Vector2DInt:
"""
@@ -320,7 +325,7 @@ def translate_point(self, point: Vector2D) -> Vector2DInt:
else:
return (
int(round((point[0] + mx)) * self._real_ratio_x),
- int(round((point[1] + my) * self._real_ratio_y))
+ int(round((point[1] + my) * self._real_ratio_y)),
)
def translate_rect(self, rect: RectLike) -> Rect:
@@ -338,9 +343,11 @@ def translate_rect(self, rect: RectLike) -> Rect:
if self._zoom_level == 1.0:
return Rect(x + mx, y + my, w, h)
else:
- return Rect(round((x + mx) * rx), round((y + my) * ry), round(w * rx), round(h * ry))
+ return Rect(
+ round((x + mx) * rx), round((y + my) * ry), round(w * rx), round(h * ry)
+ )
- def translate_points(self, points: List[Vector2D]) -> List[Vector2DInt]:
+ def translate_points(self, points: list[Vector2D]) -> list[Vector2DInt]:
"""
Translate coordinates and return screen coordinates.
@@ -361,7 +368,7 @@ def translate_points(self, points: List[Vector2D]) -> List[Vector2DInt]:
append((int(round((c[0] + sx) * rx)), int(round((c[1] + sy) * ry))))
return retval
- def translate_rects(self, rects: List[Rect]) -> List[Rect]:
+ def translate_rects(self, rects: list[Rect]) -> list[Rect]:
"""
Translate rect position and size to screen coordinates.
@@ -386,17 +393,14 @@ def translate_rects(self, rects: List[Rect]) -> List[Rect]:
round((x + sx) * rx),
round((y + sy) * ry),
round(w * rx),
- round(h * ry)
+ round(h * ry),
)
)
return retval
def _render_map(
- self,
- surface: Surface,
- rect: RectLike,
- surfaces: List[Surface]
- ):
+ self, surface: Surface, rect: RectLike, surfaces: list[Surface]
+ ) -> None:
"""
Render the map and optional surfaces to destination surface.
@@ -421,7 +425,7 @@ def _render_map(
surfaces_offset = -offset[0], -offset[1]
self._draw_surfaces(surface, surfaces_offset, surfaces)
- def _clear_surface(self, surface: Surface, area: RectLike = None):
+ def _clear_surface(self, surface: Surface, area: RectLike = None) -> None:
"""
Clear the surface using the right clear color.
@@ -430,10 +434,12 @@ def _clear_surface(self, surface: Surface, area: RectLike = None):
area: area to clear
"""
- clear_color = self._rgb_clear_color if self._clear_color is None else self._clear_color
+ clear_color = (
+ self._rgb_clear_color if self._clear_color is None else self._clear_color
+ )
surface.fill(clear_color, area)
- def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces):
+ def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces) -> None:
"""
Draw surfaces while correcting overlapping tile layers.
@@ -467,7 +473,7 @@ def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces):
damage_rect.x,
damage_rect.y + (damage_rect.height - self.tall_sprites),
damage_rect.width,
- self.tall_sprites
+ self.tall_sprites,
)
for hit_rect in hit(damage_rect):
sprite_damage.add((l, hit_rect))
@@ -522,7 +528,7 @@ def _draw_surfaces(self, surface: Surface, offset: Vector2DInt, surfaces):
draw_list2.append(blit_op)
surface.blits(draw_list2, doreturn=False)
- def _queue_edge_tiles(self, dx: int, dy: int):
+ def _queue_edge_tiles(self, dx: int, dy: int) -> None:
"""
Queue edge tiles and clear edge areas on buffer if needed.
@@ -535,11 +541,20 @@ def _queue_edge_tiles(self, dx: int, dy: int):
tw, th = self.data.tile_size
self._tile_queue = iter([])
- def append(rect):
- self._tile_queue = chain(self._tile_queue, self.data.get_tile_images_by_rect(rect))
+ def append(rect) -> None:
+ self._tile_queue = chain(
+ self._tile_queue, self.data.get_tile_images_by_rect(rect)
+ )
# TODO: optimize so fill is only used when map is smaller than buffer
- self._clear_surface(self._buffer, ((rect[0] - v.left) * tw, (rect[1] - v.top) * th,
- rect[2] * tw, rect[3] * th))
+ self._clear_surface(
+ self._buffer,
+ (
+ (rect[0] - v.left) * tw,
+ (rect[1] - v.top) * th,
+ rect[2] * tw,
+ rect[3] * th,
+ ),
+ )
if dx > 0: # right side
append((v.right - 1, v.top, dx, v.height))
@@ -554,18 +569,14 @@ def append(rect):
append((v.left, v.top, v.width, -dy))
@staticmethod
- def _calculate_zoom_buffer_size(size: Vector2DInt, value: float):
+ def _calculate_zoom_buffer_size(size: Vector2DInt, value: float) -> tuple[int, int]:
if value <= 0:
- log.error('zoom level cannot be zero or less')
+ log.error("zoom level cannot be zero or less")
raise ValueError
value = 1.0 / value
return int(size[0] * value), int(size[1] * value)
- def _create_buffers(
- self,
- view_size: Vector2DInt,
- buffer_size: Vector2DInt
- ):
+ def _create_buffers(self, view_size: Vector2DInt, buffer_size: Vector2DInt) -> None:
"""
Create the buffers, taking in account pixel alpha or colorkey.
@@ -594,7 +605,7 @@ def _create_buffers(
self._buffer.set_colorkey(self._clear_color)
self._buffer.fill(self._clear_color)
- def _initialize_buffers(self, view_size: Vector2DInt):
+ def _initialize_buffers(self, view_size: Vector2DInt) -> None:
"""
Create the buffers to cache tile drawing.
@@ -603,7 +614,7 @@ def _initialize_buffers(self, view_size: Vector2DInt):
"""
- def make_rect(x, y):
+ def make_rect(x, y) -> Rect:
return Rect((x * tw, y * th), (tw, th))
tw, th = self.data.tile_size
@@ -623,8 +634,10 @@ def make_rect(x, y):
self._x_offset = 0
self._y_offset = 0
- rects = [make_rect(*i) for i in product(range(buffer_tile_width),
- range(buffer_tile_height))]
+ rects = [
+ make_rect(*i)
+ for i in product(range(buffer_tile_width), range(buffer_tile_height))
+ ]
# TODO: figure out what depth -actually- does
# values <= 8 tend to reduce performance
@@ -632,7 +645,7 @@ def make_rect(x, y):
self.redraw_tiles(self._buffer)
- def _flush_tile_queue(self, surface: Surface):
+ def _flush_tile_queue(self, surface: Surface) -> None:
"""
Blit the queued tiles and block until the tile queue is empty.
@@ -646,5 +659,7 @@ def _flush_tile_queue(self, surface: Surface):
self.data.prepare_tiles(self._tile_view)
- blit_list = [(image, (x * tw - ltw, y * th - tth)) for x, y, l, image in self._tile_queue]
+ blit_list = [
+ (image, (x * tw - ltw, y * th - tth)) for x, y, l, image in self._tile_queue
+ ]
surface.blits(blit_list, doreturn=False)
diff --git a/pyscroll/quadtree.py b/pyscroll/quadtree.py
index 32eb8aa..3209993 100644
--- a/pyscroll/quadtree.py
+++ b/pyscroll/quadtree.py
@@ -6,7 +6,8 @@
from __future__ import annotations
import itertools
-from typing import TYPE_CHECKING, Sequence, Set, Tuple
+from collections.abc import Sequence
+from typing import TYPE_CHECKING
from pygame import Rect
@@ -32,7 +33,7 @@ class FastQuadTree:
__slots__ = ["items", "cx", "cy", "nw", "sw", "ne", "se"]
- def __init__(self, items: Sequence, depth: int=4, boundary=None):
+ def __init__(self, items: Sequence, depth: int = 4, boundary=None) -> None:
"""Creates a quad-tree.
Args:
@@ -88,18 +89,26 @@ def __init__(self, items: Sequence, depth: int=4, boundary=None):
# Create the sub-quadrants, recursively.
if nw_items:
- self.nw = FastQuadTree(nw_items, depth, (boundary.left, boundary.top, cx, cy))
+ self.nw = FastQuadTree(
+ nw_items, depth, (boundary.left, boundary.top, cx, cy)
+ )
if ne_items:
- self.ne = FastQuadTree(ne_items, depth, (cx, boundary.top, boundary.right, cy))
+ self.ne = FastQuadTree(
+ ne_items, depth, (cx, boundary.top, boundary.right, cy)
+ )
if se_items:
- self.se = FastQuadTree(se_items, depth, (cx, cy, boundary.right, boundary.bottom))
+ self.se = FastQuadTree(
+ se_items, depth, (cx, cy, boundary.right, boundary.bottom)
+ )
if sw_items:
- self.sw = FastQuadTree(sw_items, depth, (boundary.left, cy, cx, boundary.bottom))
+ self.sw = FastQuadTree(
+ sw_items, depth, (boundary.left, cy, cx, boundary.bottom)
+ )
def __iter__(self):
return itertools.chain(self.items, self.nw, self.ne, self.se, self.sw)
- def hit(self, rect: RectLike) -> Set[Tuple[int, int, int, int]]:
+ def hit(self, rect: RectLike) -> set[tuple[int, int, int, int]]:
"""
Returns the items that overlap a bounding rectangle.
diff --git a/tests/pyscroll/test_pyscroll.py b/tests/pyscroll/test_pyscroll.py
index e07c55a..c7f2cef 100644
--- a/tests/pyscroll/test_pyscroll.py
+++ b/tests/pyscroll/test_pyscroll.py
@@ -8,8 +8,8 @@
class DummyDataAdapter(PyscrollDataAdapter):
- tile_size = 32, 32
- map_size = 32, 32
+ tile_size = (32, 32)
+ map_size = (32, 32)
visible_tile_layers = [1]
def get_animations(self):
@@ -28,27 +28,26 @@ class DummyBufferer:
class TestTileQueue(unittest.TestCase):
- def setUp(self):
+ def setUp(self) -> None:
self.mock = DummyBufferer()
self.queue = BufferedRenderer._queue_edge_tiles
- def verify_queue(self, expected):
+ def verify_queue(self, expected: set[tuple[int, int]]) -> None:
queue = {i[:2] for i in self.mock._tile_queue}
self.assertEqual(queue, set(expected))
- def test_queue_left(self):
+ def test_queue_left(self) -> None:
self.queue(self.mock, -1, 0)
self.verify_queue({(2, 3), (2, 2)})
- def test_queue_top(self):
+ def test_queue_top(self) -> None:
self.queue(self.mock, 0, -1)
self.verify_queue({(2, 2), (3, 2)})
- def test_queue_right(self):
+ def test_queue_right(self) -> None:
self.queue(self.mock, 1, 0)
self.verify_queue({(3, 3), (3, 2)})
- def test_queue_bottom(self):
+ def test_queue_bottom(self) -> None:
self.queue(self.mock, 0, 1)
self.verify_queue({(2, 3), (3, 3)})
-