From b0c37aae52ec2d6841aeaf8c6bafcc742b2577d4 Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Mon, 8 Jun 2020 16:56:30 +0200 Subject: [PATCH] first commit --- .idea/.gitignore | 2 + .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/misc.xml | 7 ++ .idea/modules.xml | 8 ++ .idea/schafkopf.iml | 10 ++ .idea/untitled.iml | 8 ++ .idea/vcs.xml | 6 ++ src/Card.py | 53 ++++++++++ src/ConsoleRenderer.py | 35 +++++++ src/Deck.py | 11 ++ src/Errors.py | 12 +++ src/Game.py | 88 ++++++++++++++++ src/Glyph.py | 95 ++++++++++++++++++ src/Player.py | 52 ++++++++++ src/Renderer.py | 20 ++++ src/Round.py | 39 +++++++ src/RufSpiel.py | 11 ++ src/__init__.py | 0 src/__pycache__/Card.cpython-36.pyc | Bin 0 -> 1622 bytes .../ConsoleRenderer.cpython-36.pyc | Bin 0 -> 1672 bytes src/__pycache__/Deck.cpython-36.pyc | Bin 0 -> 642 bytes src/__pycache__/Errors.cpython-36.pyc | Bin 0 -> 868 bytes src/__pycache__/Game.cpython-36.pyc | Bin 0 -> 3361 bytes src/__pycache__/Glyph.cpython-36.pyc | Bin 0 -> 2730 bytes src/__pycache__/Player.cpython-36.pyc | Bin 0 -> 1724 bytes src/__pycache__/Renderer.cpython-36.pyc | Bin 0 -> 1104 bytes src/__pycache__/Round.cpython-36.pyc | Bin 0 -> 1795 bytes src/__pycache__/RufSpiel.cpython-36.pyc | Bin 0 -> 683 bytes src/main.py | 33 ++++++ src/tests.py | 10 ++ 30 files changed, 506 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/schafkopf.iml create mode 100644 .idea/untitled.iml create mode 100644 .idea/vcs.xml create mode 100644 src/Card.py create mode 100644 src/ConsoleRenderer.py create mode 100644 src/Deck.py create mode 100644 src/Errors.py create mode 100644 src/Game.py create mode 100644 src/Glyph.py create mode 100644 src/Player.py create mode 100644 src/Renderer.py create mode 100644 src/Round.py create mode 100644 src/RufSpiel.py create mode 100644 src/__init__.py create mode 100644 src/__pycache__/Card.cpython-36.pyc create mode 100644 src/__pycache__/ConsoleRenderer.cpython-36.pyc create mode 100644 src/__pycache__/Deck.cpython-36.pyc create mode 100644 src/__pycache__/Errors.cpython-36.pyc create mode 100644 src/__pycache__/Game.cpython-36.pyc create mode 100644 src/__pycache__/Glyph.cpython-36.pyc create mode 100644 src/__pycache__/Player.cpython-36.pyc create mode 100644 src/__pycache__/Renderer.cpython-36.pyc create mode 100644 src/__pycache__/Round.cpython-36.pyc create mode 100644 src/__pycache__/RufSpiel.cpython-36.pyc create mode 100644 src/main.py create mode 100644 src/tests.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3999087 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..948b2d9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/schafkopf.iml b/.idea/schafkopf.iml new file mode 100644 index 0000000..9477bc8 --- /dev/null +++ b/.idea/schafkopf.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/untitled.iml b/.idea/untitled.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/untitled.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Card.py b/src/Card.py new file mode 100644 index 0000000..341782c --- /dev/null +++ b/src/Card.py @@ -0,0 +1,53 @@ +import enum +from typing import * +from collections import namedtuple + + +class Card: + class Suit(enum.Enum): + Eichel = 0 + Gras = 1 + Herz = 2 + Schellen = 3 + + SUITS_STR: Dict[Suit, str] = { + Suit.Eichel: "♣", + Suit.Gras: "♠", + Suit.Herz: "♥", + Suit.Schellen: "♦", + } + + class Rank(enum.Enum): + Ober = 0 + Unter = 1 + Ass = 2 + Koenig = 3 + Zehn = 4 + Neun = 5 + Acht = 6 + Sieben = 7 + + 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", + } + + def __init__(self, suit: Suit, rank: Rank): + self.suit: Card.Suit = suit + self.rank: Card.Rank = rank + + def __str__(self): + return "\n".join(self.glyph()) + + def __repr__(self): + return "Card: <" + self.suit.name + ", " + self.rank.name + ">" + + + + diff --git a/src/ConsoleRenderer.py b/src/ConsoleRenderer.py new file mode 100644 index 0000000..45d453e --- /dev/null +++ b/src/ConsoleRenderer.py @@ -0,0 +1,35 @@ +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 +import os + + +class ConsoleRenderer(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)) + self.rerender() + + def render_hand(self, player: Player): + self.hand = player.get_name() + "'s hand:\n" + \ + glyph_to_colored_string(side_by_side_glyphs(player.card_options())) + self.rerender() + + def render_message(self, message: str): + self.header = message + self.rerender() + + def rerender(self): + os.system("clear") + print(self.header) + print() + print("Current Stack: ") + print(self.stack) + print(self.hand) diff --git a/src/Deck.py b/src/Deck.py new file mode 100644 index 0000000..6c0c300 --- /dev/null +++ b/src/Deck.py @@ -0,0 +1,11 @@ +from Card import Card +from typing import List + + +class Deck(list): + def __init__(self): + deck = [] + for rank in Card.Rank: + for suit in Card.Suit: + deck.append(Card(suit, rank)) + super().__init__(deck) diff --git a/src/Errors.py b/src/Errors.py new file mode 100644 index 0000000..ad85ffb --- /dev/null +++ b/src/Errors.py @@ -0,0 +1,12 @@ +class SchafkopfError(Exception): + pass + + +class RoundLevelError(SchafkopfError): + def __init__(self, message): + self.message = message + + +class GameLevelError(SchafkopfError): + def __init__(self, message): + self.message = message \ No newline at end of file diff --git a/src/Game.py b/src/Game.py new file mode 100644 index 0000000..420e1eb --- /dev/null +++ b/src/Game.py @@ -0,0 +1,88 @@ +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 +import random as rn +from itertools import cycle + + +Stack = List[Card] + + +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) + + def play(self): + self.renderer.render_message("Game start!") + while not self.game_over: + self.current_round = Round(self.players) + self.play_round() + self.game_over = True + self.renderer.render_message("Game end!") + + def play_round(self): + self.deal_cards() + while not self.current_round.is_over(): + self.stack.clear() + self.round_turn() + self.renderer.render() + self.current_round.next_turn() + + def round_turn(self): + self.renderer.render_message("Turn {} start.".format(self.current_round.turns_taken() + 1)) + 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_message("Turn end.") + + def deal_cards(self) -> None: + rn.shuffle(self.deck) + player_iterator = cycle(self.players) + for i in range(0, len(self.deck), 4): + 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)) + + 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 get_stack(self) -> Stack: + return self.stack.copy() diff --git a/src/Glyph.py b/src/Glyph.py new file mode 100644 index 0000000..d3e5c01 --- /dev/null +++ b/src/Glyph.py @@ -0,0 +1,95 @@ +from typing import Dict, List +from Card import Card +from collections import namedtuple + + +SUIT_COLOR: Dict[Card.Suit, str] = { + Card.Suit.Eichel: "Black", + Card.Suit.Gras: "Green", + Card.Suit.Herz: "Red", + Card.Suit.Schellen: "Yellow", +} +CARD_GLYPH_CHARS: List[str] = [ + "R S", + " ", + " S ", + " ", + "S R", +] +CARD_GLYPH_COLOR: List[str] = [ + "QWWWWWS", + "WWWWWWW", + "WWWSWWW", + "WWWWWWW", + "SWWWWWQ", +] +C_COLOR: Dict[str, str] = { + "Green": "\033[32;107m", + "Red": "\033[31;107m", + "Yellow": "\033[33;107m", + "Black": "\033[30;107m", + "ResetAll": "\033[0m", + "TextReset": "\033[0m", + "BgReset": "\033[1;30m", +} +Glyph = namedtuple("Glyph", "chars colors") +C_COLOR_FROM_GLYPH_CHAR: Dict[str, str] = { + "Q": C_COLOR["Black"], + "R": C_COLOR["Red"], + "G": C_COLOR["Green"], + "Y": C_COLOR["Yellow"], + "D": C_COLOR["ResetAll"], + "W": C_COLOR["Black"], +} +GLYPH_CHAR: Dict[str, str] = { + "Black": "Q", + "Red": "R", + "Green": "G", + "Yellow": "Y", + "Default": "D", + "WhiteBg": "W", +} + + +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]) + for i, line in enumerate(glyph.colors): + glyph.colors[i] = line.replace("S", GLYPH_CHAR[SUIT_COLOR[card.suit]]) + return glyph + + +def glyph_to_colored_string(glyph): + out = "" + for chars_line, colors_line in zip(glyph.chars, glyph.colors): + for char, color_code in zip(chars_line, colors_line): + out += C_COLOR_FROM_GLYPH_CHAR[color_code] + char + C_COLOR["ResetAll"] + out += "\n" + return out + + +def side_by_side_glyphs(cards: List[Card]) -> Glyph: + glyph = Glyph([""] * len(CARD_GLYPH_CHARS), [""] * len(CARD_GLYPH_CHARS)) + for card in cards: + for i, glyph_char_line in enumerate(glyph_for(card).chars): + glyph.chars[i] += glyph_char_line + " " + for i, glyph_color_line in enumerate(glyph_for(card).colors): + glyph.colors[i] += glyph_color_line + GLYPH_CHAR["Default"] + return glyph + + +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)): + 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]] + colors_out = result.colors[j + origin_coord[0]] + chars_out = chars_out[:origin_coord[1]] + chars_line + colors_out = colors_out[:origin_coord[1]] + glyph_colors_line + result.chars[j + origin_coord[0]] = chars_out + result.colors[j + origin_coord[0]] = colors_out + return result \ No newline at end of file diff --git a/src/Player.py b/src/Player.py new file mode 100644 index 0000000..026aac3 --- /dev/null +++ b/src/Player.py @@ -0,0 +1,52 @@ +from typing import List, Tuple +from Card import Card +from copy import deepcopy + + +Hand = List[Card] +Trick = Tuple[Card, Card, Card, Card] + + +class Player: + Id = int + instances = 0 + + @classmethod + def get_new_id(cls): + new_id = cls.instances + cls.instances += 1 + return new_id + + def __init__(self, name: str): + self.id = self.get_new_id() + self.name: str = name + self.hand: Hand = [] + self.tricks: List[Trick] = [] + + def __str__(self): + return self.name + + def deal_cards(self, *cards: List[Card]) -> None: + self.hand += cards + + def card_options(self) -> List[Card]: + return self.hand.copy() + + def add_trick(self, trick: Trick) -> None: + self.tricks.append(trick) + + def play_card(self, card_index) -> Card: + return self.hand.pop(card_index) + + def reset(self) -> None: + self.tricks = [] + self.hand = [] + + def 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 diff --git a/src/Renderer.py b/src/Renderer.py new file mode 100644 index 0000000..9b81b33 --- /dev/null +++ b/src/Renderer.py @@ -0,0 +1,20 @@ +from Player import Player +from Card import Card +from typing import List + + +class Renderer: + def __init__(self): + pass + + def render_stack(self, cards: List[Card]): + pass + + def render_hand(self, player: Player): + pass + + def render_message(self, message: str): + pass + + def render(self) -> None: + pass diff --git a/src/Round.py b/src/Round.py new file mode 100644 index 0000000..b8f9e2b --- /dev/null +++ b/src/Round.py @@ -0,0 +1,39 @@ +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 new file mode 100644 index 0000000..40d1042 --- /dev/null +++ b/src/RufSpiel.py @@ -0,0 +1,11 @@ +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/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/__pycache__/Card.cpython-36.pyc b/src/__pycache__/Card.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..005ec418e3c8ce116a52ab72031981fd96908f56 GIT binary patch literal 1622 zcmZux&2HO95Z)!1KcXc6#gXjv2SzU~gEVT9Ly{s0VkALYI8_SE28unDU^r`w3Pq_) z>R`b@4)zQ5SfGajMGN#5`X~$Z(w_7RIdz7T8ny8f{AM|`GduI`Ebla%!4G%;{LT`L z{moW}jq^wN7Pk?A0neBuX=VI~q@7tqTiTpGV<5n}WZ+y_QULF<(4CxSI$n+QQ3 z4;7S?ai$V7EXM{;9^qU3fuNY=z@&9d0XbM0&{Fn5T-ef4&cFp5(F1|#18HjDra}g) zt{l}kv3A+(06jhM(bHdeHdS-51~tTW2oN`*j<^X8#4RA}HCRL3hIPd2u#x(*g%O*J zh%JoWgw3BU*n(@9LbhQWu3s{_11)Hy+=U%1ch`u=v4;QD#+S9dzfv1;X#MA7$?I9piUr)knxnGw%) zTp01C(o^I2NR_Glcc#}dRt0aKclx8D>SPKa?(_jVPe#dPsPeMte4|IFIvx(w{Iv6R zob=ON?LAkq&Ph=u{rKe7XnfKsbkeDk9-kROiF+8l@DY42sz-0)$At3#>&IUf^5=#8 zbs>Mdnz38xwCsj=8IvBk(g#o0z?T8kkilTV;9;DwNH-&0 zH>_*QSJwyv1S4L&RN6Sld5OZ_FAC!xj8vYU8X;9bH=?U1i2F&uG;S|dFIE1Q)cK4$ z+6c;H8)pT5E(w{_BDnsL*6scL#26IMo~y1i66#YK5bTq zy0j(Kh%`w4yZ;(~03Wb(HsBZRg6_N3H9Vl)2ctBX&S`cw?uT4c?Ei1^+!TxAxxfhoN1SZv@?s zIW1SGgj{Gf#+*J|$!W>N+|mtiywfczpA3z7idgl!uA{eZ5HtyhCRytpe|6N0dPj#w zJV}$%*jQuL0lGzt+5|fUy974~-XoxT^#=&DcDUa?s6yzQq$J`rWxB#_b+zeSHJv2& zc<~WJz_&aXkJaTZd#gU5EwN>92;Z*OhmQUnR~xrH8{;)M^<;#pNdw;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`_ literal 0 HcmV?d00001 diff --git a/src/__pycache__/Deck.cpython-36.pyc b/src/__pycache__/Deck.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89b256c77681561d2e8e7d304df16bc07962d4b2 GIT binary patch literal 642 zcmYLGO^?$s5S@vgs#~?dfx{kFhy#~MXmfxgLP%8NQdTSWuu>$jtk`YSIv=$YX&1Gp zuC%{}zhsFMT=)xcVVsu5){I}qo|iW>uO^ex&)46-`~iR;WDkoIdPnA7l2f42KnWRO zz>&c+$|8ghpqS!!ptxpxmT?tdK{EJ78IYjo@1<4YoE5IkL-5~XN8ru~Xvk1O7Tv=f z6{_fNn6Z1{kZ~eoo=Xpab+(`(p_uc768SjAfmF$MG&yhsK%A~w%I$!hKSRadlrAe%;4%Ezv2wDm(F zimGUZNVw-to4MzTqP$kLERw3Z>rJS$pW}Z5mr=bDo&8f)fZlBE{HkAx^|)tP|DLTox|4mUlSpB%;J+8811W1{U{g7#6sPl5|AkI7tU&=C9Y oizGhuYtY^{q}Yaecy4IW{r!Kc0}4cb_~xMHZ5XBtv{V553vY&(;Q#;t literal 0 HcmV?d00001 diff --git a/src/__pycache__/Errors.cpython-36.pyc b/src/__pycache__/Errors.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7809b0da35eb109d82f475fa7cf453cc5621f04a GIT binary patch literal 868 zcmbVKJx{|h5VhlkmQtVt5{#@_ngK?HDgi=5r9yP;fEC4V6OoT{ZV{kv^w02Xn90h& zVB*dwg_ACY*xuXPf zp*eB~xvM6H&yeZ|~G)%u%UKn_o%fd%5h>J`Isa7fsVx@x9BARA8pP1kZiV=ia zmgJ-0GK}IR*ZW-^LQX$SRXH#a1-86tj~{{Br7#Xcom>7(5B=}Zh8=`&J<6H0flvZ< zx#jkCG0jz1-|N(7j_42#`cj-=m&PX^6u|KD=DUYWJjf#I679YpFKo+JV}b&J^||t(Bd-R%KFjQQ$4m% Khdge%E$YOi8r<(KnDV7mSFE{K}aYvh=h%hvR1T?HcLy;>g}rWT>3Jp zt_k*F^T34Hed2Nd0Dq|^Uf?NzVW0R;^~_`)0yC&xZVKl*Za2M5GnvvJ>)*y33uq*ZxL}bbZRs2`=?eapB`jgTaw1PS!hOY}mheOi ztuK7hM%xyF=zPURSM)F{5Ph))ZD*z37CX>(Wl#3SF4npw_VB%hFPih7xQ(xE5y0~O z+M)9fthzNbrdPGER>q`sNcvIhqC7;Yo}tq0iZ8j~Q(N8BTv*y_G)unEguTy}{IC3q zU$NL04r=GhLhWjA$BA9==tJ zl#T#wwOi>VohmY{?xk}DvvsTt+IHvD%)EavwU2RMKRF(ixjfFK5XtdKh~r0P zI?pk&I)19kbCu-zxHv!lBuPi(LWWOdq6$(}=_naa%h~X_Qt2^y{AhMr`*A!j#yXC7 zuy(bLit#Q#;GX&Ws24pmc0DfTGzLFvEfj;uZy>;zYyw1KtqIbC#Uw-A0&`tGwu+k7Dy?habU3w6Hc3$snR~-jGUeiDFp5vC zC957lvSBK;kla=4OfZYz$y2tlVk>VB@Eatpq8#)7bhK2(ZS$ zIst5rg~B3w8JM4HPzw4MeLrGuC(@~w`A%*H1?!# zd~^TZA<=&Z%fmlkH&Jo)nxI&;jS`V@G}vl*^)0AtZ&<24(Tz7dV})-B4j0M!1H!uS1E z_}Zt6U{HNj2xLnYQL7rwhr>*&ThtLyMdVW7r;6NOJLClQ9f$+RxFFhUJQ9z!REaK? z`XM@ThkzD(#fzKFHM4(?K^1L4#0`+GcGu2%!>5f$ZsaLC25h8aXnROm2UN7*WRK|J zTw?E4GG3n#ck9LfAQ=(t0ZK*GVSI7RQ0wLbo6`_J9TY>azeAwYEODA8yT%24ZC#Au zeKz&g#pMa^G@J72k^={aaJaG^T!mA&JQ3E^DI5!!sM9N3*aw(f_&^oemb2&V#jeA& zHw~2L7z6BG;Zo1<(epWqp<2rjmG*C6Ar7eDXkkV85)040PJj9t7 z-$my*mEoU~DlF%^8VlJBGaNfwZ2hiynPg)Tre@sHAW-C`x=U-tp2?jt_=TNt+<2(UJlZkaj0rQ+ zh(7TP;Ju)1I!LhG&p^#3IUyj|MGJYPlZkFZi| zf&l`xHi`lKa|n2*5Zv^1vDEH40&t_TprL$-zC(7-&+x=q_e~x3Xf2x@i^oj|U=c~r z8qn6146zAPj7fClzE!$WA3%P~{CX!#suj^<6YAm z#XKXq8^w?zoT2T>Ti`b(<2SF3YTH66!B#4jtfKC#(GjjXO>V2oup=MDjQPcmdeH{U6I$ B`D*|G literal 0 HcmV?d00001 diff --git a/src/__pycache__/Glyph.cpython-36.pyc b/src/__pycache__/Glyph.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2026e30a077a41a1b17fb4d73c1f490a81879c10 GIT binary patch literal 2730 zcmZuy+iqLO6`eU(-Xu{nV_lrOjN&F{lbEuVHbrB!wxw1oYRfU{fbg+fmgS6i@gXVC z88xzSh(0I<`p^$33jaWXenP)wfW9P8`Gq`n?K!L@8YMA%_GM+W>f_6ca#DZAFSb}zOrd9lF*H}jCrwtn^<6bBwn;=xXCV93;BNjknWL0&`*98A+j!k78;32XUwS()fEy z%CT`9QkdYGjN{2yr(vT?zvgLZ=W5k5O$etU(Q363g-=@aYeu1+TP@2pAw(h3R_n=W z{@U~HJD=a)`fOy`+ZXKi1-q58-nHjjqto!(^V^?qZ;fi93h@v{S)%ep0uYmH$VBS< zaUA8J$`fs+D16Yf44x9dKI)&Ay8Vtys@+LEQHd&GUo9~FiQx^y_Y8k#_^#nCg@j~k zfyg3}B_d@a%OJJD9+;hPZ$g(a;jTRF9LE}O?GLnk&?BU~z}5sx>SHzTBAM;a1EnkP zU?hJ7(MZV9s!F4#yrEHjA$2!Ok#|vESa=zu#!$U6Z5N^ZLgRA2&>1j*mxDbu?1QB5gt?kC01J(W6bi`Q>k(Hrvgo z4dW%p18wq+`v4n9~dtljO9O>`T*+h zAJzMlk*vp3h)%sPM16PC#f8Q?sehp+J=GbF2IF4+QK#F-uIfYCQDaghxa{G}$k_zJJ^TNqWYZbCkfDn;}>sxKf2jyN_n1J)eFIdc%p zds!q-Zb9#)uGLkc^QL~vwVR?@q~0pioNAE472%bc%kMS z_jGWiHgKq_f|%8v46gRC8;>7dikt~&2ITPjw9uU#Yg4eHY7>xAF~i>q2s5fBDp*y! zI}y^NoRJUhIT=8D0xK#?iguzE0R1J5Gq^my2C&}cC9ZBmi=yzUC=ykHGzT8lvn`3g zgVBJm$2Hf^8L!Oowmfvo0A@UOPPVj*M8~RGv0Mk3?$j1f>iUe}6b=FDG8D$oP#B+{ z?+9#hY}y@we;CYm_&OBms^`0gE%xfhIq<56`+d)=Y6WvGm(_?H%dxr&g<2;S)^8n*|ZV@+vHZOdX^MpL5pBRHA?r(`|jtN6aIA@NB6md??Ra8pF2$5W?* zhDBex3mLI-PyVev?UNsi?!RWq?WJDNnFc5SfHlxziqq+#W`IbI;@-bc9*5ad2h&!zaqvj5MviD z&_7VrtG^?nwu#tK7u2W3+bC0Gv|+XJHaR8#Bvm4X`iKnk;jfi!0HTtT2|f_*uLeT* zqlKB&mxEqk(?^1gHYsd+M5T!u^af*;)&w6BK2&wMj+QT_N0MbsKC54p@~l>qHu$&G zW--zDrlUl&jA_7GLLGzI0^Q_Sd5+&icccDR0o~y0=dd2sR!yK^AEBz116jF5Y&WM~ z5zDR~OvZ`wp*CKVntJf7LANhs!Dk_pqTIixrWI(QwyHAK%NIxcy z$|b{)T82$xdzVX3qIR!r56aygO~i35e?ieo=vfXcqurFe9A1>;8?M8%|Kk7Q$Wn0% UPQd^7Uprp~i&xI)@|D7W0oi?P`v3p{ literal 0 HcmV?d00001 diff --git a/src/__pycache__/Player.cpython-36.pyc b/src/__pycache__/Player.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..987b7ca13ee3186c70187e2c9aba6ce943f935e3 GIT binary patch literal 1724 zcmbVMOK;RL5Vqqao89iKEp4fUcnAq;FYJK>7le?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 literal 0 HcmV?d00001 diff --git a/src/__pycache__/Renderer.cpython-36.pyc b/src/__pycache__/Renderer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eac8798cc91ca6dccb7b0b6ac42a77ce420484e3 GIT binary patch literal 1104 zcmb7@&5qMB5P=rF_LvK$@Rt{Y2KAa;Q4l8tBkkz z;{cq{iV>*%;S+dXJ#`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 literal 0 HcmV?d00001 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..9f30f78 --- /dev/null +++ b/src/main.py @@ -0,0 +1,33 @@ +from typing import List +from Player import Player +from Game import Game +from ConsoleRenderer import ConsoleRenderer + + +def main() -> None: + players: List[Player] = get_players() + renderer = ConsoleRenderer() + game: Game = Game(players, renderer) + game.play() + + +def get_players(): + return get_default_players() + + +def get_default_players() -> List[Player]: + names = ["Andy", "Ben", "Chris", "Daniel"] + return list(map(Player, names)) + + +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: ")) + return [p1, p2, p3, p4] + + +if __name__ == "__main__": + main() diff --git a/src/tests.py b/src/tests.py new file mode 100644 index 0000000..576cce6 --- /dev/null +++ b/src/tests.py @@ -0,0 +1,10 @@ +import unittest +from Player import Player, PlayerLoop + + +class MyTestCase(unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main()