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

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

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

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

Q&A

解決済

1回答

208閲覧

ビットボードでオセロを実装したい。合法手のエラーが特定できない。

miraimirai

総合スコア48

Python

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

0グッド

1クリップ

投稿2025/05/15 08:42

編集2025/05/15 08:45

実現したいこと

2ターン目白の合法手が間違っているのを直したい

発生している問題・分からないこと

2ターン目白の合法手が間違っている

エラーメッセージ

error

1オセロを開始します 2 A B C D E F G H 31 . . . . . . . . 42 . . . . . . . . 53 . . . . . . . . 64 . . . ● ○ . . . 75 . . . ○ ● . . . 86 . . . . . . . . 97 . . . . . . . . 108 . . . . . . . . 11 12合法手: 100000010000000000100000010000000000000000000 13黒の番です 14合法手: d6 c5 f4 e3 15石を置く位置を入力してください (例: d3): e3 16 A B C D E F G H 171 . . . . . . . . 182 . . . . . . . . 193 . . . . ● . . . 204 . . . ● ● . . . 215 . . . ○ ● . . . 226 . . . . . . . . 237 . . . . . . . . 248 . . . . . . . . 25 26合法手: 10000001010000000010000100000000100000000000000000000 27白の番です 28合法手: e6 f5 c4 d3 f3 e2 29石を置く位置を入力してください (例: d3):

該当のソースコード

python

1BOARD_SIZE = 8 2# 盤面(ビットボード)の初期化 3black_disc_bit = 0x0000000810000000 # 黒石(16進数で) 4white_disc_bit = 0x0000001008000000 # 白石(16進数で) 5white_or_black = 0 # 0:黒, 1:白 6blank = ~(black_disc_bit | white_disc_bit) # 空きマス 7 8 9def shift(bitboard, direction): 10 if direction > 0: 11 return bitboard << direction 12 else: 13 return bitboard >> -direction 14 15 16def get_legal_moves(black_disc_bit, white_disc_bit, black_or_white): 17 legal = 0 18 if black_or_white == 0: # 黒のターン 19 player = black_disc_bit 20 opponent = white_disc_bit 21 else: # 白のターン 22 player = white_disc_bit 23 opponent = black_disc_bit 24 25 blank = ~(player | opponent) 26 27 directions = [1, -1, 8, -8, 7, -7, 9, -9] 28 masks = [ 29 0x7F7F7F7F7F7F7F7F, # 左から右 30 0xFEFEFEFEFEFEFEFE, # 右から左 31 0xFFFFFFFFFFFFFFFF, # 上から下 32 0xFFFFFFFFFFFFFFFF, # 下から上 33 0x7F7F7F7F7F7F7F7F, # 左上から右下 34 0xFEFEFEFEFEFEFEFE, # 右上から左下 35 0x7F7F7F7F7F7F7F7F, # 左下から右上 36 0xFEFEFEFEFEFEFEFE, # 右下から左上 37 ] 38 39 for direction, mask in zip(directions, masks): 40 temp = shift(player, direction) & opponent & mask 41 # print(f"temp:{temp},direction:{direction}") 42 while temp: 43 next_temp = shift(temp, direction) & opponent & mask 44 if next_temp: 45 temp |= next_temp 46 else: 47 break 48 # print("temp:{temp},direction:{direction}") 49 # print("legal:", legal) 50 # 合法手を更新 51 legal |= shift(temp, direction) & blank & mask 52 53 return legal # ここで legal を返す 54 55 56def print_board(black, white): 57 print(" A B C D E F G H") 58 for y in range(BOARD_SIZE): 59 row = [] 60 for x in range(BOARD_SIZE): 61 idx = (7 - y) * 8 + x 62 mask = 1 << idx # 検査するマスク 63 if black & mask: 64 row.append("●") 65 elif white & mask: 66 row.append("○") 67 else: 68 row.append(".") 69 print(f"{y+1} {' '.join(row)}") 70 print() 71 72 73def pos_to_bit(pos): 74 # pos: 例 "d3" → (x=3, y=2) 75 x = ord(pos[0]) - ord("a") 76 y = int(pos[1]) - 1 77 idx = (7 - y) * 8 + x 78 return 1 << idx 79 80 81def flip_discs(black, white, pos, black_or_white): 82 bit = pos_to_bit(pos) 83 player = black if black_or_white == 0 else white 84 opponent = white if black_or_white == 0 else black 85 86 directions = [1, -1, 8, -8, 7, -7, 9, -9] 87 masks = [ 88 0x7F7F7F7F7F7F7F7F, # 左から右 89 0xFEFEFEFEFEFEFEFE, # 右から左 90 0xFFFFFFFFFFFFFFFF, # 上から下 91 0xFFFFFFFFFFFFFFFF, # 下から上 92 0x7F7F7F7F7F7F7F7F, # 左上から右下 93 0xFEFEFEFEFEFEFEFE, # 右上から左下 94 0x7F7F7F7F7F7F7F7F, # 左下から右上 95 0xFEFEFEFEFEFEFEFE, # 右下から左上 96 ] 97 98 for direction, mask in zip(directions, masks): 99 flipped = 0 100 temp = shift(bit, direction) & opponent & mask 101 while temp: 102 flipped |= temp 103 temp = shift(temp, direction) & opponent & mask 104 if shift(flipped, direction) & player & mask: 105 player |= flipped 106 opponent &= ~flipped 107 108 if black_or_white == 0: 109 black = player 110 else: 111 white = player 112 113 return black, white 114 115 116# オセロの表示 117def put_disc(black, white, pos, black_or_white): 118 black, white = flip_discs(black, white, pos, black_or_white) 119 bit = pos_to_bit(pos) 120 if black_or_white == 0: 121 black |= bit 122 else: 123 white |= bit 124 return black, white 125 126 127def play_game(): 128 black = black_disc_bit 129 white = white_disc_bit 130 turn = 0 # 0: 黒, 1: 白 131 132 while True: 133 print_board(black, white) 134 legal_moves = get_legal_moves(black, white, turn) 135 print(f"合法手: {legal_moves:b}") # ビット表示で合法手を表示,デバッグ用 136 if legal_moves == 0: 137 turn = 1 - turn # パス 138 legal_moves = get_legal_moves(black, white, turn) 139 if legal_moves == 0: # 両方パスならゲーム終了 140 print("両方パスです。ゲーム終了") 141 break 142 else: 143 print("パス") 144 continue # もう一方のプレイヤーのターンへ 145 146 if turn == 0: 147 print("黒の番です") 148 else: 149 print("白の番です") 150 151 print("合法手: ", end="") 152 legal_moves_list = [] 153 for i in range(64): 154 if (legal_moves >> i) & 1: 155 x = chr(ord("a") + i % 8) 156 y = str(8 - i // 8) 157 move = x + y 158 legal_moves_list.append(move) 159 print(move, end=" ") 160 print() 161 162 while True: 163 pos = input("石を置く位置を入力してください (例: d3): ").lower() 164 if pos == "q": 165 print("中断します。") 166 return 167 if pos in legal_moves_list: 168 black, white = put_disc(black, white, pos, turn) 169 turn = 1 - turn 170 break 171 else: 172 print("無効な手です。もう一度入力してください。") 173 174 print_board(black, white) 175 print("ゲーム終了") 176 black_count = bin(black).count("1") 177 white_count = bin(white).count("1") 178 print(f"黒: {black_count}, 白: {white_count}") 179 if black_count > white_count: 180 print("黒の勝ちです!") 181 elif white_count > black_count: 182 print("白の勝ちです!") 183 else: 184 print("引き分けです!") 185 186 187def main(): 188 print("オセロを開始します") 189 play_game() 190 print("オセロを終了します") 191 print("お疲れ様でした") 192 print("また遊んでください") 193 194 195if __name__ == "__main__": 196 main() 197

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

print文を追加してlegalを出力しましたがよくわかりません。chatgptにも質問しましたが具体的なエラー元の特定方法がわかりません。

補足

macM1
Python 3.10.16
参考
ビットボード解説
https://speakerdeck.com/antenna_three/bitutobodojie-shuo?slide=37
chatgpt
gemini

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

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

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

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

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

guest

回答1

0

自己解決

chatgptに聞いて解決しました。
主な修正、リファクタリングした箇所

今日行った主な修正・リファクタリングは以下のとおりです。

  1. 方向ベクトル&マスクの定義を一元化

    • (direction, mask) タプルを DIR_MASKS にまとめ、合法手検出/反転処理で両方使い回しに
  2. shift 関数の簡素化

    • 方向ごとに左シフト・右シフトを使い分け、左シフト時には必ず & FULL で64ビットにマスク
  3. 合法手取得 (get_legal_moves) の整理

    • 空きマス empty のマスク化
    • DIR_MASKS をループで回しつつ、連続する相手石の追跡→最後に空きマスを判定する流れをまとめて記述
  4. 石返し処理 (flip_discs) の統一

    • 合法手取得と同じ方向/マスクループを流用し、ひっくり返すビット集合 to_flip を一括返却に変更
  5. 座標変換ヘルパーの追加

    • pos_to_bit(pos):入力文字列(上から1–8)→ビットインデックス
    • bit_to_pos(i):ビットインデックス→文字列(上から1–8)
      この二つで入出力の座標整合性を完全に担保 
  6. 盤面表示 (print_board) の行番号を上から「1→8」へ

    • range(7, -1, -1)enumerate(..., start=1) を使い、表示行と内部ビットの上下方向を一致させた 
  7. put_disc の修正

    • まず pos_to_bit でビット変換し、そのビットを flip_discs に渡すように。その後 |= で駒を配置 
  8. グローバル変数・初期化の整理

    • 初期石配置を play_game 内にまとめ、不要だった変数(blank, white_or_black)を削除

これらの変更により、

  • 合法手検出・石返し処理は一貫した方向・マスク操作になり
  • 表示・入力・内部ビット表現の座標系が完全に一致し
  • 方向マスクの重複定義が解消され、コードがスッキリ見通しよくなりました。

python

1BOARD_SIZE = 8 2FULL = 0xFFFFFFFFFFFFFFFF 3NOT_A = 0xFEFEFEFEFEFEFEFE # ファイルAを消す 4NOT_H = 0x7F7F7F7F7F7F7F7F # ファイルHを消す 5 6# 方向とマスクのペアを一元管理 7DIR_MASKS = [ 8 (1, NOT_H), # east、→ 9 (-1, NOT_A), # west、← 10 (8, FULL), # north、↑ 11 (-8, FULL), # south、↓ 12 (9, NOT_H), # northeast、↗︎ 13 (-9, NOT_A), # southwest、↙︎ 14 (7, NOT_A), # northwest、↖︎ 15 (-7, NOT_H), # southeast、↘︎ 16] 17 18 19# 境界外シフト 20def shift(bb: int, d: int) -> int: 21 return (bb << d) & FULL if d > 0 else bb >> -d 22 23 24# 合法手取得 25def get_legal_moves(black: int, white: int, turn: int) -> int: 26 player, opp = (black, white) if turn == 0 else (white, black) 27 empty = (~(player | opp)) & FULL 28 legal = 0 29 for d, m in DIR_MASKS: 30 t = shift(player, d) & opp & m 31 flips = 0 32 while t: 33 flips |= t 34 t = shift(t, d) & opp & m 35 legal |= shift(flips, d) & empty & m 36 return legal 37 38 39# 石ひっくり返し 40def flip_discs(black: int, white: int, pos_bit: int, turn: int): 41 player, opp = (black, white) if turn == 0 else (white, black) 42 to_flip = 0 43 for d, m in DIR_MASKS: 44 t = shift(pos_bit, d) & opp & m 45 cand = 0 46 while t: 47 cand |= t 48 t = shift(t, d) & opp & m 49 if shift(cand, d) & player & m: 50 to_flip |= cand 51 player ^= to_flip 52 opp ^= to_flip 53 return (player, opp) if turn == 0 else (opp, player) 54 55 56# ビット⇔座標変換 57def pos_to_bit(pos: str) -> int: 58 file = ord(pos[0]) - ord("a") # a→0 … h→7 59 row = 8 - int(pos[1]) # 上から1→ y=7 60 idx = row * BOARD_SIZE + file 61 return 1 << idx 62 63 64def bit_to_pos(i: int) -> str: 65 file = chr(ord("a") + (i % BOARD_SIZE)) 66 rank = str(8 - (i // BOARD_SIZE)) 67 return file + rank 68 69 70# 盤面表示 71def print_board(black: int, white: int): 72 print(" A B C D E F G H") 73 for disp, y in enumerate(range(7, -1, -1), 1): 74 row = [] 75 for x in range(BOARD_SIZE): 76 idx = y * BOARD_SIZE + x 77 mask = 1 << idx 78 row.append("●" if black & mask else "○" if white & mask else ".") 79 print(f"{disp} {' '.join(row)}") 80 print() 81 82 83# 指し手配置 84def put_disc(black: int, white: int, pos: str, turn: int): 85 bit = pos_to_bit(pos) 86 black, white = flip_discs(black, white, bit, turn) 87 if turn == 0: 88 black |= bit 89 else: 90 white |= bit 91 return black, white 92 93 94# ゲーム進行 95 96 97def play_game(): 98 black = 0x0000000810000000 99 white = 0x0000001008000000 100 turn = 0 101 while True: 102 print_board(black, white) 103 legal = get_legal_moves(black, white, turn) 104 print(f"合法手: {legal:b}") 105 if legal == 0: 106 turn ^= 1 107 if get_legal_moves(black, white, turn) == 0: 108 print("両者パス。終了") 109 break 110 print("パス") 111 continue 112 print("黒の番" if turn == 0 else "白の番") 113 moves = [bit_to_pos(i) for i in range(64) if (legal >> i) & 1] 114 print("合法手:", *moves) 115 while True: 116 p = input("(例:d3)→").lower() 117 if p == "q": 118 return 119 if p in moves: 120 black, white = put_disc(black, white, p, turn) 121 turn ^= 1 122 break 123 print("無効") 124 print_board(black, white) 125 b = bin(black).count("1") 126 w = bin(white).count("1") 127 print(f"黒{b}{w} ->", "黒勝ち" if b > w else "白勝ち" if w > b else "引き分け") 128 129 130def main(): 131 print("オセロ開始") 132 play_game() 133 134 135if __name__ == "__main__": 136 main() 137

投稿2025/05/19 01:16

編集2025/05/19 01:19
miraimirai

総合スコア48

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問