複数人で楽しめるインタラクティブビンゴゲーム！FlaskとHTMLでルーレット機能付きWebアプリを作成しよう

## はじめに

Pythonで簡単に作成できるビンゴゲームを紹介します。このアプリケーションは、複数のプレイヤーが参加できるように設計されており、各プレイヤーが固有のビンゴカードを持ちます。また、ルーレット機能には回数制限を設けて、戦略性を高めたゲームプレイを実現しています。

この記事では、Flaskを使ったバックエンドと、HTMLおよびJavaScriptを使ったフロントエンドの実装方法をステップバイステップで説明します。

## 完成イメージ

このプロジェクトでは、以下の機能を持つビンゴゲームを実装します:

- 各プレイヤーごとに固有のビンゴカードを生成
- ルーレットによる番号選択機能
- ルーレット回数に制限を設け、戦略的なゲームプレイを実現

## 必要な環境

- Python 3.x
- Flask
- HTML/CSS
- JavaScript (Fetch API)

## プロジェクトのセットアップ

### 1. Flaskプロジェクトの準備

まず、Flaskプロジェクトのフォルダを作成します。

```bash
mkdir bingo_game
cd bingo_game
python3 -m venv venv
source venv/bin/activate
pip install flask
```

次に、Flaskのメインファイルとなる`app.py`を作成します。

### 2. `app.py`の実装

`app.py`では、複数プレイヤーのビンゴカードを管理し、ルーレットの回数制限をサーバー側で制御します。

```python
from flask import Flask, render_template, jsonify, request
import random

app = Flask(__name__)

class BingoCard:
    def __init__(self):
        self.card = self.generate_card()

    def generate_card(self):
        card = []
        for col in range(5):
            start = col * 15 + 1
            column = random.sample(range(start, start + 15), 5)
            card.append(column)
        card[2][2] = 'FREE'  # 真ん中のマスはフリー
        return card

players = {}  # プレイヤーごとのビンゴカードを管理
roll_count = 0  # ルーレットの回数制限
max_rolls = 10  # ルーレットの最大回数

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/generate_card', methods=['POST'])
def generate_card():
    player_id = request.json.get('player_id')
    if player_id not in players:
        players[player_id] = BingoCard().card
    return jsonify(players[player_id])

@app.route('/roll_number')
def roll_number():
    global roll_count
    if roll_count < max_rolls:
        roll_count += 1
        number = random.randint(1, 75)
        return jsonify(number=number, remaining_rolls=max_rolls - roll_count)
    else:
        return jsonify(error="Maximum roll limit reached.")

if __name__ == "__main__":
    app.run(debug=True)
```

### 3. フロントエンドの準備

#### `templates/index.html`

`index.html`ファイルには、各プレイヤーごとのビンゴカード生成と、ルーレット番号の表示機能を実装します。

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bingo Game</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
    <style>
        body { font-family: Arial, sans-serif; }
        .bingo-grid { display: grid; grid-template-columns: repeat(5, 100px); gap: 5px; }
        .bingo-cell { width: 100px; height: 100px; display: flex; justify-content: center; align-items: center; border: 1px solid #000; font-size: 24px; }
        .free-cell { background-color: #eee; }
        .highlighted { background-color: yellow; }
        #selected-number { font-size: 24px; margin-top: 20px; }
    </style>
</head>
<body>
    <h1>Bingo Game</h1>
    <label for="player-id">Player ID:</label>
    <input type="text" id="player-id" value="player1">
    <button onclick="generateCard()">Generate New Card</button>
    <div id="bingo-card" class="bingo-grid"></div>
    <button onclick="rollNumber()">Roll Number</button>
    <div id="selected-number">Selected Number: None</div>
    <div id="remaining-rolls">Remaining Rolls: 10</div>

    <script src="/static/script.js"></script>
</body>
</html>
```

#### `static/script.js`

`script.js`では、カード生成と番号選択のロジックを実装し、ユーザーインタラクションを制御します。

```javascript
let currentCard = [];
let remainingRolls = 10;

function generateCard() {
    const playerId = document.getElementById('player-id').value;
    fetch('/generate_card', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ player_id: playerId })
    })
    .then(response => response.json())
    .then(card => {
        currentCard = card;
        const bingoCardDiv = document.getElementById('bingo-card');
        bingoCardDiv.innerHTML = '';  // Clear previous card
        for (let row = 0; row < 5; row++) {
            for (let col = 0; col < 5; col++) {
                const cellDiv = document.createElement('div');
                cellDiv.classList.add('bingo-cell');
                if (row === 2 && col === 2) {
                    cellDiv.textContent = 'FREE';
                    cellDiv.classList.add('free-cell');
                } else {
                    cellDiv.textContent = card[col][row];
                    cellDiv.setAttribute('data-number', card[col][row]);
                }
                bingoCardDiv.appendChild(cellDiv);
            }
        }
    });
}

function rollNumber() {
    if (remainingRolls > 0) {
        fetch('/roll_number')
            .then(response => response.json())
            .then(data => {
                if (data.error) {
                    document.getElementById('selected-number').textContent = data.error;
                } else {
                    const selectedNumber = data.number;
                    document.getElementById('selected-number').textContent = `Selected Number: ${selectedNumber}`;
                    document.getElementById('remaining-rolls').textContent = `Remaining Rolls: ${data.remaining_rolls}`;
                    remainingRolls = data.remaining_rolls;

                    const cells = document.querySelectorAll('.bingo-cell');
                    cells.forEach(cell => {
                        if (cell.getAttribute('data-number') == selectedNumber) {
                            cell.classList.add('highlighted');
                        }
                    });
                }
            });
    } else {
        alert("Maximum roll limit reached. No more rolls allowed.");
    }
}
```

### 4. アプリケーションの実行

プロジェクトフォルダ内で以下のコマンドを実行し、アプリケーションを起動します。

```bash
python app.py
```

ブラウザで`http://127.0.0.1:5000/`を開くと、ビンゴゲームのインターフェースが表示されます。プレイヤーIDを入力してビンゴカードを生成し、ルーレットボタンをクリックしてゲームを進めます。

##画面表示

![スクリーンショット 2024-08-20 17.48.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75802/d42c8280-ae48-e4d6-f084-62828d5693ad.png)

## まとめ

今回の記事では、複数プレイヤーが参加できるインタラクティブなビンゴゲームを作成しました。Flaskでバックエンドを構築し、HTMLとJavaScriptでユーザーインターフェースを作成することで、簡単ながらも楽しいゲームを実現できました。ぜひこのコードを参考にして、独自の機能を追加したり、アレンジしてみてください。