diff --git a/UI.py b/UI.py index 313a081..42bffca 100644 --- a/UI.py +++ b/UI.py @@ -5,6 +5,8 @@ from signalslot import Signal class GenericUI: def __init__(self, x, y, width, height): + self.draw_update = Signal() + self.x = x self.y = y self.width = width @@ -82,13 +84,13 @@ class TextBox(GenericUI): def redraw(self): super().redraw() - if self.visible: - outline = (0, 0, self.rect.w, self.rect.h) - pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness) - rendered_text = self.font.render(self.text, True, self.text_colour).convert_alpha() - rect_center = self.background.get_rect().center - text_rect = rendered_text.get_rect(center=rect_center) - self.background.blit(rendered_text, text_rect) + #if self.visible: + outline = (0, 0, self.rect.w, self.rect.h) + pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness) + rendered_text = self.font.render(self.text, True, self.text_colour).convert_alpha() + rect_center = self.background.get_rect().center + text_rect = rendered_text.get_rect(center=rect_center) + self.background.blit(rendered_text, text_rect) def set_text(self, text): self.text = text @@ -112,13 +114,13 @@ class Button(TextBox): self.background.fill((255, 255, 255)) else: super().redraw() - if self.visible: - outline = (0, 0, self.rect.w, self.rect.h) - pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness) - rendered_text = self.font.render(self.text, True, self.text_colour).convert_alpha() - rect_center = self.background.get_rect().center - text_rect = rendered_text.get_rect(center=rect_center) - self.background.blit(rendered_text, text_rect) + #if self.visible: + outline = (0, 0, self.rect.w, self.rect.h) + pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness) + rendered_text = self.font.render(self.text, True, self.text_colour).convert_alpha() + rect_center = self.background.get_rect().center + text_rect = rendered_text.get_rect(center=rect_center) + self.background.blit(rendered_text, text_rect) def hold(self, *args): if not self.button_down: @@ -164,17 +166,17 @@ class ScrollList(GenericUI): def redraw(self): super().redraw() - if self.visible: - outline = (0, 0, self.rect.w, self.rect.h) - pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness) - i = 0 - for text, text_rect in zip(self.texts, self.text_rects): - if i == self.selected: - pygame.draw.rect(self.background, self.selected_colour, text_rect) - rendered_text = self.font.render(text, True, self.text_colour).convert_alpha() + #if self.visible: + outline = (0, 0, self.rect.w, self.rect.h) + pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness) + i = 0 + for text, text_rect in zip(self.texts, self.text_rects): + if i == self.selected: + pygame.draw.rect(self.background, self.selected_colour, text_rect) + rendered_text = self.font.render(text, True, self.text_colour).convert_alpha() - self.background.blit(rendered_text, text_rect) - i += 1 + self.background.blit(rendered_text, text_rect) + i += 1 def process_events(self, event): draw_update = super().process_events(event) @@ -274,6 +276,7 @@ class ScrollList(GenericUI): current_y += text_rect.height self.max_offset = max(0, current_y - self.height - self.outline_thickness) self.redraw() + self.draw_update.emit() class CallPanel(GenericUI): @@ -291,7 +294,7 @@ class CallPanel(GenericUI): ui_width = 75 ui_height = 25 width_spacings = (width - 3 * ui_width - 2 * margins) / 4 - height_spacings = (height - 2 * margins - 2 * ui_height) / 3 + height_spacings = (height - 2 * margins - 3 * ui_height) / 4 self.output_text = ['', ''] self.label1 = TextBox(margins+width_spacings, margins, @@ -309,33 +312,39 @@ class CallPanel(GenericUI): self.list2.list_selected.connect(lambda text, **z: self.print_list_selection(text, 1)) self.output_box = TextBox(margins+width_spacings*3+ui_width*2, margins+height_spacings, - ui_width, ui_height, text_size=self.text_size) + ui_width, ui_height, text='Bid', text_size=self.text_size) self.confirm_button = Button(margins+width_spacings*3+ui_width*2, margins+height_spacings*2+ui_height, ui_width, ui_height, text='OK', text_size=self.text_size) self.confirm_button.clicked.connect(self.emit_output) + self.cancel_button = Button(margins + width_spacings * 3 + ui_width * 2, + margins + height_spacings * 3 + ui_height * 2, + ui_width, ui_height, text='Cancel', text_size=self.text_size) + self.cancel_button.visible = False + self.cancel_button.clicked.connect(self.cancelling) self.children = [self.label1, self.list1, self.label2, self.list2, - self.confirm_button, self.output_box] + self.confirm_button, self.output_box, self.cancel_button] for element in self.children: element.parent = self self.redraw() - def redraw(self): + def redraw(self, **kwargs): super().redraw() #self.background.fill((255,0,255)) - if self.visible: - outline = (0, 0, self.rect.w, self.rect.h) - pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness) + #if self.visible: + outline = (0, 0, self.rect.w, self.rect.h) + pygame.draw.rect(self.background, self.outline_colour, outline, self.outline_thickness) - for element in self.children: + for element in self.children: + if element.visible: self.background.blit(element.background, element.get_pos()) def process_events(self, event): draw_update = False for element in self.children: - if element.process_events(event): + if element.visible and element.process_events(event): draw_update = True if draw_update: @@ -347,9 +356,17 @@ class CallPanel(GenericUI): self.output_box.set_text(' '.join(self.output_text)) def emit_output(self, **kwargs): - output = self.output_text[0]+self.output_text[1][0].lower() + initial = '' + if self.output_text[1]: + initial = self.output_text[1][0].lower() + + output = self.output_text[0].lower() + initial self.confirm_output.emit(output=output) + def cancelling(self, **kwargs): + self.confirm_output.emit(output='') + + class TestScreen(view.PygView): def __init__(self, *args, **kwargs): @@ -393,6 +410,10 @@ class TestScreen(view.PygView): if event.key == pygame.K_ESCAPE: running = False + if event.key == pygame.K_o: + self.panel.list1.replace_list([str(i+1) for i in range(7)]) + draw_update = True + for element in self.elements: if element.process_events(event): draw_update = True diff --git a/game_consts.py b/game_consts.py index a83779b..d67b032 100644 --- a/game_consts.py +++ b/game_consts.py @@ -21,3 +21,4 @@ HIGHEST_CARD = 414 LOWEST_CARD = 102 DOUBLE_CLICK_EVENT = pygame.USEREVENT + 1 DOUBLE_CLICK_TIMING = 300 +CALL_EVENT = pygame.USEREVENT + 2 diff --git a/players.py b/players.py index 7102725..ccf6e4d 100644 --- a/players.py +++ b/players.py @@ -1,7 +1,7 @@ import cards import pprint import pygame -from game_consts import GameState, PlayerRole, STARTING_HAND, DOUBLE_CLICK_EVENT, DOUBLE_CLICK_TIMING +from game_consts import GameState, PlayerRole, STARTING_HAND, DOUBLE_CLICK_EVENT, DOUBLE_CLICK_TIMING, CALL_EVENT class Player(cards.Deck): @@ -57,7 +57,7 @@ class Player(cards.Deck): if sub_state == 0: if self.AI: return self.AI.make_a_bid() - return self.make_a_bid() + return self.make_a_bid(game_events=game_events) else: if self.AI: return self.AI.call_partner() @@ -69,7 +69,7 @@ class Player(cards.Deck): return self.remove_card(pos) return self.make_a_play(sub_state, game_events=game_events) - def make_a_bid(self): + def make_a_bid(self, game_events=None): """ The procedure to make a bid :return: A valid bid number @@ -191,6 +191,36 @@ class MainPlayer(Player): self.left_mouse_down = False self.double_clicking = False + def make_a_bid(self, game_events=None): + """ + The procedure to make a bid + :return: A valid bid number + """ + if game_events: + for event in game_events: + if event.type == CALL_EVENT: + bid = event.call + print(bid) + + if not bid: + return 0 + + bid = cards.convert_bid_string(bid) + if bid < 0: + print("Error in processing bid") + return -1 + + if self._table_status["bid"] >= bid: + if bid > 75: + print("You cannot bid beyond 7 No Trump") + else: + print("You might need to bid higher") + return -1 + return bid + return -1 + return -1 + + def make_a_play(self, substate, game_events=None): card = None if game_events: diff --git a/table.py b/table.py index 2167ee6..b3bdc72 100644 --- a/table.py +++ b/table.py @@ -8,7 +8,7 @@ import copy import time from signalslot import Signal from ai_comp import ai -from game_consts import GameState, PlayerRole, STARTING_HAND, NUM_OF_PLAYERS +from game_consts import GameState, PlayerRole, STARTING_HAND, NUM_OF_PLAYERS, CALL_EVENT VIEW_TRANSPARENT = False # Make the text box not transparent @@ -49,10 +49,10 @@ class Table: All played cards go into a hidden discard pile. """ - update_table = Signal() def __init__(self, x, y, width, height, clear_colour, autoplay=False, view_all_cards=False): # TODO: Reduce the amount of update_table call + self.update_table = Signal() self.x = x self.y = y self.width = width @@ -195,8 +195,15 @@ class Table: self.calling_panel = UI.CallPanel(playdeckx[0]+w_deck+5,playdecky[0]+w_deck-140, 250, 140) self.calling_panel.parent = self + self.calling_panel.visible = False self.parent = None + self.calling_panel.confirm_output.connect(self.emit_call) + + def emit_call(self, output, **kwargs): + print(output) + pygame.event.post(pygame.event.Event(CALL_EVENT, call=output)) + def get_offset_pos(self): x, y = 0, 0 if self.parent: @@ -306,7 +313,6 @@ class Table: if draw_update: self.update_table.emit() - def continue_game(self, game_events): """ This is where the FSM is. State transition should occur here. @@ -329,7 +335,7 @@ class Table: self.write_message("Start to Bid") self.prepare_bidding() elif self.game_state == GameState.BIDDING: - bid_complete = self.start_bidding() + bid_complete = self.start_bidding(game_events) if bid_complete: self.game_state = GameState.PLAYING self.update_all_players(role=True, wins=True) @@ -385,30 +391,52 @@ class Table: 1 * (not self.first_player)) % NUM_OF_PLAYERS) self.write_message(msg, line=2, delay_time=1) - def start_bidding(self): + self.calling_panel.list1.replace_list([str(i+1) for i in range(7)]) + self.calling_panel.list2.replace_list(['Clubs', 'Diamonds', 'Hearts', 'Spades', 'No Trump']) + self.calling_panel.cancel_button.visible = True + self.calling_panel.redraw() + + self.update_table.emit() + + def start_bidding(self, game_events): """ The bidding procedure. :return: Whether bidding is completed """ # Highest bid: 7 NoTrump. No further check required if self.passes < NUM_OF_PLAYERS - 1 and self.table_status["bid"] < 75: - player_bid = self.players[self.current_player].make_decision(self.game_state, 0) + if not self.require_player_input: + if not self.players[self.current_player].AI: + self.require_player_input = True + self.calling_panel.visible = True + self.update_table.emit() + return + else: + player_bid = self.players[self.current_player].make_decision(self.game_state, 0) + else: + player_bid = self.players[self.current_player].make_decision(self.game_state, 0, game_events) + + if player_bid < 0: + return + self.require_player_input = False + self.calling_panel.visible = False + self.update_table.emit() + if not player_bid: if not self.first_player: # Starting bidder pass do not count at the start self.passes += 1 else: self.table_status["bid"] = player_bid self.passes = 0 + msg = "Current Bid: {0:d} {1:s}".format(self.table_status["bid"] // 10, + cards.get_suit_string(self.table_status["bid"] % 10)) + self.write_message(msg, line=1, update_now=False) + msg = 'Bid Leader: Player {0:d}'.format(self.current_player) + self.write_message(msg, line=2, update_now=True) if self.table_status["bid"] < 75: self.current_player += 1 self.current_player %= NUM_OF_PLAYERS - msg = "Current Bid: {0:d} {1:s}".format(self.table_status["bid"] // 10, - cards.get_suit_string(self.table_status["bid"] % 10)) - self.write_message(msg, line=1, update_now=False) - msg = 'Bid Leader: Player {0:d}'.format((self.current_player - self.passes - - 1 * (not self.first_player)) % NUM_OF_PLAYERS) - self.write_message(msg, line=2, update_now=False) self.display_current_player(self.current_player) if self.first_player: self.first_player = False