質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.36%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

230閲覧

IndexError: pop from empty listを解消したい

qwerty_

総合スコア1

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2024/11/01 17:35

編集2024/11/02 02:20

実現したいこと

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万回やらせたところエラーを吐かなかった

補足

特になし

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

qwerty_

2024/11/02 05:36 編集

不足している情報等ございましたらご指摘ください
guest

回答1

0

何らかの理由で

len(self.deck) == 0

かつ

n = len(self.discard_pile) - 1のnがゼロ以下

になる場合、提示されたエラーになります。

要するに空のdeckからpopしようとした場合です。

逆(deckの範囲から上でpopする)でも同じエラーになりますが、以下の計算式なら、そうならないので問題無いです。

index = math.floor(random.random() * len(self.deck))

(訂正)
IndexError: pop from empty list
なので、逆は同じエラーでは無いですね。すみません。

投稿2024/11/02 03:23

編集2024/11/02 03:30
hiroki-o

総合スコア1072

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

qwerty_

2024/11/02 05:55 編集

回答していただきありがとうございます。 不足している情報が多いので分かりにくいとは思いますが、discard_pileのサイズが1以下になる原因はなんだと思いますか?私にはよくわからなくて… discard_pileへの要素の追加はカードを出すときのみで以下のように実装しており、要素の削除はresetDeckでしかやっていません。山札、捨て札、4つの手札の合計枚数が常に108枚になっているはずなんですよね。 if (card.get('color') != Color.BLACK and (card.get('special') == Special.WILD or card.get('special') == Special.WILD_DRAW_4 or card.get('special') == Special.WILD_SHUFFLE)): self.discard_pile.append({'color': Color.BLACK, 'special': card.get('special')}) self.field_card = {'color': Color.BLACK, 'special': card.get('special')} self.removeCard() else: self.discard_pile.append(card) 訂正:確認してみたところカード総数が108枚になってないときがありました。恐らくこれが原因でエラーを吐いていると思います。失礼しました。
hiroki-o

2024/11/02 05:56

提示されているソースだけでは、そのif文自体の実行条件がわからないので、何とも言えません。 len(self.discard_pile)が1になった後、そのif文を通らずに、resetDeckを実行するパターンがあるのでは? len(self.deck)とlen(self.discard_pile)をprintしてみるしかないと思います。 数千なら確認できるでしょう。
hiroki-o

2024/11/02 05:58

コメントが前後しました。がんばって!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問