50代サラリーマンの暮らし

50代前半のサラリーマンです。

簡単なゲームを自分で改良してみる

今日は、淀川の花火大会でした。

大阪では一番大きな花火大会です。たぶん。

www.yodohanabi.com

 

最後に行ったのは、子どもが生まれる前なので、もう15年以上前でしょうか。

当時は、有料観覧席ができ始めたぐらいで、たしか1,000~2,000円ぐらいだったように思います。記憶はあやふやですが、ぼくらの金銭感覚だと出してもせいぜいそのくらいかなと。

それで暑い中早くから場所取りせずとも迫力ある位置で、椅子に座ってゆったり見れて、こら値打ちあるなと妻と言ってたのような記憶があります。

それが今年の値段見たら、安い席でも5,000円。いい席は12,000円。子ども(小学生まで)はその半額。いやぁ、無理ですね。

お金だけでなく気候的にももうムリかも。今日も死ぬほど暑くて、日が落ちても気持ち悪い暑さの中で見てられないです。

 

そんな暑かった今週も、ぽちぽちプログラミングしました。

前回、Pythonチュートリアルをひととおりやってみたので、

ryonepon.hatenablog.com

次は何か簡単なゲームを自分の手を動かしてプログラミングしてみようとやってみました。

と言っても、いきなりゼロから作るのは途中でくじけそうなので、昔のように人様のプログラムを変えてみることから始めました。

 

昔はネットもなく、紙の媒体しかなかったので、自分で全部入力するしかなかったですが、今はネットからコピペで簡単に入力できて、楽ちんです。

こんなサイトを見つけました。

jp-seemore.com

こちらで掲載されている簡単なゲームのサンプルを試してみます。簡単なボードゲームと紹介されていますが、子どもの頃よくやったマルペケです。

 

コードは、こんな感じ。


    # 初期ボードを作成
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

 

ここでは、最低限の実装です。一応、これでもプレイはできますが、実際のマルペケでは、同じマスは選べないなどのルールがありますので、それを実装する課題が出されています。(機能追加)

また、一連の処理を上からベタに書いているので、プログラム全体の流れが少しわかりにくくなっているので、それを見直したりなど。(リファクタリング

これらを自分でやってみます。

選択した場所がすでに〇か×が書かれている場合、再入力を促します。

そして、勝利判定を関数化することで、プログラムの見通しをよくしてみました。


def check_row(brd,plyr): #横並びを判定する関数
    for row in brd:
        if row.count(plyr) == 3:
            return True
    return False

def check_col(brd,plyr): #縦並びを判定する関数
    for col in range(3):
        if [brd[row][col] for row in range(3)].count(plyr) == 3:
            return True
    return False

def check_diag(brd,plyr): #斜め並びを判定する関数
    if [brd[i][i] for i in range(3)].count(plyr) == 3 or [brd[i][2-i] for i in range(3)].count(plyr) == 3:
         return True
    return False

def check_draw(brd): #引き分けを判定する関数
    cnt = 0
    for row in brd:
        for mark in row:
            if mark == ' ':
                cnt = cnt + 1
            if cnt > 1: #空欄が2個以上あればゲーム続行
                return False
    return True #空欄が1個以下なら、引き分け

def print_board(brd): #ボードを表示する関数
     for row in brd:
          print(row)

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

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

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

# ゲームのループ
while not game_over:
    for player in players:
        print_board(board) # ボードを表示
        # プレイヤーの入力を受け付け
        while True:
            x = int(input('行を入力してください: '))
            y = int(input('列を入力してください: '))
            # 入力された座標にマークをつける
            if  board[x][y] == ' ': #入力座標にマークがなければセット
                board[x][y] = player
                break
            print('そこは選べません。もう一度。') #すでにマークがあるので再入力を促す
        # 引き分けをチェック
        if check_draw(board):
            print_board(board) # ボードを表示
            print('引き分け!')
            game_over = True
            break
        # 勝利条件をチェック
        # 横列
        if check_row(board,player):
             print_board(board) # ボードを表示
             print(f'横並びでプレイヤー{player}の勝利!')
             game_over = True
             break
        # 縦列
        if check_col(board,player):
             print_board(board) # ボードを表示
             print(f'縦並びでプレイヤー{player}の勝利!')
             game_over = True
             break
        # 斜め
        if check_diag(board,player):
             print_board(board) # ボードを表示
             print(f'斜め並びでプレイヤー{player}の勝利!')
             game_over = True
             break

 

こんな感じです。動かしてみます。

 

うまく動きました!

 

サンプルプログラムを自分で改良してみましたが、いまや簡単にAIに書かせることもできます。

試しにChatGPTに、マルペケのプログラムをPythonでリクエストしたら、

 

マルペケは通じませんでした。方言ですかね。三目並べと言い直すと以下のコードが生成されました。


def print_board(board):
    for row in board:
        print(" | ".join(row))
        print("-" * 5)

def check_winner(board):
    # 横のチェック
    for row in board:
        if row.count(row[0]) == len(row) and row[0] != ' ':
            return True

    # 縦のチェック
    for col in range(len(board[0])):
        if board[0][col] == board[1][col] == board[2][col] != ' ':
            return True

    # 斜めのチェック
    if board[0][0] == board[1][1] == board[2][2] != ' ' or \
       board[0][2] == board[1][1] == board[2][0] != ' ':
        return True

    return False

def main():
    board = [[" " for _ in range(3)] for _ in range(3)]
    current_player = "X"

    while True:
        print_board(board)
        row = int(input(f"Player {current_player}, enter row (0, 1, or 2): "))
        col = int(input(f"Player {current_player}, enter column (0, 1, or 2): "))

        if board[row][col] == " ":
            board[row][col] = current_player
        else:
            print("That spot is already taken!")
            continue

        if check_winner(board):
            print_board(board)
            print(f"Player {current_player} wins!")
            break

        if all([cell != " " for row in board for cell in row]):
            print_board(board)
            print("It's a tie!")
            break

        current_player = "O" if current_player == "X" else "X"

if __name__ == "__main__":
    main()

 

うーん、こっち方がすっきりわかりやすいかな。

実行するとこんな感じ。

ちなみに、ChatGPTに回答を読み上げさせると、みつめならべって言ってました。ちょっと気持ち悪いです。

 

次はもう少し高度なことにトライしてみよう。