def getInput(self): # get user input, :: printed by GUI thread already command = input('') data = None # first check command isn't empty if command != '': # draw cards if command[0] == 'D': data = Move('Draw', self.gui.active_draw) # pickup discard pile elif command[0] == 'P': data = Move('Pickup') # select which cards to play facedown elif command[0] == 'F': try: data = self.inputToMove('Facedown', command[1:]) # strip the leading F except Exception as e: print(e) # otherwise assume it is a list of cards to play else: data = self.inputToMove('Play', command) return data
def play_cards(self, index): # make index an array to play multiple cards try: # try to pop selected cards out of player hand _, active_hand = self.valid_cards() cards = [ active_hand.pop(i) for i in sorted(index, reverse=True) ] # Pop indexed cards, need to pop in reverse over to preserve index except IndexError: # if we can't pop cards, we have been given bad index so is_valid is False return False log.debug(f'2. Cards: {cards}') move = Move('Play', cards) is_valid, move_return = self.game.do_move( self.id, move ) # do_move will return the card if not valid move, otherwise empty # Need to make this return card to same position it was played out of, # perhaps if unsuccessful, don't send any card data just say it failed sorry fam if len(move_return) > 0: for i, ind in enumerate(index): self.in_hand[ind:ind] = [move_return[i]] log.debug(f'4. Move is {is_valid}') return is_valid
def on_block_button_press_event(self, widget, event, block): if self.current_piece is None: return # Filter double/triple click events if event.type != Gdk.EventType.BUTTON_PRESS: return if event.button == 1: move = Move( player_id=self.player_id, piece_id=self.current_piece.piece_id, rotation=self.current_piece_rotation, mirror=self.current_piece_mirror, position=self.index(block), ) if self.is_valid_move(move): self.move_queue.put(move, block=False) self.on_block_leave(widget, event, block) self.current_piece = None else: print self._valid_reason elif event.button == 2: self.on_block_leave(widget, event, block) self.current_piece_mirror = not self.current_piece_mirror self.on_block_enter(widget, event, block) elif event.button == 3: self.on_block_leave(widget, event, block) self.current_piece_rotation = (self.current_piece_rotation + 1) % 4 self.on_block_enter(widget, event, block)
def guiThread(self): data = self.getInput() # send move if we have data if data is not None: self.socket.send(data.encode()) log.debug('Sent play') # if we have no data, send out null will only cost 26 bytes else: self.socket.send(Move().encode())
def get_move(self): for piece in self.board.get_remaining_piece_ids(self.player_id): for rotation in xrange(4): for mirror in (False, True): for x in xrange(self.board.cols): for y in xrange(self.board.rows): move = Move(self.player_id, piece, rotation, mirror, (x, y)) if self.board.is_valid_move(move): return move return super(ExhaustiveSearchBot, self).get_move()
def thread(self): while self.player.valid_cards() != ([], None): try: package = self.listen_for_moves() except BufferError: # except network error, want to shut down thread/connection log.warning(f'Player {self.id} disconnected') # client has disconnected so stop trying to send data Client.clients = [ client for client in Client.clients if client.id != self.id ] break log.info('Handling package') move = Move(**package) was_valid = self.player.execute_move(move) try: if was_valid is True: log.info('Sending package to all') self.sendTo(Client.clients, Client.observers, f'Yeet from server courtesy of {self.id}') else: log.info(f'Sending package to {self.id}') # need to wrap self in a list here as we try to iterate over it self.sendTo([self], [], f'Yeet from server courtesy of {self.id}') except Exception as e: print(e) if Client.game.current_turn != 0: # if we have got to this point we have no cards or disconnected from server mid game and # so we should be removed from the game Client.game.remove_player(self.id) # if we remove a client, we sync client.id with player id for client in Client.clients: client.id = client.player.id # allow player to spectate the rest of the game while Client.game.winner is False: try: # send updates pass except KeyboardInterrupt: break # Go straight to exit else: for client in Client.clients: if client.id > self.id: client.id -= 1 client.player = Client.game.players[client.id] self.id = 99 # do this so we can easily identify the current client so we can close it self.close() # remove client and close thread
def inputToMove(self, action, string): str_cards = '[' + string + ']' # convert input into string list # try to eval sting list into valid list try: cards = list(literal_eval(str_cards)) assert (any(isinstance(x, int) for x in cards)) # if we can't convert to a valid list, we start again except (AssertionError, SyntaxError, ValueError): print('Not a valid command, try again you derelict') return self.getInput() return Move(action, cards)
def is_valid_move(self, player, move): action = move.action cards = move.cards is_valid = False if player == self.current_player: if action == 'Draw': # If current draw cards, valid to draw if cards == self.no_to_draw: is_valid = True # Also check that we cannot play anything else so draw won't be a valid move playing_from, other_cards = self.players[player].valid_cards() # Deal with playing from facedown as a legit move if playing_from != 'face_down': for card in other_cards: _, other_valid = self.is_valid_move( player, Move('Play', [card])) if other_valid: is_valid = False break # Insert gambling logic here else: pass elif action == 'Play': no_cards = len(cards) log.info(f'\nsame active cards: {self.same_active_cards}') log.info(f'len cards: {len(cards)}\n') # Check if we are trying to play more than one card if no_cards > 1: # Need to be same value if playing multiple cards values = [card.value for card in cards] same_cards = len(set(values)) == 1 if not same_cards: # Return straight away if not the same cards return action, False # Normal gameplay if self.active_draw_card is None: # This takes into account power cards: 6 >= 7 is True, Draw 4 >= Draw 2 == False if cards[0] >= self.active_card: is_valid = True # Check for burns if len(self.discard_pile.deck) != 0: if cards[0].value == self.discard_pile.deck[ 0].value: log.info( f'inn turnn burn, 4 should be {no_cards + self.same_active_cards}' ) if no_cards + self.same_active_cards == 4: action = 'Burn' # 10 auto burns, easy case elif len(cards) == 4 or cards[0].value == '10': action = 'Burn' elif len(cards) == 4 or cards[0].value == '10': action = 'Burn' # There is an active draw card else: if cards[0] >= self.active_draw_card: is_valid = True # Still want to burn if cards[0].value == self.discard_pile.deck[0].value: if no_cards + self.same_active_cards == 4: action = 'Burn' elif action == 'Pickup': # cannot pickup if there is a draw card if self.active_draw_card is None: # otherwise assume pickup is true is_valid = True active_hand, playable_cards = self.players[ player].valid_cards() # if we are playing out of face down cards, pickup will always be true # however if you can play something else, not valid to pickup for card in playable_cards: print(f'card: {card.value}') _, other_valid = self.is_valid_move( player, Move('Play', [card])) print(other_valid) if other_valid is True: is_valid = False break else: if action == 'Play': # Check if we are trying to play more than one card if len(cards) > 1: # Need to be same value if playing multiple cards values = [card.value for card in cards] same_cards = len(set(values)) == 1 if not same_cards: # Return straight away if not the same cards return action, False # Deal with the case when we are playing the first card if self.active_card == '': log.debug(f'3. Playing on {self.active_card}') # Need to be same value if playing multiple cards values = [card.value for card in cards] log.debug(f'3.1 Values: {values}') same_cards = set(values) == set( ['4']) # Check we are playing a 4 log.debug(f'3.2 Same cards: {same_cards}') if same_cards: # TO DO: EXPAND THIS IF NO 4s (pretty rare I think) # MAKE PEOPLE PICK UP IN ORDER TILL SOMEONE HAS A 4 to play # P(No 4s) = C(124/HAND SIZE * NO PLAYERS) / C(132/HAND SIZE * NO PLAYERS) = 0.6% is_valid = True # Otherwise assume we are trying to burn out of turn else: if len(self.discard_pile.deck) != 0: if cards[0].value == self.discard_pile.deck[0].value: if len(cards) + self.same_active_cards == 4: log.info('out of turn burnnnnnnn') action = 'Burn' is_valid = True log.info(f'action {action}, valid {is_valid}') return action, is_valid