Qwen-Code を試す
GitHubレポジトリ
Qwen Code
referred from https://github.com/QwenLM/qwen-code開発者向けAI搭載コマンドラインワークフローツール
Qwen Codeは、Gemini CLI (詳細) をベースにし、Qwen3-Coder モデル向けに最適化された強力なコマンドラインAIワークフローツールです。高度なコード理解、自動化タスク、インテリジェントアシストにより、開発ワークフローを向上させます。
[!WARNING]
トークン使用に関する注意: Qwen Codeはサイクルごとに複数のAPIコールを行う可能性があり、トークン使用量が増大する場合があります (Claude Codeと同様)。API効率の最適化に努めています。💡 無料オプション: ModelScopeは中国本土のユーザーに1日2,000回の無料APIコールを提供しています。OpenRouterは全世界で1日1,000回の無料APIコールを提供しています。設定方法はAPI構成を参照してください。
主な機能
- コード理解 & 編集 - 従来のコンテキストウィンドウ制限を超えて大規模なコードベースをクエリ・編集できます
- ワークフロー自動化 - プルリクエストの処理や複雑なリベースなどの運用タスクを自動化します
- 拡張パーサー - Qwen-Coderモデル向けに最適化されたパーサーを採用
Gemini-CLIがベースなのね。
ライセンスは Apache-2.0
一般的には、OpenRouterで使うパターンが多いのだろうけど、以下でQwen3-Coder-30B-A3B-Instructをllama.cppで動かしたので、これを使ってローカルで完結できるか確認してみたい。
以下のような構成でやってみたい。
- Ubuntuサーバ
- Ubuntu-22.04 + RTX4090
- llama-server(llama.cpp)を起動し、Qwen3-Coder-30B-A3B−Instruct(
UD-Q4_K_XL
)をロード。
- Mac
- Qwen-Codeをインストール
- モデルアクセスを上記サーバに向ける。
まず、Ubuntuサーバでllama-serverを起動してモデルをロード。
./build/bin/llama-server \
-hf unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_XL \
--jinja \
-ngl 99 \
--threads -1 \
--ctx-size 32684 \
--temp 0.7 \
--min-p 0.0 \
--top-p 0.80 \
--top-k 20 \
--repeat-penalty 1.05 \
--host 0.0.0.0
main: server is listening on http://0.0.0.0:8080 - starting the main loop
次にMac。Dockerコンテナを立ち上げてその中でQwen-Codeを試すことにする。devcontainerイメージを使わせてもらう。
作業ディレクトリ作成
mkdir qwen-code-work && cd $_
コンテナ起動
docker run \
--rm \
-it \
-u node \
-v "$PWD:/workspace:cached" \
-w "/workspace" \
mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm \
bash
以下はDockerコンテナ内で。
Qwen-Codeをインストール
npm install -g @qwen-code/qwen-code@latest
確認
qwen --version
0.0.2
Usageも。
qwen --help
qwen [options]
Qwen Code - Launch an interactive CLI, use -p/--prompt for non-interactive mode
Options:
-m, --model Model [string] [default: "qwen3-coder-plus"]
-p, --prompt Prompt. Appended to input on stdin (if any). [string]
-i, --prompt-interactive Execute the provided prompt and continue in interactive
mode [string]
-s, --sandbox Run in sandbox? [boolean]
--sandbox-image Sandbox image URI. [string]
-d, --debug Run in debug mode? [boolean] [default: false]
-a, --all-files Include ALL files in context? [boolean] [default: false]
--all_files Include ALL files in context?
[deprecated: Use --all-files instead. We will be removing --all_files in the coming
weeks.] [boolean] [default: false]
--show-memory-usage Show memory usage in status bar
[boolean] [default: false]
--show_memory_usage Show memory usage in status bar
[deprecated: Use --show-memory-usage instead. We will be removing --show_memory_usage in
the coming weeks.] [boolean] [default: false]
-y, --yolo Automatically accept all actions (aka YOLO mode, see
https://www.youtube.com/watch?v=xvFZjo5PgG0 for more
details)? [boolean] [default: false]
--telemetry Enable telemetry? This flag specifically controls if
telemetry is sent. Other --telemetry-* flags set
specific values but do not enable telemetry on their
own. [boolean]
--telemetry-target Set the telemetry target (local or gcp). Overrides
settings files. [string] [choices: "local", "gcp"]
--telemetry-otlp-endpoint Set the OTLP endpoint for telemetry. Overrides
environment variables and settings files. [string]
--telemetry-log-prompts Enable or disable logging of user prompts for telemetry.
Overrides settings files. [boolean]
-c, --checkpointing Enables checkpointing of file edits
[boolean] [default: false]
--allowed-mcp-server-names Allowed MCP server names [array]
-e, --extensions A list of extensions to use. If not provided, all
extensions are used. [array]
-l, --list-extensions List all available extensions and exit. [boolean]
--ide-mode Run in IDE mode? [boolean]
--openai-logging Enable logging of OpenAI API calls for debugging and
analysis [boolean]
--openai-api-key OpenAI API key to use for authentication [string]
--openai-base-url OpenAI base URL (for custom endpoints) [string]
-v, --version Show version number [boolean]
-h, --help Show help [boolean]
APIの設定
モデル名はこんな感じで確認できる。
curl -s http://<ローカルサーバのIP>:8080/models | jq -r .models[0].name | awk -F"/" '{ print $NF }'
unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF_Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf
export OPENAI_API_KEY="dummy"
export OPENAI_BASE_URL="http://<ローカルサーバのIP>:8080/v1"
export OPENAI_MODEL="unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF_Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf"
では実行
qwen
最初にテーマの選択と適用範囲の設定。テーマを選択して、タブで適用範囲に移動して、適用範囲を指定という感じ。自分の場合は元のターミナル側との兼ね合いで、ANSI以外は見えない感じだった。適用範囲は、今回はコンテナなのでどれでも同じかなと思ったけど、"Workspace Settings"にした。選択したらEnterで確定。
ClearcutLogger disabled - no data collection.
██╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗
╚██╗ ██╔═══██╗██║ ██║██╔════╝████╗ ██║
╚██╗ ██║ ██║██║ █╗ ██║█████╗ ██╔██╗ ██║
██╔╝ ██║▄▄ ██║██║███╗██║██╔══╝ ██║╚██╗██║
██╔╝ ╚██████╔╝╚███╔███╔╝███████╗██║ ╚████║
╚═╝ ╚══▀▀═╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═══╝
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
3. Create QWEN.md files to customize your interactions with Qwen Code.
4. /help for more information.
╭───────────────────────────────────────────────────────────────────────────────╮
│ │
│ > Select Theme Preview │
│ ▲ ┌──────────────────────────────────────┐ │
│ ○ Qwen Light Light │ │ │
│ ○ Qwen Dark Dark │ 1 # function │ │
│ ● ANSI Dark │ 2 -def fibonacci(n): │ │
│ ○ Atom One Dark │ 3 - a, b = 0, 1 │ │
│ ○ Ayu Dark │ 4 - for _ in range(n): │ │
│ ○ Default Dark │ 5 - a, b = b, a + b │ │
│ ○ Dracula Dark │ 6 - return a │ │
│ ○ GitHub Dark │ │ │
│ ▼ │ ╭──────────────────────────────────╮ │ │
│ │ │ │ │ │
│ Apply To │ │ No changes detected. │ │ │
│ ● User Settings │ │ │ │ │
│ ○ Workspace Settings │ ╰──────────────────────────────────╯ │ │
│ ○ System Settings │ │ │
│ └──────────────────────────────────────┘ │
│ │
│ (Use Enter to select, Tab to change focus) │
│ │
╰───────────────────────────────────────────────────────────────────────────────╯
/workspace no sandbox (see /docs) qwen3-coder-plus (100% context left)
次にモデルプロバイダの設定。環境変数セットしたものが適用されてるのかな?とりあえずEnter。
╭───────────────────────────────────────────────────────────────────────────────╮
│ │
│ Get started │
│ │
│ How would you like to authenticate for this project? │
│ │
│ ● OpenAI │
│ │
│ (Use Enter to Set Auth) │
│ │
│ Terms of Services and Privacy Notice for Qwen Code │
│ │
│ https://github.com/QwenLM/Qwen3-Coder/blob/main/README.md │
│ │
╰───────────────────────────────────────────────────────────────────────────────╯
/workspace no sandbox (see /docs) qwen3-coder-plus (100% context left)
一応モデル名とかが見えたので環境変数は効いてるみたい。メッセージを入力すれば進めそう。
╭───────────────────────────────────────────────────────────────────────────────╮
│ > Type your message or @path/to/file │
╰───────────────────────────────────────────────────────────────────────────────╯
/workspno sandbox (see unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF_Qwen3-Coder-30B
ace /docs) -A3B-Instruct-UD-Q4_K_XL.gguf (100% context left)
以下のメッセージを入力
> ターミナルで動く三目並べを作って。Pythonで。
一応なんか進んでるっぽいけど、下の方にエラーが起きてるメッセージが見える・・・
╭──────────────────────────────────────────────────╮
│ > ターミナルで動く三目並べを作って。Pythonで。 │
╰──────────────────────────────────────────────────╯
⠇ Greasin' the cogs of the machine... (esc to cancel, 52s)
/work no sandbox unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF
space (see /docs) _Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.| ✖ 8 errors (ctrl+o
guf (100% context left) for details)
✕ [API Error: OpenAI API error: 500 Value is not callable: null at row 62, column
114:
{%- if json_key not in handled_keys %}
{%- set normed_json_key = json_key | replace("-", "_") |
replace(" ", "_") | replace("$", "") %}
^
{%- if param_fields[json_key] is mapping %}
at row 62, column 21:
{%- if json_key not in handled_keys %}
{%- set normed_json_key = json_key | replace("-", "_") |
replace(" ", "_") | replace("$", "") %}
^
{%- if param_fields[json_key] is mapping %}
at row 61, column 55:
{%- for json_key in param_fields %}
{%- if json_key not in handled_keys %}
^
{%- set normed_json_key = json_key | replace("-", "_") |
replace(" ", "_") | replace("$", "") %}
at row 61, column 17:
{%- for json_key in param_fields %}
{%- if json_key not in handled_keys %}
^
{%- set normed_json_key = json_key | replace("-", "_") |
replace(" ", "_") | replace("$", "") %}
at row 60, column 48:
{%- set handled_keys = ['type', 'description', 'enum', 'required'] %}
{%- for json_key in param_fields %}
^
{%- if json_key not in handled_keys %}
at row 60, column 13:
{%- set handled_keys = ['type', 'description', 'enum', 'required'] %}
{%- for json_key in param_fields %}
^
{%- if json_key not in handled_keys %}
at row 49, column 80:
{{- '\n<parameters>' }}
{%- for param_name, param_fields in tool.parameters.properties|items %}
^
{{- '\n<parameter>' }}
at row 49, column 9:
{{- '\n<parameters>' }}
{%- for param_name, param_fields in tool.parameters.properties|items %}
^
{{- '\n<parameter>' }}
at row 42, column 29:
{{- "<tools>" }}
{%- for tool in tools %}
^
{%- if tool.function is defined %}
at row 42, column 5:
{{- "<tools>" }}
{%- for tool in tools %}
^
{%- if tool.function is defined %}
at row 39, column 51:
{%- endif %}
{%- if tools is iterable and tools | length > 0 %}
^
{{- "\n\nYou have access to the following functions:\n\n" }}
at row 39, column 1:
{%- endif %}
{%- if tools is iterable and tools | length > 0 %}
^
{{- "\n\nYou have access to the following functions:\n\n" }}
at row 1, column 69:
{#- Copyright 2025-present the Unsloth team. All rights reserved. #}
^
{#- Licensed under the Apache License, Version 2.0 (the "License") #}
]
んー、チャットテンプレートが正しくない?
llama-server側のログ見てると
srv params_from_: Chat format: Hermes 2 Pro
とか出てる。
チャットテンプレートはこれかな?
ダウンロード
wget --content-disposition https://huggingface.co/unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF/resolve/main/template?download=true
このテンプレートを指定してllama-serverを起動し直し。
./build/bin/llama-server \
-hf unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_XL \
--jinja \
-ngl 99 \
--threads -1 \
--ctx-size 32684 \
--temp 0.7 \
--min-p 0.0 \
--top-p 0.80 \
--top-k 20 \
--repeat-penalty 1.05 \
--host 0.0.0.0 \
--chat-template-file template
再度Qwen-Codeを実行
> ターミナルで動く三目並べを作って。Pythonで。
いけてそう?
╭──────────────────────────────────────────────────╮
│ > ターミナルで動く三目並べを作って。Pythonで。 │
╰──────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────╮
│ ⊶ WriteFile Writing to tic_tac_toe.py │
╰────────────────────────────────────────────────────────────────────────────────────────╯
⠧ Polishing the pixels... (esc to cancel, 19s)
一応それっぽくは動いてる。
╭──────────────────────────────────────────────────╮
│ > ターミナルで動く三目並べを作って。Pythonで。 │
╰──────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────╮
│ ✔ WriteFile Writing to tic_tac_toe.py │
│ │
│ 1 import random │
│ 2 │
│ 3 class TicTacToe: │
│ 4 def __init__(self): │
│ 5 self.board = [' ' for _ in range(9)] # 3x3 board │
│ 6 self.current_player = 'X' │
│ 7 │
│ 8 def print_board(self): │
│ 9 print("\n") │
│ 10 for i in range(3): │
│ 11 print(f" {self.board[i*3]} | {self.board[i*3+1]} | │
│ {self.board[i*3+2]} ") │
│ 12 if i < 2: │
│ 13 print("-----------") │
│ 14 print("\") │
│ 15 │
│ 16 def make_move(self, position): │
│ 17 if self.board[position] == ' ': │
│ 18 self.board[position] = self.current_player │
│ 19 return True │
│ 20 return False │
│ 21 │
│ 22 def check_winner(self): │
│ 23 # Check rows │
│ 24 for i in range(0, 9, 3): │
│ 25 if self.board[i] == self.board[i+1] == self.board[i+2] != ' │
│ ': │
│ 26 return self.board[i] │
│ 27 │
│ 28 # Check columns │
│ 29 for i in range(3): │
│ 30 if self.board[i] == self.board[i+3] == self.board[i+6] != ' │
│ ': │
│ 31 return self.board[i] │
│ 32 │
│ 33 # Check diagonals │
│ 34 if self.board[0] == self.board[4] == self.board[8] != ' ': │
│ 35 return self.board[0] │
│ 36 if self.board[2] == self.board[4] == self.board[6] != ' ': │
│ 37 return self.board[2] │
│ 38 │
│ 39 return None │
│ 40 │
│ 41 def is_board_full(self): │
│ 42 return ' ' not in self.board │
│ 43 │
│ 44 def switch_player(self): │
│ 45 self.current_player = 'O' if self.current_player == 'X' else │
│ 'X' │
│ 46 │
│ 47 def play(self): │
│ 48 print("Welcome to Tic Tac Toe!") │
│ 49 print("You are X, computer is O.") │
│ 50 │
│ 51 while True: │
│ 52 self.print_board() │
│ 53 │
│ 54 # Player's turn │
│ 55 try: │
│ 56 move = int(input("Enter position (1-9): ")) - 1 │
│ 57 if move < 0 or move > 8: │
│ 58 print("Invalid position. Please enter a number │
│ between 1 and 9.") │
│ 59 continue │
│ 60 │
│ 61 if not self.make_move(move): │
│ 62 print("Position already taken. Try again.") │
│ 63 continue │
│ 64 except ValueError: │
│ 65 print("Please enter a valid number.") │
│ 66 continue │
│ 67 │
│ 68 # Check for winner or tie │
│ 69 winner = self.check_winner() │
│ 70 if winner: │
│ 71 self.print_board() │
│ 72 print(f"{winner} wins!") │
│ 73 break │
│ 74 │
│ 75 if self.is_board_full(): │
│ 76 self.print_board() │
│ 77 print("It's a tie!") │
│ 78 break │
│ 79 │
│ 80 # Computer's turn │
│ 81 self.switch_player() │
│ 82 self.computer_move() │
│ 83 │
│ 84 # Check for winner or tie after computer move │
│ 85 winner = self.check_winner() │
│ 86 if winner: │
│ 87 self.print_board() │
│ 88 print(f"{winner} wins!") │
│ 89 break │
│ 90 │
│ 91 if self.is_board_full(): │
│ 92 self.print_board() │
│ 93 print("It's a tie!") │
│ 94 break │
│ 95 │
│ 96 # Switch back to player │
│ 97 self.switch_player() │
│ 98 │
│ 99 def computer_move(self): │
│ 100 # Simple AI: Try to win, block player, or make random move │
│ 101 # Check if computer can win │
│ 102 for i in range(9): │
│ 103 if self.board[i] == ' ': │
│ 104 self.board[i] = 'O' │
│ 105 if self.check_winner() == 'O': │
│ 106 return │
│ 107 self.board[i] = ' ' │
│ 108 │
│ 109 # Block player from winning │
│ 110 for i in range(9): │
│ 111 if self.board[i] == ' ': │
│ 112 self.board[i] = 'X' │
│ 113 if self.check_winner() == 'X': │
│ 114 self.board[i] = 'O' │
│ 115 return │
│ 116 self.board[i] = ' ' │
│ 117 │
│ 118 # Make a random move │
│ 119 empty_positions = [i for i in range(9) if self.board[i] == ' '] │
│ 120 if empty_positions: │
│ 121 move = random.choice(empty_positions) │
│ 122 self.board[move] = 'O' │
│ 123 │
│ 124 if __name__ == "__main__": │
│ 125 game = TicTacToe() │
│ 126 game.play() │
╰─────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────╮
│ ✔ Shell python /workspace/tic_tac_toe.py (Running the Tic Tac Toe game) │
│ │
│ bash: line 1: python: command not found │
│ │
╰─────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────╮
│ ? Shell python3 /workspace/tic_tac_toe.py (Running the Tic Tac Toe game with pytho… ← │
│ │
│ python3 /workspace/tic_tac_toe.py │
│ │
│ Allow execution? │
│ │
│ ● Yes, allow once │
│ ○ Yes, allow always "python3 ..." │
│ ○ No (esc) │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────╯
⠏ Waiting for user confirmation...
ただ、llama-server側のログにはテンプレートのエラーっぽいものはちらほら見えるし、モデルページのDiscussions見てるとどうやらllama.cpp側にも問題があるっぽくて(上でHermes 2 Pro
とかでてたのがそれみたい)、パッチで直してる人がいる模様。
もうしばらく待ったほうがいいかも。
チャットテンプレートは正しくはこれか(HuggingFaceのファイルの管理、いまいちわかってない)
ダウンロードのやり方がわかんなくて、ローカルのtemplateファイルにコピペして、再度llama-serverを起動。
Qwen-Codeから試してみる。
> ターミナルで動く三目並べを作って。Pythonで。
llama-server側でコアダンプした・・・自分の環境の問題なのかどうなのかも、ちょっとわからない。
(snip)
<tool_call>
<function=write_file' not found at start of '# 三目並べ (Tic-Tac-Toe) ゲームの作成
では、ターミナルで動作する三目並べゲームをPythonで作ってみましょう。
このゲームは、2人のプレイヤーが交互にマスを選択して、3つを一列に並べるゲームです。以下のような機能を実装します:
1. 3x3のボードを表示
2. プレイヤーの入力を受け取る
3. 入力された位置にマークを置く
4. 勝敗を判定する
5. ボードが埋まった場合、引き分けを判定する
以下は実装コードです:
<tool_call>
'
中止 (コアダンプ)
一旦ペンディングかな。
とりあえずはOpenRouterでやったほうが良さそう。
Discussions進んでて、llama.cpp側のPR投げられてるので、これ待ちだな。
2025/08/03追加
その後をざっと追いかけてみた感じ、
- llama.cppへのPRは一部マージされたが、ツール呼び出しはまだ上手くいかない場合がある
- 原因はCoderモデルのツール呼び出しのパーサーがInstruct/Thinkingモデルとは異なる独自のものになっているため
- Instruct/Thinkingモデルだと問題なく動作する
- vLLMで動かした場合でもOpenCodeなどでクラッシュした?
- QwenもしくはUnsloth側での修正が必要
という感じ。CoderモデルをQwen-Codeで使うのは現時点では厳しそう。
とりあえずOpenRouterに変えて試してみる予定。
どうやら直ってるっぽい?
GGUFが全部更新されてるので再ダウンロード、あとllama.cppも最新版にする必要がありそう。
ってことでllama.cppを最新でビルドしなおし。
git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j 16
llama-serverで起動。なお、-hf
でモデル指定している場合、モデルが更新されていれば勝手に再ダウンロードされる様子。
./build/bin/llama-server \
-hf unsloth/Qwen3-Coder-30B-A3B-Instruct-GGUF:Q4_K_XL \
--jinja \
-ngl 99 \
--threads -1 \
--ctx-size 32684 \
--temp 0.7 \
--min-p 0.0 \
--top-p 0.80 \
--top-k 20 \
--repeat-penalty 1.05 \
--host 0.0.0.0
ローカルのMacでコンテナを立ち上げてQwen-Codeをインストール
mkdir qwen-code-work && cd $_
docker run \
--rm \
-it \
-u node \
-v "$PWD:/workspace:cached" \
-w "/workspace" \
mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm \
bash
npm install -g @qwen-code/qwen-code@latest
バージョン少し上がってた。
qwen --version
0.0.4
実行
export OPENAI_API_KEY="dummy"
export OPENAI_BASE_URL="http://<ローカルサーバのIP>:8080/v1"
export OPENAI_MODEL="unsloth_Qwen3-Coder-30B-A3B-Instruct-GGUF_Qwen3-Coder-30B-A3B-Instruct-UD-Q4_K_XL.gguf"
qwen
作ったアプリを動かすところまでやりたいので、Node.jsにした。
> ターミナルで動く三目並べを作って。Node.jsで。
██╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗
╚██╗ ██╔═══██╗██║ ██║██╔════╝████╗ ██║
╚██╗ ██║ ██║██║ █╗ ██║█████╗ ██╔██╗ ██║
██╔╝ ██║▄▄ ██║██║███╗██║██╔══╝ ██║╚██╗██║
██╔╝ ╚██████╔╝╚███╔███╔╝███████╗██║ ╚████║
╚═╝ ╚══▀▀═╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═══╝
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
3. Create QWEN.md files to customize your interactions with Qwen Code.
4. /help for more information.
╭───────────────────────────────────────────────────╮
│ > ターミナルで動く三目並べを作って。Node.jsで。 │
╰───────────────────────────────────────────────────╯
✦ 了解しました。Node.jsでターミナル上で動作する三目並べ(Tic-Tac-Toe)ゲームを作成します。
まず、プロジェクトの構造を設定し、必要なファイルを作成します。
<tool_call>
<function=run_shell_command
╭──────────────────────────────────────────────────────────────────────────╮
│ ✔ Shell mkdir -p /workspace/tictactoe (プロジェクトディレクトリを作成) │
╰──────────────────────────────────────────────────────────────────────────╯
✦ 次に、package.jsonファイルを作成してプロジェクトの依存関係を設定します。
<tool_call>
<function=write_file
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✔ WriteFile Writing to tictactoe/package.json │
│ │
│ 1 { │
│ 2 "name": "tictactoe-terminal", │
│ 3 "version": "1.0.0", │
│ 4 "description": "A terminal-based Tic-Tac-Toe game", │
│ 5 "main": "index.js", │
│ 6 "scripts": { │
│ 7 "start": "node index.js" │
│ 8 }, │
│ 9 "keywords": ["game", "tic-tac-toe", "terminal"], │
│ 10 "author": "Your Name", │
│ 11 "license": "MIT" │
│ 12 } │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ {
"tool_calls": [],
"content": "次に、ゲームのメインロジックを含むindex.jsファイルを作成します。このファイルには、ゲームの状態
管理、ボード表示、入力処理、勝敗判定などの機能が含まれます。\n<tool_call>\n<function=write_file"
}
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✔ WriteFile Writing to tictactoe/index.js │
│ │
│ ... first 83 lines hidden ... │
│ 84 if (this.board[0][2] !== ' ' && │
│ 85 this.board[0][2] === this.board[1][1] && │
│ 86 this.board[1][1] === this.board[2][0]) { │
│ 87 this.gameOver = true; │
│ 88 this.winner = this.board[0][2]; │
│ 89 return; │
│ 90 } │
│ 91 │
│ 92 // 引き分けのチェック │
│ 93 let isDraw = true; │
│ 94 for (let i = 0; i < 3; i++) { │
│ 95 for (let j = 0; j < 3; j++) { │
│ 96 if (this.board[i][j] === ' ') { │
│ 97 isDraw = false; │
│ 98 break; │
│ 99 } │
│ 100 } │
│ 101 if (!isDraw) break; │
│ 102 } │
│ 103 │
│ 104 if (isDraw) { │
│ 105 this.gameOver = true; │
│ 106 this.winner = 'Draw'; │
│ 107 } │
│ 108 } │
│ 109 │
│ 110 // ゲームをリセットするメソッド │
│ 111 reset() { │
│ 112 this.board = [ │
│ 113 [' ', ' ', ' '], │
│ 114 [' ', ' ', ' '], │
│ 115 [' ', ' ', ' '] │
│ 116 ]; │
│ 117 this.currentPlayer = 'X'; │
│ 118 this.gameOver = false; │
│ 119 this.winner = null; │
│ 120 } │
│ 121 } │
│ 122 │
│ 123 // ゲームのメインロジック │
│ 124 function main() { │
│ 125 const game = new TicTacToe(); │
│ 126 │
│ 127 // readlineインターフェースを作成 │
│ 128 const rl = readline.createInterface({ │
│ 129 input: process.stdin, │
│ 130 output: process.stdout │
│ 131 }); │
│ 132 │
│ 133 console.log('Welcome to Terminal Tic-Tac-Toe!'); │
│ 134 console.log('Enter row and column (0-2) separated by space. Example: "1 2"'); │
│ 135 │
│ 136 // ゲームループ │
│ 137 function gameLoop() { │
│ 138 if (game.gameOver) { │
│ 139 game.displayBoard(); // 最後のボードを表示 │
│ 140 │
│ 141 if (game.winner === 'Draw') { │
│ 142 console.log('It\'s a draw!'); │
│ 143 } else { │
│ 144 console.log(`Player ${game.winner} wins!`); │
│ 145 } │
│ 146 │
│ 147 // ゲームをリセットするか確認 │
│ 148 rl.question('\nDo you want to play again? (y/n): ', (answer) => { │
│ 149 if (answer.toLowerCase() === 'y') { │
│ 150 game.reset(); │
│ 151 console.log('\nNew game started!'); │
│ 152 gameLoop(); │
│ 153 } else { │
│ 154 console.log('Thanks for playing!'); │
│ 155 rl.close(); │
│ 156 } │
│ 157 }); │
│ 158 return; │
│ 159 } │
│ 160 │
│ 161 // 現在のボードを表示 │
│ 162 game.displayBoard(); │
│ 163 │
│ 164 // 現在のプレイヤーを表示 │
│ 165 console.log(`Player ${game.currentPlayer}\'s turn`); │
│ 166 │
│ 167 // 入力を受け取る │
│ 168 rl.question('Enter row and column (0-2): ', (input) => { │
│ 169 const [row, col] = input.split(' ').map(Number); │
│ 170 │
│ 171 if (isNaN(row) || isNaN(col) || row < 0 || row > 2 || col < 0 || col > 2) { │
│ 172 console.log('Invalid input! Please enter numbers between 0 and 2.'); │
│ 173 gameLoop(); │
│ 174 return; │
│ 175 } │
│ 176 │
│ 177 if (!game.makeMove(row, col)) { │
│ 178 console.log('Invalid move! That position is already taken or out of bounds.'); │
│ 179 gameLoop(); │
│ 180 return; │
│ 181 } │
│ 182 │
│ 183 // 次のターンへ │
│ 184 gameLoop(); │
│ 185 }); │
│ 186 } │
│ 187 │
│ 188 // ゲームを開始 │
│ 189 gameLoop(); │
│ 190 } │
│ 191 │
│ 192 // プログラムのエントリーポイント │
│ 193 main(); │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ {
"tool_calls": [],
"content": "次に、Node.jsの依存関係をインストールします。このゲームは標準ライブラリのみを使用するため、特
に追加のパッケージは必要ありません。\n<tool_call>\n<function=run_shell_command"
}
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✔ Shell cd /workspace/tictactoe && npm install (Node.jsの依存関係をインストール) │
│ │
│ │
│ up to date, audited 1 package in 144ms │
│ │
│ found 0 vulnerabilities │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ {
"tool_calls": [],
"content":
"それでは、ゲームを起動して実際にプレイしてみましょう。\n<tool_call>\n<function=run_shell_command"
}
╭────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✔ Shell cd /workspace/tictactoe && npm start (ゲームを起動) │
│ │
│ │
│ > tictactoe-terminal@1.0.0 start │
│ > node index.js │
│ │
│ Welcome to Terminal Tic-Tac-Toe! │
│ Enter row and column (0-2) separated by space. Example: "1 2" │
│ │
│ │
│ | | │
│ | | │
│ ___|___|___ │
│ | | │
│ | | │
│ ___|___|___ │
│ | | │
│ | | │
│ | | │
│ │
│ │
│ Player X's turn │
│ Enter row and column (0-2): │
╰────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ 三目並べゲームが正常に起動しました!
ゲームの説明:
- ターミナル上で動作する三目並べゲームです
- プレイヤーはXとOのどちらかを選びます
- ボードは3x3のグリッドで、各マスは0から2までの行番号と列番号で指定します
- 例: "1 2" は2行目の3列目を意味します
ゲームの流れ:
1. ゲーム開始時にXのプレイヤーが先手になります
2. 行番号と列番号をスペースで区切って入力してください(例: "1 2")
3. 入力された位置にマークが置かれます
4. 3つのマークが一直線になったらそのプレイヤーの勝ちです
5. すべてのマスが埋まった場合、引き分けになります
ゲームを終了するには、ゲーム終了時に「y」または「n」を入力してください。
それでは、ゲームを楽しんでください!
╭──────────────────╮
│ > 起動方法は? │
╰──────────────────╯
✦ 三目並べゲームを起動するには、以下の手順を行ってください:
1. ターミナルで以下のコマンドを実行します:
1 cd /workspace/tictactoe && npm start
2. または、package.jsonに記載されているスクリプトを使って起動することもできます:
1 cd /workspace/tictactoe && node index.js
これでゲームが開始されます。ゲームの説明は前回の回答をご参照ください。
╭───────────╮
│ > /quit │
╰───────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Agent powering down. Goodbye! │
│ │
│ Interaction Summary │
│ Tool Calls: 5 ( ✔ 5 ✖ 0 ) │
│ Success Rate: 100.0% │
│ User Agreement: 100.0% (5 reviewed) │
│ │
│ Performance │
│ Wall Time: 2m 50s │
│ Agent Active: 2m 14s │
│ » API Time: 1m 33s (69.8%) │
│ » Tool Time: 40.7s (30.2%) │
│ │
│ │
│ Model Usage Reqs Input Tokens Output Tokens │
│ ─────────────────────────────────────────────────────────────── │
│ unsloth_Qwen3-Coder-30B-A 17 132,516 6,948 │
│ 3B-Instruct-GGUF_Qwen3-Co │
│ der-30B-A3B-Instruct-UD-Q │
│ 4_K_XL.gguf │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
実行してみた。
cd tictactoe/
npm start
> tictactoe-terminal@1.0.0 start
> node index.js
Welcome to Terminal Tic-Tac-Toe!
Enter row and column (0-2) separated by space. Example: "1 2"
| |
| |
___|___|___
| |
| |
___|___|___
| |
| |
| |
Player X's turn
Enter row and column (0-2): 2 2
| |
| |
___|___|___
| |
| |
___|___|___
| |
| | X
| |
Player O's turn
Enter row and column (0-2): 1 1
| |
| |
___|___|___
| |
| O |
___|___|___
| |
| | X
| |
Player X's turn
Enter row and column (0-2): 2 0
| |
| |
___|___|___
| |
| O |
___|___|___
| |
X | | X
| |
Player O's turn
Enter row and column (0-2): 2 1
| |
| |
___|___|___
| |
| O |
___|___|___
| |
X | O | X
| |
Player X's turn
Enter row and column (0-2): 1 0
| |
| |
___|___|___
| |
X | O |
___|___|___
| |
X | O | X
| |
Player O's turn
Enter row and column (0-2): 0 1
| |
| O |
___|___|___
| |
X | O |
___|___|___
| |
X | O | X
| |
Player O wins!
Do you want to play again? (y/n): n
Thanks for playing!
使い方全般はこちらが参考になる