class PseudoHeart(Htapi): """ Create a virtual game which can play faster! """ game_score_cards = [Card("QS"), Card("TC"), Card("2H"), Card("3H"), Card("4H"), Card("5H"), Card("6H"), Card("7H"), Card("8H"), Card("9H"), Card("TH"), Card("JH"), Card("QH"), Card("KH"), Card("AH")] # If have all penalty cards, the player shoots the moon and win a lot. game_penalty_cards = [Card("QS"), Card("2H"), Card("3H"), Card("4H"), Card("5H"), Card("6H"), Card("7H"), Card("8H"), Card("9H"), Card("TH"), Card("JH"), Card("QH"), Card("KH"), Card("AH")] def __init__(self, player_bots): if len(player_bots) != 4: print ("Invalid player num != 4") sys.exit() self.db = {} self.htapi = Htapi(is_debug=True) self.game_heart_cards = self.htapi.get_cards_by_suit(self.htapi.get52cards(), 'H') # Save bot object into self.player_bots self.player_tups = [] id = 0 for p in player_bots: player_tup = { # Constant data 'bot': p, 'name': p.get_name(), 'id': id, # Deal data 'hand_origin': [], 'recv3': [], 'pass3': [], 'hand': [], 'pick': [], 'round_pick': [], 'shoot': [], 'expose': False, 'score': 0, 'shoot_moon': False, # Game data 'score_game': 0, # MISC data 'score_accl': 0, 'shoot_moon_accl': 0, 'score_negative_accl': 0, 'winner': [0, 0, 0, 0], } self.player_tups.append(player_tup) self.htapi.msg("Add new player: " + player_tup['name']) id += 1 self.db['dealNumber'] = 0 self.db['gameNumber'] = 0 def player_tups_rotate(self, shift=1): """ Shift order of the players. """ for i in range(shift): p = self.player_tups.pop(0) self.player_tups.append(p) def game_next_deal(self): self.db['dealNumber'] += 1 self.db['heartBreak'] = False self.db['expose'] = False self.db['unusedCards'] = self.htapi.get52cards() self.db['usedCards'] = [] self.db['roundNumber'] = 0 self.db['is_first_play'] = True for ptup in self.player_tups: ptup['pick'] = [] ptup['recv3'] = [] ptup['pass3'] = [] ptup['hand_origin'] = [] ptup['hand'] = [] ptup['shoot'] = [] ptup['score'] = 0 ptup['shoot_moon'] = False # Re-arrange position to default layout for i in range(0, 4): ctup = self.player_tups[0] if ctup['id'] == 0: break else: self.player_tups_rotate(1) def game_next_round(self): self.db['roundNumber'] += 1 self.db['roundCards'] = [] for ptup in self.player_tups: ptup['round_pick'] = [] def _ev_new_game(self, ptup): """ Event: new game """ players = [] for ptup_this in self.player_tups: pdata = { 'playerName': ptup_this['name'], 'playerNumber': ptup_this['id'], 'status': 0 } players.append(pdata) data = { 'players': players } pbot = ptup['bot'] pbot.new_game(data) def _ev_receive_cards(self, ptup): """ Event: receive_cards data = {"players": [{"playerName": "hac", "cards": ["3S", "8S", "9S", "TS", "JS", "4H", "6H", "9H", "3D", "7D", "9D", "JD", "8C"], "dealNumber": 1, "gameNumber": 1}]} """ data = { 'players': [ { 'playerName': ptup['name'], 'cards': self.htapi.clone_cards([x.toString() for x in ptup['hand']]), 'gameNumber': self.db['gameNumber'], 'dealNumber': self.db['dealNumber'], }, { 'playerName': 'self', 'cards': self.htapi.clone_cards([x.toString() for x in ptup['hand']]), 'gameNumber': self.db['gameNumber'], 'dealNumber': self.db['dealNumber'], }, # TBD: Add other players' data? Not necessary, I guess. ] } # Call player method. pbot = ptup['bot'] pbot.receive_cards(data) def _ev_deal_end(self, ptup): """ End of a deal """ data = {} data['dealNumber'] = self.db['dealNumber'] data['roundNumber'] = self.db['roundNumber'] data['gameNumber'] = self.db['gameNumber'] data['players'] = [] data_players = data['players'] for ptup_this in self.player_tups: # Setup this player data. pld = {} pld['playerNumber'] = ptup_this['id'] pld['playerName'] = ptup_this['name'] pld['dealScore'] = ptup_this['score'] pld['gameScore'] = ptup_this['score_game'] pld['scoreCards'] = self.htapi.clone_cards([x.toString() for x in ptup_this['pick']]) pld['initialCards'] = self.htapi.clone_cards([x.toString() for x in ptup_this['hand_origin']]) pld['receivedCards'] = self.htapi.clone_cards([x.toString() for x in ptup_this['recv3']]) pld['pickedCards'] = self.htapi.clone_cards([x.toString() for x in ptup_this['pass3']]) pld['shootingTheMoon'] = ptup_this['shoot_moon'] # Add data into list data_players.append(pld) pbot = ptup['bot'] pbot.deal_end(data) def _ev_pass3cards(self, ptup): """ Output: picked 3 cards """ data = {} data['self'] = { 'cards': self.htapi.clone_cards([x.toString() for x in ptup['hand']]) } pbot = ptup['bot'] picked = pbot.pass_cards(data) if picked == None or len(picked) != 3: self.htapi.errmsg("Invalid picked num of bot: " + ptup['name']) # Convert ['XX', 'OO', 'AA'] into Card list picked = [Card(c) for c in picked] # Verify if there's any duplicated picked card to avoid buggy bot. for c in picked: if picked.count(c) != 1: self.errmsg("Player: " + ptup['name'] + " tries to pick invalid cards: " + format(picked)) # Check picked cards are really in list to keep away from stupid bot. found_cards = self.htapi.find_cards(ptup['hand'], picked) if found_cards == None or len(found_cards) != 3: self.htapi.errmsg("Player: " + ptup['name'] + " attemps to pick invalid cards") return picked def _ev_receive_opponent_cards(self, ptup, picked, received): """ Pass 3 cards to the player """ data = { 'players': [ { 'playerName': ptup['name'], 'cards': self.htapi.clone_cards([x.toString() for x in ptup['hand']]), 'pickedCards': self.htapi.clone_cards([x.toString() for x in picked]), 'receivedCards': self.htapi.clone_cards([x.toString() for x in received]), 'gameNumber': self.db['gameNumber'], 'dealNumber': self.db['dealNumber'], }, { 'playerName': 'self', 'cards': self.htapi.clone_cards([x.toString() for x in ptup['hand']]), 'pickedCards': self.htapi.clone_cards([x.toString() for x in picked]), 'receivedCards': self.htapi.clone_cards([x.toString() for x in received]), 'gameNumber': self.db['gameNumber'], 'dealNumber': self.db['dealNumber'], }, # TBD: Add other players' data? Not necessary, I guess. ] } pbot = ptup['bot'] pbot.receive_opponent_cards(data) def _ev_expose_ah(self, ptup): """ Ask the player if he wants to expose AH. """ data = { 'dealNumber': self.db['dealNumber'], 'cards': ['AH'] } pbot = ptup['bot'] card = pbot.expose_my_cards(data) if card == None: # The player rejects to expose AH return False return True def _ev_turn_end(self, ptup, turn_ptup, card2shoot): data_players = [] data_player = {} data_player['playerName'] = turn_ptup['name'] data_player['cards'] = self.htapi.clone_cards([card2shoot.toString()]) data_players.append(data_player) data = { 'turnCard': card2shoot.toString(), 'turnPlayer': turn_ptup['name'], 'players': data_players, 'serverRandom': str(False) } pbot = ptup['bot'] pbot.turn_end(data) def _ev_round_end(self, ptup, next_lead_ptup): """ Inform the user the end of a round """ data = {'roundPlayers': [], 'roundPlayer': next_lead_ptup['name'], 'players': []} data_round_players = data['roundPlayers'] for ptup_this in self.player_tups: data_round_players.append(ptup_this['name']) data_players = data['players'] for ptup_this in self.player_tups: dp = {} dp['playerNumber'] = ptup_this['id'] dp['playerName'] = ptup_this['name'] dp['gameScore'] = ptup_this['score_game'] dp['dealScore'] = ptup_this['score'] dp['shootingTheMoon'] = ptup_this['shoot_moon'] dp['roundCard'] = ptup_this['shoot'][-1].toString() data_players.append(dp) pbot = ptup['bot'] pbot.round_end(data) def _ev_expose_ah_end(self, ptup): """ Inform the player the expose result. """ data = {'players': []} data_players = data['players'] for ptup_this in self.player_tups: data_this = {} data_this['playerNumber'] = ptup_this['id'] data_this['playerName'] = ptup_this['name'] if ptup_this['expose'] == True: data_this['exposedCards'] = [Card('AH').toString()] else: data_this['exposedCards'] = [] data_players.append(data_this) if ptup_this['name'] == ptup['name']: # Add a 'self'! another_data_this = dict(data_this) another_data_this['playerName'] = 'self' data['self'] = another_data_this pbot = ptup['bot'] pbot.expose_cards_end(data) def _ev_game_end(self, ptup): """ Inform the player the game result. """ data = {'players': []} data_players = data['players'] for ptup_this in self.player_tups: data_this = {} data_this['playerName'] = ptup_this['name'] data_this['gameScore'] = ptup_this['score_accl'] data_players.append(data_this) pbot = ptup['bot'] pbot.game_over(data) def game_new_deal_manual_deliver(self): """ Deliever fixed 13 cards to each player. ['7C', '2C', '2D', 'JS', 'QD', '4C', 'QC', 'QS', '3C', 'TD', '2H', '7D', 'KS', '8C', 'JD', '6D', '3D', 'KC', 'AS', '9D', '8S', 'AD', 'TS', '7H', '5D', '4H', '8H', '3H', 'AC', '8D', 'KH', 'KD', '4S', '9C', '2S', '4D', 'JH', '5C', 'JC', 'AH', '9H', 'TH', '9S', '6S', '6H', '7S', 'QH', 'TC', '3S', '6C', '5H', '5S'] """ card2deliver = [ ['8C', 'JD', '6D', '3D', 'KC', 'AS', '9D', '8S', 'AH', 'TS', 'JH', '5D', 'QH'], ['7C', '2C', '2D', 'JS', 'QD', '4C', 'QC', 'QS', '3C', 'TD', '2H', '7D', 'KS'], ['8H', '3H', 'AC', '8D', 'AD', 'KD', '4S', '9C', '2S', '4D', '7H', '5C', 'JC'], ['KH', '9H', 'TH', '9S', '6S', '6H', '7S', '4H', 'TC', '3S', '6C', '5H', '5S'] ] # Avoid stupid assignment error. Check if there're 52 different cards tgt = [] card52 = self.htapi.get52cards() for card13 in card2deliver: card13 = [Card(x) for x in card13] tgt += card13 if len(self.htapi.find_cards(tgt, card52)) != 52: self.htapi.errmsg("Invalid card2deliver table.") for ptup in self.player_tups: if ptup['id'] >= 4: self.htapi.errmsg("Cannot allow player id >= 4") # Assume player id is from 0 ~ 3, so assign the cards directly. ptup['hand'] = [Card(x) for x in card2deliver[ptup['id']]] ptup['hand_origin'] = [Card(x) for x in card2deliver[ptup['id']]] def game_new_deal_random_deliver(self): unused_card = self.htapi.get52cards() unused_card = self.htapi.shuffle_cards(unused_card) if len(self.player_tups) != 4: self.htapi.errmsg("Invalid player bot num") # Save 13 cards for ptup in self.player_tups: picked = [] for i in range(13): picked.append(unused_card.pop()) # The player get 13 cards. Generate event: 'receive_cards' picked = self.htapi.arrange_cards(picked) ptup['hand'] = picked ptup['hand_origin'] = self.htapi.clone_cards(picked) def game_new_deal(self): """ Deliver 13 cards to each player """ manual_deliver = False if manual_deliver == True: self.htapi.msg(" *** WARNING: You are running in manual-deliver mode") self.game_new_deal_manual_deliver() else: self.game_new_deal_random_deliver() # Inform every player for ptup in self.player_tups: self._ev_receive_cards(ptup) def game_pass3cards(self): """ Each player has to pick 3 cards and pass to one opponent. """ picked = {} for ptup in self.player_tups: picked[ptup['name']] = self._ev_pass3cards(ptup) # Remove the picked 3 cards! removed = self.htapi.remove_cards(ptup['hand'], picked[ptup['name']]) if len(removed) != 3: self.htapi.errmsg("Cannot pop picked 3 cards of user: "******""" Get position of a player. (position=1 ~ 4) position = 1 means he is the lead playerr. """ position = 1 for p in self.player_tups: if p['name'] == ptup['name']: return position position += 1 self.errmsg("Cannot find player position") return None def _get_candidates(self, ptup): """ Help players to find candidate cards. """ if self.db['is_first_play'] == True: self.db['is_first_play'] = False candidates = [Card('2C')] return candidates player_pos = self._get_player_pos(ptup) if player_pos == 1: if self.db['heartBreak'] == True: # Can select any card after heart break. candidates = ptup['hand'] return candidates else: # Not heart break. Cannot pick heart unless there's no other suit. candidates = self.htapi.get_cards_by_suits(ptup['hand'], ['S', 'D', 'C']) if len(candidates) == 0: # Only heart suit left. Allow heart break, of course. candidates = ptup['hand'] return candidates else: # Follow the leading card unless there's no the same suit. round_cards = self.db['roundCards'] lead_card = round_cards[0] candidates = self.htapi.get_cards_by_suit(ptup['hand'], lead_card.get_suit()) if len(candidates) > 0: return candidates else: # No card in the same suit. Can pick any card. candidates = ptup['hand'] return candidates self.errmsg("Cannot get candidate cards") return None def _ev_pick_card(self, ptup): """ Event: pick_card - Ask the player to shoot a card. """ # round cards round_cards = self.db['roundCards'] # Candidate cards candidates = self._get_candidates(ptup) candidates = self.htapi.arrange_cards(candidates) candidates = self.htapi.clone_cards(candidates) # Round players round_players = [] for ptup_this in self.player_tups: rp = {} rp = ptup_this['name'] round_players.append(rp) data = {'self': { 'cards': [x.toString() for x in ptup['hand']], 'candidateCards': [x.toString() for x in candidates], 'gameNumber': self.db['gameNumber'], 'dealNumber': self.db['dealNumber'], 'roundCard': '' }, 'roundPlayers': round_players } pbot = ptup['bot'] card2shoot = pbot.pick_card(data) card2shoot = Card(card2shoot) return card2shoot def game_shoot1card(self, ptup): """ Make the player shoot 1 card! """ card2shoot = self._ev_pick_card(ptup) if self.htapi.find_card(ptup['hand'], card2shoot) == None: self.errmsg("Cannot shoot un-existed card at player: " + ptup['name']) # Shoot the card. removed = self.htapi.remove_card(ptup['hand'], card2shoot) if removed == None: self.errmsg("(BUG) Cannot remove player card: " + card2shoot.toString() + ", " + ptup['name']) ptup['shoot'].append(card2shoot) self.db['roundCards'].append(card2shoot) self.db['usedCards'].append(card2shoot) self.db['unusedCards'].remove(card2shoot) if card2shoot.get_suit() == 'H': self.db['heartBreak'] = True for ptup_this in self.player_tups: self._ev_turn_end(ptup_this, ptup, card2shoot) def _recalculate_round_score(self, ptup): """ Calculate player's score in current round. """ score = 0 picked_cards = ptup['pick'] my_score_cards = self.htapi.find_cards(picked_cards, self.game_score_cards) my_heart_cards = self.htapi.find_cards(picked_cards, self.game_heart_cards) my_penalty_cards = self.htapi.find_cards(picked_cards, self.game_penalty_cards) if self.db['expose'] == True: score = len(my_heart_cards) * 2 * (-1) else: score = len(my_heart_cards) * (-1) if self.htapi.find_card(my_score_cards, Card('QS')) != None: score += -13 if self.htapi.find_card(my_score_cards, Card('TC')) != None: score *= 2 if len(self.htapi.find_cards(my_score_cards, my_penalty_cards)) == len(self.game_penalty_cards): # Shoot the moon. Score becomes postive! Score x 4! score *= -1 score *= 4 ptup['shoot_moon'] = True ptup['score'] = score def game_round_end(self, round_num): """ Decide round player, round player = the player "win" the round. """ round_cards = self.db['roundCards'] if len(round_cards) != 4: self.htapi.errmsg("Invalid cards of this round.") lead_card = round_cards[0] lead_ptup = self.player_tups[0] lead_card_suit = lead_card.get_suit_num() lead_card_rank = lead_card.get_rank_num() idx = 0 rotate = 0 next_lead_ptup = lead_ptup highest_rank = lead_card_rank for ptup in self.player_tups: if idx == 0: # This is lead player. Skip idx += 1 continue shoot_card = round_cards[idx] shoot_card_suit = shoot_card.get_suit_num() shoot_card_rank = shoot_card.get_rank_num() if shoot_card_suit == lead_card_suit: if shoot_card_rank > highest_rank: next_lead_ptup = ptup rotate = idx highest_rank = shoot_card_rank idx += 1 # The cards belong to the next lead player. Give him the cards. next_lead_ptup['pick'] += round_cards next_lead_ptup['round_pick'] = round_cards """ Calculate scores of this round and store the result. """ for ptup in self.player_tups: self._recalculate_round_score(ptup) """ Inform the user an event """ for ptup in self.player_tups: self._ev_round_end(ptup, next_lead_ptup) """ Rotate ptup position for next round. """ if rotate > 0: self.player_tups_rotate(rotate) def show_score(self): for ptup in self.player_tups: self.htapi.dbg( "Player: " + ptup['name'] + ", score: " + str(ptup['score']) + ", score_accl: " + str(ptup['score_accl']) + ", score_negative_accl:" + str(ptup['score_negative_accl']) + ", shoot_moon_accl: " + str(ptup['shoot_moon_accl']) + ", winner: " + str(ptup['winner']) ) def game_round(self, round_num): """ Play 1 round = 4 turn """ self.htapi.msg("Round: " + str(self.db['roundNumber']) + ", Deal: " + str(self.db['dealNumber']) + ", Game: " + str(self.db['gameNumber'])) for ptup in self.player_tups: self.game_shoot1card(ptup) self.game_round_end(round_num) def game_finish_deal(self): """ The end of a single deal. """ score_list = [] for ptup in self.player_tups: if ptup['score'] < 0: ptup['score_negative_accl'] += ptup['score'] ptup['score_accl'] += ptup['score'] ptup['score_game'] += ptup['score'] if ptup['shoot_moon'] == True: ptup['shoot_moon_accl'] += 1 score_list.append({'name': ptup['name'], 'score': ptup['score'], 'ptup': ptup}) score_list_sorted = sorted(score_list, key=lambda v: v['score']) winner = score_list_sorted[3]['ptup'] winner['winner'][0] += 1 # winner winner = score_list_sorted[2]['ptup'] winner['winner'][1] += 1 # 2nd winner winner = score_list_sorted[1]['ptup'] winner['winner'][2] += 1 # 3rd... winner = score_list_sorted[0]['ptup'] winner['winner'][3] += 1 # loser... # Inform players for ptup in self.player_tups: self._ev_deal_end(ptup) def game_expose_ah(self): for ptup in self.player_tups: hand_cards = ptup['hand'] card_ah = self.htapi.find_card(hand_cards, Card('AH')) if card_ah != None: if self._ev_expose_ah(ptup): # The player decides to expose, all heart score will double ptup['expose'] = True self.db['expose'] = True break # Inform players expose end. for ptup in self.player_tups: self._ev_expose_ah_end(ptup) def game_decide_lead_player(self): """ Decide who must start the deal, i.e. the player has 2C. """ for i in range(0, 4): ptup = self.player_tups[0] card2c = self.htapi.find_card(ptup['hand'], Card('2C')) if card2c != None: break else: self.player_tups_rotate(1) def game_play1deal(self): """ event: new_deal """ self.game_new_deal() """ event: receive_opponent_cards & pass_cards """ self.game_pass3cards() self.game_decide_lead_player() """ event: Ask if the player wants to expose AH """ self.game_expose_ah() """ event: your_turn & turn_end & expose_cards & expose_cards_end && round_end """ for round in range(1, 14): # Round 1 to 13 self.game_next_round() self.game_round(round) self.game_finish_deal() def game_next_game(self): self.db['gameNumber'] += 1 self.db['dealNumber'] = 0 for ptup in self.player_tups: # Reset game-specific data ptup['score_game'] = 0 def game_over(self): """ playerName, gameScore """ for ptup in self.player_tups: self._ev_game_end(ptup) def game_single(self): """ There're 16 deals in a game """ self.game_next_game() # Inform the players there's a new game to start. for ptup in self.player_tups: self._ev_new_game(ptup) DEAL_PER_GAME = 16 for deal in range(1, DEAL_PER_GAME + 1): self.game_next_deal() self.game_play1deal() self.game_over() self.show_score() random.shuffle(self.player_tups) def game_loop(self, loop_max=1): for loop in range(1, loop_max + 1): self.game_single()
class HacBotV(PokerBot, Htapi): """ Anti-Score Mode (AS) Shoot-Moon Mode (SM) ...Player: HacBotV, score: 0, score_accl: 24516, score_negative_accl:-56136, shoot_moon_accl: 273, winner: [2234, 1790, 1280, 1096] """ SM_THOLD_PASS3 = 8.0 SM_THOLD_PICK = 9.5 AS_THOLD_PASS3 = 7.0 def __init__(self, name, is_debug=False): super(HacBotV, self).__init__(name) self.htapi = Htapi(is_debug=is_debug) self.players = {} self.stat = {} self.stat['roundCard'] = [] self.stat['usedCard'] = [] self.stat['nextPlayers'] = [] self.stat['roundPlayers'] = [] self.big_rank_cards = [ Card('AS'), Card('KS'), Card('JS'), Card('TS'), Card('AH'), Card('KH'), Card('QH'), Card('JH'), Card('TH'), Card('AD'), Card('KD'), Card('QD'), Card('JD'), Card('TD'), Card('AC'), Card('KC'), Card('QC'), Card('JC'), Card('TC'), ] self.score_cards = [ Card("QS"), Card("TC"), Card("2H"), Card("3H"), Card("4H"), Card("5H"), Card("6H"), Card("7H"), Card("8H"), Card("9H"), Card("TH"), Card("JH"), Card("QH"), Card("KH"), Card("AH") ] self.plz_rebuild_players = True self.stat['expose_ah_mode'] = False self.stat['sm_mode'] = True self.stat['sm_mode_started'] = False self.stat['pass3_as_ability'] = 0 self.stat['pass3_as_ability_history'] = [] self.stat['pass3_sm_ability'] = 0 self.stat['pass3_sm_ability_history'] = [] def _rebuild_players(self, data): """ Configure player table by input data. """ self.htapi.dbg("Rebuild players: ", format(data)) data_players = data['players'] if len(data_players) != 4: self.errmsg("Invalid player number " + str(len(data_players)) + " in server msg") self.players = {} for dp in data_players: self.players[dp['playerName']] = { 'playerName': dp['playerName'], 'score_accl': 0, 'score': 0, 'sm': 0, 'expose': False, 'shoot': [], 'suit_leak': [], 'pick': [], } def new_game(self, data): """ Event: The start of a new game. """ if self.plz_rebuild_players == True: self._rebuild_players(data) self.plz_rebuild_players = False def receive_cards(self, data): """ Event: Receive my 13 cards. """ self.stat['hand'] = self.get_cards(data) self.stat['hand'] = self.htapi.clone_cards(self.stat['hand']) def _calc_hand_cards_num(self, my_hand_cards, reverse=False): card_num_stat = {'S': 0, 'H': 0, 'D': 0, 'C': 0} for c in my_hand_cards: card_num_stat[c.get_suit()] += 1 card_num_stat_sorted = sorted(card_num_stat.iteritems(), key=lambda (k, v): (v, k), reverse=reverse) return card_num_stat_sorted def _select_card2pass_sm_mode(self, my_hand_cards): """ Pop out 1 card to pass Reserve cards in long suit. Remove small cards in shortage suit. """ card_num_stat_sorted = self._calc_hand_cards_num(my_hand_cards) # Remove small cards in shortage suit. for di in card_num_stat_sorted: suit, num = di if num == 0: continue if suit == 'H': continue same_suit_cards = self.htapi.get_cards_by_suit(my_hand_cards, suit) smaller_card = self.htapi.pick_smaller_card(same_suit_cards, [Card('9S')], auto_choose_big=False) if smaller_card != None: return self.htapi.remove_card(my_hand_cards, smaller_card) # Remove suit for di in card_num_stat_sorted: suit, num = di if num == 0: continue if suit == 'H': continue same_suit_cards = self.htapi.get_cards_by_suit(my_hand_cards, suit) small_card = self.htapi.pick_small_card(same_suit_cards) return self.htapi.remove_card(my_hand_cards, small_card) # Remove suit, in case I have 13 hearts... A.A for di in card_num_stat_sorted: suit, num = di if num == 0: continue same_suit_cards = self.htapi.get_cards_by_suit(my_hand_cards, suit) small_card = self.htapi.pick_small_card(same_suit_cards) return self.htapi.remove_card(my_hand_cards, small_card) self.htapi.errmsg("BUG") def _select_card2pass(self, my_hand_cards): """ Pop out 1 card to pass. Anti-score mode. - Remove big rank card from shortage suit - Remove big rank card in long suit """ card_num_stat_sorted = self._calc_hand_cards_num(my_hand_cards) # card_num_stat_sorted_dict = dict(card_num_stat_sorted) if self.htapi.find_card(my_hand_cards, Card('QS')) == None: card = self.htapi.remove_card(my_hand_cards, Card('AS')) if card != None: return card # Spade in shortage. KS and AS will be dangerous. card = self.htapi.remove_card(my_hand_cards, Card('KS')) if card != None: return card # Remove big rank card from shortage suit for di in card_num_stat_sorted: suit, num = di if num == 0: continue same_suit_cards = self.htapi.get_cards_by_suit(my_hand_cards, suit) big_card = self.htapi.pick_big_card(same_suit_cards) if big_card.get_rank_num() > Card('8S').get_rank_num(): return self.htapi.remove_card(my_hand_cards, big_card) # Remove suit for di in card_num_stat_sorted: suit, num = di if num == 0: continue same_suit_cards = self.htapi.get_cards_by_suit(my_hand_cards, suit) big_card = self.htapi.pick_big_card(same_suit_cards) return self.htapi.remove_card(my_hand_cards, big_card) self.htapi.errmsg("BUG") def _get_used_cards(self): return self.stat['usedCard'] def _get_used_cards_by_suits(self, suits=[]): used_cards = self.stat['usedCard'] output = self.htapi.get_cards_by_suits(used_cards, suits) return output def _get_unused_cards_by_suits(self, my_hand_cards, suits=[]): """ Get unused cards of other opponents so that I can know how many cards are left.. """ all52cards = self.htapi.get52cards() output = all52cards self.htapi.remove_cards(output, self.stat['usedCard']) self.htapi.remove_cards(output, my_hand_cards) if len(suits) == 0: # Do not need to pick a specific suit pass else: # Pick unused cards of target suits output = self.htapi.get_cards_by_suits(output, suits) return output def _get_unused_cards(self, my_hand_cards): return self._get_unused_cards_by_suits(my_hand_cards, suits=[]) def _get_round_cards(self): round_cards = self.stat['roundCard'] return round_cards def _get_avail_cards(self): my_avail_cards = self.stat['avail'] return my_avail_cards def _get_hand_cards(self): my_hand_cards = self.stat['hand'] return my_hand_cards def _calc_as_point(self, card, oppo_unused_cards): """ Calculate the power of this card to avoid taking grades. Output: 1.0 - This card won't take the trick. (Won't be next leader) Output: 0.0 - This card will take the trick. Possibly a big card. """ oppo_unused_same_suit_cards = self.htapi.get_cards_by_suit( oppo_unused_cards, card.get_suit()) if len(oppo_unused_same_suit_cards) == 0: # Opponent does not have same suit... # oppo_no_score_cards = self.htapi.find_no_score_cards(oppo_unused_cards) # return len(oppo_no_score_cards) / float(len(oppo_unused_cards)) return 0.0 lose_cnt = 0 for c in oppo_unused_same_suit_cards: if card.get_rank_num() < c.get_rank_num(): lose_cnt += 1 point = lose_cnt / float(len(oppo_unused_same_suit_cards)) return point def _calc_sm_point(self, card, oppo_unused_cards): """ Calculate the power of this card to win others. """ oppo_unused_same_suit_cards = self.htapi.get_cards_by_suit( oppo_unused_cards, card.get_suit()) if len(oppo_unused_same_suit_cards) == 0: # No same suit at opponent. return 1.0 win_cnt = 0 for c in oppo_unused_same_suit_cards: if card.get_rank_num() > c.get_rank_num(): win_cnt += 1 return win_cnt / float(len(oppo_unused_same_suit_cards)) def _pass_cards_calc_sm_ability(self, data=None): """ Detect if I can shoot the moon. """ my_hand_cards = self._get_hand_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) if len(oppo_unused_cards) != (52 - 13) or len(my_hand_cards) != 13: self.htapi.errmsg("BUG") return self._pick_card_calc_sm_ability(data=None) def _pass_cards_calc_as_ability(self, data=None): """ Detect if I can anti-score """ return self._pick_card_calc_as_ability(data=None) def _pass_cards_sm_mode(self, data): """ Pick 3 cards which can help myself to shoot moon. """ my_hand_cards = self._get_hand_cards() output = [] for i in range(3): card = self._select_card2pass_sm_mode(my_hand_cards) if card == None: self.htapi.errmsg("Cannot pick a card to pass") output.append(card.toString()) self.htapi.dbg(self.get_name() + " pass 3 cards: " + format(output)) return output def _pass_cards_as_mode(self, data): """ Pick 3 cards which can avoid taking score """ my_hand_cards = self._get_hand_cards() output = [] for i in range(3): card = self._select_card2pass(my_hand_cards) if card == None: self.htapi.errmsg("Cannot pick a card to pass") output.append(card.toString()) self.htapi.dbg(self.get_name() + " pass 3 cards: " + format(output)) return output def pass_cards(self, data): """ Event: Pick 3 cards to pass to others """ self.stat['hand'] = self.htapi.clone_cards( [Card(x) for x in data['self']['cards']]) self.stat['hand'] = self.htapi.arrange_cards(self.stat['hand']) self.htapi.dbg("Select 3 cards from: " + format(self.stat['hand'])) sm_ability = self._pass_cards_calc_sm_ability() as_ability = self._pass_cards_calc_as_ability() self.stat['pass3_sm_ability'] = sm_ability self.stat['pass3_as_ability'] = as_ability self.htapi.dbg("Ability stat. sm: ", format(sm_ability), ", as: ", format(self.stat['pass3_as_ability'])) if sm_ability > self.SM_THOLD_PASS3: self.htapi.dbg("shoot moon mode pass3: " + str(sm_ability)) return self._pass_cards_sm_mode(data) else: self.htapi.dbg("anti score mode pass3") return self._pass_cards_as_mode(data) def receive_opponent_cards(self, data): """ Event: Recv 3 cards from one opponent """ pass def ______pass3(self): pass def expose_my_cards(self, data): """ Event: The server asks me to expose AH... """ output = [] candidates = data['cards'] if candidates == None or len(candidates) == 0: return output candidates = [Card(x) for x in candidates] if self.htapi.find_card(candidates, Card('AH')) == None: return output if self.stat['pass3_sm_ability'] > self.SM_THOLD_PASS3: output = ['AH'] return output if self.stat['pass3_as_ability'] > self.AS_THOLD_PASS3: # I have confidence not taking score... output = ['AH'] return output return output def expose_cards_end(self, data): """ Event: Check if somebody expose AH. Damn. """ data_players = data['players'] for dp in data_players: local_player = self.players[dp['playerName']] if len(dp['exposedCards']) > 0: # This guy exposed AH. local_player['expose'] = True self.stat['expose_ah_mode'] = True else: local_player['expose'] = False def ______expose_card(self): pass def _do_i_have_lead_suit(self): """ Cannot be called at lead play!! """ round_cards = self._get_round_cards() if len(round_cards) == 0: self.errmsg("BUG") lead_card = round_cards[0] my_avail_cards = self._get_avail_cards() if len( self.htapi.get_cards_by_suit(my_avail_cards, lead_card.get_suit())) == 0: return False return True def _pick_card_calc_sm_ability(self, data=None): """ Detect if I can shoot the moon. """ my_hand_cards = self._get_hand_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) my_sm_point = 0.0 for c in my_hand_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if this_sm_point >= 1.0: # Power card my_sm_point += this_sm_point else: my_sm_point += this_sm_point my_sm_point *= (13 / float(len(my_hand_cards))) my_sm_point = round(my_sm_point, 3) print("my_hand_cards: ", my_hand_cards, ", sm ability -> " + format(my_sm_point)) return my_sm_point def _pick_card_calc_as_ability(self, data=None): """ Detect if I can anti-score """ my_hand_cards = self._get_hand_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) my_as_point = 0.0 for c in my_hand_cards: this_as_point = self._calc_as_point(c, oppo_unused_cards) if this_as_point >= 1.0: my_as_point += this_as_point else: my_as_point += this_as_point my_as_point *= (13 / float(len(my_hand_cards))) my_as_point = round(my_as_point, 3) print("my_hand_cards: ", my_hand_cards, ", as ability -> " + format(my_as_point)) return my_as_point def _pick_card_should_i_sm(self, data): """ Predict my ability to shoot moon. """ sm_ability = self._pick_card_calc_sm_ability(data=None) as_ability = self._pick_card_calc_as_ability(data=None) if self.stat['sm_mode'] == False: return False if self.stat['sm_mode_started'] == True: # Already starting to sm. Don't stop. return True my_hand_cards = self._get_hand_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) my_heart_cards = self.htapi.get_cards_by_suit(my_hand_cards, 'H') power_heart_num = 0 for c in my_hand_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if this_sm_point >= 1.0 and c.get_suit() == 'H': power_heart_num += 1 if power_heart_num == 0 and len(my_heart_cards) > 0: sm_ability -= 1.0 if sm_ability > self.SM_THOLD_PICK: self.stat['sm_mode_started'] = True return True return False def _pick_card_sm_mode_leadplay(self, data): """ I am the leader. If I have sm ability. Eat! Eat! Eat you to the hole! """ my_hand_cards = self._get_hand_cards() my_avail_cards = self._get_avail_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) for c in my_avail_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if this_sm_point >= 1.0 and c.get_suit() == 'S': self.htapi.dbg("This card will win..." + format(c)) return c for c in my_avail_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if this_sm_point >= 1.0: self.htapi.dbg("This card will win..." + format(c)) return c for c in my_avail_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if this_sm_point >= 1.0 and c.get_suit() == 'H': self.htapi.dbg("This card will win..." + format(c)) return c # Pick short suit candidates self.htapi.dbg("Low strength stage... I am weak :-(.") max_sm_point = 0 candidates = [] for c in my_avail_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if len(candidates) == 0: candidates = [c] max_sm_point = this_sm_point elif this_sm_point > max_sm_point: candidates = [c] max_sm_point = this_sm_point elif this_sm_point < max_sm_point: pass else: candidates.append(c) card_num_stat_sorted = self._calc_hand_cards_num(my_hand_cards) for di in card_num_stat_sorted: suit, num = di if num == 0: continue prefer_candidates = self.htapi.get_cards_by_suit( my_avail_cards, suit) if len(prefer_candidates) > 0: prefer_candidates = self.htapi.arrange_cards(prefer_candidates) return prefer_candidates[-1] self.htapi.errmsg("BUG") def _pick_card_sm_mode_freeplay(self, data): """ Do not have the same suit to follow. So I am free to shoot. """ my_hand_cards = self._get_hand_cards() my_avail_cards = self._get_avail_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) my_noscore_cards = self.htapi.find_no_score_cards(my_avail_cards) if len(my_noscore_cards) == 0: # Too bad... I have to give up sm. Turn to as mode. return self._pick_card_as_mode(data) min_sm_cards = [] min_sm_point = None for c in my_noscore_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if min_sm_point == None: min_sm_point = this_sm_point min_sm_cards = [c] elif this_sm_point < min_sm_point: min_sm_point = this_sm_point min_sm_cards = [c] elif this_sm_point > min_sm_point: pass else: min_sm_cards.append(c) card_num_stat_sorted = self._calc_hand_cards_num(my_hand_cards) for di in card_num_stat_sorted: suit, num = di if num == 0: continue prefer_candidates = self.htapi.get_cards_by_suit( min_sm_cards, suit) if len(prefer_candidates) > 0: prefer_candidates = self.htapi.arrange_cards(prefer_candidates) return prefer_candidates[-1] # TBD: Use the big rank one!? self.htapi.errmsg("BUG") def _pick_card_sm_mode_midplay(self, data): """ I am the mid player. Try to win... """ round_cards = self._get_round_cards() lead_card = round_cards[0] filtered_round_cards = self.htapi.get_cards_by_suit( round_cards, lead_card.get_suit()) round_card_score = self.htapi.calc_score(round_cards) my_hand_cards = self._get_hand_cards() my_avail_cards = self._get_avail_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) if self._do_i_have_lead_suit() == True: # Try to win this round. if self.htapi.pick_bigger_card(my_avail_cards, filtered_round_cards) == None: # ...I don't have bigger card to win. self.htapi.dbg("Give up sm... Oops") return self._pick_card_as_mode(data) else: # I have chance to win. king_cards = [] max_sm_point = 0 my_avail_cards = self.htapi.arrange_cards(my_avail_cards) for c in my_avail_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if this_sm_point > max_sm_point: max_sm_point = this_sm_point king_cards = [c] elif this_sm_point < max_sm_point: pass else: king_cards.append(c) print(" + king cards to win next players are: ", format(king_cards)) if len(king_cards) == 0: self.htapi.errmsg("BUG") return king_cards[0] else: if round_card_score > 0: # Cannot win this round. Have to give up shoot moon. self.htapi.dbg("Give up sm... Oops") return self._pick_card_as_mode(data) else: return self._pick_card_sm_mode_freeplay(data) def _pick_card_sm_mode_lastplay(self, data): """ I am the last player. I can decide to win or not to win. Try to win...maybe. """ round_cards = self._get_round_cards() lead_card = round_cards[0] filtered_round_cards = self.htapi.get_cards_by_suit( round_cards, lead_card.get_suit()) round_card_score = self.htapi.calc_score(round_cards) my_hand_cards = self._get_hand_cards() my_avail_cards = self._get_avail_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) if self._do_i_have_lead_suit() == True: if round_card_score > 0: # # Have score on this round. I have to decide to take or not to take... # # Try to win... bigger_card = self.htapi.pick_bigger_card( my_avail_cards, filtered_round_cards) if bigger_card != None: return bigger_card else: # Cannot win this round. Have to give up shoot moon. self.htapi.dbg("Give up sm... Oops") return self._pick_card_as_mode(data) else: # # Have no score, I can give up to win if I have a too weak point. # same_suit_card_num = self.htapi.calc_card_num_by_suit( my_hand_cards + oppo_unused_cards, lead_card.get_suit()) min_sm_card = None min_sm_point = None for c in my_avail_cards: this_sm_point = self._calc_sm_point(c, oppo_unused_cards) if min_sm_point == None: min_sm_point = this_sm_point min_sm_card = c elif this_sm_point < min_sm_point: min_sm_point = this_sm_point min_sm_card = c if same_suit_card_num > 9: return min_sm_card else: # Try to win this round... bigger_card = self.htapi.pick_bigger_card( my_avail_cards, filtered_round_cards) if bigger_card != None: return bigger_card else: # Cannot win this round. return min_sm_card else: if round_card_score > 0: # Cannot win this round. Have to give up shoot moon. self.htapi.dbg("Give up sm... Oops") return self._pick_card_as_mode(data) else: return self._pick_card_sm_mode_freeplay(data) def _pick_card_sm_mode(self, data): """ Pick a card! """ # roundPlayers is in the correct order of shooting cards. round_players = self.stat['roundPlayers'] # Identify my position in this round my_pos = round_players.index(self.get_name()) if my_pos == 0: card = self._pick_card_sm_mode_leadplay(data) elif my_pos == 3: card = self._pick_card_sm_mode_midplay(data) else: card = self._pick_card_sm_mode_lastplay(data) return card def _pick_card_as_mode_leadplay(self, data): """ I don't want to take a trick... """ my_avail_cards = self._get_avail_cards() my_hand_cards = self._get_hand_cards() oppo_unused_cards = self._get_unused_cards(my_hand_cards) candidates = [] current_max_point = None for c in my_avail_cards: as_point = self._calc_as_point(c, oppo_unused_cards) if current_max_point == None: current_max_point = as_point candidates = [c] elif as_point > current_max_point: candidates = [c] current_max_point = as_point elif as_point < current_max_point: pass else: candidates.append(c) if len(candidates) == 0: self.htapi.errmsg("BUG") # # Try to make others eat 'QS' # spade_candidates = self.htapi.get_cards_by_suit(candidates, 'S') if len(spade_candidates) > 0: used_spade_cards = self._get_used_cards_by_suits(['S']) if self.htapi.find_card(used_spade_cards, Card('QS')) != None: for c in spade_candidates: if self._calc_as_point(c, oppo_unused_cards) == 1.0: return c # # Pick strong small heart cards first, so that I won't eat more later. # heart_candidates = self.htapi.get_cards_by_suit(candidates, 'H') if len(heart_candidates) > 0: for c in heart_candidates: if self._calc_as_point(c, oppo_unused_cards) == 1.0: return c # # Pick small rank cards from shortage suit first # card_num_stat_sorted = self._calc_hand_cards_num(my_hand_cards) for di in card_num_stat_sorted: suit, num = di if num == 0: continue prefer_candidates = self.htapi.get_cards_by_suit(candidates, suit) if len(prefer_candidates) > 0: return prefer_candidates[0] self.htapi.errmsg("BUG") def _get_current_winner(self): round_players = self.stat['roundPlayers'] round_cards = self._get_round_cards() if len(round_cards) == 0: self.errmsg("BUG") lead_card = round_cards[0] idx = 0 winner_idx = 0 for c in round_cards: if c.get_suit() == lead_card.get_suit( ) and c.get_rank_num() > lead_card.get_rank_num(): winner_idx = idx idx += 1 winner = round_players[winner_idx] return winner def _is_sm_possible(self): # # Check if there're 2 player having score. Then it's impossible to shoot moon. # have_score_player = 0 for key in self.players: lp = self.players[key] score = self.htapi.calc_score( lp['pick'], is_expose_ah=self.stat['expose_ah_mode']) if score != 0: have_score_player += 1 if have_score_player >= 2: return False # Not possible to shoot moon. return True def _check_current_winner_sm(self): """ Detect the round winner is now shooting moon. """ if self._is_sm_possible() == False: return False winner = self._get_current_winner() pl = self.players[winner] winner_picked_cards = pl['pick'] winner_picked_score_cards = self.htapi.find_score_cards( winner_picked_cards) if len(winner_picked_score_cards) > 7: return True # Possibly want to shoot moon. return False def _pick_card_as_mode_freeplay(self, data): """ I am in midplay or last play. I don't have lead suit, so I can shoot anything. """ my_avail_cards = self.htapi.arrange_cards(self._get_avail_cards()) my_hand_cards = self._get_hand_cards() round_cards = self._get_round_cards() if len(round_cards) == 0: self.errmsg("BUG") oppo_unused_cards = self._get_unused_cards(my_hand_cards) if self._check_current_winner_sm() == True: # Current winner is a sm player. Prefer no score card here. self.htapi.dbg("Current winner player is a suspicous pig.") if self.htapi.find_card(oppo_unused_cards, Card('QS')) != None: # I don't want to eat QS card = self.htapi.find_card(my_avail_cards, Card('AS')) if card != None: return card card = self.htapi.find_card(my_avail_cards, Card('KS')) if card != None: return card # # Shoot heart card but reserve the biggest rank # my_heart_cards = self.htapi.get_cards_by_suit(my_avail_cards, 'H') # if len(my_heart_cards) >= 2: # my_heart_cards = self.htapi.arrange_cards(my_heart_cards) # # my_2nd_heart_card = my_heart_cards[-2] # score = self._calc_as_point(my_2nd_heart_card, oppo_unused_cards) # # if score < 0.5: # # Remove the 2nd dangerous heart. # return my_2nd_heart_card # Shoot no-score cards candidates = [] as_point_min = None for c in my_avail_cards: this_as_point = self._calc_as_point(c, oppo_unused_cards) if self.htapi.calc_card_num_by_suit(oppo_unused_cards, c.get_suit()) == 0: # Don't worry. The oppo won't send the suit again... so I won't eat the trick. # But if I am the lead... I will eat 100%. continue if len(self.htapi.find_score_cards([c])) > 0: # Avoid giving out score cards for sm player continue if as_point_min == None: as_point_min = this_as_point candidates = [c] elif this_as_point < as_point_min: as_point_min = this_as_point candidates = [c] elif this_as_point > as_point_min: pass else: candidates.append(c) if len(candidates) > 0: card_num_stat_sorted = self._calc_hand_cards_num(my_hand_cards) # Remove small cards in shortage suit. for di in card_num_stat_sorted: suit, num = di if num == 0: continue prefer_candidates = self.htapi.get_cards_by_suit( candidates, suit) if len(prefer_candidates) > 0: prefer_candidates = self.htapi.arrange_cards( prefer_candidates) return prefer_candidates[-1] # # Shoot QS out if I have chance. # card = self.htapi.find_card(my_avail_cards, Card('QS')) if card != None: return card # # Prefer AS, KS if the oppo has QS. Otherwise, KS, AS are not so dangerous... # if self.htapi.find_card(oppo_unused_cards, Card('QS')) != None: card = self.htapi.find_card(my_avail_cards, Card('AS')) if card != None: return card card = self.htapi.find_card(my_avail_cards, Card('KS')) if card != None: return card # # Shoot dangerous heart while opponents still have hearts. # oppo_heart_cards = self.htapi.get_cards_by_suit(oppo_unused_cards, 'H') if len(oppo_heart_cards) > 0: my_heart_cards = self.htapi.get_cards_by_suit(my_avail_cards, 'H') my_heart_cards = self.htapi.arrange_cards(my_heart_cards) for c in reversed(my_heart_cards): point = self._calc_as_point(c, oppo_unused_cards) if point < 0.5: return c # # Shoot 'TC' # card = self.htapi.find_card(my_avail_cards, Card('TC')) if card != None: return card # # Avoid eating 'TC' # if self.htapi.find_card(oppo_unused_cards, Card('TC')) != None: my_club_cards = self.htapi.get_cards_by_suit(my_hand_cards, 'C') if len(my_club_cards) > 0: my_small_club_card = self.htapi.pick_small_card(my_club_cards) if my_small_club_card.get_rank_num() < Card('TC'): # I still have small club. Won't eat 'TC'. pass else: card = self.htapi.find_card(my_avail_cards, Card('AC')) if card != None: return card card = self.htapi.find_card(my_avail_cards, Card('KC')) if card != None: return card card = self.htapi.find_card(my_avail_cards, Card('QC')) if card != None: return card card = self.htapi.find_card(my_avail_cards, Card('JC')) if card != None: return card # # Choose the most dangerous cards # candidates = [] as_point_min = None for c in my_avail_cards: this_as_point = self._calc_as_point(c, oppo_unused_cards) if self.htapi.calc_card_num_by_suit(oppo_unused_cards, c.get_suit()) == 0: # Don't worry. The oppo won't send the suit again... so I won't eat the trick. # But if I am the lead... I will eat 100%. continue if as_point_min == None: as_point_min = this_as_point candidates = [c] elif this_as_point < as_point_min: as_point_min = this_as_point candidates = [c] elif this_as_point > as_point_min: pass else: candidates.append(c) if len(candidates) > 0: card_num_stat_sorted = self._calc_hand_cards_num(my_hand_cards) # Remove small cards in shortage suit. for di in card_num_stat_sorted: suit, num = di if num == 0: continue prefer_candidates = self.htapi.get_cards_by_suit( candidates, suit) if len(prefer_candidates) > 0: prefer_candidates = self.htapi.arrange_cards( prefer_candidates) return prefer_candidates[-1] # # Shoot the min as point card # candidates = [] as_point_min = None for c in my_avail_cards: this_as_point = self._calc_as_point(c, oppo_unused_cards) if as_point_min == None: as_point_min = this_as_point candidates = [c] elif this_as_point < as_point_min: as_point_min = this_as_point candidates = [c] elif this_as_point > as_point_min: pass else: candidates.append(c) if len(candidates) == 0: self.htapi.errmsg("BUG") candidates = self.htapi.arrange_cards(candidates) return candidates[-1] def _pick_card_as_mode_midplay(self, data): my_hand_cards = self._get_hand_cards() my_avail_cards = self._get_avail_cards() round_cards = self._get_round_cards() lead_card = round_cards[0] filtered_round_cards = self.htapi.get_cards_by_suit( round_cards, lead_card.get_suit()) filtered_round_cards_sorted = self.htapi.arrange_cards( filtered_round_cards) round_score_cards = self.htapi.find_score_cards(round_cards) oppo_same_suit_cards = self._get_unused_cards_by_suits( my_hand_cards, [lead_card.get_suit()]) if self._do_i_have_lead_suit() == True: # # I have the lead suit... Don't have many choice... # filtered_round_cards = self.htapi.get_cards_by_suit( round_cards, lead_card.get_suit()) card2shoot = self.htapi.pick_smaller_card(my_avail_cards, filtered_round_cards, auto_choose_big=False) if card2shoot != None: return card2shoot else: # I don't have smaller cards, so I am possible to eat the trick. Avoid picking QS here, or I will eat it myself. my_no_score_cards = self.htapi.find_no_score_cards( my_avail_cards) if len(my_no_score_cards) > 0: # Prefer no score card to decrease the score I will eat. if len(oppo_same_suit_cards) > 9 and len( round_score_cards) == 0: # Gamble! Pick a big card and guess the next player have the same suit. # TODO: Improve... return self.htapi.pick_big_card(my_no_score_cards) else: return self.htapi.pick_small_card(my_no_score_cards) else: # All cards left are score cards... what can I do... return self.htapi.pick_small_card(my_avail_cards) else: # # I don't have the same suit. Can do anything. # return self._pick_card_as_mode_freeplay(data) self.htapi.errmsg("BUG") def _pick_card_as_mode_lastplay(self, data): my_hand_cards = self._get_hand_cards() my_avail_cards = self._get_avail_cards() round_cards = self._get_round_cards() lead_card = round_cards[0] filtered_round_cards = self.htapi.get_cards_by_suit( round_cards, lead_card.get_suit()) filtered_round_cards_sorted = self.htapi.arrange_cards( filtered_round_cards) oppo_same_suit_cards = self._get_unused_cards_by_suits( my_hand_cards, [lead_card.get_suit()]) round_score_cards = self.htapi.find_score_cards(round_cards) if self._do_i_have_lead_suit() == True: # # I have the lead suit... Don't have many choice... # if len(round_score_cards) > 0: card2shoot = self.htapi.pick_smaller_card( my_avail_cards, filtered_round_cards, auto_choose_big=False) if card2shoot != None: return card2shoot else: if self.stat['sm_mode'] == False: return self.htapi.pick_big_card(my_avail_cards) else: return self.htapi.pick_bigger_card( my_avail_cards, filtered_round_cards) else: # No score card on table. Choose any no score card first. # Pick the score card if I have no choice. current_winner_card = filtered_round_cards_sorted[-1] candidates = [] for c in my_avail_cards: if len(self.htapi.find_score_cards( [c])) > 0 and c.get_rank_num( ) > current_winner_card.get_rank_num(): # This is a score card and will eat the trick. Don't shoot it. pass else: candidates.append(c) if len(candidates) > 0: candidates = self.htapi.arrange_cards(candidates) score_candidates = self.htapi.find_score_cards(candidates) if len(score_candidates) > 0: return self.htapi.pick_big_card(score_candidates) else: return self.htapi.pick_big_card(candidates) else: # I don't have good candidates. Usually means I will eat the trick myself. return self.htapi.pick_big_card(my_avail_cards) else: return self._pick_card_as_mode_freeplay(data) def _pick_card_as_mode(self, data): """ Pick a card! """ # roundPlayers is in the correct order of shooting cards. round_players = self.stat['roundPlayers'] # Identify my position in this round my_pos = round_players.index(self.get_name()) if my_pos == 0: card = self._pick_card_as_mode_leadplay(data) elif my_pos == 3: card = self._pick_card_as_mode_lastplay(data) else: card = self._pick_card_as_mode_midplay(data) return card def pick_card(self, data): """ Event: My turn to shoot a card. """ self.stat['hand'] = [Card(x) for x in data['self']['cards']] self.stat['hand'] = self.htapi.arrange_cards(self.stat['hand']) self.stat['avail'] = self.htapi.arrange_cards( [Card(x) for x in data['self']['candidateCards']]) self.stat['roundPlayers'] = data['roundPlayers'][:] # Get players in next turn. round_players = self.stat['roundPlayers'] my_pos = round_players.index(self.get_name()) self.stat['nextPlayers'] = data['roundPlayers'][(my_pos + 1):] if self._pick_card_should_i_sm(data) == True: self.htapi.dbg("sm mode") card2shoot = self._pick_card_sm_mode(data) else: self.htapi.dbg("as mode") card2shoot = self._pick_card_as_mode(data) self.htapi.dbg(self.get_name() + " shoot card: " + format(card2shoot) + ", from: " + format(data['self']['cards']) + ", next players: " + format(self.stat['nextPlayers'])) return card2shoot.toString() def _detect_card_shortage(self, local_player, turn_card): # # If the player is not lead player and he shoots a card not the same as lead card, # mark the player as card shortage. # # If I know the shortage status of players, can have advantage to predict. # round_cards = self._get_round_cards() if len(round_cards) <= 1: # The turn player is leader. Ignore. return False lead_card = round_cards[0] if lead_card.get_suit_num() != turn_card.get_suit_num(): if local_player['suit_leak'].count(lead_card.get_suit()): local_player['suit_leak'].append(lead_card.get_suit()) self.htapi.dbg("Player: " + local_player['playerName'] + " card leakage: " + lead_card.get_suit()) def turn_end(self, data): """ Event: turn end """ data_turn_player = data['turnPlayer'] data_turn_card = data['turnCard'] data_turn_card = Card(data_turn_card) if data_turn_player != self.get_name(): self.htapi.dbg(data_turn_player + " shoot card: " + format(data_turn_card)) local_player = self.players[data_turn_player] local_player['shoot'].append(data_turn_card) self.stat['roundCard'].append(data_turn_card) self.stat['usedCard'].append(data_turn_card) self._detect_card_shortage(local_player, data_turn_card) def pick_history(self, data, is_timeout, pick_his): """ Event: turn end """ self.turn_end(data) def ______pick(self): pass def _round_end_detect_sm_ability(self): # # Check if somebody get any score and give up shoot-moon mode. # if self.stat['sm_mode'] == False: # Already disable shoot moon mode. return for key in self.players: lp = self.players[key] score = self.htapi.calc_score( lp['pick'], is_expose_ah=self.stat['expose_ah_mode']) if score != 0 and lp['playerName'] != self.get_name(): self.htapi.dbg("Give up shoot-moon mode...So sad.") self.stat['sm_mode'] = False def round_end(self, data): """ Event: round end """ round_player_name = data['roundPlayer'] round_cards = self._get_round_cards() local_player = self.players[round_player_name] local_player['pick'] += round_cards self.htapi.dbg( "Player: " + round_player_name + " picked 4 cards: " + format(round_cards), " overall pick: " + format(local_player['pick'])) # Disable shoot moon mode if somebody rx a score. self._round_end_detect_sm_ability() # # Reset round data # self.stat['roundCard'] = [] self.stat['nextPlayers'] = [] self.stat['roundPlayers'] = [] def deal_end(self, data): """ Event: deal end """ data_players = data['players'] self.htapi.dbg(format(data)) for player in data_players: local_player = self.players[player['playerName']] local_player['score_accl'] = player['gameScore'] if player['shootingTheMoon'] == True: local_player['sm'] += 1 if player['playerName'] == self.get_name(): self.stat['pass3_sm_ability_history'].append( self.stat['pass3_sm_ability']) if player['gameScore'] == 0 and self.get_name( ) == player['playerName']: self.stat['pass3_as_ability_history'].append( self.stat['pass3_as_ability']) for key in self.players.keys(): p = self.players[key] # Reset deal-specific data. p['score'] = 0 p['shoot'] = [] p['expose'] = False p['pick'] = [] self.stat['usedCard'] = [] self.stat['sm_mode'] = True self.stat['sm_mode_started'] = False self.stat['expose_ah_mode'] = False self.stat['pass3_sm_ability'] = 0.0 self.stat['pass3_as_ability'] = 0.0 def game_over(self, data): """ Event: game end """ self.htapi.dbg(format(data)) print(self.stat) print(self.players[self.get_name()])