【Python】pygameでオセロゲームを作ってみた

プログラミング
広告
広告
広告

皆さんこんにちは!

今回は、Pythonのpygameライブラリを使用してオセロゲームを実装する方法を紹介します。

ゲームを作ることでプログラミングスキルも向上しますので、今回の記事を参考にしていただければと思います。

オセロは、8×8のボード上で黒と白の駒を置いていくゲームで、相手の駒を自分の駒で挟むと相手の駒を自分の色に変えることができます。最終的に自分の色の駒が多い方が勝ちです。

広告
広告
広告

ゲーム画面

以下に作成したゲームの画面を表示します。

初期画面
プレイ画面

必要なライブラリのインストール

始めにpygameをインストールする必要があります。以下のコマンドを実行してインストールしてください。

pip install pygame

なおpygameの公式ドキュメントはこちらです。

コードの全体像

以下が、オセロゲームの完全な実装コードです。

import pygame
import sys

# Pygameの初期化
pygame.init()

# 色の定義
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 128, 0)

# 画面サイズの設定
SIZE = 600
GRID_SIZE = SIZE // 8
screen = pygame.display.set_mode((SIZE, SIZE))
pygame.display.set_caption("オセロゲーム")

# ボードの初期化(8x8の二次元リスト)
board = [[None] * 8 for _ in range(8)]

# 初期配置
board[3][3] = WHITE
board[3][4] = BLACK
board[4][3] = BLACK
board[4][4] = WHITE

# 8方向の定義(左上、上、右上、左、右、左下、下、右下)
DIRECTIONS = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]

# フォントの設定(日本語フォントを指定して文字化けを防ぐ)
font = pygame.font.SysFont('meiryo', 36)

# 駒の描画関数
def draw_piece(x, y, color):
    # 指定された座標に駒を描く
    pygame.draw.circle(screen, color, (x * GRID_SIZE + GRID_SIZE // 2, y * GRID_SIZE + GRID_SIZE // 2), GRID_SIZE // 2 - 4)

# ボードの描画関数
def draw_board():
    screen.fill(GREEN)  # ボード全体を緑色で塗りつぶす
    for x in range(8):
        for y in range(8):
            rect = pygame.Rect(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE)  # グリッドの矩形を作成
            pygame.draw.rect(screen, BLACK, rect, 1)  # グリッドの枠を描く
            if board[x][y] is not None:
                draw_piece(x, y, board[x][y])  # 駒がある場合は描画する

# 合法手のチェック関数
def is_valid_move(x, y, turn):
    if board[x][y] is not None:
        return False  # 既に駒が置かれている場合は無効
    opponent = WHITE if turn == BLACK else BLACK  # 相手の駒の色を設定
    valid = False
    for dx, dy in DIRECTIONS:
        nx, ny = x + dx, y + dy
        if 0 <= nx < 8 and 0 <= ny < 8 and board[nx][ny] == opponent:
            while 0 <= nx < 8 and 0 <= ny < 8:
                nx += dx
                ny += dy
                if not (0 <= nx < 8 and 0 <= ny < 8):
                    break
                if board[nx][ny] is None:
                    break
                if board[nx][ny] == turn:
                    valid = True  # 相手の駒を挟むことができる場合
                    break
    return valid

# 駒を裏返す関数
def flip_pieces(x, y, turn):
    opponent = WHITE if turn == BLACK else BLACK
    for dx, dy in DIRECTIONS:
        pieces_to_flip = []
        nx, ny = x + dx, y + dy
        while 0 <= nx < 8 and 0 <= ny < 8 and board[nx][ny] == opponent:
            pieces_to_flip.append((nx, ny))  # 裏返す駒のリストに追加
            nx += dx
            ny += dy
        if 0 <= nx < 8 and 0 <= ny < 8 and board[nx][ny] == turn:
            for px, py in pieces_to_flip:
                board[px][py] = turn  # 駒を裏返す

# 合法手があるかどうかのチェック関数
def has_valid_move(turn):
    for x in range(8):
        for y in range(8):
            if is_valid_move(x, y, turn):
                return True
    return False

# ゲーム終了の判定と勝者の表示関数
def game_over():
    black_count = sum(row.count(BLACK) for row in board)
    white_count = sum(row.count(WHITE) for row in board)
    if black_count > white_count:
        winner = "黒の勝ち!"
    elif white_count > black_count:
        winner = "白の勝ち!"
    else:
        winner = "引き分け!"
    text = font.render(winner, True, BLACK if black_count >= white_count else WHITE)
    screen.blit(text, (SIZE // 2 - text.get_width() // 2, SIZE // 2 - text.get_height() // 2))
    pygame.display.flip()
    pygame.time.wait(3000)  # 3秒間待機して結果を表示
    pygame.quit()
    sys.exit()

# メインループ
def main():
    turn = BLACK  # 最初のターンは黒
    while True:
        try:
            if not has_valid_move(BLACK) and not has_valid_move(WHITE):
                game_over()  # 両方のプレイヤーに合法手がない場合ゲーム終了

            if not has_valid_move(turn):
                turn = WHITE if turn == BLACK else BLACK  # 合法手がない場合はターンをスキップ
                continue

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()  # ウィンドウが閉じられた場合はゲーム終了
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    x, y = event.pos
                    x //= GRID_SIZE
                    y //= GRID_SIZE
                    if is_valid_move(x, y, turn):
                        board[x][y] = turn
                        flip_pieces(x, y, turn)  # 駒を置いて裏返す
                        turn = WHITE if turn == BLACK else BLACK  # ターンを交代

            draw_board()  # ボードの再描画
            pygame.display.flip()  # 画面の更新
        except Exception as e:
            pygame.quit()
            print(f"An error occurred: {e}")
            sys.exit()  # 例外発生時はゲームを終了

if __name__ == "__main__":
    main()

主要な部分の解説

pygameの初期化とウィンドウの設定

pygameの初期化とゲームのウィンドウのサイズを設定してます。
また、ウィンドウのタイトルも設定してます。

pygame.init()
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 128, 0)
SIZE = 600
GRID_SIZE = SIZE // 8
screen = pygame.display.set_mode((SIZE, SIZE))
pygame.display.set_caption("オセロゲーム")

ボードの初期化

8×8のボードを初期化し、初期配置を行います。
このとき、白と黒の駒が交互に配置されるようにしています。

board = [[None] * 8 for _ in range(8)]
board[3][3] = WHITE
board[3][4] = BLACK
board[4][3] = BLACK
board[4][4] = WHITE

駒の描画

ボード全体を描画する関数です。まず、ボード全体を緑色で塗りつぶし、その後、各マス目とその枠を描画します。また、駒が置かれている場合は、その駒を描画します。

def draw_piece(x, y, color):
    # 指定された座標に駒を描く
    pygame.draw.circle(screen, color, (x * GRID_SIZE + GRID_SIZE // 2, y * GRID_SIZE + GRID_SIZE // 2), GRID_SIZE // 2 - 4)

def draw_board():
    screen.fill(GREEN)  # ボード全体を緑色で塗りつぶす
    for x in range(8):
        for y in range(8):
            rect = pygame.Rect(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE)  # グリッドの矩形を作成
            pygame.draw.rect(screen, BLACK, rect, 1)  # グリッドの枠を描く
            if board[x][y] is not None:
                draw_piece(x, y, board[x][y])  # 駒がある場合は描画する

合法手のチェックと駒を裏返す処理

プレイヤーが駒を置けるかどうかのチェックと、駒を裏返す処理を行います。
駒を置く座標が合法手かどうかを確認し、相手の駒を挟むことができる場合は、その駒を裏返します。

def is_valid_move(x, y, turn):
    if board[x][y] is not None:
        return False  # 既に駒が置かれている場合は無効
    opponent = WHITE if turn == BLACK else BLACK  # 相手の駒の色を設定
    valid = False
    for dx, dy in DIRECTIONS:
        nx, ny = x + dx, y + dy
        if 0 <= nx < 8 and 0 <= ny < 8 and board[nx][ny] == opponent:
            while 0 <= nx < 8 and 0 <= ny < 8:
                nx += dx
                ny += dy
                if not (0 <= nx < 8 and 0 <= ny < 8):
                    break
                if board[nx][ny] is None:
                    break
                if board[nx][ny] == turn:
                    valid = True  # 相手の駒を挟むことができる場合
                    break
    return valid

def flip_pieces(x, y, turn):
    opponent = WHITE if turn == BLACK else BLACK
    for dx, dy in DIRECTIONS:
        pieces_to_flip = []
        nx, ny = x + dx, y + dy
        while 0 <= nx < 8 and 0 <= ny < 8 and board[nx][ny] == opponent:
            pieces_to_flip.append((nx, ny))  # 裏返す駒のリストに追加
            nx += dx
            ny += dy
        if 0 <= nx < 8 and 0 <= ny < 8 and board[nx][ny] == turn:
            for px, py in pieces_to_flip:
                board[px][py] = turn  # 駒を裏返す

ゲーム終了の判定と勝者の表示

ゲームが終了したときに勝者を表示します。
黒の駒と白の駒の数を数え、多い方が勝者となります。引き分けの場合も考慮しております。

def game_over():
    black_count = sum(row.count(BLACK) for row in board)
    white_count = sum(row.count(WHITE) for row in board)
    if black_count > white_count:
        winner = "黒の勝ち!"
    elif white_count > black_count:
        winner = "白の勝ち!"
    else:
        winner = "引き分け!"
    text = font.render(winner, True, BLACK if black_count >= white_count else WHITE)
    screen.blit(text, (SIZE // 2 - text.get_width() // 2, SIZE // 2 - text.get_height() // 2))
    pygame.display.flip()
    pygame.time.wait(3000)  # 3秒間待機して結果を表示
    pygame.quit()
    sys.exit()

メインループ

ゲームのメインループです。プレイヤーの入力を待ち、各種処理を行います。すべてのイベント(ウィンドウの閉じるボタンのクリックやマウスのクリックなど)を処理し、それに応じてゲームの状態を更新します。

def main():
    turn = BLACK  # 最初のターンは黒
    while True:
        try:
            if not has_valid_move(BLACK) and not has_valid_move(WHITE):
                game_over()  # 両方のプレイヤーに合法手がない場合ゲーム終了

            if not has_valid_move(turn):
                turn = WHITE if turn == BLACK else BLACK  # 合法手がない場合はターンをスキップ
                continue

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()  # ウィンドウが閉じられた場合はゲーム終了
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    x, y = event.pos
                    x //= GRID_SIZE
                    y //= GRID_SIZE
                    if is_valid_move(x, y, turn):
                        board[x][y] = turn
                        flip_pieces(x, y, turn)  # 駒を置いて裏返す
                        turn = WHITE if turn == BLACK else BLACK  # ターンを交代

            draw_board()  # ボードの再描画
            pygame.display.flip()  # 画面の更新
        except Exception as e:
            pygame.quit()
            print(f"An error occurred: {e}")
            sys.exit()  # 例外発生時はゲームを終了

if __name__ == "__main__":
    main()

最後に

これで、Pythonとpygameを使ったオセロゲームの実装が完成しました。

Pygameはシンプルでありながら強力なライブラリで、少し複雑なゲームの開発ができるようになります!

今回作成したコードはまだまだ粗があると思います。
pythonの学習用にコードを改修してより良いゲームを作ってみてはどうでしょうか?
改修することでプログラミングスキルはかなり向上すると思いますので是非チャレンジしてみてください!

また、pythonでゲームを作ってみたい方は以下の本を参考にしてください。
内容もわかりやすく実践的で良本です。
プログラミング未経験者にもおすすめの本ですよ。

Pythonではじめるゲーム制作 超入門 知識ゼロからのプログラミング&アルゴリズムと数学

新品価格
¥2,376から
(2024/6/11 11:45時点)

人気ブログランキング

クリックするとブログランキングサイトに移動します。

にほんブログ村 IT技術ブログへ
インターネット・コンピュータランキング
広告
プログラミング
広告
広告
technyankoをフォローする
広告
広告
プロフィール
technyanko

元情シス・SEです。
当ブログではPCに関して困ったことや役立つ情報を発信していきます。
たまにバッチスクリプトやPythonに関する記事も投稿します。

technyankoをフォローする
広告
タイトルとURLをコピーしました