diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..5c292f1 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,67 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python application + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + mypy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install mypy + - name: Run mypy + run: | + mypy azul --strict + mypy test --strict + + lint: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + - name: Run lint + run: | + pylint azul/ + pylint test/ + + test: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Tests + run: | + python3 -m unittest + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c4323f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.mypy_cache +__pycache__ diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..ef55b24 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,2 @@ +[MASTER] +disable = missing-module-docstring, missing-class-docstring, missing-function-docstring, too-few-public-methods diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d97399a --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +check_and_test: FORCE + mypy azul --strict + mypy test --strict + python3 -m unittest +lint: FORCE + pylint azul/ + pylint test/ + +format: FORCE + autopep8 -i azul/*.py + autopep8 -i test/*.py +FORCE: ; diff --git a/azul/__init__.py b/azul/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/azul/floor.py b/azul/floor.py new file mode 100644 index 0000000..99112fb --- /dev/null +++ b/azul/floor.py @@ -0,0 +1,31 @@ +from __future__ import annotations +from typing import List +from itertools import chain, repeat, islice +from azul.interfaces import UsedTilesGiveInterface +from azul.simple_types import Tile, compress_tile_list, Points + + +class Floor: + _point_pattern: List[Points] + _used_tiles: UsedTilesGiveInterface + _tiles: List[Tile] + + def __init__(self, point_pattern: List[Points], used_tiles: UsedTilesGiveInterface): + self._point_pattern = point_pattern.copy() + self._used_tiles = used_tiles + self._tiles = [] + + def put(self, tiles: List[Tile]) -> None: + self._tiles.extend(tiles) + + def finish_round(self) -> Points: + extended_pattern = chain( + self._point_pattern, repeat(self._point_pattern[-1])) + points: Points = Points.sum( + list(islice(extended_pattern, 0, len(self._tiles)))) + self._used_tiles.give(self._tiles.copy()) + self._tiles = [] + return points + + def state(self) -> str: + return compress_tile_list(self._tiles) diff --git a/azul/interfaces.py b/azul/interfaces.py new file mode 100644 index 0000000..fe162c7 --- /dev/null +++ b/azul/interfaces.py @@ -0,0 +1,8 @@ +from __future__ import annotations +from typing import List +from azul.simple_types import Tile + + +class UsedTilesGiveInterface: + def give(self, tiles: List[Tile]) -> None: + pass diff --git a/azul/simple_types.py b/azul/simple_types.py new file mode 100644 index 0000000..8f9e0bf --- /dev/null +++ b/azul/simple_types.py @@ -0,0 +1,42 @@ +from __future__ import annotations +from typing import List + + +class Points: + _value: int + + def __init__(self, value: int): + self._value = value + + @property + def value(self) -> int: + return self._value + + @staticmethod + def sum(points_list: List[Points]) -> Points: + return Points(sum((x.value for x in points_list))) + + def __str__(self) -> str: + return str(self._value) + + +class Tile: + _representation: str + + def __init__(self, representation: str): + self._representation = representation + + def __str__(self) -> str: + return self._representation + + +STARTING_PLAYER: Tile = Tile("S") +RED: Tile = Tile("R") +BLUE: Tile = Tile("B") +YELLOW: Tile = Tile("Y") +GREEN: Tile = Tile("G") +BLACK: Tile = Tile("L") + + +def compress_tile_list(tiles: List[Tile]) -> str: + return "".join([str(x) for x in tiles]) diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_floor.py b/test/test_floor.py new file mode 100644 index 0000000..02e4def --- /dev/null +++ b/test/test_floor.py @@ -0,0 +1,37 @@ +from __future__ import annotations +import unittest +from typing import List +from azul.interfaces import UsedTilesGiveInterface +from azul.simple_types import Tile, STARTING_PLAYER, RED, GREEN, Points +from azul.floor import Floor + + +class FakeUsedTiles(UsedTilesGiveInterface): + tiles_given: List[Tile] + + def __init__(self) -> None: + self.tiles_given = [] + + def give(self, tiles: List[Tile]) -> None: + self.tiles_given.extend(tiles) + + +class TestFloor(unittest.TestCase): + def setUp(self) -> None: + self.used_tiles: FakeUsedTiles = FakeUsedTiles() + self.floor: Floor = Floor( + [Points(1), Points(2), Points(2)], self.used_tiles) + + def test_many_tiles(self) -> None: + tiles = [STARTING_PLAYER, RED, GREEN, RED] + self.assertCountEqual(self.floor.state(), "") + self.floor.put(tiles) + self.assertCountEqual(self.floor.state(), "SRRG") + points: Points = self.floor.finish_round() + self.assertEqual(str(points), "7") + self.assertCountEqual(tiles, self.used_tiles.tiles_given) + self.assertCountEqual(self.floor.state(), "") + + +if __name__ == '__main__': + unittest.main()