実現したいこと
UNOでのモンテカルロ法による探索を実装したい
発生している問題・分からないこと
一万回ゲームさせようと思ったところ数千ゲーム目でエラーを吐く
エラーメッセージ
error
1Traceback (most recent call last): 2 File "(ファイルのパス)\play.py", line 393, in play_game 3 chose_card = montecarlo(state) 4 ^^^^^^^^^^^^^^^^^ 5 File "(ファイルのパス)\player\montecarlo.py", line 84, in montecarlo 6 ranks[i] += playout(copy_state, valid_action[i]) 7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8 File "(ファイルのパス)\player\montecarlo.py", line 28, in playout 9 state.playCard(chose_card) 10 File "(ファイルのパス)\game_uno.py", line 230, in playCard 11 self.drawCards(4) 12 File "(ファイルのパス)\game_uno.py", line 173, in drawCards 13 self.each_hand[self.player_id].append(self.deck.pop(index)) 14 ^^^^^^^^^^^^^^^^^^^^ 15IndexError: pop from empty list
該当のソースコード
python
1# play.py 2def play_game(): 3 try: 4 winner = 0 5 game = 1000000 6 win = [0, 0, 0, 0] 7 win_rate = [0, 0, 0, 0] 8 total_point = [0, 0, 0, 0] 9 ave_point = [0, 0, 0, 0] 10 for count in range(game): 11 state = State() 12 state.initGame() 13 turn = 0 14 log.debug(f"GAME{count+1}") 15 log.debug(f"The first card is {state.field_card}") 16 play_log = queue.Queue(maxsize=12) 17 play_log.put(state.field_card) 18 while True: 19 turn += 1 20 crr_player = state.player_id 21 valid_cards, valid_wd4 = state.getValidCards() 22 23 # 出すことのできるカードが無い場合カードをドローする 24 if len(valid_cards) == 0 and len(valid_wd4) == 0: 25 state.drawCards(1) 26 log.debug(f"Player{crr_player} draw {state.each_hand[crr_player][len(state.each_hand[crr_player])-1]}") 27 valid_cards, valid_wd4 = state.getValidCards() 28 if play_log.full(): 29 play_log.get() 30 play_log.put((crr_player, "DRAW")) 31 32 # 一枚引いても出すことのできるカードが無い場合 33 # 次のプレイヤーに手番が移る 34 if len(valid_cards) == 0 and len(valid_wd4) == 0: 35 log.debug(f"Player{crr_player} pass") 36 if play_log.full(): 37 play_log.get() 38 play_log.put((crr_player, "PASS")) 39 else: 40 # 出すことのできるカードが1つしか無い場合 41 # そのカードで確定(ワイルドは除く) 42 if (len(valid_cards) == 1 and 43 valid_cards[0].get('special') != Special.WILD and 44 len(valid_wd4) == 0): 45 chose_card = valid_cards[0] 46 # 出すことのできるカードが複数ある場合 47 else: 48 strat = player_strategy[crr_player] 49 if strat == Strategy.MONTECARLO: 50 chose_card = montecarlo(state) 51 elif strat == Strategy.HEURISTIC: 52 chose_card = heuristic(state) 53 elif strat == Strategy.RANDOM: 54 if len(valid_cards) != 0: 55 index = math.floor(random.random() * len(valid_cards)) 56 chose_card = valid_cards[index] 57 else: 58 chose_card = valid_wd4[0] 59 state.playCard(chose_card) 60 log.debug(f"Player{crr_player} play {state.field_card}") 61 if play_log.full(): 62 play_log.get() 63 play_log.put((crr_player, "PLAY", state.field_card)) 64 state.passTurn() 65 66 if len(state.each_hand[crr_player]) == 0: 67 log.debug(f"Player{crr_player} win") 68 winner = crr_player 69 points = [] 70 for _ in range(state.num_player): 71 points.append(0) 72 # 各プレイヤの得点を計算 73 for i in range(state.num_player): 74 points[i] = state.calcPoints(i, crr_player) 75 log.info(f"{points}") 76 77 # print(list(play_log.queue)) 78 while not play_log.empty(): 79 t = play_log.get() 80 make_input_action(t) 81 82 win[winner] += 1 83 for i in range(4): 84 total_point[i] += points[i] 85 break 86 87 for i in range(4): 88 win_rate[i] = win[i] / game 89 ave_point[i] = total_point[i] / game 90 log.info(f"{win=}") 91 log.info(f"{win_rate=}") 92 log.info(f"{total_point=}") 93 log.info(f"{ave_point=}") 94 except Exception as e: 95 log.exception(e)
python
1# game_uno.py 2def resetDeck(self): 3 n = len(self.discard_pile) - 1 4 for _ in range(n): 5 index = math.floor(random.random() * (len(self.discard_pile) - 1)) 6 self.deck.append(self.discard_pile.pop(index)) 7 8def drawCards(self, num: int): 9 for _ in range(num): 10 if len(self.deck) == 0: 11 self.resetDeck() 12 index = math.floor(random.random() * len(self.deck)) 13 self.each_hand[self.player_id].append(self.deck.pop(index)) 14 self.num_each_hand[self.player_id] = len(self.each_hand[self.player_id])
python
1# montecarlo.py 2import math 3import random 4 5from game_uno import State 6from .const import USES_POINT, USES_RANK, PLAYOUT 7 8def playout(state: State, card: dict): 9 state.initPlayout() 10 # print(state.player_id, card) 11 state.playCard(card) 12 state.passTurn() 13 14 while True: 15 crr_player = state.player_id 16 valid_cards, valid_wd4 = state.getValidCards() 17 18 # 出すことのできるカードがなければカードをドローする 19 if len(valid_cards) == 0 and len(valid_wd4) == 0: 20 state.drawCards(1) 21 valid_cards, valid_wd4 = state.getValidCards() 22 23 if len(valid_cards) != 0 or len(valid_wd4) != 0: 24 if len(valid_cards) != 0: 25 index = math.floor(random.random() * len(valid_cards)) 26 chose_card = valid_cards[index] 27 else: 28 chose_card = valid_wd4[0] 29 state.playCard(chose_card) 30 # print(crr_player, chose_card) 31 state.passTurn() 32 33 if len(state.each_hand[crr_player]) == 0: 34 # print("winner is ", crr_player) 35 if USES_POINT: 36 return state.calcPoints(0, crr_player) 37 elif USES_RANK: 38 # その試合での順位を返す 39 # 勝者であれば、得点計算せずそのまま1を返す 40 if crr_player == 0: 41 # print(1) 42 return 1 43 else: 44 # 敗者であれば、得点を計算する 45 rank = 4 46 points = [0, 0, 0, 0] 47 for i in range(state.num_player): 48 points[i] += state.calcPoints(i, crr_player) 49 # print(points) 50 # 自分の得点を下回るプレイヤがいたら、順位を1つ上げる 51 for i in range(1, state.num_player): 52 if points[0] >= points[i]: 53 rank -= 1 54 # print(rank) 55 return rank 56 else: 57 if crr_player == 0: 58 return 1 59 else: 60 return 0 61 62 63def montecarlo(state: State): 64 valid_cards, valid_wd4 = state.getValidCards() 65 points = [] 66 wins = [] 67 ranks = [] 68 if len(valid_cards) != 0: 69 valid_action = state.getValidAction(valid_cards) 70 else: 71 valid_action = state.getValidAction(valid_wd4) 72 73 for _ in range(len(valid_action)): 74 points.append(0) 75 wins.append(0) 76 ranks.append(0) 77 78 # 各可能手に対してプレイアウトを実行 79 for i in range(len(valid_action)): 80 for _ in range(10000): 81 copy_state = state.clone() 82 if USES_POINT: 83 points[i] += playout(copy_state, valid_action[i]) 84 elif USES_RANK: 85 ranks[i] += playout(copy_state, valid_action[i]) 86 else: 87 wins[i] += playout(copy_state,valid_action[i]) 88 89 max = 0 90 if USES_POINT: 91 for i in range(len(points)): 92 if points[i] > points[max]: 93 max = i 94 elif USES_RANK: 95 for i in range(len(ranks)): 96 if ranks[i] < ranks[max]: 97 max = i 98 # print(ranks) 99 else: 100 for i in range(len(wins)): 101 if wins[i] > wins[max]: 102 max = i 103 return valid_action[max]
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
空のリストから要素の取り出しを行っていることでエラーを吐いていることは分かったので、drawCardsとresetDeckが悪いのかと思いランダムプレイヤ4体でのゲームを20万回やらせたところエラーを吐かなかった
補足
特になし
不足している情報等ございましたらご指摘ください