From 39c88aada180719eb2c3f890853b6c152e184e9c Mon Sep 17 00:00:00 2001 From: En Yi Date: Wed, 5 Jun 2019 12:16:29 +0100 Subject: [PATCH] Fix input cleansing and partner reveal --- cards.py | 17 +++++++++++------ players.py | 50 +++++++++++++++++++++++++++++--------------------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/cards.py b/cards.py index 0cdaf47..10dec83 100644 --- a/cards.py +++ b/cards.py @@ -17,11 +17,11 @@ CARDS_SYMBOLS = {14: "A", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 100: "Clubs", 200: "Diamonds", 300: "Hearts", 400: "Spades", 500: "No Trump", } -INPUT_SYMBOLS = {"c": 100, "d": 200, "h": 300, "s": 400, "n":500, "a": 14, +INPUT_SYMBOLS = {"c": 100, "d": 200, "h": 300, "s": 400, "n": 500, "a": 14, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "10": 10, "j": 11, "q": 12, "k": 13, } - +BID_SYMBOLS = {"c": 100, "d": 200, "h": 300, "s": 400, "n": 500} class DeckReveal(Enum): SHOW_ALL = 1 @@ -353,19 +353,24 @@ def get_suit_string(value): def convert_input_string(string): + string = string.lower() try: - return INPUT_SYMBOLS[string[0:-1]] + INPUT_SYMBOLS[string[-1]] + if string[0:-1].isalnum() and string[-1].isalpha(): + return INPUT_SYMBOLS[string[0:-1]] + INPUT_SYMBOLS[string[-1]] + return -1 except KeyError: return -1 def convert_bid_string(string): + string = string.lower() try: - return int(string[0])*10 + INPUT_SYMBOLS[string[1]]//100 + if string[0].isdecimal() and string[1].isalpha(): + return int(string[0])*10 + BID_SYMBOLS[string[1]]//100 + return -1 except KeyError: return -1 - except ValueError: - return -1 + class test_screen(view.PygView): diff --git a/players.py b/players.py index c1b2acb..6ad2ae8 100644 --- a/players.py +++ b/players.py @@ -13,7 +13,7 @@ NUM_OF_PLAYERS = 4 STARTING_HAND = 13 HIGHEST_CARD = 414 LOWEST_CARD = 102 -VIEW_TRANSPARENT = False +VIEW_TRANSPARENT = False # Make the text box not transparent class GameState(Enum): @@ -33,10 +33,11 @@ class PlayerRole(Enum): class Table: """ A Table is the place where all actions takes place. It is essentially a FSM, doing different - routines at each state. It needs to keep track of the score, roles, and the rules. It needs + routines at each state. It needs to keep track of the score, roles, the rules, etc. It needs to ask each player for decisions and respond to them accordingly. The table will also need to inform any decision to the Main Screen so that it can update the screen to reflect that - change through the use of callbacks (Signal and Slot). + change through the use of callbacks (Signal and Slot). This call should be minimised by making + all the changes before calling to update the screen in one go. FSM cycles --- @@ -80,6 +81,9 @@ class Table: # For gameplay self.game_state = GameState.DEALING self.current_round = 0 + self.passes = 0 + self.current_player = 0 + self.first_player = False # This is for bidding purposes self.players = [] self.players_playzone = [] # Table status will be made known to the player by reference @@ -93,25 +97,29 @@ class Table: self.background.fill(clear_colour) self.background = self.background.convert() + # TODO: Update the drawing of the table? + # Prepare the card with dimensions w_deck = min(self.height, self.width) * 0.18 l_deck = min(self.width, self.height) * 0.7 # This is not a deck as it will never be drawn self.discard_deck = cards.prepare_playing_cards(int(w_deck*0.7), int(w_deck*0.9)) + game_margins = 5 + # Players' deck positioning playerx = ((self.width - l_deck)//2, - 0, + game_margins, (self.width - l_deck)//2, - self.width - w_deck) - playery = (self.height - w_deck, + self.width - w_deck - game_margins) + playery = (self.height - w_deck - game_margins, (self.height - l_deck)//2, - 0, + game_margins, (self.height - l_deck)//2) - h_spacing = 20 v_spacing = 25 + # Middle playfield for announcer and player playing deck positioning playfield_margins = 10 - margins_with_w_deck = w_deck + playfield_margins + margins_with_w_deck = w_deck + playfield_margins + game_margins playfield_x = margins_with_w_deck playfield_y = margins_with_w_deck playfield_width = self.width - margins_with_w_deck * 2 @@ -126,6 +134,7 @@ class Table: playfield_y, playfield_y + (playfield_height - margins_with_w_deck) // 2) + # Player stats positioning stats_width = 100 self.stats_height = 100 stats_spacing = 10 @@ -141,6 +150,7 @@ class Table: self.player_stats = [[], [], [], []] # TODO: change surface to use colorkey, maybe, if the performance is tanked + # Prepare all the player surfaces for i in range(4): vert = i % 2 == 1 spacing = h_spacing @@ -167,8 +177,11 @@ class Table: self.center_text_on_surface(surf, rendered_text, (255, 255, 255, 255 * VIEW_TRANSPARENT)) self.player_stats[i].append(surf) + if autoplay: self.players[0].add_ai(ai.RandomAI(self.table_status)) + + # Announcer positioning and surface creation announcer_margins = 5 announcer_spacing = announcer_margins + w_deck self.announcer_x = playfield_x + announcer_spacing @@ -186,9 +199,7 @@ class Table: self.ongoing = False - self.passes = 0 - self.current_player = 0 - self.first_player = False + def center_text_on_surface(self, surf, rendered_text, clear_colour): line_center = surf.get_rect().center @@ -422,9 +433,9 @@ class Table: # Leading player starts with the leading card, which determines the leading suit self.current_player = self.table_status['leading player'] self.display_current_player(self.current_player) - leading_card = self.players[self.current_player].make_decision(self.game_state, 0) - self.table_status["played cards"][self.current_player] = leading_card - self.players_playzone[self.current_player].add_card(leading_card) + card = self.players[self.current_player].make_decision(self.game_state, 0) + self.table_status["played cards"][self.current_player] = card + self.players_playzone[self.current_player].add_card(card) elif not all(self.table_status["played cards"]): # Subsequent player make their plays, following suit if possible self.display_current_player(self.current_player) @@ -472,8 +483,7 @@ class Table: return if not self.table_status['partner reveal']: - leading_card = self.table_status["played cards"][self.table_status['leading player']] - if leading_card.value == self.table_status['partner']: + if card.value == self.table_status['partner']: self.table_status['partner reveal'] = True self.write_message("Partner Revealed!", delay_time=1) self.reveal_all_roles(self.current_player) @@ -519,7 +529,7 @@ class Player(cards.Deck): A player is essentially a Deck with decision making function or AI component if it is a bot that returns a valid action for the Table/Board. - The player has the knowledge of Table status in the form of a dictatary (as it is mutable, thus passed by ref) + The player has the knowledge of Table status in the form of a dictionary (as it is mutable, thus passed by ref) so all validation is done by the player Possible decisions, each decision has to be enum maybe: @@ -592,7 +602,7 @@ class Player(cards.Deck): bid = cards.convert_bid_string(bid) if bid < 0: - print("Please enter integer only") + print("Error in processing bid") continue if self._table_status["bid"] < bid: @@ -610,7 +620,6 @@ class Player(cards.Deck): """ current_card_values = self.get_deck_values() while True: - # TODO: Make a more natural input parsing partner = input("Please call your partner card. Enter card number + suit number \n" "e.g. qs is Queen Spade, 8c is 8 Clubs, ah is Ace Hearts\n") @@ -628,7 +637,6 @@ class Player(cards.Deck): :return: A valid Card """ while True: - # TODO: Make a more natural input parsing play = input("Please play a card.Enter card number + suit number \n" "e.g. qs is Queen Spade, 8c is 8 Clubs, ah is Ace Hearts\n") if play == "v":