4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【ローカルLLMコーディング性能比較】テトリスを生成できるか試してみた

4
Last updated at Posted at 2025-06-22

1. はじめに

ここ数か月でgemma3, qwen3, mistral-samll3.2, ph4-readsoningなど、コンシューマ用のGPUで十分動作可能で、かつ、それなりに性能の高いLLMが複数発表されました。このため、個人的にコーディングで普段使いするためのLLMを決めるため、各LLMにテトリスを生成するための同一のプロンプトを入力し、結果を比較してみました。

2. 比較対象LLM

今回、試行したローカルLLMのモデルは以下の通りです。

ローカルLLM(すべて4bit量子化したバージョン)

モデル (Model) パラメータ数 (Number of Parameters)
gemma3 12B
deepcoder 14B
Deepseek-r1 14B
qwen3 14B
phi4 14B
phi4-reasoning 14B
magistral 24b
Mistral-small3.2 24b
gemma3 27B
qwen3 30B-A3B
qwen3 32B
Deepseek-r1 32B

また、比較のために以下の商用LLMにについても試行しています。

■商用LLM

  • o4-mini
  • o3-mini
  • Gemini 2.5 Flash
  • Deepseek-r1

3.測定環境

ハードウェア

ハードウェアは以下のものを使用しています。GPUは2枚刺しです。

項目 (Item) 内容 (Content)
OS Kubuntu 24.02.02
CPU Ryzen 5 7600
メモリ (Memory) DDR5-5600 64GB
マザーボード (Motherboard) MSI MAG B650 TOMAHAWK WIFI
GPU1 ZOTAC GAMING GeForce RTX 5060 Ti 16GB Twin Edge
GPU2 MSI GeForce RTX 3060 VENTUS 2X 12G OC
GPUドライババージョン (GPU Driver Version) 570.169

ソフトウェア

使用したソフトウェア環境は以下の通り。特に設定を変更せず、デフォルトのまま使用しています。

ソフトウェア (Software) バージョン (Version)
ollama v0.9.2
Open-webui v0.6.15

4. プロンプト

テトリスを生成させるためのプロンプトは以下の通りです。
プロンプトはDeepSeek-R1に生成してもらいました。

下の要件でブラウザ上で動作するテトリスゲームの完全なソースコードを生成してください。HTML/CSS/JavaScriptを単一ファイルに記述し、コピペで実行可能な形式(<!DOCTYPE html>から</html>まで全てを含む)で出力してください。JavaScriptは<script>タグ内に、CSSは<style>タグ内に記述してください。

### 必須要件
1.  **キー操作**
    * 左矢印キー: ブロックを左に1マス移動
    * 右矢印キー: ブロックを右に1マス移動
    * 下矢印キー: ブロックを1マス加速落下(ソフトドロップ)
    * エンターキー: ブロックを時計回りに90度回転。テトリス公式ルールに準拠したスーパーローテーションシステム(SRS)を実装し、壁や他のブロックとの衝突を回避しながら回転を試みる。

2.  **ゲーム機能**
    * 7種類のテトロミノ(I, O, T, S, Z, J, L)を、テトリス公式ルールに準拠したランダム生成アルゴリズム(7種類すべてが出現するまで重複なし、その後シャッフル)で生成。
    * ブロックが最下部または他のブロックに到達すると固定され、新しいブロックが出現。
    * 横一列が揃ったらライン消去。ライン消去時、効果音(コードコメントで「効果音再生」と記述するのみで良い)を鳴らすことを想定し、その後、消したライン数に応じてスコアを加算。
        * 1ライン: 100点
        * 2ライン: 300点
        * 3ライン: 500点
        * 4ライン(テトリス): 800点
    * 新しいブロックが出現する場所に既存のブロックがある場合、ゲームオーバーと判定。
    * スコア表示(消したライン数に応じて加算)
    * ゲーム開始時、ブロックの初期落下速度は500msとし、ラインを5つ消すごとに落下速度を10%ずつ加速する。
    * 画面を表示する
    
3.  **表示要素**
    * メインゲームボード(10x20グリッド)。各グリッドは正方形で、黒い背景に薄いグレーのグリッド線を表示。
    * 現在のスコア表示領域をゲームボードの右側に配置。フォントはゴシック系、サイズは24px、色は白。
    * 次のブロックをプレビュー表示する領域をスコア表示の下に配置。プレビューも各テトロミノの色で表示。
    * ゲームオーバー時は、ゲームボード中央に「GAME OVER」という明確なメッセージを赤色、大きなフォント(48px)で表示。
    * 各テトロミノの色は以下の通りとする: I:シアン, O:イエロー, T:マゼンタ, S:グリーン, Z:レッド, J:ブルー, L:オレンジ。

4.  **技術要件**
    * 純粋なJavaScriptのみ使用(ライブラリ、フレームワーク、ビルドツール、外部ファイルは不可)。
    * CSSは`<style>`タグ内に、JavaScriptは`<script>`タグ内に記述し、HTMLファイル内にすべてを完結させる。
    * レスポンシブデザインに対応し、画面サイズが変化してもゲームボードのアスペクト比が維持されるように調整。キャンバスの最大幅は400pxとする。
    * ES2015(ES6)以降のJavaScript構文(let, const, アロー関数など)を使用しても良い。
    * コードは可読性を重視し、適切な変数名、関数名、コメントを使用すること。特に複雑なロジックにはコメントを付加すること。
    * プログラムは予期せぬキー入力に対して堅牢であり、パフォーマンスが良好でスムーズなアニメーションが維持されること。

5. 試行結果サマリ

ローカルLLM

ローカルLLMでの試行結果のサマリを以下に示します。パラメタ数が14Bまでのモデルは、どれも「全く動作せず」。
パラメタ数が24Bを超えてくると、「ちょっとは動く」レベルのもの、「普通に遊べる」レベルのものがチラホラ現れてくるといった結果でした。やはりモデルのパラメタ数は性能に直結するというのを再確認できました。
Mistral-small3.2は比較的少なめならパラメタ(24B)なのに、「普通に遊べる」ものが出力されて、かなり優秀な印象を受けました。

モデル パラメタ数 評価 理由
gemma3 12B × まったく動作せず。真っ黒な画面のまま。描画ロジック欠落。
deepcoder 14B × まったく動作せず。真っ黒な画面のまま。変数定義欠落。
Deepseek-r1 14B × まったく動作せず。真っ黒な画面のまま。変数定義欠落。
qwen3 14B × まったく動作せず。真っ黒な画面のまま。引数の型誤り。
phi4 14B ××× まったく動作せず。真っ黒な画面のまま。HTML/Javascriptの構造が壊れてる。
phi4-reasoning 14B × まったく動作せず。真っ黒な画面のまま。原因は複合要因でよくわからない。
magistral 24b ×× まったく動作せず。真っ黒な画面のまま。コードが途中で途切れていた。
Mistral-small3.2 24b 普通に遊べるものが生成された。
gemma3 27B ゲームが始まると即ゲームオーバー。
qwen3 30B-A3B × まったく動作せず。原因は複合要因でよくわからない。
qwen3 32B ブロックを3個置いた時点でフリーズ。
Deepseek-r1 32B 普通に遊べるものが生成された。ただしブロックの回転に若干のバグあり。

商用LLM

商用の試行結果のサマリを以下に示します。今回試したモデルはどれも「普通に遊べる」レベルのテトリスのコードが生成できました。やっぱり商用LLMはさすがといったところです。

モデル 評価 理由
o4-mini 普通に遊べるものが生成された。Mistral-small3.2(24B)と同程度。
o3-mini 普通に遊べるものが生成された。Mistral-small3.2(24B)と同程度。
Gemini 2.5 Flash ◎◎ 普通に遊べるものが生成された。見た目も美しい。
Deepseek-r1 ◎◎◎ 普通に遊べるものが生成された。見た目がとても美しい。

6. 試行結果詳細

gemma3(12B)

真っ黒な画面のまま全く動作せず。
image.png

主な原因はテトロミノ(ブロック)の描画ロジックが抜け落ちてコメントだけになっていたため。

        // テトロミノの描画
        function drawBlock() {
            const x = Math.floor(boardWidth / 2) - Math.floor(currentBlock.data[0].length / 2);
            const y = 0;

            for (let row = 0; row < currentBlock.data.length; row++) {
                for (let col = 0; col < currentBlock.data[row].length; col++) {
                    if (currentBlock.data[row][col]) {
                        if (x + col >= 0 && x + col < boardWidth && y + row >= 0 && y + row < boardHeight && board[y + row][x + col] === 0) {
                            //  描画処理をここに記述(Canvas APIを利用するなど)
                        }
                    }
                }
            }
        }

deepcoder(14B)

真っ黒な画面のまま全く動作せず。

image.png

原因は複数あるが、一番大きいミスはブロックの位置を保持する変数の定義が漏れている点。

    ctx.fillRect(
        (currentPosX + x) * BLOCK_SIZE,  // currentPosXを定義せずに使用
        (currentPosY + y) * BLOCK_SIZE,  // currentPosYを定義せずに使用
        BLOCK_SIZE - 1,
        BLOCK_SIZE - 1
    );

DeepSeek-R1(14B)

真っ黒な画面のまま全く動作せず。

image.png

動作しない原因はdeepcoder同様に変数の定義漏れ。

        function drawBlock(block, position) {
            block.forEach((row, y) => {
                row.forEach((value, x) => {
                    if (value) {
                        ctx.fillStyle = COLORS[blockType]; // blockTypeが未定義
                        ctx.fillRect(
                            (position.x + x) * BLOCK_SIZE,
                            (position.y + y) * BLOCK_SIZE,
                            BLOCK_SIZE - 1,
                            BLOCK_SIZE - 1
                        );
                    }
                });
            });
        }

Qwen3(14B)

真っ黒な画面のまま全く動作せず。

image.png

こちらも原因は複数があるが、一番大きいのは引数をオブジェクトとして渡しているのに文字列として扱っている点。

    function drawPiece(piece, x, y) { // pieceはオブジェクト
      const shape = PIECES[piece]; // piecce.shapeが正解。
      for (let i = 0; i < shape.length; i++) {
        for (let j = 0; j < shape[i].length; j++) {
          if (shape[i][j]) {
            context.fillStyle = COLORS[piece];
            context.fillRect((x + j) * BLOCK_SIZE, (y + i) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
          }
        }
      }
    }

phi-4(14B)

真っ黒な画面のまま全く動作せず。

image.png

phi-4は、これまでの中でもっともひどい。
HTMLの構造が完全に壊れていた。これはひどい。

()
    const width = boardWidth * blockSize;
    const height = (boardHeight + 3) * blockSize;

    canvas.width = width;
    canvas.height = height;

    drawNextPiece();

    startTimestamp = performance.now();
    startGame();

// 一続きのコードとして出力されたが、ここで完全に壊れている。

**HTML:**

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tetris</title>
    <style>
(略)

phi-4-reasoning(14B)

真っ黒な画面のまま全く動作せず。

image.png

動作しない理由はいっぱいあってよくわかりませんでした。
が、ブロックの生成処理や描画処理の呼び出しが漏れているようでした。

なお、個人的な意見も入りますが、phi-4-reasoningはthinkingの時間が長すぎて普段使いには厳しいと感じました。

magistral(24B)

真っ黒な画面のまま全く動作せず。

image.png

原因はコードの出力が途切れたこと。コンテキスト長の最大に達したわけではなく純粋にコードの出力が突然途切れていた。これはひどい。

    gameLoop();
    
    // 画面リサイズ時の再描画
    window.addEventListener('resize', () => {
        drawBoard();
        drawNextBlockPreview();
    });
    }
// ここで途切れている
// 少なくとも以降に</script>などのHTML要素が必要だが出力されていない。

Mistral-small3.2(24B)

普通に遊べるものが出力されていた。凄い。ブロックの回転も、ライン消去も普通にできた。

image.png

ゲームオーバーも正しく判定されていた。

image.png

gemma3(32B)

ゲームが始まってブロックが表示された瞬間にゲームオーバー。理不尽。

image.png

原因はよくわからず(複数要因が絡んでよくわからず)

qwen3(30B-A3B)

真っ黒な画面のまま動作しない。

image.png

主な原因は未定義の変数参照。

        // ピース回転
        function rotatePiece() {
            const nextIndex = (currentPiece.shapeIndex + 1) %  currentPiece.shape.length;  // shapeIndexという変数が未定義
            const newShape = currentPiece.shape[nextIndex];
            const temp = currentPiece.shape;
            currentPiece.shape = newShape;
            if (isCollision(currentPiece)) {
                currentPiece.shape = temp;
            } else {
                currentPiece.shapeIndex = nextIndex;
            }
        }

qwen3(32B)

途中まで動作するがブロック3つ置くとフリーズする。
また、底面の位置がおかしい(空中にある)

image.png

原因はよくわからなったのですが、AIに聞くとゲームオーバー判定ロジックの誤りと、生成したブロックのキュー管理がおかしいとのことでした。

Deepseek-R1(32B)

普通に遊べるものが生成された。

image.png

ゲームオーバー判定も問題なし。

image.png

ただし、ブロックを回転すると、ブロックが左に1マスずつずれていくというバグあり。

o4-mini

普通に遊べるものが生成された。Mistral-small3.2(24B)と同程度の印象。

image.png

o3-mini

普通に遊べるものが生成された。Mistral-small3.2(24B)と同程度の印象。

image.png

Gemini 2.5 Flash

完璧。ただし、見た目のリッチ感は後述のDeepseek-R1に若干劣る。

image.png

Deepseek-R1

完璧すぎて言うことなし。

image.png

7. まとめ

今回はローカルLLMコーディング性能の比較をしてみました。ローカルLLMでも24Bから32Bのモデルになれば、今回のようにそれなりに複雑なゲームのコードも生成できる性能があるということが確認できました。
これまでは、qwwen3をメインに使っていましたが、今回の検証でMistral-small3.2(24B)が非常に優秀なことが分かったため、しばらくはMistral-small3.2(24B)を使って、いろいろ遊んでみようと思います。
あと、商用LLMはさすがですね(笑)

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?