Closed4

MetaGPTを試してみた

kun432kun432

https://github.com/geekan/MetaGPT

  1. MetaGPTは1行の要件を入力とし、ユーザーストーリー/競合分析/要件/データ構造/API/ドキュメントなどを出力します。
  2. MetaGPTの内部には、プロダクトマネージャー、アーキテクト、プロジェクトマネージャー、エンジニアがいます。入念にオーケストレーションされたSOPとともに、ソフトウェア会社の全プロセスを提供します。
kun432kun432

インストール

Dockerで。公式の手順では/optとか使ってるけどカレントディレクトリにした。

$ mkdir metagpt && cd metagpt
$ docker pull metagpt/metagpt:v0.3.1
$ mkdir -p {config,workspace}
$ docker run --rm metagpt/metagpt:v0.3.1 cat /app/metagpt/config/config.yaml > ./config/key.yaml

config/key.yamlを修正してAPIキーを設定する。GPT-4を使いたいので今回はOpenAIで(AzureでやりたいけどGPT-4はまだwaitlist・・・)。MAX_TOKENSは日本語だとちょっと小さすぎないかなぁと思いつつ、とりあえずデフォルトのままで。

OPENAI_API_BASE: "https://api.openai.com/v1"
OPENAI_API_KEY: "sk-XXXXXXXXXX"
OPENAI_API_MODEL: "gpt-4"
MAX_TOKENS: 1500
RPM: 10

コンテナ起動

$ docker run --name metagpt -d \
    --privileged \
    -v ./config/key.yaml:/app/metagpt/config/key.yaml \
    -v ./workspace:/app/metagpt/workspace \
    metagpt/metagpt:v0.3.1

コンテナに入る

$ docker exec -it metagpt /bin/bash

では動かしてみる。まずは公式の例に従って。CLIで動くsnake gameを作らせる。snake gameはこういうやつ。

https://g.co/kgs/J2QxkJ

root@ee0dc346a04a:/app/metagpt# python startup.py "Write a cli snake game"

ここから出力を見ていく。まず最初にプロダクトマネージャーがプロダクトの定義を行う。

2023-08-15 05:24:54.753 | INFO     | metagpt.config:__init__:44 - Config loading done.
2023-08-15 05:24:55.648 | INFO     | metagpt.software_company:invest:39 - Investment: $3.0.
2023-08-15 05:24:55.649 | INFO     | metagpt.roles.role:_act:166 - Alice(Product Manager): ready to WritePRD
## Original Requirements
The boss wants us to create a command-line interface (CLI) snake game.

## Product Goals
```python
[
    "Create a CLI-based snake game that is easy to play",
    "Ensure the game is compatible with various operating systems",
    "Provide a high-score feature to enhance user engagement"
]
```

## User Stories
```python
[
    "As a user, I want to be able to easily start a new game so that I can play whenever I want",
    "As a user, I want the game to run on my OS, so that I can play it on my preferred device",
    "As a user, I want to see my high score, so that I can track my progress and aim to beat it",
    "As a user, I want to be able to control the speed of the snake, so that I can adjust the game's difficulty",
    "As a user, I want a clear and simple CLI interface, so that I can focus on the gameplay"
]
```

## Competitive Analysis
```python
[
    "Python Snake Game: A simple CLI-based snake game. It lacks a high-score feature",
    "Java Snake Game: A CLI-based game with high-score feature. However, it requires Java environment",
    "Ruby Snake Game: A game built with Ruby. It has a high-score feature but lacks speed control",
    "Bash Snake Game: A game built with Bash. It is OS-dependent and lacks a high-score feature",
    "C++ Snake Game: A CLI-based game with high-score feature. However, it requires C++ environment",
    "JavaScript Snake Game: A web-based game. It is not CLI-based and requires a browser to play",
    "Go Snake Game: A CLI-based game built with Go. It lacks speed control and high-score feature"
]
```

## Competitive Quadrant Chart
```mermaid
quadrantChart
    title Reach and engagement of campaigns
    x-axis Low Reach --> High Reach
    y-axis Low Engagement --> High Engagement
    quadrant-1 We should expand
    quadrant-2 Need to promote
    quadrant-3 Re-evaluate
    quadrant-4 May be improved
    "Python Snake Game": [0.3, 0.6]
    "Java Snake Game": [0.45, 0.23]
    "Ruby Snake Game": [0.57, 0.69]
    "Bash Snake Game": [0.78, 0.34]
    "C++ Snake Game": [0.40, 0.34]
    "JavaScript Snake Game": [0.35, 0.78]
    "Go Snake Game": [0.5, 0.4]
    "Our Target Product": [0.7, 0.8]
```

## Requirement Analysis
The product should be a CLI-based snake game that is easy to play and compatible with various operating systems. It should have a high-score feature to track the user's progress and a speed control feature to adjust the game's difficulty.

## Requirement Pool
```python
[
    ("Create a CLI interface for the game", "P0"),
    ("Implement the snake movement logic", "P0"),
    ("Implement the food generation and eating logic", "P0"),
    ("Implement a high-score feature", "P1"),
    ("Implement a speed control feature", "P2")
]
```

## UI Design draft
The user interface will be a simple CLI interface. It will display the game grid, the snake, and the food. The snake will be represented by a line of characters, and the food will be represented by a single character. The high score will be displayed at the top of the screen. The user will control the snake's movement using the arrow keys.

## Anything UNCLEAR
There are no unclear points.

プロダクトの背景要件、目標、ユーザーストーリー、競合分析(+クワドラントチャート)、要件分析、要件プール、UI デザイン案をリストアップして、最後に不明点がないかを確認する。

次にアーキテクトが設計を行う。

2023-08-15 05:25:54.277 | INFO     | metagpt.roles.role:_act:166 - Bob(Architect): ready to WriteDesign
## Implementation approach
The game will be implemented using Python's built-in libraries such as `curses` for the CLI interface, and `random` for food generation. The game logic will be implemented in a class-based approach, with separate classes for the Game, Snake, and Food. The high-score feature will be implemented using Python's built-in file handling features, and the speed control feature will be implemented using the `time` library.

## Python package name
```python
"cli_snake_game"
```

## File list
```python
[
    "main.py",
    "game.py",
    "snake.py",
    "food.py",
    "high_score_manager.py"
]
```

## Data structures and interface definitions
```mermaid
classDiagram
    class Game{
        +int score
        +int speed
        +start_game()
        +end_game()
    }
    class Snake{
        +list body
        +move()
        +eat(Food)
    }
    class Food{
        +tuple position
        +generate()
    }
    class HighScoreManager{
        +int high_score
        +load_high_score()
        +save_high_score(int)
    }
    Game "1" -- "1" Snake: controls
    Game "1" -- "1" Food: controls
    Game "1" -- "1" HighScoreManager: uses
```

## Program call flow
```mermaid
sequenceDiagram
    participant M as main
    participant G as Game
    participant S as Snake
    participant F as Food
    participant H as HighScoreManager
    M->>G: create game
    G->>S: create snake
    G->>F: create food
    G->>H: load high score
    loop game loop
        G->>S: move snake
        alt snake eats food
            S->>F: eat food
            G->>F: generate new food
            G->>S: grow snake
            G->>G: increase score
        end
        alt game over
            G->>H: save high score
            G->>M: end game
        end
    end
```

## Anything UNCLEAR
The requirement is clear to me.

Pythonのパッケージ名、ファイル一覧、データ構造、インタフェース定義、フローをリストアップして、要件に不備がないか、ここでもチェックを行って、そして設計書が作成される。

2023-08-15 05:26:32.874 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/competitive_analysis.pdf..
Generating single mermaid chart
2023-08-15 05:26:33.553 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/competitive_analysis.svg..
Generating single mermaid chart
2023-08-15 05:26:34.149 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/competitive_analysis.png..
Generating single mermaid chart
2023-08-15 05:26:34.734 | INFO     | metagpt.actions.design_api:_save_prd:110 - Saving PRD to /app/metagpt/workspace/cli_snake_game/docs/prd.md
2023-08-15 05:26:34.735 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/data_api_design.pdf..
Generating single mermaid chart
2023-08-15 05:26:35.293 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/data_api_design.svg..
Generating single mermaid chart
2023-08-15 05:26:35.853 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/data_api_design.png..
Generating single mermaid chart
2023-08-15 05:26:36.436 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/seq_flow.pdf..
Generating single mermaid chart
2023-08-15 05:26:36.994 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/seq_flow.svg..
Generating single mermaid chart
2023-08-15 05:26:37.585 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_snake_game/resources/seq_flow.png..
Generating single mermaid chart
2023-08-15 05:26:38.213 | INFO     | metagpt.actions.design_api:_save_system_design:119 - Saving System Designs to /app/metagpt/workspace/cli_snake_game/docs/system_design.md

そしてプロジェクトマネージャーがタスク化する。

2023-08-15 05:26:38.215 | INFO     | metagpt.roles.role:_act:166 - Eve(Project Manager): ready to WriteTasks
## Required Python third-party packages
```python
"""
curses==2.2
random==1.2.1
time==1.0
"""
```

## Required Other language third-party packages
```python
"""
No third-party packages required.
"""
```

## Full API spec
```python
"""
No APIs are used in this project.
"""
```

## Logic Analysis
```python
[
    ("main.py", "Contains the main entry point for the game. It creates a Game object and starts the game loop."),
    ("game.py", "Contains the Game class which controls the game logic. It creates and controls the Snake, Food, and HighScoreManager objects."),
    ("snake.py", "Contains the Snake class. The Snake object is controlled by the Game object and interacts with the Food object."),
    ("food.py", "Contains the Food class. The Food object is controlled by the Game object and interacts with the Snake object."),
    ("high_score_manager.py", "Contains the HighScoreManager class. The HighScoreManager object is used by the Game object to load and save the high score.")
]
```

## Task list
```python
[
    "main.py",
    "game.py",
    "snake.py",
    "food.py",
    "high_score_manager.py"
]
```

## Shared Knowledge
```python
"""
The 'curses' library is used to create the CLI interface for the game.
The 'random' library is used to generate the position of the food in the game.
The 'time' library is used to control the speed of the game.
The game logic is implemented in a class-based approach, with separate classes for the Game, Snake, and Food.
The high-score feature is implemented using Python's built-in file handling features.
"""
```

## Anything UNCLEAR
There is no unclear part in the project requirements and design. The main entry point of the game is in 'main.py'. The 'curses', 'random', and 'time' libraries need to be initialized in the main function before the game loop starts.
[
    "main.py",
    "game.py",
    "snake.py",
    "food.py",
    "high_score_manager.py"
]

必要なパッケージ、API仕様、ロジック解析、タスクリスト、共通認識、そしてここでも最後に不明点を確認している。

で最後にタスクリストを元に各スクリプトが生成される。

2023-08-15 05:27:15.442 | INFO     | metagpt.actions.write_code:run:77 - Writing main.py..
## Code: main.py
```python
## main.py
import curses
import time
from game import Game

def main(stdscr):
    # Set up the screen
    curses.curs_set(0)
    stdscr.nodelay(1)
    stdscr.timeout(100)

    # Create a new game
    game = Game(stdscr)

    # Game loop
    while True:
        # Get the user input
        key = stdscr.getch()

        # Pass the key to the game
        game.handle_key(key)

        # Update the game state
        game.update()

        # Draw the game
        game.draw()

        # If the game is over, break the loop
        if game.is_game_over():
            break

        # Control the speed of the game
        time.sleep(1 / game.speed)

if __name__ == "__main__":
    curses.wrapper(main)
```
In the main function, we first set up the screen using the `curses` library. We then create a new `Game` object and start the game loop. In each iteration of the loop, we get the user's input, pass it to the game, update the game state, and draw the game. If the game is over, we break the loop. We control the speed of the game by sleeping for a certain amount of time in each iteration of the loop. The amount of time is determined by the game's speed. The `curses.wrapper` function is used to initialize and clean up the `curses` environment.
(snip)

最終的な成果物はworkspace以下に出力される

$ tree workspace/
workspace/
└── cli_snake_game
    ├── cli_snake_game
    │   ├── food.py
    │   ├── game.py
    │   ├── high_score_manager.py
    │   ├── main.py
    │   └── snake.py
    ├── docs
    │   ├── api_spec_and_tasks.md
    │   ├── prd.md
    │   └── system_design.md
    ├── requirements.txt
    └── resources
        ├── competitive_analysis.mmd
        ├── competitive_analysis.pdf
        ├── competitive_analysis.png
        ├── competitive_analysis.svg
        ├── data_api_design.mmd
        ├── data_api_design.pdf
        ├── data_api_design.png
        ├── data_api_design.svg
        ├── seq_flow.mmd
        ├── seq_flow.pdf
        ├── seq_flow.png
        └── seq_flow.svg

4 directories, 21 files

ドキュメントはmarkdownやPDFで生成される。こんな感じ。

そして生成されたスクリプト、果たして動くのか、仮想環境作って実行してみる。workspace/cli_snake_gameを仮想環境に持ってきて、実行していく。

$ pip install -r requirements.txt
ERROR: Could not find a version that satisfies the requirement curses==2.2 (from versions: none)
ERROR: No matching distribution found for curses==2.2

requirements.txtは以下となっていた。

curses==2.2
random==1.2.1

どちらも標準パッケージのようなので、pip installする必要はなさそう。スクリプトを実行してみる。

$ cd cli_snake_game
$ python main.py
Traceback (most recent call last):
  File "/data/repository/snake-game-metagpt/cli_snake_game/cli_snake_game/main.py", line 37, in <module>
    curses.wrapper(main)
(snip)
  File "/data/repository/snake-game-metagpt/cli_snake_game/cli_snake_game/snake.py", line 4, in __init__
    self.direction = curses.KEY_RIGHT
NameError: name 'curses' is not defined

snake.pyを見てみる。

class Snake:
    def __init__(self):
        self.body = [(10, 10), (10, 9), (10, 8)]
        self.direction = curses.KEY_RIGHT
(snip)

cursesがsnake.pyでimportされていないのが原因。ということで、以下のように修正

import curses

class Snake:
    def __init__(self):
        self.body = [(10, 10), (10, 9), (10, 8)]
        self.direction = curses.KEY_RIGHT
(snip)

再度実行してみると、全部の機能は確認はしてないけども、とりあえず動作した。すごい・・・

kun432kun432

日本語でも試してみた。

$ python startup.py "CLIで操作する「まるばつゲーム」を作成して。画面やメッセージ等はすべて日本語にすること。テスト及びデバッグも行って正常に動作することを保証すること。"

残念ながら出力されるメッセージ等は英語のままだけど、ゲーム内のメッセージは日本語化されていて、かつ、ユニットテストのコードも書いてくれた。テスト実行まではしてくれてない模様。

2023-08-15 07:01:00.973 | INFO     | metagpt.roles.role:_act:166 - Alice(Product Manager): ready to WritePRD
## Original Requirements
The boss has requested the creation of a Tic-Tac-Toe game that can be operated using the Command Line Interface (CLI). The game interface and messages should all be in Japanese. The game should be thoroughly tested and debugged to ensure it functions correctly.

## Product Goals
```python
[
    "Create a CLI-based Tic-Tac-Toe game",
    "Ensure all game interface and messages are in Japanese",
    "Ensure the game is thoroughly tested and debugged"
]
```

## User Stories
```python
[
    "As a user, I want to start a new game of Tic-Tac-Toe from the command line",
    "As a user, I want to see the game board in the command line interface",
    "As a user, I want to make a move by entering the coordinates in the command line",
    "As a user, I want to see messages in Japanese",
    "As a user, I want to know when the game is over and who has won"
]
```

## Competitive Analysis
```python
[
    "CLI Chess: A command line interface game with complex rules and gameplay",
    "CLI Sudoku: A command line interface game with complex puzzle solving",
    "CLI Hangman: A command line interface game with simple gameplay and language learning aspect",
    "CLI Minesweeper: A command line interface game with simple gameplay and strategic thinking",
    "CLI Snake: A command line interface game with simple gameplay and quick reflexes",
    "CLI Tetris: A command line interface game with simple gameplay and spatial reasoning"
]
```

## Competitive Quadrant Chart
```mermaid
quadrantChart
    title Reach and engagement of campaigns
    x-axis Low Reach --> High Reach
    y-axis Low Engagement --> High Engagement
    quadrant-1 We should expand
    quadrant-2 Need to promote
    quadrant-3 Re-evaluate
    quadrant-4 May be improved
    "CLI Chess": [0.7, 0.6]
    "CLI Sudoku": [0.8, 0.7]
    "CLI Hangman": [0.5, 0.4]
    "CLI Minesweeper": [0.6, 0.5]
    "CLI Snake": [0.4, 0.3]
    "CLI Tetris": [0.7, 0.8]
    "Our Target Product": [0.5, 0.6]
```

## Requirement Analysis
The product should be a command line interface game of Tic-Tac-Toe. The game should be simple to start and play, with clear instructions in Japanese. The game should end when one player has three in a row, and the winner should be announced.

## Requirement Pool
```python
[
    ("Create a CLI interface for the game", "P0"),
    ("Implement the game logic for Tic-Tac-Toe", "P0"),
    ("Translate all messages and interface to Japanese", "P0"),
    ("Implement a system to announce the end of the game and the winner", "P0"),
    ("Test and debug the game to ensure it works correctly", "P0")
]
```

## UI Design draft
The game will be text-based and run in the command line interface. The game board will be displayed as a 3x3 grid with numbers representing empty spaces and 'X' or 'O' representing player moves. Instructions and messages will be displayed in Japanese above and below the game board.

## Anything UNCLEAR
There are no unclear points.
2023-08-15 07:02:25.877 | INFO     | metagpt.roles.role:_act:166 - Bob(Architect): ready to WriteDesign
## Implementation approach
We will use Python's built-in libraries to create this CLI-based Tic-Tac-Toe game. The game logic will be implemented using simple data structures and control flow. For the Japanese interface, we can use Python's built-in `gettext` module for internationalization (i18n). This module provides an easy way to translate text in the program to different languages. We will also use Python's built-in `unittest` module to write tests for our game. This will help us ensure that the game works correctly.

## Python package name
```python
"cli_tic_tac_toe"
```

## File list
```python
[
    "main.py",
    "game.py",
    "player.py",
    "locale/ja/LC_MESSAGES/messages.po",
    "tests/test_game.py",
    "tests/test_player.py"
]
```

## Data structures and interface definitions
```mermaid
classDiagram
    class Game{
        +list board
        +Player player1
        +Player player2
        +Player current_player
        +str winner
        +__init__(player1: Player, player2: Player)
        +start_game()
        +make_move(row: int, col: int)
        +check_winner()
        +print_board()
    }
    class Player{
        +str name
        +str symbol
        +__init__(name: str, symbol: str)
    }
    Game "2" -- "1" Player: has
```

## Program call flow
```mermaid
sequenceDiagram
    participant M as Main
    participant G as Game
    participant P1 as Player1
    participant P2 as Player2
    M->>P1: __init__("Player 1", "X")
    M->>P2: __init__("Player 2", "O")
    M->>G: __init__(P1, P2)
    M->>G: start_game()
    loop Game
        G->>G: print_board()
        G->>G: make_move(row, col)
        G->>G: check_winner()
    end
    G->>M: end game
```

## Anything UNCLEAR
The requirement is clear to me.
2023-08-15 07:02:59.967 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/competitive_analysis.pdf..
Generating single mermaid chart
2023-08-15 07:03:00.558 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/competitive_analysis.svg..
Generating single mermaid chart
2023-08-15 07:03:01.097 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/competitive_analysis.png..
Generating single mermaid chart
2023-08-15 07:03:01.632 | INFO     | metagpt.actions.design_api:_save_prd:110 - Saving PRD to /app/metagpt/workspace/cli_tic_tac_toe/docs/prd.md
2023-08-15 07:03:01.633 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/data_api_design.pdf..
Generating single mermaid chart
2023-08-15 07:03:02.165 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/data_api_design.svg..
Generating single mermaid chart
2023-08-15 07:03:02.766 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/data_api_design.png..
Generating single mermaid chart
2023-08-15 07:03:03.378 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/seq_flow.pdf..
Generating single mermaid chart
2023-08-15 07:03:03.932 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/seq_flow.svg..
Generating single mermaid chart
2023-08-15 07:03:04.483 | INFO     | metagpt.utils.mermaid:mermaid_to_file:40 - Generating /app/metagpt/workspace/cli_tic_tac_toe/resources/seq_flow.png..
Generating single mermaid chart
2023-08-15 07:03:05.060 | INFO     | metagpt.actions.design_api:_save_system_design:119 - Saving System Designs to /app/metagpt/workspace/cli_tic_tac_toe/docs/system_design.md
2023-08-15 07:03:05.061 | INFO     | metagpt.roles.role:_act:166 - Eve(Project Manager): ready to WriteTasks
## Required Python third-party packages
```python
"""
No third-party packages required
"""
```

## Required Other language third-party packages
```python
"""
No third-party packages required
"""
```

## Full API spec
```python
"""
No API required for this CLI-based game
"""
```

## Logic Analysis
```python
[
    ("player.py", "Contains Player class with attributes 'name' and 'symbol' and initializer method"),
    ("game.py", "Contains Game class with attributes 'board', 'player1', 'player2', 'current_player', 'winner' and methods '__init__', 'start_game', 'make_move', 'check_winner', 'print_board'"),
    ("main.py", "Contains the main entry point of the application, creates instances of Player and Game classes and starts the game"),
    ("locale/ja/LC_MESSAGES/messages.po", "Contains translations for the Japanese interface"),
    ("tests/test_game.py", "Contains unit tests for the Game class"),
    ("tests/test_player.py", "Contains unit tests for the Player class")
]
```

## Task list
```python
[
    "player.py",
    "game.py",
    "main.py",
    "locale/ja/LC_MESSAGES/messages.po",
    "tests/test_game.py",
    "tests/test_player.py"
]
```

## Shared Knowledge
```python
"""
'player.py' contains the Player class which is used to create player instances with a 'name' and 'symbol'.
'game.py' contains the Game class which is used to create a game instance, start the game, make moves, check the winner and print the board.
'main.py' is the main entry point of the application where player instances are created, a game instance is created and the game is started.
'locale/ja/LC_MESSAGES/messages.po' contains translations for the Japanese interface.
'tests/test_game.py' and 'tests/test_player.py' contain unit tests for the Game and Player classes respectively.
"""
```

## Anything UNCLEAR
The requirements and implementation approach are clear. The team can start with the implementation of the Player class as it is a prerequisite for the Game class. After that, the Game class can be implemented followed by the main entry point of the application. Once the basic functionality is implemented, the Japanese interface translations can be added and finally, the unit tests can be written.
[
    "player.py",
    "game.py",
    "main.py",
    "locale/ja/LC_MESSAGES/messages.po",
    "tests/test_game.py",
    "tests/test_player.py"
]

2023-08-15 07:03:51.078 | INFO     | metagpt.actions.write_code:run:77 - Writing player.py..
## Code: player.py
```python
class Player:
    def __init__(self, name: str, symbol: str):
        self.name = name
        self.symbol = symbol
```
2023-08-15 07:03:56.989 | INFO     | metagpt.actions.write_code:run:77 - Writing game.py..
## Code: game.py
```python
## game.py
import gettext

class Game:
    def __init__(self, player1, player2):
        self.board = [[' ' for _ in range(3)] for _ in range(3)]
        self.player1 = player1
        self.player2 = player2
        self.current_player = player1
        self.winner = None
        self._ = gettext.gettext

    def start_game(self):
        print(self._("ゲームを開始します。"))
        while not self.winner:
            self.print_board()
            row, col = self.get_move()
            self.make_move(row, col)
            self.winner = self.check_winner()
            self.current_player = self.player2 if self.current_player == self.player1 else self.player1
        self.print_board()
        if self.winner == 'draw':
            print(self._("引き分けです。"))
        else:
            print(self._("勝者は {} です。").format(self.winner.name))

    def get_move(self):
        while True:
            move = input(self._("{}さん、次の移動を入力してください (行,列): ").format(self.current_player.name))
            try:
                row, col = map(int, move.split(','))
                if row in range(3) and col in range(3) and self.board[row][col] == ' ':
                    return row, col
                else:
                    print(self._("無効な移動です。もう一度入力してください。"))
            except ValueError:
                print(self._("無効な入力です。もう一度入力してください。"))

    def make_move(self, row, col):
        self.board[row][col] = self.current_player.symbol

    def check_winner(self):
        for i in range(3):
            if self.board[i][0] == self.board[i][1] == self.board[i][2] != ' ':
                return self.current_player
            if self.board[0][i] == self.board[1][i] == self.board[2][i] != ' ':
                return self.current_player
        if self.board[0][0] == self.board[1][1] == self.board[2][2] != ' ':
            return self.current_player
        if self.board[0][2] == self.board[1][1] == self.board[2][0] != ' ':
            return self.current_player
        if all(self.board[i][j] != ' ' for i in range(3) for j in range(3)):
            return 'draw'
        return None

    def print_board(self):
        print('\n'.join([' '.join(row) for row in self.board]))
```

2023-08-15 07:05:06.412 | INFO     | metagpt.actions.write_code:run:77 - Writing main.py..
## Code: main.py
```python
## main.py
from game import Game
from player import Player

def main():
    player1 = Player("Player 1", "X")
    player2 = Player("Player 2", "O")
    game = Game(player1, player2)
    game.start_game()

if __name__ == "__main__":
    main()
```
2023-08-15 07:05:14.790 | INFO     | metagpt.actions.write_code:run:77 - Writing locale/ja/LC_MESSAGES/messages.po..
## Code: locale/ja/LC_MESSAGES/messages.po
```python
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ja\n"

msgid "ゲームを開始します。"
msgstr "ゲームを開始します。"

msgid "{}さん、次の移動を入力してください (行,列): "
msgstr "{}さん、次の移動を入力してください (行,列): "

msgid "無効な移動です。もう一度入力してください。"
msgstr "無効な移動です。もう一度入力してください。"

msgid "無効な入力です。もう一度入力してください。"
msgstr "無効な入力です。もう一度入力してください。"

msgid "引き分けです。"
msgstr "引き分けです。"

msgid "勝者は {} です。"
msgstr "勝者は {} です。"
```
Note: This is a .po file for Japanese translation. The `msgid` is the original string in English and `msgstr` is the translated string in Japanese. When the program runs, it will replace the English strings with the corresponding Japanese strings.
2023-08-15 07:05:46.478 | INFO     | metagpt.actions.write_code:run:77 - Writing tests/test_game.py..
## Code: tests/test_game.py
```python
## tests/test_game.py
import unittest
from game import Game
from player import Player

class TestGame(unittest.TestCase):
    def setUp(self):
        self.player1 = Player("Player 1", "X")
        self.player2 = Player("Player 2", "O")
        self.game = Game(self.player1, self.player2)

    def test_init(self):
        self.assertEqual(self.game.player1, self.player1)
        self.assertEqual(self.game.player2, self.player2)
        self.assertEqual(self.game.current_player, self.player1)
        self.assertEqual(self.game.winner, None)
        self.assertEqual(self.game.board, [[' ' for _ in range(3)] for _ in range(3)])

    def test_make_move(self):
        self.game.make_move(0, 0)
        self.assertEqual(self.game.board[0][0], "X")
        self.game.make_move(0, 1)
        self.assertEqual(self.game.board[0][1], "O")

    def test_check_winner(self):
        self.game.board = [['X', 'X', 'X'], [' ', ' ', ' '], [' ', ' ', ' ']]
        self.assertEqual(self.game.check_winner(), self.player1)
        self.game.board = [['O', ' ', ' '], ['O', ' ', ' '], ['O', ' ', ' ']]
        self.assertEqual(self.game.check_winner(), self.player2)
        self.game.board = [['X', ' ', ' '], [' ', 'X', ' '], [' ', ' ', 'X']]
        self.assertEqual(self.game.check_winner(), self.player1)
        self.game.board = [[' ', ' ', 'O'], [' ', 'O', ' '], ['O', ' ', ' ']]
        self.assertEqual(self.game.check_winner(), self.player2)
        self.game.board = [['X', 'O', 'X'], ['O', 'X', 'O'], ['O', 'X', 'O']]
        self.assertEqual(self.game.check_winner(), 'draw')

if __name__ == "__main__":
    unittest.main()
```

2023-08-15 07:06:22.932 | INFO     | metagpt.actions.write_code:run:77 - Writing tests/test_player.py..
## Code: tests/test_player.py
```python
import unittest
from player import Player

class TestPlayer(unittest.TestCase):
    def setUp(self):
        self.player = Player("Player 1", "X")

    def test_init(self):
        self.assertEqual(self.player.name, "Player 1")
        self.assertEqual(self.player.symbol, "X")

if __name__ == "__main__":
    unittest.main()
```
-----
2023-08-15 07:06:33.706 | INFO     | metagpt.roles.engineer:_act_sp:141 - Done /app/metagpt/workspace/cli_tic_tac_toe/cli_tic_tac_toe generating.
[
    "player.py",
    "game.py",
    "main.py",
    "locale/ja/LC_MESSAGES/messages.po",
    "tests/test_game.py",
    "tests/test_player.py"
]

ちゃんと日本語で動作した。

$ python main.py
ゲームを開始します。



Player 1さん、次の移動を入力してください (行,列): 0,0
X


Player 2さん、次の移動を入力してください (行,列): 2,2
X

    O
Player 1さん、次の移動を入力してください (行,列): 0,1
X X

    O
Player 2さん、次の移動を入力してください (行,列): 1,1
X X
  O
    O
Player 1さん、次の移動を入力してください (行,列): 0,2
X X X
  O
    O
勝者は Player 1 です。
kun432kun432

コードをサラッと読んでみたけど、ここに各ロールがある。

https://github.com/geekan/MetaGPT/tree/main/metagpt/roles

で、startup.pyの中で各ロールをアサインするという流れっぽい。company.hire()とかあって面白い。

https://github.com/geekan/MetaGPT/blob/main/startup.py

公式のDockerイメージにあるstartup.pyとは違うみたいで、QAエンジニアがいなかったwwwなのでテストが実行されなかったのだと思う。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import asyncio

import fire

from metagpt.roles import Architect, Engineer, ProductManager, ProjectManager
from metagpt.software_company import SoftwareCompany


async def startup(idea: str, investment: float = 3.0, n_round: int = 5, code_review: bool = False):
    """Run a startup. Be a boss."""
    company = SoftwareCompany()
    company.hire([ProductManager(),
                  Architect(),
                  ProjectManager(),
                  Engineer(n_borg=5, use_code_review=code_review)])
    company.invest(investment)
    company.start_project(idea)
    await company.run(n_round=n_round)


def main(idea: str, investment: float = 3.0, n_round: int = 5, code_review: bool = False):
    """
    We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities.
    :param idea: Your innovative idea, such as "Creating a snake game."
    :param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company.
    :param n_round:
    :param code_review: Whether to use code review.
    :return:
    """
    asyncio.run(startup(idea, investment, n_round, code_review))


if __name__ == '__main__':
    fire.Fire(main)
このスクラップは2023/08/15にクローズされました