diff --git a/playground/cheaters.py b/playground/cheaters.py new file mode 100644 index 0000000..b7a2e82 --- /dev/null +++ b/playground/cheaters.py @@ -0,0 +1,72 @@ +import logging +import random + +from codenames.game.color import TeamColor +from codenames.game.exceptions import QuitGame +from codenames.game.player import GamePlayers +from codenames.game.runner import GameRunner +from codenames.online.codenames_game.runner import CodenamesGameRunner + +from playground.boards.english import * # noqa +from playground.boards.hebrew import * # noqa +from playground.printer import print_results +from solvers.cheater.cheaters import CheaterGuesser, CheaterHinter +from solvers.cli import CLIGuesser # noqa +from solvers.models import ( # noqa + DEFAULT_MODEL_ADAPTER, + HEBREW_SUFFIX_ADAPTER, + IS_STEMMED_ENV_KEY, + MODEL_NAME_ENV_KEY, + ModelIdentifier, + load_language_async, + load_model_async, +) +from solvers.naive import NaiveGuesser, NaiveHinter # noqa +from solvers.other.naive_cli_guesser import ModelAwareCliGuesser # noqa + +random.seed(42) +log = logging.getLogger(__name__) + + +def get_cheaters() -> GamePlayers: + blue_hinter = CheaterHinter(name="Yoda", team_color=TeamColor.BLUE) + red_hinter = CheaterHinter(name="Einstein", team_color=TeamColor.RED) + blue_guesser = CheaterGuesser(name="Anakin", team_color=TeamColor.BLUE, hinter=blue_hinter) + red_guesser = CheaterGuesser(name="Newton", team_color=TeamColor.RED, hinter=red_hinter) + return GamePlayers.from_collection([blue_hinter, blue_guesser, red_hinter, red_guesser]) + + +def run_offline(board: Board = ENGLISH_BOARDS[2]): # noqa: F405 + log.info("Running cheaters game...") + log.setLevel(logging.INFO) + game_runner = None + try: + players = get_cheaters() + game_runner = GameRunner(players=players, board=board) + game_runner.run_game() + except QuitGame: + log.info("Game quit") + except: # noqa + log.exception("Error occurred") + finally: + print_results(game_runner) # type: ignore + + +def run_online(): + log.info("Running online game...") + online_manager = runner = None + try: + players = get_cheaters() + online_manager = CodenamesGameRunner(*players.hinters, *players.guessers, show_host=True) + runner = online_manager.auto_start() + except QuitGame: + log.info("Game quit") + except: # noqa + log.exception("Error occurred") + finally: + print_results(runner) + online_manager.close() + + +if __name__ == "__main__": + run_online() diff --git a/playground/offline.py b/playground/offline.py index 366bfc0..0eb5cbe 100644 --- a/playground/offline.py +++ b/playground/offline.py @@ -2,7 +2,9 @@ import os import random +from codenames.game.color import TeamColor from codenames.game.exceptions import QuitGame +from codenames.game.player import GamePlayers from codenames.game.runner import GameRunner from playground.boards.english import * # noqa @@ -44,18 +46,25 @@ def run_offline(board: Board = ENGLISH_BOARDS[2]): # noqa: F405 game_runner = None try: # blue_hinter = GPTHinter(name="Yoda", api_key=GPT_API_KEY) - blue_hinter = NaiveHinter(name="Yoda", model_adapter=adapter, max_group_size=4) - red_hinter = NaiveHinter(name="Einstein", model_identifier=model_id, model_adapter=adapter, max_group_size=3) + blue_hinter = NaiveHinter(name="Yoda", team_color=TeamColor.BLUE, model_adapter=adapter, max_group_size=4) + red_hinter = NaiveHinter( + name="Einstein", + team_color=TeamColor.RED, + model_identifier=model_id, + model_adapter=adapter, + max_group_size=3, + ) # red_hinter = GPTHinter(name="Einstein", api_key=GPT_API_KEY) # red_hinter = OlympicHinter(name="Yoda", model_adapter=adapter) - blue_guesser = CLIGuesser(name="Anakin") + blue_guesser = CLIGuesser(name="Anakin", team_color=TeamColor.BLUE) # blue_guesser = GPTGuesser(name="Anakin", api_key=GPT_API_KEY) # blue_guesser = NaiveGuesser(name="Anakin", model_identifier=model_id, model_adapter=adapter) - red_guesser = CLIGuesser(name="Newton") + red_guesser = CLIGuesser(name="Newton", team_color=TeamColor.RED) # red_guesser = GPTGuesser(name="Newton", api_key=GPT_API_KEY) # red_guesser = NaiveGuesser(name="Newton", model_identifier=model_id, model_adapter=adapter) - game_runner = GameRunner(blue_hinter, red_hinter, blue_guesser, red_guesser) - game_runner.run_game(board=board) + players = GamePlayers.from_collection([blue_hinter, blue_guesser, red_hinter, red_guesser]) + game_runner = GameRunner(players=players, board=board) + game_runner.run_game() except QuitGame: log.info("Game quit") except: # noqa diff --git a/playground/online_codenames_game.py b/playground/online_codenames_game.py index 0143638..2bcf2e2 100644 --- a/playground/online_codenames_game.py +++ b/playground/online_codenames_game.py @@ -1,6 +1,7 @@ import logging import os +from codenames.game.color import TeamColor from codenames.game.exceptions import QuitGame from codenames.online.codenames_game.adapter import CodenamesGameLanguage from codenames.online.codenames_game.runner import CodenamesGameRunner @@ -36,23 +37,31 @@ def run_online(): log.info("Running online game...") - online_manager = None + online_manager = runner = None try: # blue_hinter = GPTHinter(name="Einstein", api_key=GPT_API_KEY) - blue_hinter = NaiveHinter("Einstein", model_identifier=model_id, model_adapter=adapter) # noqa - red_hinter = NaiveHinter(name="Yoda", model_identifier=model_id, model_adapter=adapter) # noqa - blue_guesser = NaiveGuesser(name="Newton", model_identifier=model_id, model_adapter=adapter) # noqa + blue_hinter = NaiveHinter( + "Einstein", team_color=TeamColor.BLUE, model_identifier=model_id, model_adapter=adapter + ) + red_hinter = NaiveHinter( + name="Yoda", team_color=TeamColor.RED, model_identifier=model_id, model_adapter=adapter + ) + blue_guesser = NaiveGuesser( + name="Newton", team_color=TeamColor.BLUE, model_identifier=model_id, model_adapter=adapter + ) # red_guesser = GPTGuesser(name="Anakin", api_key=GPT_API_KEY) - red_guesser = NaiveGuesser(name="Anakin", model_identifier=model_id, model_adapter=adapter) # noqa + red_guesser = NaiveGuesser( + name="Anakin", team_color=TeamColor.RED, model_identifier=model_id, model_adapter=adapter + ) online_manager = CodenamesGameRunner(blue_hinter, red_hinter, blue_guesser, red_guesser, show_host=True) # online_manager = CodenamesGameGameRunner(blue_hinter, red_hinter, blue_guesser, red_guesser, show_host=False) - online_manager.auto_start() + runner = online_manager.auto_start() except QuitGame: log.info("Game quit") except: # noqa log.exception("Error occurred") finally: - print_results(online_manager.game_runner) + print_results(runner) online_manager.close() diff --git a/playground/printer.py b/playground/printer.py index 7b0f58d..662b352 100644 --- a/playground/printer.py +++ b/playground/printer.py @@ -1,4 +1,5 @@ import logging +from typing import Optional from codenames.game.move import GuessMove, HintMove, Move, PassMove from codenames.game.player import Player, PlayerRole @@ -8,7 +9,7 @@ log = logging.getLogger(__name__) -def print_results(game_runner: GameRunner): +def print_results(game_runner: Optional[GameRunner]): if game_runner is None or game_runner.state is None: return state = game_runner.state @@ -42,7 +43,7 @@ def _print_moves(game_runner: GameRunner): def _get_player(game_runner: GameRunner, move: Move) -> Player: role = PlayerRole.HINTER if isinstance(move, HintMove) else PlayerRole.GUESSER - return game_runner.get_player(team_color=move.team_color, role=role) + return game_runner.players.get_player(team_color=move.team_color, role=role) def _print_result(state: GameState): diff --git a/poetry.lock b/poetry.lock index 0864ced..d62746b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -677,6 +677,7 @@ files = [ [package.dependencies] beautifultable = ">=1.0,<2.0" pydantic = ">=1.9,<2.0" +selenium = {version = ">=4.1,<5.0", optional = true, markers = "extra == \"web\""} [package.extras] web = ["selenium (>=4.1,<5.0)"] @@ -1399,6 +1400,17 @@ files = [ {file = "glcontext-2.3.7.tar.gz", hash = "sha256:bb2d0503f45ad85ca7319bd37fd983e374b3f824c38a450b5f72cfc974114156"}, ] +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + [[package]] name = "identify" version = "2.5.24" @@ -2848,6 +2860,20 @@ dev = ["black (>=21.6b0,<22.0)", "pytest (==6.*)", "pytest-asyncio", "pytest-moc embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "scipy", "tenacity (>=8.0.1)"] wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"] +[[package]] +name = "outcome" +version = "1.3.0.post0" +description = "Capture the outcome of Python function calls." +optional = false +python-versions = ">=3.7" +files = [ + {file = "outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b"}, + {file = "outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8"}, +] + +[package.dependencies] +attrs = ">=19.2.0" + [[package]] name = "packaging" version = "23.1" @@ -3434,6 +3460,18 @@ files = [ {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, ] +[[package]] +name = "pysocks" +version = "1.7.1" +description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, + {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, + {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, +] + [[package]] name = "pytest" version = "7.2.2" @@ -3903,6 +3941,23 @@ files = [ Cython = {version = "*", markers = "sys_platform == \"darwin\""} pyobjc-framework-Cocoa = {version = "*", markers = "sys_platform == \"darwin\""} +[[package]] +name = "selenium" +version = "4.16.0" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "selenium-4.16.0-py3-none-any.whl", hash = "sha256:aec71f4e6ed6cb3ec25c9c1b5ed56ae31b6da0a7f17474c7566d303f84e6219f"}, + {file = "selenium-4.16.0.tar.gz", hash = "sha256:b2e987a445306151f7be0e6dfe2aa72a479c2ac6a91b9d5ef2d6dd4e49ad0435"}, +] + +[package.dependencies] +certifi = ">=2021.10.8" +trio = ">=0.17,<1.0" +trio-websocket = ">=0.9,<1.0" +urllib3 = {version = ">=1.26,<3", extras = ["socks"]} + [[package]] name = "send2trash" version = "1.8.2" @@ -4033,6 +4088,17 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + [[package]] name = "soupsieve" version = "2.4.1" @@ -4196,6 +4262,42 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] +[[package]] +name = "trio" +version = "0.23.2" +description = "A friendly Python library for async concurrency and I/O" +optional = false +python-versions = ">=3.8" +files = [ + {file = "trio-0.23.2-py3-none-any.whl", hash = "sha256:5a0b566fa5d50cf231cfd6b08f3b03aa4179ff004b8f3144059587039e2b26d3"}, + {file = "trio-0.23.2.tar.gz", hash = "sha256:da1d35b9a2b17eb32cae2e763b16551f9aa6703634735024e32f325c9285069e"}, +] + +[package.dependencies] +attrs = ">=20.1.0" +cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""} +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +idna = "*" +outcome = "*" +sniffio = ">=1.3.0" +sortedcontainers = "*" + +[[package]] +name = "trio-websocket" +version = "0.11.1" +description = "WebSocket library for Trio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "trio-websocket-0.11.1.tar.gz", hash = "sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f"}, + {file = "trio_websocket-0.11.1-py3-none-any.whl", hash = "sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638"}, +] + +[package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +trio = ">=0.11" +wsproto = ">=0.14" + [[package]] name = "types-requests" version = "2.30.0.0" @@ -4268,6 +4370,9 @@ files = [ {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, ] +[package.dependencies] +PySocks = {version = ">=1.5.6,<1.5.7 || >1.5.7,<2.0", optional = true, markers = "extra == \"socks\""} + [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] @@ -4480,6 +4585,20 @@ files = [ {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, ] +[[package]] +name = "wsproto" +version = "1.2.0" +description = "WebSockets state-machine based protocol implementation" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, + {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, +] + +[package.dependencies] +h11 = ">=0.9.0,<1" + [[package]] name = "yarl" version = "1.9.2" @@ -4589,4 +4708,4 @@ gpt = ["openai"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "19b3119276870a11af7709f048446b4c5931b7591707972bc6c3a8df6af27a00" +content-hash = "c3f3b7e4e79d72f0361bd65caeb5ffc047fe308ce35f3f842a592e5e030062b8" diff --git a/pyproject.toml b/pyproject.toml index d66edd1..e1d0094 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ openai = { version = "^0.27", optional = true } [tool.poetry.group.test.dependencies] pytest = "7.2.2" coverage = "7.2.3" +codenames = { extras = ["web"], version = "^4.0.3" } [tool.poetry.group.lint.dependencies] ruff = "^0.0.269" diff --git a/solvers/cheater/__init__.py b/solvers/cheater/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/solvers/cheater/cheaters.py b/solvers/cheater/cheaters.py new file mode 100644 index 0000000..a343b43 --- /dev/null +++ b/solvers/cheater/cheaters.py @@ -0,0 +1,39 @@ +from typing import List, Optional +from uuid import uuid4 + +from codenames.game.card import Card +from codenames.game.color import TeamColor +from codenames.game.move import Guess, Hint +from codenames.game.player import Guesser, Hinter +from codenames.game.state import HinterGameState + + +class CheaterHinter(Hinter): + def __init__(self, name: str, team_color: TeamColor): + super().__init__(name, team_color) + self.game_state: Optional[HinterGameState] = None + + def pick_hint(self, game_state: HinterGameState) -> Hint: + self.game_state = game_state + random_word = uuid4().hex[:4] + return Hint(word=random_word, card_amount=4) + + +class CheaterGuesser(Guesser): + def __init__(self, name: str, team_color: TeamColor, hinter: CheaterHinter): + super().__init__(name, team_color) + self.hinter = hinter + self.team_cards: List[Card] = [] + + def guess(self, game_state: HinterGameState) -> Guess: + if self.team_cards is None: + self._init_cheating() + next_card = self.team_cards.pop() + card_index = game_state.board.find_card_index(next_card.word) + return Guess(card_index=card_index) + + def _init_cheating(self): + hinter_state = self.hinter.game_state + if hinter_state is None: + raise Exception("Hinter has not yet picked a hint") + self.team_cards = list(hinter_state.board.cards_for_color(card_color=self.team_color.as_card_color)) diff --git a/solvers/naive/naive_hinter.py b/solvers/naive/naive_hinter.py index 7cce6df..22f4564 100644 --- a/solvers/naive/naive_hinter.py +++ b/solvers/naive/naive_hinter.py @@ -4,6 +4,7 @@ import numpy as np from codenames.game.board import Board +from codenames.game.color import TeamColor from codenames.game.move import Hint from codenames.game.player import Hinter from codenames.game.state import HinterGameState @@ -44,6 +45,7 @@ class NaiveHinter(NaivePlayer, Hinter): def __init__( self, name: str, + team_color: TeamColor, model: Optional[KeyedVectors] = None, model_identifier: Optional[ModelIdentifier] = None, proposals_thresholds: Optional[ProposalThresholds] = None, @@ -52,7 +54,13 @@ def __init__( gradual_distances_filter_active: bool = True, proposal_grade_calculator: Callable[[Proposal], float] = default_proposal_grade_calculator, ): - super().__init__(name=name, model=model, model_identifier=model_identifier, model_adapter=model_adapter) + super().__init__( + name=name, + team_color=team_color, + model=model, + model_identifier=model_identifier, + model_adapter=model_adapter, + ) self.max_group_size = max_group_size self.opponent_card_color = None self.proposals_thresholds = proposals_thresholds or DEFAULT_THRESHOLDS diff --git a/solvers/naive/naive_player.py b/solvers/naive/naive_player.py index 999940c..0383025 100644 --- a/solvers/naive/naive_player.py +++ b/solvers/naive/naive_player.py @@ -2,6 +2,7 @@ from typing import Optional from codenames.game.board import Board +from codenames.game.color import TeamColor from codenames.game.player import Player from gensim.models import KeyedVectors @@ -18,11 +19,12 @@ class NaivePlayer(Player, ABC): def __init__( self, name: str, + team_color: TeamColor, model: Optional[KeyedVectors] = None, model_identifier: Optional[ModelIdentifier] = None, model_adapter: Optional[ModelFormatAdapter] = None, ): - super().__init__(name=name) + super().__init__(name=name, team_color=team_color) self.model: KeyedVectors = model self.model_identifier = model_identifier self.model_adapter = model_adapter or DEFAULT_MODEL_ADAPTER diff --git a/solvers/naive/proposal_generator.py b/solvers/naive/proposal_generator.py index c794122..c544b06 100644 --- a/solvers/naive/proposal_generator.py +++ b/solvers/naive/proposal_generator.py @@ -199,7 +199,8 @@ def proposal_from_similarity( ) -> Optional[Proposal]: hint, similarity_score = similarity # pylint: disable=unused-variable # word = format_word(word) - if self.should_filter_hint(hint=hint, word_group=word_group, filter_expressions=self.game_state.illegal_words): + filter_expressions = self.game_state.illegal_hint_words + if self.should_filter_hint(hint=hint, word_group=word_group, filter_expressions=filter_expressions): return None hint_vector = self.model[hint] board_distances: np.ndarray = cosine_distance(hint_vector, self.board_vectors) # type: ignore diff --git a/solvers/other/naive_cli_guesser.py b/solvers/other/naive_cli_guesser.py index 5bd70be..56c90a7 100644 --- a/solvers/other/naive_cli_guesser.py +++ b/solvers/other/naive_cli_guesser.py @@ -3,6 +3,7 @@ import matplotlib.pyplot as plt import numpy as np from codenames.game.board import Board +from codenames.game.color import TeamColor from codenames.game.move import Guess from codenames.game.state import GuesserGameState from gensim.models import KeyedVectors @@ -15,9 +16,13 @@ class ModelAwareCliGuesser(CLIGuesser): def __init__( - self, name: str, model: KeyedVectors = None, model_adapter: ModelFormatAdapter = DEFAULT_MODEL_ADAPTER + self, + name: str, + team_color: TeamColor, + model: KeyedVectors = None, + model_adapter: ModelFormatAdapter = DEFAULT_MODEL_ADAPTER, ): - super().__init__(name=name) + super().__init__(name=name, team_color=team_color) self.model: KeyedVectors = model self.model_adapter = model_adapter diff --git a/tests/test_cli_players.py b/tests/test_cli_players.py index f48f102..a59c5ae 100644 --- a/tests/test_cli_players.py +++ b/tests/test_cli_players.py @@ -5,6 +5,7 @@ from codenames.game.board import Board from codenames.game.color import TeamColor from codenames.game.move import PASS_GUESS, QUIT_GAME +from codenames.game.player import GamePlayers from codenames.game.runner import GameRunner from codenames.game.winner import WinningReason @@ -18,11 +19,12 @@ def english_board() -> Board: @patch("builtins.input") def test_cli_players_game(mock_input, english_board: Board): - blue_hinter = CLIHinter("Leonardo") - blue_guesser = CLIGuesser("Bard") - red_hinter = CLIHinter("Adam") - red_guesser = CLIGuesser("Eve") - runner = GameRunner(blue_hinter, red_hinter, blue_guesser, red_guesser) + blue_hinter = CLIHinter("Leonardo", team_color=TeamColor.BLUE) + blue_guesser = CLIGuesser("Bard", team_color=TeamColor.BLUE) + red_hinter = CLIHinter("Adam", team_color=TeamColor.RED) + red_guesser = CLIGuesser("Eve", team_color=TeamColor.RED) + + players = GamePlayers.from_collection([blue_hinter, blue_guesser, red_hinter, red_guesser]) mock_input.side_effect = [ "YOU_CANNOT_PARSE_THIS", # Invalid hint, ignore "Ice, 4", @@ -37,6 +39,7 @@ def test_cli_players_game(mock_input, english_board: Board): "god", # Black f"{QUIT_GAME}", ] - winner = runner.run_game(board=english_board) + runner = GameRunner(players=players, board=english_board) + winner = runner.run_game() assert winner.team_color == TeamColor.RED assert winner.reason == WinningReason.OPPONENT_HIT_BLACK diff --git a/tests/test_naive_flow.py b/tests/test_naive_flow.py index dd4ef55..7a68fb3 100644 --- a/tests/test_naive_flow.py +++ b/tests/test_naive_flow.py @@ -4,6 +4,7 @@ import pytest from codenames.game.board import Board from codenames.game.color import TeamColor +from codenames.game.player import GamePlayers from codenames.game.runner import GameRunner from gensim.models import KeyedVectors @@ -88,12 +89,13 @@ def mock_load_word2vec_format(*args, **kwargs): @mock.patch("gensim.models.KeyedVectors.load", new=mock_load_word2vec_format) def test_complete_naive_flow(english_board: Board): - blue_hinter = NaiveHinter("Leonardo") - blue_guesser = NaiveGuesser("Bard") - red_hinter = NaiveHinter("Adam") - red_guesser = NaiveGuesser("Eve") + blue_hinter = NaiveHinter("Leonardo", team_color=TeamColor.BLUE) + blue_guesser = NaiveGuesser("Bard", team_color=TeamColor.BLUE) + red_hinter = NaiveHinter("Adam", team_color=TeamColor.RED) + red_guesser = NaiveGuesser("Eve", team_color=TeamColor.RED) - runner = GameRunner(blue_hinter, red_hinter, blue_guesser, red_guesser) - runner.run_game(board=english_board) + players = GamePlayers.from_collection([blue_hinter, blue_guesser, red_hinter, red_guesser]) + runner = GameRunner(players, board=english_board) + runner.run_game() assert runner.state.winner is not None diff --git a/tests/test_online_flow.py b/tests/test_online_flow.py new file mode 100644 index 0000000..e0bee54 --- /dev/null +++ b/tests/test_online_flow.py @@ -0,0 +1,2 @@ +def test_online_flow(): + pass