Skip to content

Commit

Permalink
Reorganise code (#4)
Browse files Browse the repository at this point in the history
* Reorganise code

* Add pre-commit hooks

* Fix mypy errors
  • Loading branch information
eclectic-boy committed Feb 4, 2023
1 parent 008c088 commit d8d6e07
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 81 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Test
- name: Check all code
run: |
make check_all_code
- name: Run test suite
run: |
make test
31 changes: 31 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: requirements-txt-fixer
- id: check-yaml
- repo: https://github.com/PyCQA/autoflake
rev: v2.0.1
hooks:
- id: autoflake
args:
- -i
- -r
- --remove-all-unused-imports
- --expand-star-imports
- --ignore-init-module-imports
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
hooks:
- id: mypy
args: [--python-version=3.11]
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
play:
python main.py
python game/main.py

check_all_code:
pre-commit run -a

test:
python -m unittest
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

During the festive holidays 2022 I was scrolling on Insta and I bumped into [this reel](https://www.instagram.com/reel/CmK8aKQDvTm/?igshid=YmMyMTA2M2Y=). Entertaining! It definitely tickled my engineering mind and I thought I could try to reverse-engineer its logic and give a go at trying to reproduce the whole game. Here is my attempt!

![](rps.gif)
![](docs/rps.gif)

## Basic Rules

Expand Down
File renamed without changes
Empty file added game/__init__.py
Empty file.
68 changes: 40 additions & 28 deletions main.py → game/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys
from enum import Enum
from time import sleep
from typing import Self
from typing import Self # type: ignore


class GestureSuit(Enum):
Expand All @@ -23,39 +23,37 @@ class Gesture:
GestureSuit.SCISSOR: "✂️ ",
}

SUIT_TO_WEAKER_SUIT: [GestureSuit, GestureSuit] = {
SUIT_TO_WEAKER_SUIT: dict[GestureSuit, GestureSuit] = {
GestureSuit.ROCK: GestureSuit.SCISSOR,
GestureSuit.PAPER: GestureSuit.ROCK,
GestureSuit.SCISSOR: GestureSuit.PAPER,
}

def __init__(self, suit: GestureSuit):
self.suit: GestureSuit = suit
self.cell: Cell | None = None
self.cell: Cell
self.alive = True

def __str__(self) -> str:
return self.SUIT_TO_EMOJI[self.suit]

def __gt__(self, other: Self):
return self.SUIT_TO_WEAKER_SUIT[self.suit] == other.suit
def __gt__(self, other: Self): # type: ignore
return self.SUIT_TO_WEAKER_SUIT[self.suit] == other.suit # type: ignore

def equals(self, other: Self):
return self.suit == other.suit
def equals(self, other: Self): # type: ignore
return self.suit == other.suit # type: ignore

def transform(self, suit: GestureSuit):
self.suit = suit


class Cell:
GAME_MODE_TO_FUNCTION_NAME = {
item: f"_challenge_{item.value}" for item in GameMode
}
GAME_MODE_TO_FUNCTION_NAME = {item: f"_challenge_{item.value}" for item in GameMode}

def __init__(self, game: "RockPaperScissor", gesture: Gesture | None = None):
self.game = game
self.m = None # y coordinate
self.n = None # x coordinate
self.m: int # y coordinate
self.n: int # x coordinate

self.gesture: Gesture | None = gesture

Expand All @@ -79,6 +77,9 @@ def remove_gesture(self):
self.gesture = None

def _challenge_transform(self, incoming: Gesture):
if self.gesture is None:
raise Exception("This cell does not have a gesture")

if self.gesture > incoming:
self.stats[f"remaining_{incoming.suit.value}"] -= 1
incoming.transform(self.gesture.suit)
Expand Down Expand Up @@ -143,13 +144,11 @@ def __init__(
}

def _init_gestures(self):
self.gestures = [
Gesture(GestureSuit.ROCK) for _ in range(self.COUNT_ROCK)
] + [
Gesture(GestureSuit.PAPER) for _ in range(self.COUNT_PAPER)
] + [
Gesture(GestureSuit.SCISSOR) for _ in range(self.COUNT_SCISSOR)
]
self.gestures = (
[Gesture(GestureSuit.ROCK) for _ in range(self.COUNT_ROCK)]
+ [Gesture(GestureSuit.PAPER) for _ in range(self.COUNT_PAPER)]
+ [Gesture(GestureSuit.SCISSOR) for _ in range(self.COUNT_SCISSOR)]
)

def _init_matrix(self):
cells = [Cell(self, gesture) for gesture in self.gestures]
Expand All @@ -173,19 +172,28 @@ def _get_all_surrounding_cells(self, cell: Cell):
n = cell.n

coords_list = (
(m - 1, n - 1), (m - 1, n), (m - 1, n + 1),
(m, n - 1), (m, n + 1),
(m + 1, n - 1), (m + 1, n), (m + 1, n + 1),
(m - 1, n - 1),
(m - 1, n),
(m - 1, n + 1),
(m, n - 1),
(m, n + 1),
(m + 1, n - 1),
(m + 1, n),
(m + 1, n + 1),
)

return [
self.matrix[i][j] for i, j in coords_list if 0 <= i < self.M and 0 <= j < self.N
self.matrix[i][j]
for i, j in coords_list
if 0 <= i < self.M and 0 <= j < self.N
]

def _get_available_cells_to_move_to(self, gesture: Gesture):
all_surrounding_cells = self._get_all_surrounding_cells(gesture.cell)
filtered_cells = [
c for c in all_surrounding_cells if not c.gesture or not c.gesture.equals(gesture)
c
for c in all_surrounding_cells
if not c.gesture or not c.gesture.equals(gesture)
]
return filtered_cells

Expand Down Expand Up @@ -219,15 +227,17 @@ def _clear_screen():

def _print_board(self):
self._clear_screen()
sys.stdout.write(f"""
sys.stdout.write(
f"""
[=========================]
[ Rock-Paper-Scissor ]
[=========================]
Mode: {self.GAME_MODE.value.title()}
Round: {self.stats["round_number"] or "-":<4}
{GestureSuit.ROCK.value.title()}: {self.stats[f"remaining_{GestureSuit.ROCK.value}"]:<3} {GestureSuit.PAPER.value.title()}: {self.stats[f"remaining_{GestureSuit.PAPER.value}"]:<3} {GestureSuit.SCISSOR.value.title()}: {self.stats[f"remaining_{GestureSuit.SCISSOR.value}"]:<3}
\n""")
\n"""
)

for row in self.matrix:
sys.stdout.write("|" + "·".join(f"{str(cell):^0}" for cell in row) + "|\n")
Expand Down Expand Up @@ -258,9 +268,11 @@ def play(self):

sleep(self.ROUND_DELAY)

sys.stdout.write(f"""
sys.stdout.write(
f"""
The winner is {self.get_winning_suit().value.title()}!!!
\n\n""")
\n\n"""
)


if __name__ == "__main__":
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
parameterized
ipdb
parameterized
pre-commit
Empty file added tests/__init__.py
Empty file.
Loading

0 comments on commit d8d6e07

Please sign in to comment.