From ff737216785e0b62a5b8f57a56ad590e12c25a28 Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Wed, 10 Jun 2020 18:58:30 +0200 Subject: [PATCH] Massive update --- .idea/misc.xml | 2 +- .idea/schafkopf.iml | 2 +- src/Card.py | 84 +++--- src/DecisionMaker.py | 19 ++ src/Deck.py | 4 +- src/Game.py | 253 ++++++++++++++---- src/Glyph.py | 21 +- src/HumanDecisionInterface.py | 78 ++++++ src/Player.py | 44 ++- src/Renderer.py | 13 +- src/Round.py | 39 --- src/RufSpiel.py | 11 - src/SequentialConsoleRenderer.py | 21 ++ ...erer.py => SingleScreenConsoleRenderer.py} | 9 +- src/__pycache__/Card.cpython-38.pyc | Bin 0 -> 2983 bytes .../ConsoleRenderer.cpython-36.pyc | Bin 1672 -> 0 bytes src/__pycache__/DecisionMaker.cpython-36.pyc | Bin 0 -> 797 bytes src/__pycache__/DecisionMaker.cpython-38.pyc | Bin 0 -> 1218 bytes src/__pycache__/Deck.cpython-36.pyc | Bin 642 -> 602 bytes src/__pycache__/Deck.cpython-38.pyc | Bin 0 -> 675 bytes src/__pycache__/Errors.cpython-38.pyc | Bin 0 -> 872 bytes src/__pycache__/Game.cpython-36.pyc | Bin 3361 -> 3316 bytes src/__pycache__/Game.cpython-38.pyc | Bin 0 -> 8207 bytes src/__pycache__/Glyph.cpython-38.pyc | Bin 0 -> 3112 bytes .../HumanDecisionInterface.cpython-36.pyc | Bin 0 -> 1130 bytes .../HumanDecisionInterface.cpython-38.pyc | Bin 0 -> 3547 bytes src/__pycache__/Player.cpython-36.pyc | Bin 1724 -> 2693 bytes src/__pycache__/Player.cpython-38.pyc | Bin 0 -> 3873 bytes src/__pycache__/Renderer.cpython-36.pyc | Bin 1104 -> 972 bytes src/__pycache__/Renderer.cpython-38.pyc | Bin 0 -> 1052 bytes src/__pycache__/Round.cpython-36.pyc | Bin 1795 -> 0 bytes src/__pycache__/RufSpiel.cpython-36.pyc | Bin 683 -> 0 bytes .../SequentialConsoleRenderer.cpython-38.pyc | Bin 0 -> 1651 bytes src/main.py | 25 +- src/tests.py | 2 +- 35 files changed, 456 insertions(+), 171 deletions(-) create mode 100644 src/DecisionMaker.py create mode 100644 src/HumanDecisionInterface.py delete mode 100644 src/Round.py delete mode 100644 src/RufSpiel.py create mode 100644 src/SequentialConsoleRenderer.py rename src/{ConsoleRenderer.py => SingleScreenConsoleRenderer.py} (76%) create mode 100644 src/__pycache__/Card.cpython-38.pyc delete mode 100644 src/__pycache__/ConsoleRenderer.cpython-36.pyc create mode 100644 src/__pycache__/DecisionMaker.cpython-36.pyc create mode 100644 src/__pycache__/DecisionMaker.cpython-38.pyc create mode 100644 src/__pycache__/Deck.cpython-38.pyc create mode 100644 src/__pycache__/Errors.cpython-38.pyc create mode 100644 src/__pycache__/Game.cpython-38.pyc create mode 100644 src/__pycache__/Glyph.cpython-38.pyc create mode 100644 src/__pycache__/HumanDecisionInterface.cpython-36.pyc create mode 100644 src/__pycache__/HumanDecisionInterface.cpython-38.pyc create mode 100644 src/__pycache__/Player.cpython-38.pyc create mode 100644 src/__pycache__/Renderer.cpython-38.pyc delete mode 100644 src/__pycache__/Round.cpython-36.pyc delete mode 100644 src/__pycache__/RufSpiel.cpython-36.pyc create mode 100644 src/__pycache__/SequentialConsoleRenderer.cpython-38.pyc diff --git a/.idea/misc.xml b/.idea/misc.xml index 3999087..d348161 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/.idea/schafkopf.iml b/.idea/schafkopf.iml index 9477bc8..e114674 100644 --- a/.idea/schafkopf.iml +++ b/.idea/schafkopf.iml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/src/Card.py b/src/Card.py index 341782c..a0330dc 100644 --- a/src/Card.py +++ b/src/Card.py @@ -1,41 +1,39 @@ import enum from typing import * -from collections import namedtuple +from Errors import SchafkopfError class Card: - class Suit(enum.Enum): - Eichel = 0 - Gras = 1 - Herz = 2 - Schellen = 3 + class OpError(SchafkopfError, TypeError): + def __init__(self): + self.message = "An '{}' object may only be operated on with others of the same type.".format( + Card.__class__.__name__) - SUITS_STR: Dict[Suit, str] = { - Suit.Eichel: "♣", - Suit.Gras: "♠", - Suit.Herz: "♥", - Suit.Schellen: "♦", - } + class Suit(enum.Enum): + Eichel = 3 + Gras = 2 + Herz = 1 + Schellen = 0 class Rank(enum.Enum): - Ober = 0 - Unter = 1 - Ass = 2 - Koenig = 3 - Zehn = 4 - Neun = 5 - Acht = 6 - Sieben = 7 + Ass = 7 + Koenig = 6 + Ober = 5 + Unter = 4 + Zehn = 3 + Neun = 2 + Acht = 1 + Sieben = 0 - RANKS_STR: Dict[Rank, str] = { - Rank.Ober: "O", - Rank.Unter: "U", - Rank.Ass: "A", - Rank.Koenig: "K", - Rank.Zehn: "X", - Rank.Neun: "9", - Rank.Acht: "8", - Rank.Sieben: "7", + RANK_SCORE: Dict[Rank, int] = { + Rank.Ass: 11, + Rank.Koenig: 4, + Rank.Ober: 3, + Rank.Unter: 2, + Rank.Zehn: 10, + Rank.Neun: 0, + Rank.Acht: 0, + Rank.Sieben: 0, } def __init__(self, suit: Suit, rank: Rank): @@ -43,11 +41,37 @@ class Card: self.rank: Card.Rank = rank def __str__(self): - return "\n".join(self.glyph()) + return self.suit.name + " " + self.rank.name def __repr__(self): return "Card: <" + self.suit.name + ", " + self.rank.name + ">" + def __gt__(self, other): + if type(other) != Card: + raise Card.OpError() + else: + return self.suit.value > other.suit.value or self.rank.value > other.rank.value + def __lt__(self, other): + if type(other) != Card: + raise Card.OpError() + else: + return self.suit.value < other.suit.value or self.rank.value < other.rank.value + def __add__(self, other): + if type(other) != Card: + raise Card.OpError() + else: + return Card.RANK_SCORE[self.rank] + Card.RANK_SCORE[other.rank] + def __sub__(self, other): + if type(other) != Card: + raise Card.OpError() + else: + return Card.RANK_SCORE[self.rank] - Card.RANK_SCORE[other.rank] + + def __int__(self): + return Card.RANK_SCORE[self.rank] + + def rep(self) -> Tuple[Suit, Rank]: + return self.suit, self.rank diff --git a/src/DecisionMaker.py b/src/DecisionMaker.py new file mode 100644 index 0000000..8722826 --- /dev/null +++ b/src/DecisionMaker.py @@ -0,0 +1,19 @@ +from __future__ import annotations +from Game import RoundType +from typing import Union, List +TYPECHECKING = False +if TYPECHECKING: + from Player import Hand, GameChoice + from Card import Card + from Game import TrickState + + +class DecisionMaker: + def __init__(self): + pass + + def make_decision(self, trick_state: TrickState, hand: Hand) -> int: + raise NotImplementedError("This is an abstract base class!") + + def choose_round_type(self, hand: Hand, sauspiel_choices: List[Card.Suit]) -> GameChoice: + raise NotImplementedError("This is an abstract base class!") diff --git a/src/Deck.py b/src/Deck.py index 6c0c300..1ba08ef 100644 --- a/src/Deck.py +++ b/src/Deck.py @@ -1,5 +1,7 @@ from Card import Card -from typing import List +TYPECHECKING = False +if TYPECHECKING: + from typing import List class Deck(list): diff --git a/src/Game.py b/src/Game.py index 420e1eb..be74c74 100644 --- a/src/Game.py +++ b/src/Game.py @@ -1,53 +1,168 @@ -from Player import Player -from typing import List -from Round import Round -from RufSpiel import RufSpiel -from Card import Card -from Deck import Deck -from Renderer import Renderer -from Errors import GameLevelError +from __future__ import annotations import random as rn from itertools import cycle +from enum import Enum +from typing import List, Union, NamedTuple, Dict, Tuple +from Errors import GameLevelError +from Card import Card +from Deck import Deck + +TYPECHECKING = False +if TYPECHECKING: + from Player import Player, Hand, GameChoice + from Renderer import Renderer -Stack = List[Card] +class RoundType(Enum): + Sauspiel = "Sauspiel" + Wenz = "Wenz" + Farbsolo = "Farbsolo" + Ramsch = "Ramsch" + + +class Round(NamedTuple): + type: RoundType + isFarbSolo: bool + trumpSuit: Union[Card.Suit, None] + + +class Turn(NamedTuple): + suit: Card.Suit + isTrumpTurn: bool + + +class Move(NamedTuple): + card: Card + playerId: Player.Id + + +Trick = List[Card] +TrickState = List[Move] +CardRep = Tuple[Card.Suit, Card.Rank] +CardRanking = Dict[CardRep, int] class Game: - def __init__(self, players: List[Player], renderer: Renderer): - if len(players) != 4: - raise GameLevelError("There must be exactly four players, but received " + str(len(players)) + "!") - self.players: List[Player] = players - self.game_over: bool = False - self.renderer = renderer - self.deck = Deck() - self.stack: List[Card] = [] - self.current_round: Round = RufSpiel(self.players) + PLAYER_COUNT = 4 + TRICK_SIZE = PLAYER_COUNT - def play(self): + SAUSPIEL_TRUMP_ORDER: List[CardRep] = [ + (Card.Suit.Eichel, Card.Rank.Ober), + (Card.Suit.Gras, Card.Rank.Ober), + (Card.Suit.Herz, Card.Rank.Ober), + (Card.Suit.Schellen, Card.Rank.Ober), + (Card.Suit.Eichel, Card.Rank.Unter), + (Card.Suit.Gras, Card.Rank.Unter), + (Card.Suit.Herz, Card.Rank.Unter), + (Card.Suit.Schellen, Card.Rank.Unter), + ] + + def __init__(self, players: List[Player], renderer: Renderer): + if len(players) != Game.PLAYER_COUNT: + raise GameLevelError("There must be exactly four players, but received " + str(len(players)) + "!") + + self.renderer: Renderer = renderer + + self.players: List[Player] = players + self.current_player_index: int = 0 + self.last_trick_winner_index: int = 0 + self.dealer_index: int = 0 + + self.game_over: bool = False + self.round_over: bool = False + self.round_count: int = 0 + self.trick_count: int = 0 + self.round_details: Round = Round( + type=RoundType.Sauspiel, + isFarbSolo=False, + trumpSuit=Card.Suit.Herz + ) + self.round_trump_ranking: CardRanking = {} + self.trick_details: Turn = Turn( + suit=Card.Suit.Schellen, + isTrumpTurn=False + ) + + self.deck = Deck() + self.current_trick: TrickState = [] + + def play(self) -> None: self.renderer.render_message("Game start!") while not self.game_over: - self.current_round = Round(self.players) + self.round_count += 1 + self.trick_count = 0 + self.current_player_index = self.neighbour_index(self.dealer_index, after=True) self.play_round() - self.game_over = True + self.dealer_index = self.neighbour_index(self.dealer_index, after=True) self.renderer.render_message("Game end!") - def play_round(self): + def play_round(self) -> None: + self.renderer.render_message("Round {} start!".format(self.round_count)) self.deal_cards() - while not self.current_round.is_over(): - self.stack.clear() - self.round_turn() - self.renderer.render() - self.current_round.next_turn() + self.determine_game_type() + self.update_trump_ranking() + for i in range(len(self.deck) // Game.PLAYER_COUNT): + self.trick_count += 1 + self.current_trick.clear() + self.play_trick() + self.current_player_index = self.last_trick_winner_index + self.renderer.render_message("Round Results:") + for player in self.players: + score = player.calc_score_for_tricks() + self.renderer.render_message("{}: {} points.".format(player.get_name(), score)) + self.renderer.render_message("Round end!") - def round_turn(self): - self.renderer.render_message("Turn {} start.".format(self.current_round.turns_taken() + 1)) + def determine_game_type(self) -> None: + player_choosing = self.current_player_index + player = None + for i in range(len(self.players)): + self.renderer.render_hand(self.players[player_choosing]) + sauspiel_choices = self.sauspiel_choices_for_hand(self.players[player_choosing].card_options()) + self.players[player_choosing].choose_round_type(sauspiel_choices) + player_choosing = self.neighbour_index(player_choosing, after=True) + + def sauspiel_choices_for_hand(self, hand: Hand) -> List[Card.Suit]: + has_card = { + Card.Suit.Eichel: {"ass": False, "normal": False}, + Card.Suit.Gras: {"ass": False, "normal": False}, + Card.Suit.Schellen: {"ass": False, "normal": False} + } + choices: List[Card.Suit] = [] + for card in hand: + if card.suit != Card.Suit.Herz: + if card.rank not in [Card.Rank.Ober, Card.Rank.Unter, Card.Rank.Ass]: + has_card[card.suit]["normal"] = True + elif card.rank == Card.Rank.Ass: + has_card[card.suit]["ass"] = True + for suit in has_card: + if has_card[suit]["normal"] and not has_card[suit]["ass"]: + choices.append(suit) + return choices + + def update_trump_ranking(self) -> None: + rank_val = 0 + if self.round_details.type == RoundType.Sauspiel: + for card_type in Game.SAUSPIEL_TRUMP_ORDER: + self.round_trump_ranking[card_type] = rank_val + rank_val += 1 + for rank in Card.Rank: + if rank != Card.Rank.Ober and rank != Card.Rank.Unter: + self.round_trump_ranking[(self.round_details.trumpSuit, rank)] = rank_val + rank_val += 1 + + def play_trick(self) -> None: + self.renderer.render_message("Turn {} start.".format(self.trick_count)) for i in range(len(self.players)): self.renderer.render_hand(self.current_player()) self.make_move_for_current_player() - self.renderer.render_stack(self.stack) - if self.current_round.turn_in_progress(): - self.current_round.move_turn_to_next_player() + self.renderer.render_stack(self.current_trick) + self.proceed_to_next_player() + winning_move: Move = self.determine_winning_move(self.current_trick) + self.last_trick_winner_index = winning_move.playerId + winner = self.players[self.last_trick_winner_index] + winner.add_trick([move.card for move in self.current_trick]) + self.renderer.render_message("{p} won the trick with the card: {c}".format( + p=winner.get_name(), c=winning_move.card)) self.renderer.render_message("Turn end.") def deal_cards(self) -> None: @@ -57,32 +172,54 @@ class Game: dealt_cards = self.deck[i:i + 4] next(player_iterator).deal_cards(*dealt_cards) - def current_player(self): - return self.players[self.current_round.get_current_player_index()] - def make_move_for_current_player(self) -> None: - cards_to_choose_from: List[Card] = self.current_player().card_options() - card_index_choice = None - while card_index_choice is None: - try: - attempt = int(input("Choose a card -> ")) - if len(cards_to_choose_from) >= attempt > 0: - card_index_choice = attempt - 1 - else: - print("Choice was outside card range.") - except ValueError: - print("Invalid choice.") - self.stack.append(self.current_player().play_card(card_index_choice)) + self.current_trick.append( + self.current_player().make_decision(self.current_trick.copy()) + ) + if self.last_trick_winner_index == self.current_player_index: + self.trick_details = Turn( + suit=self.current_trick[0].card.suit, + isTrumpTurn=self.current_trick[0].card.suit == self.round_details.trumpSuit) - def last_cards_played(self, num_cards: int) -> Stack: - if len(self.stack) == 0: - return [] - elif len(self.stack) == 1: - return [self.stack[0]] - else: - if num_cards > len(self.stack): - num_cards = len(self.stack) - return self.stack[-num_cards:] + def round_is_over(self) -> bool: + return self.round_over - def get_stack(self) -> Stack: - return self.stack.copy() + def proceed_to_next_player(self) -> None: + self.current_player_index = self.neighbour_index(self.current_player_index, after=True) + + def neighbour_index(self, player_index: int, after: bool = True) -> int: + neighbour = player_index + 1 if after else player_index - 1 + if neighbour >= len(self.players): + neighbour = 0 + elif neighbour < 0: + neighbour = len(self.players) - 1 + return neighbour + + def set_starting_player(self) -> None: + self.current_player_index = rn.randint(0, len(self.players) - 1) + + def determine_winning_move(self, trick: TrickState) -> Move: + best_move = trick[0] + for move in trick: + if self.round_details.type == RoundType.Sauspiel: + if move.card.rep() in self.round_trump_ranking: + if best_move.card.rep() in self.round_trump_ranking: + if self.new_move_wins_trump_trick(best_move, move): + best_move = move + else: + best_move = move + elif move.card.suit == self.trick_details.suit and move.card > best_move.card: + best_move = move + return best_move + + def new_move_wins_trump_trick(self, old_move: Move, new_move: Move) -> bool: + return self.round_trump_ranking[new_move.card.rep()] < self.round_trump_ranking[old_move.card.rep()] + + def trick_value(self, trick: TrickState) -> int: + return sum([move.card for move in trick]) + + def current_player(self) -> Player: + return self.players[self.current_player_index] + + def current_dealer(self) -> Player: + return self.players[self.dealer_index] diff --git a/src/Glyph.py b/src/Glyph.py index d3e5c01..ce4fba8 100644 --- a/src/Glyph.py +++ b/src/Glyph.py @@ -2,7 +2,22 @@ from typing import Dict, List from Card import Card from collections import namedtuple - +SUITS_STR: Dict[Card.Suit, str] = { + Card.Suit.Eichel: "♣", + Card.Suit.Gras: "♠", + Card.Suit.Herz: "♥", + Card.Suit.Schellen: "♦", +} +RANKS_STR: Dict[Card.Rank, str] = { + Card.Rank.Ober: "O", + Card.Rank.Unter: "U", + Card.Rank.Ass: "A", + Card.Rank.Koenig: "K", + Card.Rank.Zehn: "X", + Card.Rank.Neun: "9", + Card.Rank.Acht: "8", + Card.Rank.Sieben: "7", +} SUIT_COLOR: Dict[Card.Suit, str] = { Card.Suit.Eichel: "Black", Card.Suit.Gras: "Green", @@ -54,7 +69,7 @@ GLYPH_CHAR: Dict[str, str] = { def glyph_for(card: Card) -> Glyph: glyph = Glyph(CARD_GLYPH_CHARS.copy(), CARD_GLYPH_COLOR.copy()) for i, line in enumerate(glyph.chars): - glyph.chars[i] = line.replace("S", Card.SUITS_STR[card.suit]).replace("R", Card.RANKS_STR[card.rank]) + glyph.chars[i] = line.replace("S", SUITS_STR[card.suit]).replace("R", RANKS_STR[card.rank]) for i, line in enumerate(glyph.colors): glyph.colors[i] = line.replace("S", GLYPH_CHAR[SUIT_COLOR[card.suit]]) return glyph @@ -83,7 +98,7 @@ def stacked_glyphs(cards: List[Card]): result_width = len(CARD_GLYPH_CHARS[0]) + len(cards) - 1 result_height = len(CARD_GLYPH_CHARS) + len(cards) - 1 result = Glyph([" " * result_width] * result_height, [GLYPH_CHAR["Default"] * result_width] * result_height) - for i, card in enumerate(reversed(cards)): + for i, card in enumerate(cards): origin_coord = [i, i] for j, (chars_line, glyph_colors_line) in enumerate(zip(glyph_for(card).chars, glyph_for(card).colors)): chars_out = result.chars[j + origin_coord[0]] diff --git a/src/HumanDecisionInterface.py b/src/HumanDecisionInterface.py new file mode 100644 index 0000000..19d9014 --- /dev/null +++ b/src/HumanDecisionInterface.py @@ -0,0 +1,78 @@ +from __future__ import annotations +from DecisionMaker import DecisionMaker +from Player import GameChoice +from Game import RoundType +from typing import List +from Card import Card +TYPECHECKING = False +if TYPECHECKING: + from Player import Hand + from Game import TrickState + + +class HumanDecisionInterface(DecisionMaker): + def __init__(self): + super().__init__() + + def make_decision(self, trick_state: TrickState, hand: Hand) -> int: + card_index_choice = None + while card_index_choice is None: + try: + attempt = int(input("Choose a card -> ")) + if len(hand) >= attempt > 0: + card_index_choice = attempt - 1 + else: + print("Choice was outside card range.") + except ValueError: + print("Invalid choice.") + return card_index_choice + + def choose_round_type(self, hand: Hand, sauspiel_choices: List[Card.Suit]) -> GameChoice: + type_choice = self.prompt_round_type(sauspiel_choices) + suit_choice = None + if type_choice not in [RoundType.Wenz, None]: + suit_choice = self.prompt_suit(type_choice, sauspiel_choices) + playing_tout = False + if type_choice in [RoundType.Wenz, RoundType.Farbsolo]: + playing_tout = self.prompt_tout() + return GameChoice(type_choice, suit_choice, playing_tout) + + def prompt_round_type(self, sauspiel_choices: List[Card.Suit]) -> RoundType: + round_types = [None] + \ + ([RoundType.Sauspiel] if len(sauspiel_choices) > 0 else []) + \ + [RoundType.Farbsolo, RoundType.Wenz] + type_choice = None + type_was_chosen = False + while not type_was_chosen: + try: + print("Choose a game to play: ") + print(", ".join( + ["Pass (1)"] + ["{} ({})".format(round_type.name, i + 1) for i, round_type in enumerate(round_types) + if round_type is not None])) + attempt = int(input(" -> ")) + type_choice = round_types[attempt - 1] + type_was_chosen = True + except ValueError: + print("Invalid input.") + except IndexError: + print("Invalid choice.") + return type_choice + + def prompt_suit(self, type_choice: RoundType, sauspiel_choices: List[Card.Suit]) -> Card.Suit: + suit_choice = None + while suit_choice is None: + try: + suit_types = [suit for suit in Card.Suit] if type_choice != RoundType.Sauspiel else sauspiel_choices + print("Choose a suit to play with: ") + print(", ".join(["{} ({})".format(suit.name, i + 1) for i, suit in enumerate(suit_types)])) + attempt = int(input(" -> ")) + suit_choice = suit_types[attempt - 1] + except ValueError: + print("Invalid input.") + except IndexError: + print("Invalid choice.") + return suit_choice + + def prompt_tout(self) -> bool: + attempt = input("Will you play Tout? (Y/N) -> ") + return attempt == "Y" diff --git a/src/Player.py b/src/Player.py index 026aac3..0c32c35 100644 --- a/src/Player.py +++ b/src/Player.py @@ -1,10 +1,23 @@ -from typing import List, Tuple +from __future__ import annotations +from typing import List, Union, NamedTuple from Card import Card from copy import deepcopy +from Game import Move, RoundType +from enum import Enum +TYPECHECKING = False +if TYPECHECKING: + from DecisionMaker import DecisionMaker + from Game import TrickState Hand = List[Card] -Trick = Tuple[Card, Card, Card, Card] +Trick = List[Card] + + +class GameChoice(NamedTuple): + type: Union[RoundType, None] + suit: Union[Card.Suit, None] + tout: bool class Player: @@ -17,11 +30,13 @@ class Player: cls.instances += 1 return new_id - def __init__(self, name: str): + def __init__(self, name: str, decision_maker: DecisionMaker, is_human: bool): self.id = self.get_new_id() + self.human_controlled: bool = is_human self.name: str = name self.hand: Hand = [] self.tricks: List[Trick] = [] + self.decision_maker: DecisionMaker = decision_maker def __str__(self): return self.name @@ -42,11 +57,30 @@ class Player: self.tricks = [] self.hand = [] - def tricks(self) -> List[Trick]: + def get_tricks(self) -> List[Trick]: return deepcopy(self.tricks) def get_name(self): return self.name def get_id(self) -> int: - return self.id \ No newline at end of file + return self.id + + def make_decision(self, trick_state: TrickState) -> Move: + return Move( + self.play_card(self.decision_maker.make_decision(trick_state, self.hand)), + self.get_id() + ) + + def choose_round_type(self, sauspiel_choices: List[Card.Suit]) -> None: + self.decision_maker.choose_round_type(self.hand, sauspiel_choices) + + def game_choice(self) -> Union[RoundType, None]: + return self.game_choice() + + def calc_score_for_tricks(self) -> int: + return sum([int(card) for trick in self.tricks for card in trick]) + + def is_human(self): + return self.human_controlled + diff --git a/src/Renderer.py b/src/Renderer.py index 9b81b33..f922fb8 100644 --- a/src/Renderer.py +++ b/src/Renderer.py @@ -1,13 +1,15 @@ -from Player import Player -from Card import Card -from typing import List +from __future__ import annotations +TYPECHECKING = False +if TYPECHECKING: + from Player import Player + from Game import TrickState class Renderer: def __init__(self): pass - def render_stack(self, cards: List[Card]): + def render_stack(self, cards: TrickState): pass def render_hand(self, player: Player): @@ -15,6 +17,3 @@ class Renderer: def render_message(self, message: str): pass - - def render(self) -> None: - pass diff --git a/src/Round.py b/src/Round.py deleted file mode 100644 index b8f9e2b..0000000 --- a/src/Round.py +++ /dev/null @@ -1,39 +0,0 @@ -from Player import Player -from Errors import RoundLevelError -from typing import List -import random as rn - - -class Round: - def __init__(self, players: List[Player]): - self.current_player_index: int = 0 - self.round_ended: bool = False - self.turn_number: int = 1 - self.moves_left_in_turn: int = len(players) - self.num_players = len(players) - - def gen_player_list(self, players: List[Player]): - return list(map(lambda x: x.get_id(), players)) - - def turns_taken(self) -> int: - return self.turn_number - 1 - - def turn_in_progress(self) -> bool: - return self.moves_left_in_turn > 0 - - def is_over(self) -> bool: - return self.round_ended or self.turn_number > 8 - - def next_turn(self) -> None: - self.turn_number += 1 - self.current_player_index = 0 - - def move_turn_to_next_player(self) -> None: - if not self.turn_in_progress(): - raise RoundLevelError("Cannot move to the next player if all players have already made their move!") - self.current_player_index += 1 - if self.current_player_index >= self.num_players: - self.current_player_index = 0 - - def get_current_player_index(self) -> int: - return self.current_player_index diff --git a/src/RufSpiel.py b/src/RufSpiel.py deleted file mode 100644 index 40d1042..0000000 --- a/src/RufSpiel.py +++ /dev/null @@ -1,11 +0,0 @@ -from Card import Card -from Player import Player, Player.Id -from typing import List -from Round import Round - - -class RufSpiel(Round): - def __init__(self, players: List[Player], spieler_id: Player.Id, spieler): - super().__init__(players) - self.variable_trump: Card.Suit = Card.Suit.Herz - self.spieler: Player.Id = \ No newline at end of file diff --git a/src/SequentialConsoleRenderer.py b/src/SequentialConsoleRenderer.py new file mode 100644 index 0000000..3468d05 --- /dev/null +++ b/src/SequentialConsoleRenderer.py @@ -0,0 +1,21 @@ +from Renderer import Renderer +from typing import List +from Player import Player +from Card import Card +from Glyph import side_by_side_glyphs, stacked_glyphs, glyph_to_colored_string +from Game import TrickState +import os + + +class SequentialConsoleRenderer(Renderer): + def __init__(self): + super().__init__() + + def render_stack(self, trick_state: TrickState): + print(glyph_to_colored_string(stacked_glyphs([move.card for move in trick_state]))) + + def render_hand(self, player: Player): + print(player.get_name() + "'s hand:\n" + glyph_to_colored_string(side_by_side_glyphs(player.card_options()))) + + def render_message(self, message: str): + print(message) diff --git a/src/ConsoleRenderer.py b/src/SingleScreenConsoleRenderer.py similarity index 76% rename from src/ConsoleRenderer.py rename to src/SingleScreenConsoleRenderer.py index 45d453e..64cd436 100644 --- a/src/ConsoleRenderer.py +++ b/src/SingleScreenConsoleRenderer.py @@ -3,18 +3,19 @@ from typing import List from Player import Player from Card import Card from Glyph import side_by_side_glyphs, stacked_glyphs, glyph_to_colored_string +from Game import TrickState import os -class ConsoleRenderer(Renderer): +class SingleScreenConsoleRenderer(Renderer): def __init__(self): super().__init__() self.header = "" self.stack = "" self.hand = "" - def render_stack(self, cards: List[Card]): - self.stack = glyph_to_colored_string(stacked_glyphs(cards)) + def render_stack(self, trick_state: TrickState): + self.stack = glyph_to_colored_string(stacked_glyphs([move.card for move in trick_state])) self.rerender() def render_hand(self, player: Player): @@ -32,4 +33,4 @@ class ConsoleRenderer(Renderer): print() print("Current Stack: ") print(self.stack) - print(self.hand) + print(self.hand) \ No newline at end of file diff --git a/src/__pycache__/Card.cpython-38.pyc b/src/__pycache__/Card.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36de1d907b8da82c35c34ebe9cf93ecb96e3724e GIT binary patch literal 2983 zcmb_e+j84P7~Yjuvg|mArfHM(sB$VUofw!>pp;HCX{InOE>pKRxgnukiQ#qg*WbVU zt-#nH^fG;!_;MGYWDNlr@Q`^_8Xj=rsB`*Sz?ZxDB!41E=5b)2@suX!VBo}tG^N-Vo`o$Y2y7A3 z?iBV-&%qW8Y;3W|Ek#-El%Rk(0}f&tsAU#Ph;uN5I1gpS1&ybm+;+T0d0H0bQqx#v z@(j%0W)erj+;aowVd0tZ&cZ2Je8#+GI1Ni!uD}^Mi{&aT!wQz?U=_|`c^=Ng1uQSX zMYx3JMR|#4qIu!+a}F+C!PzduTPV@nnb-&vyrzXQ(PC*zi(CiK1QSP!L77IbJ?iCZ za{O>|QJoC8flop+0Dr+!-Z47nOTNc^0Ssp_YJ+?0k$d&0pRc;{R!25cw;SxbaTM;l zThfhtQU$35Y;b>Ur!6;5TT&%%+;p+xCP7!a>26Q1RjkZz#;O~nO5j>q(f1o+kR-mJ zIldp^cUUWQWs(Hja>z50$gnwNY6d@k^|0ECyRsTefS}p}sNRbk{jQACr24Ijw^h*X zwxjLpH$kJ-j->lg1}dV4Wa3NJL^Y~fiQX=)*N)n$?>n?}8-a1b?Y{?SrnOp|d{wIm zg-*}1lJ9q8=!e9#?EByMgD~f+fr3{A$16b*w^AB! z<#7=0j7Xvp8DnG-kdvrW1d9Zx3G(gD9NAuGt|v)mKZs@2-p<6MEvYi=X_R6iJlTpe zQImbd^+qer?0Q>nowRAfmeVB<(jv_fPKxQ!?`YHnHS@p|onU*CB2 zcynl=3!{jcP&jLY3^lo^PBz=XM=!NU;l}n1L`-LOHsm$+E_PN-rRRPR>*^|jUjD#L z+V7}6A+;E{`CwN2#>90Sq}b%5`oXef$vxI#9S(e%YDNc39q+HWJ2VxonF;dmeB~m( zA6Y&t_SjMb<$N@~vL(~QZ+RH8X_Q1IAzT$V( zHKK4F4-9A}dq{+ip9ztP5_wO9c0ReNexSrHd`a0J-{&dcG1b|Wzc601UwGsqJF_G9 zIN^@y-N1KCza#F5Iv#9wrryWlLpu|cl19?95> zJR~bV!I06vB|2m>A)|gb6Pt+opwS6et>UNZ9Km@4${Xq;!6kyr1TFyyl8JlmMw*$p z#cUpJ9z}5)r0qDuzl{8hrXxyGmQXhcJ|Q6AR-X}kj*wYT`n^!9FR0-#7!GqT$xp^i zC~=hdqP1u*&Io4kH#~nW%`6DV#M>UpNdw;k^$;I=TNFisEwGo=2$Dkq9?GgB*Y<{z9FjiR z@NE~!PxJ@+KRprXr9Jf*dg=@%J6RWzpy80BIB(v((Kn;f;IFGcfBqvThWHc@hq&Ry3hMpMD}EM zM`WhRZOjKUeNFQIKM;lF)Mu|$EtOFQeD-2#9gOKqU2NKw%fiS!_7Ck+D)H+^gmJDn ztA+Jr>x%M?lA@cRhf%mjl#Ol-ys*wJ>-nt2So;g|4wroegCd+t!lQTOno268yNt(* zspJun@o!WzncOj+s-8??hFLJ4slM#PY#>iyHjqO(x+DDL9btqIA+xbRy=-dRXtmF* zq+LiHBX|&koq%!VmfX-=B$@7rh=PrSWs<;Q(ae)P@rhlpl<}Dm%X;aA@aaMoz#6&Q4#NW^RhA0+Oab z7Ug1DtH}#hm>L&sxhSsRG^^{WHRbdur{}AU8A1jFOeF(D=zykhZ@)O+xd#onk`l^k zOXL8rJr|eY+b_X5;^>av!i{X~$W0u$K_=)dX9feA9-1*2pMh+Gdh*D8f~#j}9vsX= z+%#;rfEIu$++|VegpTR<$uZOqw*uUB6?AwHFMb^Gehto!-arKa)3WqAn`cCY8czeu(slX62S3Aae>!<})z+?9OrFD7%vm zGM9>&>W86cHTqFosxGlF;bkUhqQBV^+a zSr_FIZ0VQ>kv#nfv^}KbjtP)1;0Om`Brr?~@QwNi9Tt8A9W31 zDy@ZhUkX6|DQ?0}FnuuiSZvyi4$}v0oQzpm&rb}N-luM}g63}nhj193Cs+8(H7KsO h|3W7|#VMZnK49yG#>>8KGy4vU8Tc8}_CBLo{2w$`dwu`_ diff --git a/src/__pycache__/DecisionMaker.cpython-36.pyc b/src/__pycache__/DecisionMaker.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a567ea7e88ace226bc36d44a7a828d1b0d51c1f GIT binary patch literal 797 zcmY*Xy^hmB5Z<*N$CpbgBpM1#gOmmfXc0Oo7jh>!E7FAm7p(TYo3j=FgxO7TS8{m_ zUI6hlv|G?n@d{MTIOKF0d1lA6CAKy0IpiLwgOfuFK{+xsZ8L zHah;GB{UQ;c_EjpcD+mt^=d z(wJwsh(~E_&+B!iX;iJI04P9v4EYyF}k1@2L?8)I|2YKl@ zBsv;dYa!e~h`Lpqiu|DvUpBJZ-2gG={IanyI_%w{AOJwiB&XYI#3R3pP=n#((|kHP un@--Jr!yDMD|w@#cHg-tS_*TDcQBXxmX+w&5+tv@$pEM4jA=ye7`c{M?WHMsMT& zAPShVZwOa(ZVl17F{7^VPK|;8lTZwp_IXj16|d5=P-u4ElyxD-S2H;^4#=v8A|aB@59yS;X_N|8NSYh=1!sC{+?k{6t?@KfeSGtx&(Bo_JgNHU zT*-ctaiyN!F}*P=tHXSjN$OdOJ%ADh7Em_mkys<~FfZt$sk=E2Jr;j9u!^pEi%*wZ zT#CD1CGxx{k~}}H)9M4X@g^3d61t~&t!Amr;-uk|f^~HNf4{JS;qd4_V*|fI{+PUf z!C%7)rIIlazvBuCPNn88Y%RC&PwZ&m(0U7GejIBrj`LE~8Rj Cs2=

p^<*WX>&k5fK!G9(qwI7BtjuC=DypY=jEQ#qu~=wJ9YLfvJCnRz=8_C0WFG>D?k=4t=<4d9D$IEvLJ!}ggpG#N~B11+@NK+B98 zITf02k*FVjf$_)k?ta8#ftuFC5;=0Z#=~hX)+enK?0U7VD3{(>vJ#DGh_LEUu!<)r zRf9aJ$Y&b7na0VHQ&+P>Z@cu_3we9MupK6lqL}aTc1LG#e&nJwotv4p^URnuEn?1E tN~nmu?s^hT{%_WckXoO}Lb7wpEN$5)|K&gBt1di$e$lTcyP!i~{sI}pI(`5E delta 387 zcmYLFJxc>Y5Ph?|cf^pWg-9wZn**^?u#q4bu#ps61P=5Jw?|0WsJT5M!i7N2KM<__ z1OB7~D{cG*7Bpo*YV`oE5?RPlY403?|IRlMP^pglsdn*nXtUTAe@*@nyS!?jbvPMdM$_F4 zBkJH{dnTq^%mwp}napV4g6HF+VbORbA_YN zZtDynMGZc!$7ed2yH?qZqS*AUjT2*{C|%=P1p%e2j;}j#pWv}ur&^;b?*?8qr1Z1< l+u`*fb<32>@QzjgU+SAVp|W;3zR`CkX%kR~b-Mf>`2~A5MGXJ| diff --git a/src/__pycache__/Deck.cpython-38.pyc b/src/__pycache__/Deck.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc82feeeda42b912205a8b3e2126e1ca521c106b GIT binary patch literal 675 zcmYjOv5wO~5S{U^bG|4+Af=>8Ly-nIlqZCg9CwItI&n~7305obCNZ%cXV)nXB~?zk zl3zH=tN5QSXsGxFDrTJ{%2+#kGv0aoX69qJ+Xk}lKYx9Bh5)~PaA=%@S9I(PatajY zP(T6*Do9YH=~Is7LBSG+a0ZI2@CH<<`IaT2YF)wL=nqxG039Dnqh5P3&TO6f?4hxT zz&<6QAwdO6a0?SusDhg)VYk2`;l#)sAL(?SlHR>}okw&$i#;JXg$>s56-V&7zX=Eh zgnApc#yeKSnyrIv@Bgs50DU`4UpMC4J$nO zTXEzTa!1yX*9^C1eZUSHA--H&99a)dZy_k=bIE*5a=dGmok17{!~1+ZJO{|ZJFv^9 z5Lu8aA<7LQ(p*jx+HVQ*Fb$Hqr(jUSSO#XE8|A5T6D4KfMpC+Gc{oi~Hqq`iX$Fp2*juOFIb5o}~58d|TSs zn^SJz=_P(z0!+ zBPFfkES?BK%oNP{%ifQ?ht(yoaDZ}xO+cfDHo-#6`Ho^i1A)#f6^Q>!g-#!b`F9bZ zNmaB6HVKwBn)0Y_IAq9^8;Ax*nZxAqNDgQmp* literal 0 HcmV?d00001 diff --git a/src/__pycache__/Game.cpython-36.pyc b/src/__pycache__/Game.cpython-36.pyc index db81362d033f207e397f20fe38e0acbe741e6ec7..6edf2d4a84c32b395d95d92b73f205ac33002a21 100644 GIT binary patch literal 3316 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 B`D*|G diff --git a/src/__pycache__/Game.cpython-38.pyc b/src/__pycache__/Game.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f149247c56afdb671b8c611094658d8e9986e446 GIT binary patch literal 8207 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 B#rFUJ literal 0 HcmV?d00001 diff --git a/src/__pycache__/HumanDecisionInterface.cpython-36.pyc b/src/__pycache__/HumanDecisionInterface.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddd3c850b5cb2efe2b72ea560cce10956954ae50 GIT binary patch literal 1130 zcmZ`%!Hye65Url>u^q=wNCb!jB0g{#3Dy!^(F$d=?5-fIl@cXn4r#Tf+v~wIJx+B` zlC_+ZSISTD1BgEwBrcpH{(uuzc2)@z=~2BIRn=6zepTO&$HPB<|MThJ3BX_Q-cj`Z zJy~F|U_}Kr=+e*oFw4OM|m zbmM!jC_~x!`)1+r2lA+6<1M|153wv352A^#;N+{rMEHa~0tdOUkSDKUCakc@ts@I`?t zh|?_T($Fp)b_1;-q*A)Diea%zrjCGr}L(E)5=+ErgLkj_nM-uosVI9 zhRr#ex-R|s^u8(PrFWB`oI%fyP|VG2(JW`vfW`Fx8SgDGuuuH1F;N;8(O%v8KRS* zS1Km!WFyu}frLfcd}EV^@+u+W@a|e!`5FD|2a$%e5dt2=vz~&e7DJp1$`L@3lp{|l z@-x|ESW6PTce=WDFmIaRCT3C?w3Dy*C&*)Z!?5~-f@R?*FHD#;Z49M#yEHWZ-0iJy zp7`gcD($4$=Iv!^Crcl@wDik1cCvE5OP9#5;UiPE?hvuTEX4s;#}UJrAiu@EY2Mbo z)Vor7-^A$ZWyB$8ZGqM5#x?D-wr>}kON5UJKqxU3i5Q8`#V{F(8{)QDeffcr{ij6M z=Uwxrc3O8st?S0N6@QPke%6|57ul`@engG%6M}C1b`=X(Ra(CznF-{jLFS4)V!mnM z)fkA8+gaglfR-&11n-Hz%$ Pk1DW2BN9Q81S9z`J&+i@ literal 0 HcmV?d00001 diff --git a/src/__pycache__/HumanDecisionInterface.cpython-38.pyc b/src/__pycache__/HumanDecisionInterface.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ea4409f1f7a7f7c7cc83ff85d6b57fd2e8f4c07 GIT binary patch literal 3547 zcmai1OOxBi5uU+|AV}`ZmUgAcGNL%~GG#Aq<#BK+8>d#!L}qPeH&Fz~Lctu6izNt9 z12B$g^0cb>oVyOIsvM#}kV6jsA-K76@?Y3PoUaGuJ|u07EevO-XBsoz-`CyyhepF; zc&6GnkEV6T{zHS)kAcC*jPY!Oj$nevY`{IvsjtP_K=U-!*JFKPc!ug5u{p3jOZCmT zGO#^c^{u!%sCrfOE1nZNat}Ie?5lY-C9h&%-K%qUhY3g2o-$DjwFBL2i24p|Hom2E zHF;4Bk|fQ8JW7)c!^X|96J;2D67)lf3Fl5Q2)Fjqs1ssT{e3!0#O{+J4UGFymecQ6 zAjNI!+zk?eUuRcFo&FBAh8_CM>4!{!hi5~yA@jIkp7s^%a>0f6)b@0t3qzQ~dZvqt zu%E&V7+2{En5l~fW=xD1#NsneEQw`|EsUySMZAF(Z;A_;sfdeW^%)my;u1#oSIl5u z71zF8T)aCP1j+oC_mVu6-Jlb8xH?wZP+!BV(>WK=$=LxrJweCzAg91n>owj z!vp+Hja-{(2Yg}}Fk#=6*M-(MnuTuQb^tl|{Y_VrufC;be6>WW`$dqs=_t=45thqjko3Zh zqs4p4UxGLiZbzYcqiM<&>?_PD8IJNokHe%ehY~g^oIeKfD7+tc8%AL~J69$zKzhcB7A9zma^f~R6L!cZ z+H*dIBd0KQ&i6IJx>~;?*CB(0A=Rda;8U}dVub;@C2vA*K4()ix3J2Xm6RpQqL7jcFywTHEkv)i50ydu**}uoX z%}t>b8SrZyTEgscVTsC9mRE>mH`xJSWzV4%E9|@~Y(+tHqD>Gc*VrY}^ey1 zfMm~RBr`>AMlzDrAoI?Ao8TDW4)#AcRyU5JpJc=My9X+(`So_SgWP^!8EZ z%c;Bm<+Ld;V(Sa7u)3)n1bJa3H~<2ew(&v1BhNVnwwL_x+=#)O4j{RC9Xc{{j>Rl) ze>c8yj=Nt0@Wzce?F4bQiOyWvk)eVRoon}KVIE*qK-rM1m~GbOCH$1&w|8dzl-Fp^ zrRFl4!q`uvq^O1o5`#n(R)I;bk^BcFckWTNDdW+M5`rPzC$yMiXRApM2jp; z4jme~ceZK$v~r*X6pZ4p^@3>jd}&ph1>FCQ-0Qg>Y!1X3#(i zYl56YYi5+FttF|dXO7veuFooAZ?)v8UOPCUC$26rS zbDFweMETxFu6!5j&s^XPMG2nj0+&mQ3Y8=jH7VH4si{dqkz%E6q6c5o&E)&k{DPX7 zfJe7O=9%BlVzNc6h(7WIY7}K^iY@X(nxLrR89QVtVu~d8%AZj~x%-47^5+;fXWTde z+>bHqIb;BpgR0{B+||D_Z7U;BPf6vpRi8aH{F4yY`dKLikZ zaec1xPLA>)wUP>VeN6?r@mmj~ICh_;BNbBJUF5-EyXy~I?Iwkpf`DoS2Q z?jJ#ZAFo0XqlET7=catwbf}P}aO(So^kVwRQK_*6;4M@2E1jEO<%(sUs<1 Zsv6C{QC@bFyrs%BihA=uiihay{{tB)F7f~X literal 0 HcmV?d00001 diff --git a/src/__pycache__/Player.cpython-36.pyc b/src/__pycache__/Player.cpython-36.pyc index 987b7ca13ee3186c70187e2c9aba6ce943f935e3..15d64975bd89bddc5750057ba729f334148cb59e 100644 GIT binary patch literal 2693 zcmbVOOOM+&5GEyBmbJFm`%1ETZu&M*)CB^xC=e7uH|Zv5vIvk|szV7?!>qNdhfB$A zcj2DAKO=vj=wInC(4m)}a_qII&QOx~5g&reAVtlP{ARux4t=lJ^ZxncuOFrkV}G-A zPYcHnu;nRsz<|dr;XY3+-{K4`-&VGMigG&^iSPx=7>Kx)IKE@Xt+<`E{k9o9v72=K zPU86Tjdb;Jrpipo_ZI zTGo5bp%1Gl8K^a!F~AvXu#VsBst=^yfKB|`KplN;!4>@4#MNuKBT25pHI!_@He5%n zE6O&~8*me)SJgGO4YzR5+i(ZJufq=9#aTC?gVeoOI?v}Kqc{l;fZ;2ZdtI6<-*#|^CgH?#v-i;UoRrh7Q$-~G`z zOVlV<0KsSsFnXAU(?q33K6rbK)y`8D=#&I`I1XMNWs_Ho zkV=e$6iTO{%**u}iiV;Txr$#Iy^&~g$}m@TeOl;B8KqGnB?Tq#V#m10o%(mOTKnD1 zS_G70M9ug_M3GOGwcJ1sxxR|x%Xn=m^Fm9xgSwnZVf>`O05Ph440kKUgO-<$R>f4O z3)Ci!_GmS^Qzg)>PN0S_uD4J(v?>d(RU4Kv&?QuvO$sa-`A6r#H)xl{z)XEBDCN7~jHN!q7KxM^@ z&p8#mDh`&uf|}9^CKHuH!^RzyU1p;b07wHfL~TRHptfhB441-F&YZzF&=i2uo@A36 z*Y}KDQ3~qUx8NsOF-$P%a9oC8C@;j|g7@akFw8Vt3+6yiY={jRg4JIoK(#D zlWy&^Q7s3gpcmvZlHEYjWo6i2p7lm`kAMt={G?aIROj^q&i^@7t|?1cjJ`OB(Hz}I z_iiW~xWjTMN{4oe;Hz^OjUSZ;lXb;-ghw1NzqeDCXfi3ggM*4guu@?*ZkYiH)(VmJN)-Tmx8`a0z>2n zG%| z`$JLLLnrKPH07le?CR1T%8fKv`wQO5gd^TJ6bTJ5d< z8TbReuXW6hXxq% zh{fFJvE^Hw=2j$P;R`bsksUj}!@(k0mJ-!(V&lAZ$iN2Ynt^j=`5w6M*{Jms z&t)U4w2$U_Q92*avq+72X&(d{u=hZzEKIYDkoGkndRcjOiUY-b4$Ma`;9odcAg*oS zu9$pBxvI6#z(u459wHvJp>xgrw(7tJu6Ch^s~gaV0j_po2%FgVU<##+Ge1b#dcx`lXY&w>O>x6t$O zEGGeS6-^BEmFW%K@#}azOJg;T6hJVZ0gPX#;d!i*A|IdVbgF|mo+s1s+aR3H6ZP;& z1v;UHJe&oSvoxEGa~+N=ho5E_r6=V)nHN&h>A8!BaTkA=gL+Schnz6{sHbM4qCYIy zX&nVOu8z3w;Ii?zGF_aOEh+OtOF6_zZY1KD{UwQkP(dfe9Op|i+DC~!q(5z;Suh*v z7W_FXEAg^b@^S4UHnJ+>0gnGju%Txl50u5zP}&pZ}uIut-b-?yaF;g16*f z77s;l3Z^ce+tF9aUw`Wyt#Va7AW9nzUGB?n9kYSf6H>kliWtRiqNQigsJ%Yj26~Kp z8fkkqO;ydec>bq;ys0WAy)REzIGQSLu;bB?N}9GwS$3q1Q#g-k+?Ddvc@Wizz$NP>awuC48 z*gV@420tFP@rAp{@KvgKslF@nO&XERzWoLtLVcg+bmls;(tA+__mb4iiFU}lyem9D G6u$v@no5NL diff --git a/src/__pycache__/Player.cpython-38.pyc b/src/__pycache__/Player.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd09d300b168eb12d64e5b62ad24c7f639883de2 GIT binary patch literal 3873 zcma)9TW{RP73Pp!YPEM=EIWytDEDmYXq_~dUR1}Z>^ec>g%imSWuOu)XC%?)Wj*A! z)nfA`{eix_fb>$pf2a@hRG?1*`U~<<^gBaxbuY3D4(H67Irnqs@Nv7{F!1~Qpa1&f z-|L3)ZvsyLYyfu+gJu7~gd3biM$BBsXl_PkY`Io!yLMc2YkJL!oY-+4J-4H|xbD{V zycW&J4Yv_D-KK_}s1>)}Hs*6~M|5NkdiAIqcipbW&trej?J+}G!gBn)H><%9K!Csh%)vtvw6I#fEJ%8tEI@%dzG8jCH{Cy$UM*Ff2$_sch2SbC^ z@z-k?%s30h|6pTfMq#-&XKqb6+~n4walqU;Zu1&<_}p`g*ZKUR?beHsapx⁣~sv zyS#_>2H=I~j4$#fKux|3>@{Fl_;tW7enVql;x7Yg14nyb;Wq(w_|G)-Dt`@7m#^~I z4-L1+-_WaX^0%Cbr$lnKzp98+dKLEVM*YxfIzYS=)Jo_R42+%eD z3;r=EuJBI~44*2eu$N>|J)oibnV3*~ZBTsOz+`GXF}`P0!^WGl&kWP>j41;VbNB-t zM6Aw-NtXLbATmU`6-1fqB;qe#$WOd1uVbrf?26ngP_F>i9x_aab=YK~bl}MjWo5aP zP3)88PRch5C)MF)s(3NwimHd1w>OT#pB!5KXimClJ#Jw#HI9s9j2@d>7|khk@~mB2 zvGrJ+nwUG>*fd_SVSi5E#3^NlT=g`0FG!PIrV#>FzDg*2&ri5=a!S-pzJ?{FgPnzIO*%7H8Z~K9Mf;8tAGLr=0vuWk0A|XOA|K1-j4)NOJfL7% z1jQmure&CrDYamICf@?KZx(`)JI+hj;KGkQ&5F>KfK^zWU_t8xY&ata8D!>cx_`(h zN9=gU-_-n-O_f-Z0nW2;5oa$Q>C!&HhDv~?(n6>4s-}xG*+x6R5J;O8C+SxiMtiA; zg+ibyJJvT{WfoJu4V-fP(MTk`GS5d?J1^h7&$*|W5w;SpR|+>nN{Wc!N=YcxP9!;z zf}EqQQ93F`a2s0PFyZ2_7bF>>(rQP2a)~4}o1G04zK->6i z*($IX*j|;b@;zdB8Lzr7f~a+$pW;+ig$k#vFv)*1Gi1#hSFv-5WXOMaidnm~YJrgy zHxk_(GFUaWnkXgcOfC<6GNcatRZPgvM<}&V%p;3R?i2giJjVN|c5ED&mGvD>U4DuK zx@@X8)poC{^hHSg2G}Y`$Oq>lQ~nlf_wi&TsliOP%$)yDmP=L65S(;pg-&-k+fb~R z`QvO93REH8BxRRtN0FzsTgIeJCTNG$HmT99G_ndk7CRc10gdx& zkkh}yQ-#=#Qh|$e6b3IlXDk1 zTG&6cNRB3fn}<5P?xB044C`CNGl%Fv4bLWB8+4UYIoMu7m%;8C+pX_S1dVyX{$@RE zJ+mP^^@<4KU(?G{=GtvZMG(~-HT#_PRAW8D6&9rN=+3v;q+`KBJ*H=JyE3I81zr}U zQg}P5Eb3~uwjQN{A7ytisYoC!tZW=#T^6q4#6?oy5a+T&-28t$&zOY@#Z|=u>c0u} zr9N8)Vhc~jv8ohRUo`Army2|Gi3SQ=ZAxW+$>qy{R5OVDEQ>|HmvY_p$aiVe`!uZ4 zaGQpYY0w$^8O^8+%U{tzd8w06Qt0Xsl9Y$%dmr8MQy(a)ucV3ofT=T^*`~=FwZ(ef z>RBEAbnK2*r@2Eb9ZR1X_OH=b#?Ba>2);5TnHUY&{s`X#lA=mdc1nsZeG^K$$#r2@ zHhujRRc0QG$`<(Yl5`WO*47^$-QW1){>E2d4j&W^Clx$pe}Vo^en=$fn?g%)D%@u3 fsQP;G-E)V$myt0!Cj$KVAJ zZhnDMq%RQ0lsJee7f=No(cqSx!({r>aylN91kG=@+5E1LE>2@t6;0xv*< zm&-z?QVP=j+$;RlmtrI!fN(1y{Nbemw64XV{pK%i6a!heCb_;fNLuFx7uoW<(v``0 zaX${MOY#_zO5%CU+KSW%AA+9}LTGIR%H4*DtTX746`B@w$%>&zRvY@neN;!EO$i1K zW^^Vu_|J1l(*`6!P^29H;_?D(@+pSob>iP7nXd|yu{QL64J zRjgo>vmPt;ZKL!1733CTheb@H_HBj8hie?(wShZ;{M1;nyepEC->1)c$AhqrZ@x^f ohHtNi@869_?)K|oM11!ST-2$gb@cMSv`c;zE^{CQ8OzxJ4`#Q_{Qv*} literal 1104 zcmb7@&5qMB5P=rF_LvK$@Rt{Y2KAa;Q4l8tBkkz z;{cq{iV>*%;S+dXJ#%gVTI)Wqr9>0!Cj$KVC_ z#QWsHg%hv9iJA0=vh=iLWi&IMcs^$)AG_T)Ly5ot_^df&Kgl^17o8W3aeILVFyJ{W zc)~dYCvlWxo?^_+-NH>=j4|+XukaI}vmpZ?f;EHStCIxKny`NOi%#zIx+AqND_Lcw zwy)3+#<{#v2Cep$$d8ALji!K2=W@Czm($pqG~RJMTi(>6 z5Sh*@Ax^M}bx<+><$Ry9+ciXnKz=F>*tE%`TokhZyMPHW+A#MW6O!H81vb~Aa&=^y zFCuIu(?x`l9l)bKz+r}=eo*t4P4n(U(LF-Z+18j#4W|bX5AQ-yLhag$#->#$YvoMc z2jc7qL~jeRJ@&x?$K$&k+niCqMeWnFP(svgA&L@KIoXjA*DINCb`1S)hQ6-pkvb=} zAF03HFg|Wjt1PLIhi(s76QxRc{&ZEX44!YO<#lxRX?!_&eK~mhW;ASCQ+q>XX-1`a X?O2sMe7?cFpqa767I6<(#3T0)1E|{8 literal 0 HcmV?d00001 diff --git a/src/__pycache__/Round.cpython-36.pyc b/src/__pycache__/Round.cpython-36.pyc deleted file mode 100644 index 0f62ddf414c803302b29a40a6bee379ffb641fb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1795 zcmah~OK;mo5Z)zMB(0YpO;ZOb3Kl&nhbTY;^il)?(#N5W5VSWQx?r>`i#EAbW~m?+ zj2`OzjQoNAB?3M5znyzhWdIm>i_-Z&tHENjQz!So)GnC z_}Zr^6jQup70VbWJt%nWy*BDZztQFQ05wgClfpK$Ic>dpwoU-fXqIusvOtGgJV1xua}}uYoydAR zQUWc46T(Yp=8RR3ELr8o0Hg8lg4ltg+fhYuMd#pbpU*sR25E)b^a8=v};W zuG@LgIY%SC&+)YnQ7C@NB$9)Ys}N<-gqQq^X2n+PDTeoo8E`!D!!^lC)ZUw~0kmnP z2a$zQ`fWP^;z(*VR6A(aV5C`BuQaq_sZD!S)o-+wrCv06K+<|Uzzy9cb`rG0>T&@Q zg8ac?ABTTlOqX?~r=?aZpDvY}epk=e6++wTOQ=sFud2eFPG98nWnpxBqH{1bVdu+y zabB+$Qw#H{BjwTRqD>@17mbt&UT*s+7?0eq&o^Vg?Pd$*A2hi+r?Bavamyi{zGP>} z<1uj*%pk^@2?tuhVT^+d!ixhmJu6l=`MEX}pLN;cn?nyxVBPBfVV+Z=BBsj-hvD6r z2QCxH_=*VA)*kHu-~SJol5(@Y9zu8GV^0DCxmEcm#v5=#I+-N7Gu z{BC9T0WNiutJY&wE}3v#e}f6wRxR-!dfq(|fx`#q(Cr!6Gx}}gijQu!_!+^pj`_wn z_@nLW?Vdbwo}95OW)B)<;EMl_{eqqaDsY}*H=WTNp7-Ff@2akYHa|Vjjj5ZI+8}M} zv{~wu;8Nd2X|YK2vg|CIE_2+Lm!NZXkyg3FGfQ27JN~6BH6OBod*~DpSuSoEp>{|4 z9TT<-UN*IKv3l^&@l4ka2~UuZA>VxAQ*+mm4+x?vbg81=r4qr!@ELmXkxU$)>PFu7 zA^D1&=^CSR;;yZeF}1YGD=lT)m$ItWx+Hxl zC-6Qke@21|10Il|7Tpn4|IkB0jc%#G6MhtrL-!No*sV=q#ZH2qAnob8J?UP8 z7vPmFQBm;qz|s7~l5@ ze!3>q$pi#Ikt{_EkI@aVhb_Bhdz3UJ+jPrsL1r2dioGxMn1!)7J{D3oZ8J#esXOx3 z*bI?h7=I(qz9`?QkJiY+kFGD1$b+$KKf{Aa;Y7;Xst~072EUforgNsUMr&1Vw64y( zdUU28LbdeWwO7tH?RE7|)tja@PcDt}Ep3E)qt>6geq9A$S4V-*`kg<)TMD!=4U5g$ z&BMRtBj<8P{9EOWl<`zb*Xa@I$~c$u^Qi2h;t9diU?$MG_(Qxl)}rn+Dv&FgUr_#l yT#k-}1{zDjrhgpUG^GhE=EJT>2?zA-*z*tWkP%ocULKge!UqX~RLxNke(7)W$DM%y diff --git a/src/__pycache__/SequentialConsoleRenderer.cpython-38.pyc b/src/__pycache__/SequentialConsoleRenderer.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc0aa43b9a420453ec210a84949310c5431d045b GIT binary patch literal 1651 zcma)6OOG5i5Vrd@Jv}pv@(@cvLJ@+HzU*+0R;x%Gv6rxl7`RvhtFgT^opC?b_8`q_ zbJ~@1=FWi}_rQsN(1cdyB z!R{47@Dv`a!AK&hCKX|Xq915l1uXD>sKY8`q4y&lRS}E4AM3bESW*qxph{Wl#}YlP zGM0INphs+ULS!n3*F+8#xeD1>4rV0J{)QDvPP_D5RZC@*0Y7?PS_kjsg)Y{ck>kS1 zJnZh-QY!w_n)~-$uiJ&~_N*(!Qc1pz_x+o@hKoiw20mD4%6bkNv+qnPmNQp4bt-V> z-7DM%_G@qyVN?}DZ!O;`m7PF3P9ND3)9R^QB_cn={JQ~ zl(l;JTotCq0V@{8*|KTRrq+n*t-uf3wV43K5n@HqgkF4h``9N>?GZSAB9Ly}f(D&QA)6V1$peFJHuumVwjBuL-F*00U*$*t4+7{SY5m@Lr^pFp z^F{s~90#REjY+ysT;M{8qBp+O`(U@F4o)wIU)qO@qLxpxJT@P~M3&B#<8@J~4I#|R z##mY2w5|l5_w+u3FK^QuZRuf3WZtv;;=Sx}29O(6%GzT7u7pqrp4}K6WY@DWsMVZ$ z@IADTVd!mWds}GR1@6NyJ-Wy5MYo-lk8p=#aX9ZX&Z|bQH2P!C&sT-seDOQT3=zXV z8kCuN0HzD!xH6xhH`#7#p|s|F2P+EGpjr(c?D$v|R!Ya=9^8I3pBOAhm$-Eco7>>Y zvRMT;8jDZx^fcHxuSeg=E;_-UBC0NEtk1Kvte2_ZB None: players: List[Player] = get_players() - renderer = ConsoleRenderer() + renderer = SequentialConsoleRenderer() game: Game = Game(players, renderer) game.play() -def get_players(): +def get_players() -> List[Player]: return get_default_players() def get_default_players() -> List[Player]: names = ["Andy", "Ben", "Chris", "Daniel"] - return list(map(Player, names)) + return list(map(make_new_human_player, names)) + + +def make_new_human_player(name: str) -> Player: + return Player(name, HumanDecisionInterface(), True) def get_input_players() -> List[Player]: print("Who's playing?") - p1 = Player(input("Player 1: ")) - p2 = Player(input("Player 2: ")) - p3 = Player(input("Player 3: ")) - p4 = Player(input("Player 4: ")) + p1 = make_new_human_player(input("Player 1: ")) + p2 = make_new_human_player(input("Player 2: ")) + p3 = make_new_human_player(input("Player 3: ")) + p4 = make_new_human_player(input("Player 4: ")) return [p1, p2, p3, p4] diff --git a/src/tests.py b/src/tests.py index 576cce6..f0a92d3 100644 --- a/src/tests.py +++ b/src/tests.py @@ -1,5 +1,5 @@ import unittest -from Player import Player, PlayerLoop +from Player import Player class MyTestCase(unittest.TestCase):