読み込み中...

Pythonで作る!初心者でもわかるボードゲーム作成ガイド10選

Pythonで作成したボードゲームのサンプルイメージ Python
この記事は約19分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Pythonでボードゲームを作るなら、盤面をlistで表し、手番をwhileforで進め、勝敗をfunctionclassに分ける形から始めると理解しやすくなります。初心者のプログラミング学習では、画面装飾より先にルール、入力、判定、終了条件を小さく組み合わせるのが扱いやすいです。

このガイドでは、Pythonの文法とボードゲームの作り方を同時に学べるよう、サンプルコード、注意点、カスタマイズの考え方を一続きで整理します。コードは学習用の最小構成を中心にしており、期待される出力や表示は環境や入力値によって変わりますし、ここがポイントです。

動作確認環境
  • Python 3.12 系を想定
  • 標準ライブラリ: randomunittest
  • 実行環境: ターミナルまたはコマンドプロンプト
📖 この記事で学べること
  • Pythonで盤面型のボードゲームを表す基本設計
  • 初心者が読みやすいサンプルコードの分け方
  • チェス、オセロ、将棋、囲碁などの実装の考え方
  • 入力エラーや不正手を防ぐ注意点
  • ルール、見た目、AIをカスタマイズする方法

公式ドキュメントによれば、Pythonは高レベルのデータ構造とオブジェクト指向の仕組みを備えた言語です。基本文法を確認する場合はPython公式チュートリアル、乱数を使う処理ではrandom公式ドキュメントを参照すると根拠を追いやすくなります。

関連するPython学習として、アプリ化の流れはPython初心者のための完全ガイド!アプリ化の10ステップ、ウィンドウ操作はPythonで実現!ウィンドウ操作の自動化15選が参考になります。データ可視化を組み合わせるならPythonで折れ線グラフ作成の完全ガイド10選、表形式の扱いは初心者必見!Pythonで表を操作するための7つの詳細ガイド、出力制御はPythonで改行あり・なしを制御する方法と応用例10選も合わせて確認できるのが基本です。

Pythonとは

Pythonは、短い記述で処理の流れを表しやすいプログラミング言語です。indentでブロックを作るため、ifforwhileの範囲が見た目にも分かりやすく、初心者がボードゲームの流れを追いやすい構造になります。

一般に、ボードゲームの実装では盤面、駒、手番、勝敗、入力の管理が必要です。Pythonでは盤面をlistdict、駒をclass、行動をmethodとして表せるため、ゲームのルールをコード上の部品に対応させやすくなります。

Pythonの基本

Pythonの基本は、変数、条件分岐、繰り返し、関数、クラスの組み合わせです。たとえば盤面をboard、プレイヤーをplayers、終了状態をgame_overに分けると、サンプルコードの意図が読み取りやすくなるのが目安です。

その構造は、ボードゲームのプログラミングと相性がよいです。入力をinput()で受け、数値変換をint()で行い、状態表示をprint()で確認すれば、GUIを作る前にルールの正しさを検討できます。

Pythonの特徴

Pythonでは、複数行の処理をdefで関数化し、状態を持つ対象をclassで表せます。チェスの駒、オセロの石、モノポリーの物件のように性質を持つ対象が多いゲームでは、この表現が自然に使えるのがポイントです。

一方、Pythonは実行時に型の誤りが見つかる場面もあります。そのため、座標をtupleで持つのか、盤面をlistの入れ子で持つのかを早めに決め、NoneTrueFalseの意味を混同しない設計が必要になります。

Pythonを学ぶメリット

Pythonを学ぶと、ゲームだけでなくWeb、データ分析、自動化にも応用できるのが一般的です。ボードゲームの制作で使うconditionloopexceptionmoduleの考え方は、別分野のプログラミングにもつながります。

そのため、初心者がPythonで小さなボードゲームを作る学習は、文法暗記だけになりにくいです。ルールを決め、状態を変え、結果を表示する流れがあるため、プログラムが何を処理しているのかを把握しやすくなります。

Pythonでボードゲームを作る理由

Pythonでボードゲームを作る理由は、ルールを小さな処理に分解しやすい点にあるのが現実的です。盤面を作る、駒を置く、勝敗を判定する、入力を受けるという処理を順に組み立てれば、初心者でもプログラミングの構造を具体的に理解できます。

プログラミング学習の一環

ボードゲームは、条件分岐と繰り返しを自然に使う題材です。たとえば三目並べでは、range()で盤面を走査し、count()でマークの数を調べ、勝ち筋がそろったらbreakで処理を止めます。

こうした題材では、抽象的な文法がゲーム内の動きに対応すると整理できます。そのため、Pythonの構文を単独で覚えるよりも、サンプルコードを動かす目的が明確になり、注意点も見つけやすくなります。

独自のボードゲームを作れる楽しさ

ボードゲームは、ルールを変更すると遊び方が大きく変わります。Pythonなら、勝利条件を変える、盤面サイズを変える、駒の動きを増やすといったカスタマイズを、該当する関数やクラスの修正として扱えますが、これは押さえたい点です。

その自由度は、初心者が自分のアイデアを試す練習にもなります。ただし、ルールを増やすほど不正手、引き分け、入力ミス、終了条件の漏れが起きやすくなるため、後半の注意点も合わせて確認する必要があります。

💡 Tips: GUIやAIを急いで追加するより、最初はターミナル上で盤面と勝敗を確認できる形にすると、バグの原因を分けやすくなると理解できます。

Pythonで作るボードゲーム10選

Pythonで作れるボードゲームは、三目並べのような短いものから、チェスや将棋のようにルールが多いものまで幅があります。下の早見表では、初心者が取り組む順番、主なデータ構造、実装時の注意点をまとめます。

題材主な要素使いやすい構造学べる内容注意点
三目並べ3×3盤面list勝敗判定上書き防止
チェス駒と8×8盤面classオブジェクト管理移動規則が多い
オセロ8方向探索tuple方向ベクトル合法手判定
将棋9×9盤面function駒の移動成りと持ち駒
囲碁19×19盤面list座標管理呼吸点の判定
バックギャモンダイスrandom乱数処理進行方向
チェッカー斜め移動method捕獲判定連続捕獲
カルカソンヌタイル配置object地形の接続スコア計算
モノポリー資金と物件dict状態遷移破産処理
独自ゲーム自由ルールdataclass設計力仕様の明文化
入力処理行列番号input()型変換ValueError
表示処理盤面出力print()可視化見づらさ
勝敗判定終了条件return分岐設計引き分け
不正手判定合法性if防御的実装範囲外座標
得点計算点数sum()集計処理二重加算
AI手の選択minimax探索計算量
保存状態記録json永続化互換性
テスト関数確認unittest品質管理ケース不足
GUI画面表示Tkinterイベント処理状態同期
Pygame描画Surfaceアニメーション依存管理
CLI文字表示stdout最小実装操作性
座標行と列index配列操作0始まり
例外入力エラーtry異常系握りつぶし
リファクタ整理module再利用分割しすぎ
拡張ルール追加configカスタマイズ既存仕様との衝突
表示記号駒文字str表現力文字幅
履歴手の記録append()取り消し状態コピー
評価値AI判断score数値化偏り
速度探索深さdepth調整待ち時間
公開配布README.md説明文環境差

サンプルコード1:簡単なボードゲーム

最初のPythonサンプルコードは、三目並べに近い盤面ゲームです。3×3のboardを用意し、プレイヤーが交互に座標を入力して、横、縦、斜めのいずれかに同じ記号が並ぶと勝ちになります。

# 初期ボードを作成
board = [[' ' for _ in range(3)] for _ in range(3)]

# プレイヤーの定義
players = ['X', 'O']

# ゲームの終了フラグ
game_over = False

# ゲームのループ
while not game_over:
    for player in players:
        # ボードを表示
        for row in board:
            print(row)
        # プレイヤーの入力を受け付け
        x = int(input('行を入力してください: '))
        y = int(input('列を入力してください: '))
        # 入力された座標にマークをつける
        board[x][y] = player

        # 勝利条件をチェック
        # 横列
        for row in board:
            if row.count(player) == 3:
                game_over = True
        # 縦列
        for col in range(3):
            if [board[row][col] for row in range(3)].count(player) == 3:
                game_over = True
        # 斜め
        if [board[i][i] for i in range(3)].count(player) == 3 or [board[i][2-i] for i in range(3)].count(player) == 3:
            game_over = True

        if game_over:
            print(f'プレイヤー{player}の勝利!')
            break

結果: 期待される表示は、各ターンで盤面が出力され、どちらかの記号が一列に並ぶと勝利メッセージが出る流れです。

このコードでは、game_overFalseの間だけ処理が続きます。ただし、既に埋まったマスへの上書きや、0から2以外の座標入力を防いでいないため、実用化するなら入力検証を追加します。

その改善では、check_winner()のような関数に勝敗判定を切り出すと読みやすくなると覚えるとよいでしょう。Pythonの初心者向けガイドとしては、長いwhileループ内に処理を詰め込みすぎないことが特に押さえたい点です。

サンプルコード2:チェス

チェスでは、駒ごとに名前と色があり、盤面も8×8に広がります。PythonではPieceBoardを分け、駒の情報と盤面の情報を別々に扱うと拡張しやすくなります。

class Piece:
    def __init__(self, color, name):
        self.color = color
        self.name = name

class Board:
    def __init__(self):
        self.board = self.create_board()

    def create_board(self):
        board = [[None for _ in range(8)] for _ in range(8)]
        for i in range(8):
            board[1][i] = Piece('black', 'pawn')
            board[6][i] = Piece('white', 'pawn')
        return board

結果: 期待される状態は、8×8の盤面に黒と白のポーンだけが配置されたBoardインスタンスが作られることです。

この段階では、Pieceが駒の色と名前を持ち、Boardが盤面生成を担当します。一方、チェスとして遊ぶには、ルーク、ナイト、ビショップ、クイーン、キングの配置も必要になります。

class Board:
    def __init__(self):
        self.board = self.create_board()

    def create_board(self):
        board = [[None for _ in range(8)] for _ in range(8)]
        pieces = ['rook', 'knight', 'bishop', 'queen', 'king', 'bishop', 'knight', 'rook']
        for i in range(8):
            board[0][i] = Piece('black', pieces[i])
            board[1][i] = Piece('black', 'pawn')
            board[6][i] = Piece('white', 'pawn')
            board[7][i] = Piece('white', pieces[i])
        return board

結果: 期待される状態は、黒と白の主要な駒が初期配置に近い形で盤面へ入ることです。

ただし、このサンプルコードは移動可能判定やチェックメイトを含みません。チェスを本格化する場合は、can_move()is_check()legal_movesのような責務を分ける設計が必要になります。

サンプルコード3:オセロ

オセロは、置いた石から8方向を調べ、同じ色の石に挟まれた相手の石を裏返すボードゲームです。Pythonでは方向を(dx, dy)tupleとして持つと、上下左右と斜めを同じ処理で扱えます。

class OthelloBoard:
    def __init__(self):
        self.board = [['' for _ in range(8)] for _ in range(8)]
        self.board[3][3] = 'B'
        self.board[4][4] = 'B'
        self.board[3][4] = 'W'
        self.board[4][3] = 'W'

    def flip_stones(self, x, y, color):
        for dx, dy in [(0, 1), (1, 0), (-1, 0), (0, -1), (-1, -1), (-1, 1), (1, -1), (1, 1)]:
            nx, ny = x + dx, y + dy
            while 0 <= nx < 8 and 0 <= ny < 8:
                if self.board[nx][ny] == '':
                    break
                if self.board[nx][ny] == color:
                    while True:
                        nx -= dx
                        ny -= dy
                        if nx == x and ny == y:
                            break
                        self.board[nx][ny] = color
                    break
                nx += dx
                ny += dy

結果: 期待される状態は、初期配置を持つオセロ盤が作られ、条件に合う方向の石が指定色へ変わることです。

このコードは石を置く処理そのものや合法手判定を省いています。そのため、flip_stones()を呼ぶ前に対象マスが空かどうか、相手の石を挟めるかどうかを調べる処理が必要です。

othello = OthelloBoard()
othello.flip_stones(3, 2, 'B')

結果: 期待される状態は、OthelloBoardのインスタンスが作られ、指定座標から石の反転処理が呼び出されることです。

このとき、石を置く位置が正しいとは限りません。初心者がつまずきやすいのは、反転処理と合法手判定を同じ関数に詰め込み、失敗時の状態復元が難しくなる点です。

サンプルコード4:将棋

将棋は9x9の盤面を使うため、チェスの8x8盤面とはサイズが異なります。下のPythonコードは、王、玉、歩に絞った学習用のサンプルコードで、移動ルールの厳密な判定は含めていません。

# ボードの初期化
board = [["" for _ in range(9)] for _ in range(9)]

# 駒の配置
board[0][4] = "王"
board[8][4] = "玉"
for i in range(9):
    board[2][i] = "歩"
    board[6][i] = "と"

# 駒の動き
def move(board, from_pos, to_pos):
    # 移動元と移動先の座標を取得
    from_x, from_y = from_pos
    to_x, to_y = to_pos

    # 駒の移動
    board[to_x][to_y] = board[from_x][from_y]
    board[from_x][from_y] = ""

# 勝敗判定
def check_winner(board):
    # 王と玉が存在するかを確認
    is_king_alive = any("王" in row for row in board)
    is_gyoku_alive = any("玉" in row for row in board)

    # 勝者を返す
    if not is_king_alive:
        return "下手(玉)の勝ち"
    elif not is_gyoku_alive:
        return "上手(王)の勝ち"
    else:
        return "まだ勝負はついていません"

結果: 期待される状態は、9x9の盤面に簡易的な駒が入り、move()で駒を移せることです。

ただし、将棋では持ち駒、成り、二歩、王手などのルールがあります。このサンプルは盤面更新と勝敗判定の入り口として使い、本格的なカスタマイズでは駒ごとのmove_ruleを分離するのが現実的です。

サンプルコード5:囲碁

囲碁は19x19の盤面を使い、黒石と白石を交互に置いていくボードゲームです。Pythonで最初に作るなら、石を取る処理よりも、座標に石を置き、盤面を表示する処理から始めると整理しやすくなります。

# 盤面の初期化
board = [[" " for _ in range(19)] for _ in range(19)]

# 石を置く
def place_stone(board, pos, color):
    # 座標の取得
    x, y = pos

    # 石を置く
    board[x][y] = color

# 盤面を表示する
def print_board(board):
    for row in board:
        for cell in row:
            print(cell, end=" ")
        print()

結果: 期待される表示は、19x19の盤面に指定した色の石が入り、print_board()で各行が出力されることです。

このコードには、着手禁止点、呼吸点、コウ、終局判定がありません。これらを一度に入れると難しくなるため、最初はplace_stone()の前に範囲外と上書きだけを防ぐ処理を追加します。

サンプルコード6:バックギャモン

バックギャモンでは、ダイスの出目に応じて駒を進めます。Pythonのrandom.randint()を使うと、サイコロに近い値を作れるため、乱数を使うプログラミングの題材としても扱いやすいです。

import random

class Backgammon:
    def __init__(self):
        self.board = [0] * 24  # ボードは24のポイントから成る
        self.player1 = 15  # プレイヤー1は15個の駒を持つ
        self.player2 = 15  # プレイヤー2も15個の駒を持つ

    def roll_dice(self):
        return random.randint(1, 6), random.randint(1, 6)  # ダイスをロールする

    def move_piece(self, player, start, steps):
        if self.board[start] >= player:  # プレイヤーの駒がある場所から
            self.board[start] -= player  # 駒を取り除く
            self.board[start+steps] += player  # 新しい場所に駒を置く

    def is_game_over(self):
        return self.player1 == 0 or self.player2 == 0  # どちらかのプレイヤーの駒がなくなったらゲームオーバー

結果: 期待される状態は、24ポイントの盤面、駒数、ダイス処理を持つBackgammonクラスが定義されることです。

このサンプルコードは、実際のバックギャモンの初期配置や移動制限を省いた骨組みです。一方、roll_dice()move_piece()is_game_over()に責務を分けているため、後からルールを加えやすい形になっています。

game = Backgammon()  # ゲームの初期化
print(game.board)  # 初期ボードの状態を表示
game.board[0] = 1
game.move_piece(1, 0, 3)  # プレイヤー1の駒を0ポイントから3ポイントへ移動
print(game.board)  # 移動後のボードの状態を表示

結果: 期待される出力は、最初に24個の値を持つリストが表示され、移動後に0番と3番の値が変わる形です。

そのままではboard[0]0のため、移動確認用にgame.board[0] = 1を加えています。学習用のコードでは、期待する状態を作ってから関数を呼ぶと挙動を確認しやすくなります。

def roll_loaded_dice(self):
    return random.choices([1, 2, 3, 4, 5, 6], weights=[1, 1, 1, 2, 2, 3], k=2)

結果: 期待される出力は、高い目が選ばれやすい重み付きのサイコロ値のリストです。

このカスタマイズでは、random.choices()weightsで出やすさが変わりますし、これが一つの目安です。ただし、対戦ゲームで偏ったダイスを使う場合は、プレイヤーにルールとして明示する必要があります。

サンプルコード7:チェッカー

チェッカーは8x8の盤面で斜め方向に駒を動かすゲームです。Pythonでは、黒を1、白を2、空白を0として持つと、表示と判定を短く書けます。

# チェッカーボードを表現するクラス
class Board:
    def __init__(self):
        # ボードの初期化。空白は0、黒は1、白は2と表現
        self.board = [[0]*8 for _ in range(8)]
        for i in range(8):
            for j in range(8):
                if (i + j) % 2 == 1:
                    if i < 3:
                        self.board[i][j] = 1
                    elif i > 4:
                        self.board[i][j] = 2

    # ボードの状態を表示するメソッド
    def display(self):
        for row in self.board:
            print(' '.join(str(cell) for cell in row))

結果: 期待される状態は、黒と白の駒が斜めに使うマスへ配置されたBoardオブジェクトが作られることです。

この実装では、(i + j) % 2 == 1で使用マスを判定しています。盤面の色や駒の見た目を変える場合も、この条件と表示処理を分けておくとカスタマイズしやすくなります。

# コードの実行
board = Board()
board.display()

# 期待される出力
# 0 1 0 1 0 1 0 1
# 1 0 1 0 1 0 1 0
# 0 1 0 1 0 1 0 1
# 0 0 0 0 0 0 0 0
# 0 0 0 0 0 0 0 0
# 2 0 2 0 2 0 2 0
# 0 2 0 2 0 2 0 2
# 2 0 2 0 2 0 2 0

結果: 期待される出力は、黒駒を1、白駒を2として並べた8行の盤面です。

チェッカーを遊べる形にするには、斜め移動、相手駒の飛び越し、連続捕獲、キング化を加えますが、覚えておくと役立つでしょう。この段階でis_valid_move()apply_move()を分けると、不正手の注意点を扱いやすくなります。

サンプルコード8:カルカソンヌ

カルカソンヌでは、タイルの辺にある城、道、草原などのつながりを判定します。PythonではTileが各辺の情報を持ち、Gameが配置済みタイルと残りタイルを管理する形にできると考えられます。

class Tile:
    # タイルクラスは、各辺のタイプを表現します
    def __init__(self, north, east, south, west):
        self.north = north
        self.east = east
        self.south = south
        self.west = west

class Game:
    # ゲームクラスは、タイルの配置やプレイヤーの行動を制御します
    def __init__(self, players):
        self.players = players
        self.tiles = []
        self.board = []

    def place_tile(self, tile, x, y):
        # タイルを配置します
        self.board[x][y] = tile
        self.tiles.remove(tile)

    def calculate_score(self):
        # スコアを計算します
        pass

結果: 期待される状態は、タイルの四辺を持つTileと、タイル配置を扱うGameの骨組みが定義されることです。

ただし、このコードではself.boardが空のため、そのままplace_tile()を呼ぶと位置参照でエラーになります。初期盤面を作る処理、範囲外判定、隣接タイルとの整合性確認を先に加える必要があります。

サンプルコード9:モノポリー

モノポリーでは、プレイヤーの所持金、物件、所有者、サイコロ移動、賃料計算を管理すると言えるでしょう。PythonではPlayerPropertyを分けると、資産の変化と物件情報を追いやすくなります。

class Player:
    # プレイヤークラスは、各プレイヤーの財産や所有物を管理します
    def __init__(self, name):
        self.name = name
        self.money = 1500
        self.properties = []

    def buy_property(self, property):
        # 物件を購入します
        if self.money >= property.price:
            self.money -= property.price
            self.properties.append(property)
            property.owner = self

class Property:
    # 物件クラスは、各物件の価格や所有者を管理します
    def __init__(self, name, price):
        self.name = name
        self.price = price
        self.owner = None

結果: 期待される状態は、プレイヤーが資金を持ち、購入可能な物件を所有リストへ追加できることです。

このコードでは、購入時にproperty.owner = selfも更新しています。所有者を物件側にも残すと、賃料の支払い先を決める処理で参照しやすくなります。

サンプルコード10:独自ルールのミニゲーム

独自のボードゲームを作る場合は、最初に盤面サイズ、置ける記号、勝利条件を決めますし、ここを基本と考えるとよいでしょう。下のPythonサンプルコードでは、任意サイズの盤面を作り、同じ記号が行にそろったかを調べます。

def create_board(size):
    return [['.' for _ in range(size)] for _ in range(size)]

def has_full_row(board, mark):
    return any(all(cell == mark for cell in row) for row in board)

board = create_board(4)
board[0] = ['A', 'A', 'A', 'A']
print(has_full_row(board, 'A'))

結果: 期待される出力は、Aが横一列にそろっているためTrueです。

この小さなコードは、盤面サイズのカスタマイズと勝利条件の切り替えに使えます。初心者向けのプログラミング練習では、既存のゲームを完全再現する前に、このような小さなルールで動作を確認すると進めやすいです。

各ボードゲームの詳細な使い方

各ボードゲームをPythonで動かすには、ルール、プレイヤーの動き、終了条件を分けて考えます。どの題材でも、盤面の状態を更新する処理と、更新してよいかを判断する処理を混ぜすぎないことが読みやすさにつながりますし、ここがポイントです。

ボードゲームのルール

ルールは、コード上では条件式と状態遷移として表れます。たとえば、オセロなら挟める石がある場所だけに置ける、チェスなら駒ごとに移動範囲が違う、モノポリーなら物件の所有状態で支払い先が変わるという形です。

そのため、is_valid_move()can_buy()のように、判断だけを返す関数を用意すると保守しやすくなります。処理結果をすぐ盤面へ反映するapply_move()と分離すれば、テストもしやすくなるのが基本です。

プレイヤーの動き

プレイヤーの動きは、入力値をゲーム内の行動に変換する部分です。ターミナル版ではinput()で座標や選択肢を受け取り、GUI版ではクリック位置やボタン操作を座標に変換します。

このとき、入力とルール判定を分けておくと、あとから画面を追加しても中心となるロジックを流用できます。Pythonのボードゲームで長く使える設計にするなら、Gameクラスは表示方法に依存しない形が扱いやすいです。

ゲーム終了の条件

ゲーム終了の条件は、勝利、敗北、引き分け、手詰まりなどに分かれますが、これは押さえたい点です。三目並べなら一列完成または盤面が埋まった状態、オセロなら双方が置けない状態、モノポリーなら破産したプレイヤーが出た状態が候補になります。

その判定は、is_game_over()としてまとめると呼び出し位置が明確になります。終了後に勝者を返すならget_winner()、点数を返すならcalculate_score()に分けると、サンプルコードから本格実装へ広げやすくなるのが目安です。

ℹ️ 補足: Python標準のテスト機能はunittest公式ドキュメントで確認できます。勝敗判定や合法手判定は、入力画面を作る前に関数単位で確認すると変更に強くなります。

ボードゲーム作成の際の注意点と詳細な対処法

Pythonでボードゲームを作る際の注意点は、入力の安全性、ルール判定の漏れ、コードの読みやすさ、テストの不足に集約できるのがポイントです。どれもゲームが大きくなるほど見えにくくなるため、小さな段階から対処しておく必要があります。

エラーハンドリング

初心者がつまずきやすいのは、数値を期待している場所に文字列が入力されるケースです。Pythonではtryexceptを使い、ValueErrorを受け止めると、プログラムの停止を防ぎやすくなります。

try:
    player_input = int(input('数値を入力してください: '))
except ValueError:
    print('エラー:数値を入力してください。')

結果: 期待される表示は、数値以外が入力されたときにエラーメッセージが出ることです。

この対処法は、座標入力やメニュー選択にも使えるのが一般的です。ただし、例外を受けたあとに再入力を求める処理がなければゲームは進まないため、whileで入力受付を繰り返す設計も検討します。

コードの可読性

コードの可読性を保つには、変数名と関数名を処理の意味に合わせます。xyだけでは分かりにくい場面では、rowcolcurrent_playerwinnerのように具体化するのが現実的です。

一般的な書き方はPEP 8で確認できます。インデント、空行、命名規則が整うと、サンプルコードからカスタマイズ版へ拡張するときの見落としが減ります。

コードの再利用性

再利用性を高めるには、表示、入力、ルール、状態更新を分けますし、これが一つの目安です。たとえばprint_board()は表示だけ、is_valid_move()は判断だけ、move_piece()は状態更新だけにすると、別のボードゲームにも考え方を移せます。

逆に、ひとつの関数が入力、判定、更新、表示をすべて担うと、少しの変更で全体に影響します。その構造では注意点を修正するたびに別のバグが入りやすくなるため、学習段階でも責務を小さく保つのが実用的です。

テスト手法

テストでは、画面全体を動かす前に関数の返り値を確認すると整理できます。Pythonのunittestを使えば、勝敗判定、合法手判定、得点計算のような純粋な処理をassertEqual()assertTrue()で確認できます。

import unittest

def is_inside(size, row, col):
    return 0 <= row < size and 0 <= col < size

class TestBoardRule(unittest.TestCase):
    def test_inside_position(self):
        self.assertTrue(is_inside(3, 1, 1))

    def test_outside_position(self):
        self.assertFalse(is_inside(3, 3, 0))

if __name__ == '__main__':
    unittest.main()

結果: 期待される出力は、盤面内の座標ではテストが成功し、範囲外の座標ではFalseが確認されることです。

このようなテストを先に置くと、盤面サイズを3x3から4x4へ変えるカスタマイズにも対応しやすくなります。ボードゲームの不具合は境界値で起きやすいため、0、最大値、最大値を超える座標を確認すると効果的です。

⚠️ 注意: サンプルコードの多くは学習用の最小構成です。実際に遊べる形へ近づける場合は、不正入力、範囲外座標、同じマスへの上書き、終了条件の漏れを必ず確認します。

ボードゲームのカスタマイズ方法

ボードゲームのカスタマイズでは、ルール、見た目、AIの判断を分けて変更すると理解できます。Pythonのプログラミングでは、どの部分を変えると結果が変わるのかを関数単位で把握できると、拡張が進めやすくなります。

ルールの変更

ルール変更の例として、チェスのポーンの動きを調整します。通常ルールを厳密に再現する前の学習用サンプルコードとして、現在位置と移動先の差分を見て移動可否を返す関数を考えますが、覚えておくと役立つでしょう。

def move_pawn(board, current_location, new_location):
    # ポーンの動きを定義
    dx = new_location[0] - current_location[0]
    dy = new_location[1] - current_location[1]
    if dx in [1, -1] and dy == 0 and board[new_location] is None:
        return True
    return False

結果: 期待される返り値は、移動先が空で前後どちらかに1マス進む条件を満たすときのTrueです。

この例は説明用の単純化を含みます。実際のチェスでは色による進行方向、初手2マス、斜め取り、アンパッサンなどがあるため、colorや手数の情報も必要になります。

def move_pawn(board, current_location, new_location):
    # ポーンの新たな動きを定義
    dx = new_location[0] - current_location[0]
    dy = new_location[1] - current_location[1]
    if dx in [1, -1] and dy in [0, 1, -1] and board[new_location] is None:
        return True
    return False

結果: 期待される返り値は、前後1マスに加えて斜め移動も許可した条件を満たすときのTrueです。

このカスタマイズは、実在のチェスから離れた独自ルールとして扱います。既存ゲームの名前を使う場合は、通常ルールと独自ルールの違いを画面や説明に残すと、プレイヤーの混乱を避けやすくなります。

ゲームの見た目のカスタマイズ

見た目を変える場合は、ロジックと描画を分けることが前提になると覚えるとよいでしょう。ターミナル表示ならprint_board()の記号を変え、GUIならTkinterPygameでセルの色、駒の画像、クリック位置を扱います。

たとえば、オセロのBWを黒丸や白丸の文字へ変えるだけでも、画面の印象は変わります。ただし、全角記号は環境によって幅がずれるため、CLI版では.XOのような半角記号のほうが安定すると考えられます。

AIの強さの調整

AIを入れる場合は、すべての合法手を列挙し、それぞれを評価する流れになります。三目並べならminimax、オセロやチェスなら探索深さを制限した評価関数、複雑なゲームならMCTSを検討できます。

その調整では、depthを増やすほど先の手を読めますが、計算量も増えますし、ここを基本と考えるとよいでしょう。初心者向けのPython実装では、最初から最強AIを目指すより、ランダム選択、簡単な評価値、探索の順で段階的に広げると理解しやすくなります。

import random

def choose_random_move(legal_moves):
    if not legal_moves:
        return None
    return random.choice(legal_moves)

moves = [(0, 0), (1, 1), (2, 2)]
print(choose_random_move(moves))

結果: 期待される出力は、候補手の中からランダムに選ばれた座標、または候補がない場合のNoneです。

この程度のAIでも、対戦相手としての最低限の動きは作れます。その後、角を優先する、勝てる手を選ぶ、相手の勝ちを防ぐといった評価を加えると、プログラミングの学習内容も自然に深まりますし、ここがポイントです。

⚠️ 注意: AIや乱数を含む処理は、毎回同じ結果になるとは限りません。記事内の結果は期待される出力や状態であり、特定の手順での結果を断定するものではありません。

まとめ

Pythonでボードゲームを作る学習では、盤面を表すデータ構造、手番を進めるループ、勝敗を決める条件式を分けて考えることが出発点になります。初心者でも、三目並べのような小さな題材から始めれば、プログラミングの基本をゲームの動きとして理解できます。

そのうえで、チェス、オセロ、将棋、囲碁、バックギャモン、チェッカー、カルカソンヌ、モノポリーへ広げると、classmethodexceptionrandomunittestなどの使い道が具体的になると言えるでしょう。サンプルコードは骨組みとして読み、注意点を補いながら育てるのが実用的です。

これらの実装では、カスタマイズの前にルールの境界を決める必要があります。盤面サイズ、合法手、勝敗、表示、AIの強さを別々に扱えば、ボードゲーム作成の変更範囲が見えやすくなります。

Pythonの学習を続ける場合は、アプリ化、表操作、改行制御、グラフ表示などの周辺知識も役立ちますが、これは押さえたい点です。ゲームのログを表にしたり、勝率をグラフ化したりすると、ボードゲーム制作と別分野のプログラミングを自然につなげられます。

関連記事

著者: Japanシーモア編集部

Japanシーモアは、Web/IoT/APP/SYS 分野のプログラミング情報を体系的に提供するメディアです。本記事は編集部による執筆とAI支援を組み合わせて制作し、公開前に編集部が校正しています。誤りや改善案がございましたらお問い合わせよりご連絡ください。

※本記事は実在のエンジニア複数名で構成される Japanシーモア編集部が、AI支援を活用して作成・校正・公開しています。