Amazon Q CLI で自動車工場シミュレーションゲームを Vibe Coding で作ってみた

Amazon Q CLI で自動車工場シミュレーションゲームを Vibe Coding で作ってみた

Clock Icon2025.06.02

どうも製造ビジネステクノロジー部のふじい(大)です。

今「Amazon Q CLI を使ってゲームを作ろう Tシャツキャンペーン」が行われているのを知っていますか?

https://aws.amazon.com/jp/blogs/news/build-games-with-amazon-q-cli-and-score-a-t-shirt/

AI に全任せでゲームを作るだけでTシャツが貰えるなんてやらねば損という気持ちでやってみました、というのが今回のお話です。

またゲームの題材としては、折角なので所属する製造ビジネステクノロジー部が注力する製造業について(今回は自動車工場について)学べるゲームを作って楽しく学ぼうと考えました。

以下がその記録です。

開発環境の準備

私がやったことは、Amazon Q CLI のインストールと、作業用ディレクトリを作成し、VS Code で開いてターミナルで q と打ち込んでチャットを開始するところまでです。後は本当にチャットして全部やってもらいました。

※ 普段は Warp を使っていますが、Amazon Q CLI は執筆時点で Warp 非対応でした。

この時点でまだどのようなゲームを作るか考えていなかったので、開発環境の準備をしてもらいました。以前 VS Code 拡張機能の Amazon Q を使ってテトリスを Pygame で作ってもらったこともあり、今回も Python で作ってもらうことにし、モダンな開発ができるよう uv を使って開発環境を整えてもらいました。

uv init で uv の開発環境を整えて

以前は「UV 使って開発環境整えて」だけでは uv init してくれなかったのでコマンドを指定しています。

今回はちゃんと uv init してくれました。

UVを使用して開発環境を整えるために、`uv init`コマンドを実行しましょう。UVはPythonのパッケージマネージャーで、仮想環境の作成と管理を行うツールです。
UVの初期化が成功しました。「Initialized project `q-cli-game`」というメッセージが表示されています。

これで現在のディレクトリ(q-cli-game)にUVプロジェクトが初期化されました。次に何をしたいですか?例えば:

- 仮想環境を作成する(`uv venv`)
- パッケージをインストールする(`uv pip install <パッケージ名>`)
- 依存関係を管理するための`requirements.txt`ファイルを作成する

何か特定のパッケージをインストールしたいですか?

ちゃんと次やることを提案してくれるのはありがたいですね。Pygame を入れたいと思います。

pygame

インストールを試みましたが、仮想環境が無かったためエラーが出ました。

uv pip install pygame

error: No virtual environment found; run `uv venv` to create an environment, or pass `--system` to install into a non-virtual environment

しかしこのエラーを受け、自動(!!)で uv venv コマンドの実行、仮装環境のアクティベートを行い、Pygame をインストールまでしてくれました。すごい。

エラーが発生しました。まず仮想環境を作成する必要があります。UVでは、パッケージをインストールする前に仮想環境を作成する必要があります。以下のコマンドで仮想環境を作成しましょう:
uv venv

Using CPython 3.12.7
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate

仮想環境が正常に作成されました。次に、仮想環境をアクティベートしてからPygameをインストールしましょう:
source .venv/bin/activate
uv pip install pygame

Pygameのインストールが成功しました!以下の作業が完了しています:

1. UVプロジェクトを初期化(`uv init`2. 仮想環境を作成(`uv venv`3. 仮想環境をアクティベート(`source .venv/bin/activate`4. Pygameをインストール(`uv pip install pygame`)

Pygame バージョン2.6.1がインストールされました。これでPygameを使ったゲーム開発を始める準備が整いました。

何か特定のゲームを作成したいですか?または、Pygameの基本的な使い方についてサポートが必要ですか?

ただし uv pip install でインストールを行なったため、pyproject.tomldependencies には追加されていませんでした。ここでは一旦無視して進めます。

ゲームを作成する

開発環境構築も丸投げし、Pygame による Python 開発環境ができました。

ではメイン部分のゲームもプロンプトで丸投げしましょう。

製造業(ここでは自動車工場)について学べるゲームを作ってください。

なんかいい感じに作ってくれそうな雰囲気を感じました。

自動車工場のシミュレーションゲームを作成しましょう。Pygameを使って、製造業の基本概念を学べるシンプルなゲームを作ります。

まず、基本的なゲームの構造を作成します:

自動車工場シミュレーションゲームを作成しました!以下が実装内容です:

## ゲームの概要

このゲームでは、あなたは自動車工場の経営者となり、車の生産ラインを管理します。製造業の基本的な概念を学ぶことができるシミュレーションゲームです。

### 主な要素

1. **生産ライン管理**:
   - 4つの生産ステーション(フレーム組立、エンジン取付、塗装、品質検査)
   - 各ステーションで車が順番に処理される
2. **リソース管理**:
   - 労働者:生産速度に影響(雇用コスト:1000円、日給:100円/人)
   - 機械:生産効率に影響(購入コスト:2000円、維持費:50円/日)
3. **研究開発**:
   - 効率と品質を向上させる(コスト:5000円)
   - 研究レベルが上がると長期的に利益が増加
4. **品質管理**:
   - 品質が高いほど検査に合格する確率が上がる
   - 不良品は収益にならない
5. **経済システム**:
   - 車の販売価格:5000円
   - 生産開始コスト:1000円
   - 日々の人件費と維持費
- 資金がなくなるとゲームオーバー

### 操作方法

- **生産開始**ボタン:新しい車の生産を開始(コスト:1000円)
- **労働者雇用**ボタン:労働者を1人雇う(コスト:1000円)
- **機械購入**ボタン:機械を1台購入(コスト:2000円)
- **研究開発**ボタン:研究レベルを上げる(コスト:5000円)
- **次の日へ**ボタン:日を進める(給料と維持費が発生)

## 実行方法

ゲームを実行するには、仮想環境内のPythonを使用します:

```
.venv/bin/python car_factory_game.py
```

ゲームが起動したら、マウスでボタンをクリックして操作してください。資金を管理しながら、効率的な生産ラインを構築し、できるだけ多くの車を生産することが目標です。

このゲームを通じて、製造業における生産ライン管理、リソース配分、品質管理、コスト管理などの基本的な概念を体験的に学ぶことができます。

何か質問や改善点があれば、お気軽にお聞きください!

この時作成してくれたコードが以下になります。

car_factory_game.py
import pygame
import sys
import random
from pygame.locals import *

# 初期化
pygame.init()

# 画面設定
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('自動車工場シミュレーション')

# 色の定義
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)

# フォント設定
font = pygame.font.SysFont(None, 24)
large_font = pygame.font.SysFont(None, 36)

# ゲーム変数
money = 10000  # 初期資金
cars_produced = 0
efficiency = 1.0
quality = 0.8  # 品質 (0-1)
day = 1
workers = 5
machines = 3
research_level = 1

# 価格設定
car_price = 5000
worker_cost = 100  # 1日あたりの労働者コスト
machine_cost = 2000  # 機械の購入コスト
machine_maintenance = 50  # 1日あたりの機械メンテナンスコスト
research_cost = 5000  # 研究開発コスト

# 生産ステーション
class Station:
    def __init__(self, x, y, name, color):
        self.x = x
        self.y = y
        self.width = 100
        self.height = 80
        self.name = name
        self.color = color
        self.progress = 0
        self.max_progress = 100
        self.active = False

    def draw(self):
        # ステーション本体
        pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
        pygame.draw.rect(screen, BLACK, (self.x, self.y, self.width, self.height), 2)

        # ステーション名
        name_text = font.render(self.name, True, BLACK)
        screen.blit(name_text, (self.x + 10, self.y + 10))

        # 進捗バー
        bar_width = int((self.width - 20) * (self.progress / self.max_progress))
        pygame.draw.rect(screen, GREEN, (self.x + 10, self.y + 50, bar_width, 15))
        pygame.draw.rect(screen, BLACK, (self.x + 10, self.y + 50, self.width - 20, 15), 1)

    def update(self):
        if self.active:
            # 効率と労働者数に基づいて進捗を更新
            progress_rate = (efficiency * workers / 10) * (machines / 5)
            self.progress += progress_rate

            if self.progress >= self.max_progress:
                self.progress = 0
                self.active = False
                return True  # 工程完了
        return False

# 車のクラス
class Car:
    def __init__(self):
        self.x = 50
        self.y = 400
        self.width = 60
        self.height = 30
        self.stage = 0  # 0: フレーム, 1: エンジン, 2: 塗装, 3: 完成
        self.color = GRAY

    def draw(self):
        if self.stage >= 0:  # フレーム
            pygame.draw.rect(screen, GRAY, (self.x, self.y, self.width, self.height))

        if self.stage >= 1:  # エンジン
            pygame.draw.rect(screen, BLACK, (self.x + 10, self.y + 10, 20, 15))

        if self.stage >= 2:  # 塗装
            pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
            pygame.draw.rect(screen, BLACK, (self.x, self.y, self.width, self.height), 1)
            if self.stage >= 1:
                pygame.draw.rect(screen, BLACK, (self.x + 10, self.y + 10, 20, 15))

        # タイヤ
        pygame.draw.circle(screen, BLACK, (self.x + 15, self.y + self.height), 5)
        pygame.draw.circle(screen, BLACK, (self.x + self.width - 15, self.y + self.height), 5)

# ステーションの作成
frame_station = Station(100, 150, "フレーム組立", BLUE)
engine_station = Station(250, 150, "エンジン取付", RED)
paint_station = Station(400, 150, "塗装", YELLOW)
qc_station = Station(550, 150, "品質検査", GREEN)

stations = [frame_station, engine_station, paint_station, qc_station]
current_car = None
production_queue = []

# ボタンクラス
class Button:
    def __init__(self, x, y, width, height, text, color):
        self.rect = pygame.Rect(x, y, width, height)
        self.text = text
        self.color = color
        self.hover_color = (min(color[0] + 30, 255), min(color[1] + 30, 255), min(color[2] + 30, 255))
        self.active_color = color

    def draw(self):
        mouse_pos = pygame.mouse.get_pos()
        if self.rect.collidepoint(mouse_pos):
            pygame.draw.rect(screen, self.hover_color, self.rect)
        else:
            pygame.draw.rect(screen, self.active_color, self.rect)

        pygame.draw.rect(screen, BLACK, self.rect, 2)
        text_surf = font.render(self.text, True, BLACK)
        text_rect = text_surf.get_rect(center=self.rect.center)
        screen.blit(text_surf, text_rect)

    def is_clicked(self, pos):
        return self.rect.collidepoint(pos)

# ボタンの作成
start_button = Button(50, 50, 120, 40, "生産開始", GREEN)
hire_button = Button(650, 300, 120, 40, "労働者雇用", BLUE)
buy_machine_button = Button(650, 350, 120, 40, "機械購入", RED)
research_button = Button(650, 400, 120, 40, "研究開発", YELLOW)
next_day_button = Button(650, 500, 120, 40, "次の日へ", GRAY)

buttons = [start_button, hire_button, buy_machine_button, research_button, next_day_button]

# メインゲームループ
def main():
    global money, cars_produced, efficiency, quality, day, workers, machines, research_level, current_car

    clock = pygame.time.Clock()
    game_over = False
    message = ""
    message_timer = 0

    while not game_over:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()

                # ボタンクリック処理
                if start_button.is_clicked(pos) and not current_car:
                    if money >= 1000:  # 生産開始コスト
                        money -= 1000
                        current_car = Car()
                        frame_station.active = True
                        message = "新しい車の生産を開始しました!"
                        message_timer = 180
                    else:
                        message = "資金が足りません!"
                        message_timer = 180

                elif hire_button.is_clicked(pos):
                    if money >= 1000:  # 雇用コスト
                        money -= 1000
                        workers += 1
                        message = "労働者を1人雇用しました!"
                        message_timer = 180
                    else:
                        message = "資金が足りません!"
                        message_timer = 180

                elif buy_machine_button.is_clicked(pos):
                    if money >= machine_cost:
                        money -= machine_cost
                        machines += 1
                        message = "新しい機械を購入しました!"
                        message_timer = 180
                    else:
                        message = "資金が足りません!"
                        message_timer = 180

                elif research_button.is_clicked(pos):
                    if money >= research_cost:
                        money -= research_cost
                        research_level += 1
                        efficiency += 0.1
                        quality += 0.05
                        if quality > 1:
                            quality = 1
                        message = "研究開発を行いました!効率と品質が向上しました!"
                        message_timer = 180
                    else:
                        message = "資金が足りません!"
                        message_timer = 180

                elif next_day_button.is_clicked(pos):
                    # 日給と維持費の支払い
                    daily_cost = (worker_cost * workers) + (machine_maintenance * machines)
                    money -= daily_cost
                    day += 1

                    message = f"次の日になりました。日給と維持費として{daily_cost}円支払いました。"
                    message_timer = 180

                    if money < 0:
                        game_over = True

        # 画面クリア
        screen.fill(WHITE)

        # ステーション更新と描画
        if current_car:
            if current_car.stage == 0 and frame_station.update():
                current_car.stage = 1
                engine_station.active = True
            elif current_car.stage == 1 and engine_station.update():
                current_car.stage = 2
                paint_station.active = True
                # 色をランダムに選択
                current_car.color = (random.randint(50, 200), random.randint(50, 200), random.randint(50, 200))
            elif current_car.stage == 2 and paint_station.update():
                current_car.stage = 3
                qc_station.active = True
            elif current_car.stage == 3 and qc_station.update():
                # 品質チェック
                if random.random() < quality:
                    # 成功
                    cars_produced += 1
                    money += car_price
                    message = f"車が完成しました!売上 +{car_price}円"
                    message_timer = 180
                else:
                    # 失敗
                    message = "品質検査で不合格になりました。"
                    message_timer = 180

                current_car = None

        # ステーションの描画
        for station in stations:
            station.draw()

        # 現在の車を描画
        if current_car:
            current_car.draw()

        # ボタンの描画
        for button in buttons:
            button.draw()

        # 情報表示
        info_texts = [
            f"日: {day}",
            f"資金: {money}円",
            f"生産台数: {cars_produced}台",
            f"労働者: {workers}人",
            f"機械: {machines}台",
            f"効率: {efficiency:.2f}",
            f"品質: {int(quality * 100)}%",
            f"研究レベル: {research_level}"
        ]

        for i, text in enumerate(info_texts):
            text_surf = font.render(text, True, BLACK)
            screen.blit(text_surf, (10, 500 + i * 25))

        # メッセージ表示
        if message_timer > 0:
            message_surf = font.render(message, True, BLACK)
            screen.blit(message_surf, (WIDTH // 2 - message_surf.get_width() // 2, 100))
            message_timer -= 1

        # ゲームオーバー処理
        if game_over:
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 128))
            screen.blit(overlay, (0, 0))

            game_over_text = large_font.render("ゲームオーバー!資金がなくなりました", True, WHITE)
            result_text = large_font.render(f"最終生産台数: {cars_produced}台", True, WHITE)

            screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - 50))
            screen.blit(result_text, (WIDTH // 2 - result_text.get_width() // 2, HEIGHT // 2))

        pygame.display.flip()
        clock.tick(60)

if __name__ == "__main__":
    main()

また指示にありませんでしたが、ゲーム内容を記載した README.md まで自動で作成してくれました。

# 自動車工場シミュレーションゲーム

このゲームは製造業(自動車工場)の基本的な概念を学ぶためのシミュレーションゲームです。

## 概要

プレイヤーは自動車工場の経営者となり、車の生産ラインを管理します。以下の要素を学ぶことができます:

- 生産ライン管理
- リソース配分(労働者と機械)
- 品質管理
- 研究開発の重要性
- コスト管理と収益最大化

## 遊び方

1. 「生産開始」ボタンをクリックして新しい車の生産を開始します(コスト: 1000円)
2. 各生産ステーション(フレーム組立、エンジン取付、塗装、品質検査)で車が自動的に処理されます
3. 労働者を雇用したり、機械を購入したりして生産効率を向上させることができます
4. 研究開発に投資して品質と効率を向上させることができます
5. 「次の日へ」ボタンをクリックして日を進めます(労働者の給料と機械のメンテナンス費用が発生します)
6. 資金がなくなるとゲームオーバーです

## 操作方法

- マウスクリックでボタンを操作します

## 要素の説明

- **労働者**: 生産速度に影響します。多いほど生産が速くなりますが、日給が発生します。
- **機械**: 生産効率に影響します。多いほど効率が上がりますが、購入コストとメンテナンス費用が発生します。
- **研究開発**: 効率と品質を向上させます。高いレベルの研究開発は高価ですが、長期的には利益をもたらします。
- **品質**: 完成車が品質検査に合格する確率に影響します。品質が低いと不良品が発生し、収益が減少します。

## 実行方法

```
python car_factory_game.py
```

## 必要条件

- Python 3.x
- Pygame

そして最後にプログラムの起動までやってくれました。

日本語が文字化けし、原色を使った色合いなどとりあえず作って動かした感があって趣がありますね。

car-factory-game-001

ゲーム内容としては説明の通り、人を雇ったり、機械を購入したり、研究開発を行って車の生産効率を上げ、資金を増やしていくゲームです。それぞれ導入コストとランニングコストがあり、資金がなくなるとゲームオーバーとなります。

人や機械に投資しなければ生産速度は向上せず出荷までに時間がかかってしまいますし、研究開発を怠ると品質検査で弾かれて出荷できず材料費が無駄になるといったことが学べます。

ゲームの表示面を改善する

Pygame は日本語が文字化けしてしまうため、自分で日本語フォントを用意する必要があるそうです。

今回は Noto Sans Japanese をダウンロードしてディレクトリ内に配置しこのフォントを使うように指示をしてみました。

https://fonts.google.com/noto/specimen/Noto+Sans+JP

フォントは `./Noto_Sans_JP/static/NotoSansJP-Regular.ttf` を使用してください。

すると以下のように変更してくれました。

指示したパスを分解して os.path.josin で繋げているのは芸術点高いですね。

Noto Sans JPフォントを使用するようにゲームを更新しました。以下の変更を行いました:
1. フォント設定を変更し、システムフォントの代わりに指定されたNoto Sans JPフォントを使用するようにしました
2. `os`モジュールをインポートして、フォントパスを適切に構築できるようにしました

変更内容:
```python
# フォント設定(変更前)
font = pygame.font.SysFont(None, 24)
large_font = pygame.font.SysFont(None, 36)

# フォント設定(変更後)
font_path = os.path.join("Noto_Sans_JP", "static", "NotoSansJP-Regular.ttf")
font = pygame.font.Font(font_path, 24)
large_font = pygame.font.Font(font_path, 36)
```

これにより、ゲーム内のすべてのテキスト(ボタン、情報表示、メッセージなど)がNoto Sans JPフォントで表示されるようになります。日本語のテキストがより美しく表示され、ゲームの見た目が向上します。

ゲームは正常に起動しており、指定されたフォントを使用しています。何か他に変更や追加したい機能はありますか?

ただ日本語フォントをそのまま表示しているため文字がすごく大きいです。

car-factory-game-002

こういった改良も全部プロンプトで指示します。

日本語フォントに合わせて文字サイズを変更してください。

フォントサイズ調整後左下に表示されているステータス情報が見切れていることに気づいたのでこれも表示位置修正を指示しました。

左下のステータス情報の位置を少し上にしてください。

また色合いについても変更を指示してみました。

配色について。ださい。ください。目に優しい色合いにしてください。

指示する時にターミナル内での日本語入力で荒ぶってしまいましたが、しっかり理解してくれました。

配色を目に優しく洗練されたものに変更しましょう。現在の原色ベースの配色から、より落ち着いた目に優しい色合いに変更します。

なんやかんや調整を繰り返し、最終的には以下のようになりました。

当初と比べてだいぶスッキリしてます。

car-factory-game-003

ゲームにアイデアを追加する

実はこのゲームには根本的な欠点があります。日の概念はあっても時間の概念がありません。

生産開始ボタンを押しまくっていればどんどん資金は貯まっていきます。
次の日ボタンをユーザーが押さなければ次の日に進まないのです。金無限エンドレス Day One です。

まずは最初の1ステップとして、1日の中の時間を表示してくれるように頼んでみました。

1日の中の時間経過について表示してください。
ゲームに1日の中の時間経過を表示する機能を追加しましょう。これにより、プレイヤーは1日の中での時間の流れを視覚的に把握できるようになります。

1日の中の時間経過を表示する機能を追加しました。以下が主な変更点です:
1. **時間表示の追加**:
   - 画面右上に時計を表示(アナログ時計とデジタル時計)
   - ステータス情報に「時間: XX:00」の表示を追加

2. **時間経過システムの実装**:
   - 朝8時から始まり、各工程で時間が経過
   - 各生産工程で1時間経過
   - 18時になると自動的に次の日に移行(日給と維持費が発生)

3. **時間管理関数の作成**:
   - `update_time_and_check_day_end()` 関数を作成して時間管理をモジュール化
   - 時間経過、日付変更、コスト計算を一元管理

4. **視覚的なフィードバック**:
   - アナログ時計の針が現在の時間を示す
      - 時間経過に関するメッセージを表示

  これにより、ゲームにより現実的な時間の流れが追加され、プレイヤーは1日の中での時間管理も考慮する必要が出てきました。18時までに効率よく生産を行うことが重要になります。

  ゲームは正常に起動しており、時間経過システムが機能しています。他に調整や機能追加が必要な点はありますか?

右上に時計が追加され、時間経過が表示されるようになりました。

表示だけしてくれたらと思ったのですが、意図を汲んでくれたのか、生産で時間経過するようになり、18時になると自動的に次の日に移行といったところまで実装してくれました。すごい。

car-factory-game-004

ここまでやってくれましたが、プレイしてみるといくら効率の数値が上昇しようとも生産にかかる時間は常に固定となっていました。なので効率上昇によって所要時間も減らすように依頼しました。

効率上昇によって、各工程にかかる時間も減らしてください。
効率上昇によって各工程にかかる時間が減るように修正します。現在は効率が上がっても各工程で固定の1時間が経過していますが、効率に応じて時間経過を調整するようにしましょう。

効率上昇によって各工程にかかる時間が減るように実装しました。以下が主な変更点です:

1. **効率に基づく時間経過の調整**:
   ```python
   time_passed = max(0.5, 1.0 / efficiency)  # 効率が上がると時間が短縮(最小0.5時間)
   ```
   - 効率が1.0の場合、1時間かかります
   - 効率が2.0の場合、0.5時間かかります
   - 効率が高くなるほど時間が短縮されますが、最低でも0.5時間はかかります

2. **小数点以下の時間表示**:
   - 時間を小数点以下で扱えるように変更
   - 時間:分 形式で表示(例: 8:30)
   - アナログ時計の針も分単位で動くように改良

3. **時間管理関数の強化**:
   - `update_time_and_check_day_end()` 関数を小数点以下の時間も扱えるように改良
   - 時間と分を分けて管理

これらの変更により、以下のゲームプレイ上の効果が得られます:

- 研究開発に投資して効率を上げると、各工程にかかる時間が短縮されます
- 1日(8時〜18時)の間により多くの車を生産できるようになります
- 効率の向上が視覚的にも分かりやすくなります
- より現実的な時間の流れを表現できます

ゲームは正常に起動しており、効率に応じた時間経過システムが機能しています。他に調整や機能追加が必要な点はありますか?

プレイすると、生産効率の上昇によって所要時間も減り、短時間で効率よく稼げるようになりました。

car-factory-game-005

ひとまずこれで完成ということにします。

完成したゲーム

最終的なコードがこちらです。

もしローカルで試したい方は上述の通り、Noto Sans Japanese フォントを用意してください。

car_factory_game.py
import pygame
import sys
import random
import os
import math
from pygame.locals import *

# 初期化
pygame.init()

# 画面設定
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('自動車工場シミュレーション')

# 背景色
BG_COLOR = (245, 245, 250)  # 薄い青みがかった白

# 色の定義
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
LIGHT_GRAY = (230, 230, 230)
DARK_GRAY = (100, 100, 100)

# 目に優しい色合い
SOFT_BLUE = (100, 150, 200)
SOFT_GREEN = (100, 180, 140)
SOFT_RED = (200, 120, 120)
SOFT_YELLOW = (230, 200, 100)
SOFT_PURPLE = (150, 120, 180)

# フォント設定
font_path = os.path.join("Noto_Sans_JP", "static", "NotoSansJP-Regular.ttf")
font = pygame.font.Font(font_path, 18)  # 日本語フォント用に小さくする
large_font = pygame.font.Font(font_path, 28)  # 日本語フォント用に小さくする

# ゲーム変数
money = 10000  # 初期資金
cars_produced = 0
efficiency = 1.0
quality = 0.8  # 品質 (0-1)
day = 1
time_of_day = 8  # 朝8時からスタート
workers = 5
machines = 3
research_level = 1

# 価格設定
car_price = 5000
worker_cost = 100  # 1日あたりの労働者コスト
machine_cost = 2000  # 機械の購入コスト
machine_maintenance = 50  # 1日あたりの機械メンテナンスコスト
research_cost = 5000  # 研究開発コスト

# 時間と日付の更新関数
def update_time_and_check_day_end(hours_passed, success_message=""):
    global time_of_day, day, money

    # 時間を小数点以下で扱う
    time_of_day += hours_passed

    # 時間表示用に整数部分と小数部分を分ける
    hours = int(time_of_day)
    minutes = int((time_of_day - hours) * 60)

    message = success_message

    if time_of_day >= 18:  # 18時以降は次の日に
        message += " - 勤務時間終了。次の日になりました。"
        day += 1
        time_of_day = 8  # 朝8時にリセット

        # 日給と維持費の支払い
        daily_cost = (worker_cost * workers) + (machine_maintenance * machines)
        money -= daily_cost
        message += f" 日給と維持費として{daily_cost}円支払いました。"

    return message

# 生産ステーション
class Station:
    def __init__(self, x, y, name, color):
        self.x = x
        self.y = y
        self.width = 100
        self.height = 80
        self.name = name
        self.color = color
        self.progress = 0
        self.max_progress = 100
        self.active = False

    def draw(self):
        # ステーション本体
        pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
        pygame.draw.rect(screen, DARK_GRAY, (self.x, self.y, self.width, self.height), 2)

        # ステーション名
        name_text = font.render(self.name, True, BLACK)
        screen.blit(name_text, (self.x + 5, self.y + 5))  # 位置を調整

        # 進捗バー背景
        pygame.draw.rect(screen, LIGHT_GRAY, (self.x + 10, self.y + 50, self.width - 20, 15))

        # 進捗バー
        bar_width = int((self.width - 20) * (self.progress / self.max_progress))
        pygame.draw.rect(screen, SOFT_GREEN, (self.x + 10, self.y + 50, bar_width, 15))
        pygame.draw.rect(screen, DARK_GRAY, (self.x + 10, self.y + 50, self.width - 20, 15), 1)

    def update(self):
        if self.active:
            # 効率と労働者数に基づいて進捗を更新
            progress_rate = (efficiency * workers / 10) * (machines / 5)
            self.progress += progress_rate

            if self.progress >= self.max_progress:
                self.progress = 0
                self.active = False
                return True  # 工程完了
        return False

# 車のクラス
class Car:
    def __init__(self):
        self.x = 50
        self.y = 300  # 400から300に変更して上に移動
        self.width = 60
        self.height = 30
        self.stage = 0  # 0: フレーム, 1: エンジン, 2: 塗装, 3: 完成
        self.color = (150, 170, 190)  # デフォルトの車の色を柔らかい色に

    def draw(self):
        if self.stage >= 0:  # フレーム
            pygame.draw.rect(screen, LIGHT_GRAY, (self.x, self.y, self.width, self.height))

        if self.stage >= 1:  # エンジン
            pygame.draw.rect(screen, DARK_GRAY, (self.x + 10, self.y + 10, 20, 15))

        if self.stage >= 2:  # 塗装
            pygame.draw.rect(screen, self.color, (self.x, self.y, self.width, self.height))
            pygame.draw.rect(screen, DARK_GRAY, (self.x, self.y, self.width, self.height), 1)
            if self.stage >= 1:
                pygame.draw.rect(screen, DARK_GRAY, (self.x + 10, self.y + 10, 20, 15))

        # タイヤ
        pygame.draw.circle(screen, DARK_GRAY, (self.x + 15, self.y + self.height), 5)
        pygame.draw.circle(screen, DARK_GRAY, (self.x + self.width - 15, self.y + self.height), 5)

# ステーションの作成
frame_station = Station(100, 150, "フレーム組立", SOFT_BLUE)
engine_station = Station(250, 150, "エンジン取付", SOFT_RED)
paint_station = Station(400, 150, "塗装", SOFT_YELLOW)
qc_station = Station(550, 150, "品質検査", SOFT_GREEN)

stations = [frame_station, engine_station, paint_station, qc_station]
current_car = None
production_queue = []

# ボタンクラス
class Button:
    def __init__(self, x, y, width, height, text, color):
        self.rect = pygame.Rect(x, y, width, height)
        self.text = text
        self.color = color
        self.hover_color = (min(color[0] + 30, 255), min(color[1] + 30, 255), min(color[2] + 30, 255))
        self.active_color = color

    def draw(self):
        mouse_pos = pygame.mouse.get_pos()
        if self.rect.collidepoint(mouse_pos):
            pygame.draw.rect(screen, self.hover_color, self.rect)
        else:
            pygame.draw.rect(screen, self.active_color, self.rect)

        pygame.draw.rect(screen, DARK_GRAY, self.rect, 2)
        text_surf = font.render(self.text, True, BLACK)
        text_rect = text_surf.get_rect(center=self.rect.center)
        screen.blit(text_surf, text_rect)

    def is_clicked(self, pos):
        return self.rect.collidepoint(pos)

# ボタンの作成
start_button = Button(50, 50, 150, 40, "生産開始", SOFT_GREEN)  # 緑→ソフトグリーン
hire_button = Button(650, 300, 150, 40, "労働者雇用", SOFT_BLUE)  # 青→ソフトブルー
buy_machine_button = Button(650, 350, 150, 40, "機械購入", SOFT_PURPLE)  # 赤→ソフトパープル
research_button = Button(650, 400, 150, 40, "研究開発", SOFT_YELLOW)  # 黄→ソフトイエロー
next_day_button = Button(650, 500, 150, 40, "次の日へ", LIGHT_GRAY)  # グレー→ライトグレー

buttons = [start_button, hire_button, buy_machine_button, research_button, next_day_button]

# メインゲームループ
def main():
    global money, cars_produced, efficiency, quality, day, time_of_day, workers, machines, research_level, current_car

    clock = pygame.time.Clock()
    game_over = False
    message = ""
    message_timer = 0

    while not game_over:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()

                # ボタンクリック処理
                if start_button.is_clicked(pos) and not current_car:
                    if money >= 1000:  # 生産開始コスト
                        money -= 1000
                        current_car = Car()
                        frame_station.active = True
                        message = "新しい車の生産を開始しました!"
                        message_timer = 180
                    else:
                        message = "資金が足りません!"
                        message_timer = 180

                elif hire_button.is_clicked(pos):
                    if money >= 1000:  # 雇用コスト
                        money -= 1000
                        workers += 1
                        message = "労働者を1人雇用しました!"
                        message_timer = 180
                    else:
                        message = "資金が足りません!"
                        message_timer = 180

                elif buy_machine_button.is_clicked(pos):
                    if money >= machine_cost:
                        money -= machine_cost
                        machines += 1
                        message = "新しい機械を購入しました!"
                        message_timer = 180
                    else:
                        message = "資金が足りません!"
                        message_timer = 180

                elif research_button.is_clicked(pos):
                    if money >= research_cost:
                        money -= research_cost
                        research_level += 1
                        efficiency += 0.1
                        quality += 0.05
                        if quality > 1:
                            quality = 1
                        message = "研究開発を行いました!効率と品質が向上しました!"
                        message_timer = 180
                    else:
                        message = "資金が足りません!"
                        message_timer = 180

                elif next_day_button.is_clicked(pos):
                    # 日給と維持費の支払い
                    daily_cost = (worker_cost * workers) + (machine_maintenance * machines)
                    money -= daily_cost
                    day += 1
                    time_of_day = 8  # 朝8時にリセット

                    message = f"次の日になりました。日給と維持費として{daily_cost}円支払いました。"
                    message_timer = 180

                    if money < 0:
                        game_over = True

        # 画面クリア
        screen.fill(BG_COLOR)

        # ステーション更新と描画
        if current_car:
            if current_car.stage == 0 and frame_station.update():
                current_car.stage = 1
                engine_station.active = True
                # 時間経過(効率に応じて減少)
                time_passed = max(0.5, 1.0 / efficiency)  # 効率が上がると時間が短縮(最小0.5時間)
                message = update_time_and_check_day_end(time_passed, "フレーム組立が完了しました。")
                message_timer = 180
            elif current_car.stage == 1 and engine_station.update():
                current_car.stage = 2
                paint_station.active = True
                # 色をランダムに選択
                current_car.color = (
                    random.randint(100, 200), 
                    random.randint(100, 200), 
                    random.randint(100, 200)
                )  # より柔らかい色の範囲
                # 時間経過(効率に応じて減少)
                time_passed = max(0.5, 1.0 / efficiency)  # 効率が上がると時間が短縮(最小0.5時間)
                message = update_time_and_check_day_end(time_passed, "エンジン取付が完了しました。")
                message_timer = 180
            elif current_car.stage == 2 and paint_station.update():
                current_car.stage = 3
                qc_station.active = True
                # 時間経過(効率に応じて減少)
                time_passed = max(0.5, 1.0 / efficiency)  # 効率が上がると時間が短縮(最小0.5時間)
                message = update_time_and_check_day_end(time_passed, "塗装が完了しました。")
                message_timer = 180
            elif current_car.stage == 3 and qc_station.update():
                # 品質チェック
                if random.random() < quality:
                    # 成功
                    cars_produced += 1
                    money += car_price
                    # 時間経過(効率に応じて減少)
                    time_passed = max(0.5, 1.0 / efficiency)  # 効率が上がると時間が短縮(最小0.5時間)
                    message = update_time_and_check_day_end(time_passed, f"車が完成しました!売上 +{car_price}円")
                else:
                    # 失敗
                    # 時間経過(効率に応じて減少)
                    time_passed = max(0.5, 1.0 / efficiency)  # 効率が上がると時間が短縮(最小0.5時間)
                    message = update_time_and_check_day_end(time_passed, "品質検査で不合格になりました。")

                message_timer = 180
                current_car = None

                if money < 0:
                    game_over = True

        # ステーションの描画
        for station in stations:
            station.draw()

        # 現在の車を描画
        if current_car:
            current_car.draw()

        # ボタンの描画
        for button in buttons:
            button.draw()

        # 情報表示
        info_texts = [
            f"日: {day}",
            f"時間: {int(time_of_day)}:{int((time_of_day - int(time_of_day)) * 60):02d}",  # 時間:分 形式で表示
            f"資金: {money}円",
            f"生産台数: {cars_produced}台",
            f"労働者: {workers}人",
            f"機械: {machines}台",
            f"効率: {efficiency:.2f}",
            f"品質: {int(quality * 100)}%",
            f"研究レベル: {research_level}"
        ]

        for i, text in enumerate(info_texts):
            text_surf = font.render(text, True, BLACK)
            screen.blit(text_surf, (10, 400 + i * 22))  # 位置をさらに上に移動(450→400)

        # メッセージ表示
        if message_timer > 0:
            message_surf = font.render(message, True, BLACK)
            screen.blit(message_surf, (WIDTH // 2 - message_surf.get_width() // 2, 100))
            message_timer -= 1

        # 時計の描画
        pygame.draw.circle(screen, DARK_GRAY, (WIDTH - 50, 50), 30, 2)
        pygame.draw.circle(screen, DARK_GRAY, (WIDTH - 50, 50), 3)

        # 時計の針(時間)- 小数点以下の時間も反映
        hour_angle = math.radians(((time_of_day % 12) * 30) - 90)  # 12時間制、3時が0度
        hour_x = WIDTH - 50 + 15 * math.cos(hour_angle)
        hour_y = 50 + 15 * math.sin(hour_angle)
        pygame.draw.line(screen, BLACK, (WIDTH - 50, 50), (hour_x, hour_y), 2)

        # 時計の針(分)- 小数点以下の時間から分を計算
        minutes = (time_of_day - int(time_of_day)) * 60
        minute_angle = math.radians((minutes * 6) - 90)  # 分針は1分で6度回転
        minute_x = WIDTH - 50 + 20 * math.cos(minute_angle)
        minute_y = 50 + 20 * math.sin(minute_angle)
        pygame.draw.line(screen, BLACK, (WIDTH - 50, 50), (minute_x, minute_y), 1)

        # 時間表示
        time_text = font.render(f"{int(time_of_day)}:{int((time_of_day - int(time_of_day)) * 60):02d}", True, BLACK)
        screen.blit(time_text, (WIDTH - 85, 85))

        # ゲームオーバー処理
        if game_over:
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 128))
            screen.blit(overlay, (0, 0))

            game_over_text = large_font.render("ゲームオーバー!資金がなくなりました", True, WHITE)
            result_text = large_font.render(f"最終生産台数: {cars_produced}台", True, WHITE)

            screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - 50))
            screen.blit(result_text, (WIDTH // 2 - result_text.get_width() // 2, HEIGHT // 2))

        pygame.display.flip()
        clock.tick(60)

if __name__ == "__main__":
    main()

初期に投資しまくると生産しても品質検査で弾かれ出荷できず資金不足になったり、材料費のことを忘れ投資しまくって資金不足になったりと、単純なゲームかと思いきや意外と楽しく遊ぶことができました。

画面ポチポチして、生産効率を上げて、ひたすら生産開始ボタンを押してるうちに、Cookie Clicker を思い出しました。

ちなみに /usage コマンドでコンテキストウィンドウの推定使用量を表示できます。今回は以下の通りの使用量でした。

car-factory-game-006

ゲーム作りを任せてみての感想

Amazon Q CLI を用いてチャットだけでゲームを作ってみました。

ざっくりとした指示と自覚していますが、それでも意図通りにコードを出力してくれたり、README.md を記述してくれるなど想定以上の働きをしてくれたり、一回の指示で不足部分があっても追加で指示をすれば修正してくれたり、私がソースコードを一切見ずに、できたものをプレイしてフィードバックを与えるだけでここまでやってくれました。

アイデアも丸投げでしたが、自動車生産ラインを模した自動車工場シミュレーションを作ってくれ、自動車工場を経営している雰囲気も味わうことができました。

Vibe Coding ということでソースコードに関しては一切ノータッチです。それでここまで作ってくれたのですからすごいですね。

Amazon Q は MCP に対応していたり、プロジェクトルールを .amazonq/rules/ 配下に置いて定義できたりするなど今回使わなかった機能がまだまだあります。

実開発ではそれらの機能を用いることもあると思うので、気になった方はドキュメントを読んでみてください。

https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/what-is.html

また「Amazon Q CLI を使ってゲームを作ろう Tシャツキャンペーン」は6月20日まで開催中です。こちらも気になった方はぜひ参加してTシャツをゲットしてみてはいかがでしょうか。

https://aws.amazon.com/jp/blogs/news/build-games-with-amazon-q-cli-and-score-a-t-shirt/

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.