🔥

新しいR1で蒸留されたDeepSeek-R1-0528-Qwen3-8Bを試す

に公開

DeepSeek-R1-0528

2025年 5月29日、DeepSeek-R1のマイナーアップデート版がリリースされました。

https://huggingface.co/deepseek-ai/DeepSeek-R1-0528

蒸留モデルDeepSeek-R1-0528-Qwen3-8B

DeepSeek-R1-0528は685Bと超巨大なので個人のPCではまともに動作できませんが、蒸留されたモデルであるDeepSeek-R1-0528-Qwen3-8Bは、8Bサイズということもあり、量子化すれば十分個人のPCで動作が可能です。

ベースとなったQwen3は、2025年4月にAlibabaから発表された最新のLLMシリーズで、小さいサイズでも高性能で話題になりました。

これがDeepSeek-R1での蒸留により、より高性能になっているとかなり期待が持てますね。

一応、サイト内には元のQwen3-8Bに対して、ベンチマークAIME 2024で10%性能アップとなり、Qwen3-235B-A22Bと同等性能になったと記載されています。

日本語で軽く触ってみる

今回はDeepSeek-R1-0528-Qwen3-8Bの4bit量子化モデルをML Studioで動かして、日本語での使用感を試して見ました。

比較としてベースとなったQwen3-8Bと、それより大きいQwen3-14Bを使います。
Qwen3-235B-A22Bは量子化してもかなり大きいので、今回は検証できませんでした。

VRAM96GB使えるRyzen AI Max+ 395搭載のEVO-X2が届いたら検証してみようと思います。
https://amzn.to/43DsDJg

今回はM2 Max でRAM64GBあるMacBookProにてLMStudioを使って動かしてみます。

入力は以下の2つを試しました。

  • あいさつ
    こんにちは
    
  • javascriptプログラミング
    HTML, Javascript, CSSを用いてテトリスを作成してください。
    実行しやすいように、JavascriptとCSSはHTML内に記載してください。
    

DeepSeek-R1-0528-Qwen3-8B

あいさつ

role text
User こんにちは
Assistant(Thinking) あ、ユーザーが日本語で挨拶してきた。最初のメッセージだからこそ丁寧かつ親しみやすい対応が重要だな。ユーザーは日本語話者だろうと、少なくとも現地言語を使いこなせる可能性が高いが、文法はシンプルで正しい。このケースでは「こんにちは」にかえすのが基本だが、もっと自然な返し方を考えないと。日本語の挨拶は再三リピートしすぎると変に感じるから、バランスが難しい。ただ今回は初めてのやり取りなので安心させつつフォーマットを示すのが良さそう。「ご挨拶ありがとうございますが」は少しフォーマット感が強いけど、これが最初のやり取りだから必要。ユーザーをオープンに迎えたい気持ちとプロの対応の両方を込めて。もしユーザーが英語でも返信してきたら、次回からはもっとカジュアルに切り替えられるだろうな。
Assistant ご挨拶ありがとうございますが、何かお手伝いできることがあればお知らせください。

あ、ユーザーが日本語で挨拶してきた。

なんか良いですねw
生成し直してみても

お、朝から日本語で挨拶が来た。ユーザーは多分日本の会社や学校関係かも?

という調子なので、フランクな感じな味付けなんですね。

javascriptプログラミング

出力が長いこともあり具体的な内容は省略します。

テトリスを作成するには、以下のステップを考えます。

  1. ゲームボードの作成: 通常は10列×20行ですが、テキストベースで表示するのではなく、HTML/CSSを使ってブロックを配置します。
  2. テトロミノの種類と形状: 7つのテトロミノ(I, J, L, O, S, T, Z)とその回転形を定義します。
  3. ゲームの制御: ユーザーがブロックを操作して落下させ、満タンした行を消去し、新しいブロックが床に当たるまで続ける仕組みです。
  4. スコアリングとゲームオーバーの判定。

thinkタグ内は日本語で生成は14秒ほどと簡潔な内容です。
生成したものは指示通りHTMLファイルだけで動くように作られていましたが、まったく正しく動作しなかったです。

thinkタグ内は先ほどの挨拶とは違いしっかりと

生成されたコード
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>HTML5 テトリス</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-color: #f0f0f0;
            margin: 0;
        }
        
        .game-container {
            display: flex;
            gap: 20px;
            padding: 20px;
            background-color: white;
            border-radius: 10px;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
        }
        
        .game-info {
            width: 200px;
        }
        
        .tetris-board {
            display: grid;
            width: 305px;
            height: 610px;
            grid-template-columns: repeat(10, 1fr);
            grid-template-rows: repeat(20, 1fr);
            gap: 1px;
        }
        
        .cell {
            width: 30px;
            height: 30px;
            background-color: #444;
            box-sizing: border-box;
        }
        
        .filled {
            background-color: #457e3a;
        }
        
        .i-block { background-color: #00ffff; } /* 青 */
        .j-block { background-color: #0000ff; } /* 赤 */
        .l-block { background-color: #ff7f00; } /* 赤 */
        .o-block { background-color: #ffff00; } /* 黄 */
        .s-block { background-color: #00ff00; } /* 緑 */
        .t-block { background-color: #800080; } /* 紫 */
        .z-block { background-color: #ff0000; } /* 紫 */
        
        .next-block-container {
            margin-top: 20px;
            text-align: center;
        }
        
        #game-over {
            display: none;
            position: absolute;
            background-color: rgba(0, 0, 0, 0.8);
            color: white;
            text-align: center;
            font-size: 24px;
            padding: 20px;
        }
        
        .controls {
            margin-top: 30px;
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 10px;
        }
        
        .control-btn {
            padding: 12px;
            background-color: #457e3a;
            color: white;
            border: none;
            cursor: pointer;
            font-size: 16px;
        }
        
        .control-btn:hover {
            background-color: #345a29;
        }
        
        .score-display, h2 {
            color: #333;
            margin-bottom: 20px;
        }
        
        h1 {
            text-align: center;
            color: #457e3a;
            margin-bottom: 0;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <div class="game-info">
            <h1>TETRIS</h1>
            <p id="score-display" class="score-display">Score: 0</p>
            <p>Rows: <span id="rows-count">0</span></p>
            <div class="next-block-container">
                <h3>Next Piece</h3>
                <div id="next-cell" class="tetris-board">Next piece will appear here...</div>
            </div>
        </div>
        
        <div id="tetris-board" class="tetris-board"></div>
    </div>

    <script>
        // テトロミノの定義
        const SHAPES = {
            'I': [
                [0, 0, 0, 0],
                [1, 1, 1, 1],
                [0, 0, 0, 0],
                [0, 0, 0, 0]
            ],
            'J': [
                [1, 0, 0],
                [1, 1, 1],
                [0, 0, 0]
            ],
            'L': [
                [0, 0, 1],
                [1, 1, 1],
                [0, 0, 0]
            ],
            'O': [
                [1, 1],
                [1, 1]
            ],
            'S': [
                [0, 1, 1],
                [1, 1, 0],
                [0, 0, 0]
            ],
            'T': [
                [0, 1, 0],
                [1, 1, 1],
                [0, 0, 0]
            ],
            'Z': [
                [1, 1, 0],
                [0, 1, 1],
                [0, 0, 0]
            ]
        };

        // ゲームの状態
        const gameState = {
            board: [],      // 10x20 のボード
            currentPiece: null, // 現在のブロック
            nextPiece: null,  // 次のブロック
            score: 0,        // スコア
            level: 1,       // レベル(スピード)
            rowsCleared: 0,     // 消えた行数
            isGameOver: false,
            dropInterval: null // 降下間隔のID
        };

        // DOM 元素
        let boardElement = document.getElementById('tetris-board');
        let scoreDisplay = document.getElementById('score-display');
        let rowsCountElement = document.getElementById('rows-count');

        // ゲームの初期化
        function initGame() {
            // ボードのクリーンアップと再作成
            boardElement.innerHTML = '';
            
            // 10x20 のボードを作成(DOM)
            for (let row = 0; row < 20; row++) {
                gameState.board[row] = new Array(10).fill(0);
            }
            
            // 初期状態の設定
            gameState.score = 0;
            gameState.level = 1;
            gameState.rowsCleared = 0;
            gameState.isGameOver = false;
            
            // 次のブロックを生成
            generateNewPiece();
            
            // ゲームボードにブロックを配置する(最初は空)
            drawBoard();
            
            // キーリスナーの設定
            document.addEventListener('keydown', handleKeyPress);
            
            // ゲームループを開始(ブロックが下り続ける)
            if (gameState.dropInterval) {
                clearInterval(gameState.dropInterval);
            }
            gameState.dropInterval = setInterval(dropCheck, 1000 / gameState.level);
        }

        // ブロックを生成
        function generateNewPiece() {
            if (gameState.nextPiece) {
                gameState.currentPiece = gameState.nextPiece;
            }
            
            const shapes = Object.keys(SHAPES);
            let randomIndex, shapeKey;
            
            // ランダムに形状を選択
            do {
                randomIndex = Math.floor(Math.random() * shapes.length);
                shapeKey = shapes[randomIndex];
            } while (
                (shapeKey === 'O' && !gameState.currentPiece) // Oは常に生成されるのでスキップ
            );
            
            gameState.nextPiece = {
                shape: SHAPES[shapeKey],
                type: shapeKey,
                row: 0, // 最初は上段に配置
                col: Math.floor(Math.random() * 10) // ランダムな位置
            };
            
            if (!gameState.currentPiece) {
                gameState.currentPiece = gameState.nextPiece;
            }
            
            // 新しいブロックが場所を占有していなければゲームオーバー判定
            if (gameState.currentPiece.row < 0 || 
                gameState.board[gameState.currentPiece.row][gameState.currentPiece.col] !== 0) {
                gameOver();
            }
        }

        // ボードを描画
        function drawBoard() {
            scoreDisplay.textContent = `Score: ${gameState.score}`;
            
            // 空のセルをクリア
            Array.from(boardElement.children).forEach((cell) => {
                cell.className = 'cell';
                if (gameState.isGameOver) return;
            });
            
            // ボード上の各ブロックを描画
            for (let row = 0; row < 20; row++) {
                for (let col = 0; col < 10; col++) {
                    if (gameState.board[row][col] !== 0) {
                        const cellIndex = row * 10 + col;
                        if (boardElement.children[cellIndex]) {
                            boardElement.children[cellIndex].className = 
                                `cell filled ${gameState.board[row][col]}-block`;
                        }
                    }
                }
            }
            
            // 現在のブロックを描画
            if (!gameState.isGameOver) {
                drawCurrentPiece();
            }
        }

        // 現在のブロックを描画
        function drawCurrentPiece() {
            if (!gameState.currentPiece) return;
            
            const { shape, row, col } = gameState.currentPiece;
            for (let y = 0; y < shape.length; y++) {
                const rowCells = shape[y];
                for (let x = 0; x < rowCells.length; x++) {
                    if (rowCells[x] === 1 && col + x < gameState.board[y].length) {
                        const cellIndex = (row + y) * 10 + col + x;
                        
                        // このブロックが既に配置されている可能性があることを確認
                        if (row + y < 20 && col + x >= 0) {
                            // 確保:もしブロックが既に他のブロックと重なっている場合は何もしない
                            if (gameState.board[row + y][col + x] === 0) {
                                boardElement.children[cellIndex].className = 
                                    `cell filled ${gameState.currentPiece.type}-block`;
                            }
                        }
                    }
                }
            }
        }

        // キーリスナー
        function handleKeyPress(e) {
            if (gameState.isGameOver || !gameState.currentPiece) return;
            
            switch(e.key) {
                case 'ArrowLeft':
                    move(-1);
                    break;
                case 'ArrowRight':
                    move(1);
                    break;
                case 'ArrowDown':
                    drop();
                    break;
                case 'ArrowUp':
                    rotate();
                    break;
            }
        }

        // ブロックを移動
        function move(direction) {
            if (!gameState.currentPiece) return;
            
            gameState.currentPiece.col += direction;
            const col = gameState.currentPiece.col;
            const rowOffset = 0; // シンプルな移動のためオフセットは0
                
            if (checkCollision(rowOffset, col)) {
                gameState.currentPiece.col -= direction;
            }
            
            drawBoard();
        }

        // ブロックを回転
        function rotate() {
            if (!gameState.currentPiece) return;
            
            // TODO: ブロックの回転を実装
        }

        // ブロックを下げる
        function drop() {
            if (!gameState.currentPiece) return;
            
            // 一回分だけ下げる
            gameState.dropInterval = setInterval(drop, 50);
        }

        // 衝突をチェック
        function checkCollision(offsetRow, offsetCol) {
            if (!gameState.currentPiece) return true;
            
            const { shape, row, col } = gameState.currentPiece;
            
            for (let y = 0; y < shape.length; y++) {
                const rowCells = shape[y];
                for (let x = 0; x < rowCells.length; x++) {
                    if (!rowCells[x]) continue;
                    
                    const newRow = row + y + offsetRow;
                    const newCol = col + x + offsetCol;
                    
                    // もし新しい位置がボードの範囲外なら衝突
                    if (newRow >= 20 || newCol < 0 || newCol >= 10) {
                        return true;
                    }
                    
                    // もし新しい位置が既にブロックなら衝突
                    if (newRow >= 0 && gameState.board[newRow][newCol]) {
                        return true;
                    }
                }
            }
            
            return false;
        }

        // ドロップチェック(定期的に行われる)
        function dropCheck() {
            if (!gameState.currentPiece) return;
            
            // もし可能なら下に移動
            if (!checkCollision(1, 0)) {
                gameState.currentPiece.row += 1;
            } else {
                // ブロックが下に動くことができない場合は固定
                lockPiece();
                generateNewPiece();
            }
            
            drawBoard();
        }

        // ブロックを固定
        function lockPiece() {
            const { shape, row, col } = gameState.currentPiece || {};
            
            if (!shape || !row !== undefined) return;
            
            for (let y = 0; y < shape.length; y++) {
                const rowCells = shape[y];
                for (let x = 0; x < rowCells.length; x++) {
                    if (!rowCells[x]) continue;
                    
                    const boardRow = row + y;
                    const boardCol = col + x;
                    
                    if (boardRow < 0 || boardCol < 0) continue;
                    
                    // ボードにブロックを追加
                    gameState.board[boardRow][boardCol] = gameState.currentPiece.type;
                }
            }
        }

        // 行のクリーンアップ
        function clearLines() {
            let linesCleared = 0;
            
            for (let row = 19; row >= 0 && linesCleared < 4; row--) {
                if (gameState.board[row].every(cell => cell !== 0)) {
                    // この行をクリーンアップ
                    gameState.board.splice(row, 1);
                    gameState.board.unshift(new Array(10).fill(0));
                    linesCleared++;
                    
                    // スコアを計算(連鎖クリーンアップも考慮)
                    const points = Math.floor((linesCleared ** 2) * gameState.level);
                    gameState.score += points;
                    
                    // 行数カウント
                    gameState.rowsCleared += 1;
                }
            }
            
            // レベルアップ
            if (linesCleared > 0) {
                const newLevel = Math.floor((gameState.rowsCleared + linesCleared) / 10);
                if (newLevel > gameState.level) {
                    gameState.level = newLevel;
                    
                    // もしドロップ間隔があるなら更新
                    if (gameState.dropInterval) {
                        clearInterval(gameState.drop);
                        gameState.dropInterval = setInterval(dropCheck, 1000 / gameState.level);
                    }
                }
            }
        }

        // ゲームオーバー
        function gameOver() {
            gameState.isGameOver = true;
            
            // ゲームオーバー画面の表示
            const gameOverMessage = document.createElement('div');
            gameOverMessage.id = 'game-over';
            gameOverMessage.className = 'score-display';
            gameOverMessage.textContent = `Game Over! Score: ${gameState.score}`;
            
            document.body.appendChild(gameOverMessage);
        }

        // 初期化を実行
        initGame();
    </script>
</body>
</html>

Qwen3-8B

あいさつ

role text
User こんにちは
Assistant(Thinking) Okay, the user said "こんにちは" which is Japanese for "Hello". I need to respond in a friendly and helpful manner. Let me make sure to acknowledge their greeting and offer assistance. I should keep it simple and polite, maybe add an emoji to keep it friendly. Let me check if there's anything specific they might need, but since it's just a greeting, I'll keep the response general. Alright, that should cover it.
Assistant こんにちは!(ニャンニャン)お元気ですか?何か質問や助けが必要なことがありましたら、いつでも気軽に聞いてくださいね!少しでもお手伝いできたら嬉しいです~🌟

thinkタグ内は英語で、出力結果はちょっと変ですね。ただ、生成し直してみると

こんにちは!元気ですか?何かお手伝いが必要なければなりませんか?😊

普通の内容になっています。たまたま?

javascriptプログラミング

出力が長いこともあり具体的な内容は省略します。

thinkタグ内は英語で生成され、途中途中でコードが生成されていました。かなり長い内容になり生成にthinkタグ内の生成に3分25秒かかりました。

本文も英語になってしまい。また、プロンプトにてHTMLコード内にjavascriptコードを記載するように指示しましたが、分けて生成してしまいました。

生成されたHTMLとjsファイルを確認してみましたが、以下の様にまったく動作しませんでした。

生成されたコード
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Tetris Game</title>
  <style>
    canvas {
      border: 2px solid #000;
      display: block;
      margin: auto;
    }
  </style>
</head>
<body>
  <canvas id="tetris" width="200" height="400"></canvas>
  <script src="tetris.js"></script>
</body>
</html>

const canvas = document.getElementById('tetris');
const context = canvas.getContext('2d');

// Constants
const gridWidth = 10;
const gridHeight = 20;
const cellSize = 20;

canvas.width = gridWidth * cellSize;
canvas.height = gridHeight * cellSize;

// Grid and Colors
let grid = createEmptyGrid();
const TETROMINO_COLORS = {
  'I': '#00ffff',
  'O': '#ff0000',
  'T': '#66cc66',
  'S': '#1a8b23',
  'Z': '#de495e',
  'J': '#0086d2',
  'L': '#a3c4bc'
};

// Tetromino Shapes
const TETROMINOS = {
  'I': [
    [[0,0], [1,0], [2,0], [3,0]],
    [[0,0], [0,1], [0,2], [0,3]],
    [[0,0], [1,0], [2,0], [3,0]],
    [[0,0], [0,1], [0,2], [0,3]],
  ],
  'O': [
    [[0,0], [0,1], [1,0], [1,1]],
  ],
  'T': [
    [[0,0], [0,1], [0,2], [1,1]],
    [[0,1], [1,1], [1,2], [2,1]],
    [[1,0], [1,1], [1,2], [0,1]],
    [[0,0], [1,0], [2,0], [1,1]],
  ],
  'S': [
    [[0,1], [0,2], [1,0], [1,1]],
    [[0,0], [1,0], [1,1], [2,1]],
  ],
  'Z': [
    [[0,0], [1,0], [1,1], [2,0]],
    [[0,1], [1,1], [1,2], [2,1]],
  ],
  'J': [
    [[0,0], [1,0], [2,0], [2,1]],
    [[0,1], [0,2], [1,1], [2,1]],
    [[1,0], [1,1], [1,2], [0,2]],
    [[0,0], [1,0], [2,0], [0,1]],
  ],
  'L': [
    [[0,0], [0,1], [0,2], [1,2]],
    [[0,1], [1,1], [2,1], [2,0]],
    [[1,0], [1,1], [1,2], [2,0]],
    [[0,0], [1,0], [2,0], [2,1]],
  ]
};

function createEmptyGrid() {
  return new Array(gridHeight).fill().map(() => new Array(gridWidth).fill(0));
}

function drawGrid() {
  for (let y = 0; y < gridHeight; y++) {
    for (let x = 0; x < gridWidth; x++) {
      if (grid[y][x] !== 0) {
        drawCell(x, y, grid[y][x]);
      }
    }
  }
}

function drawCell(x, y, color) {
  context.fillStyle = color;
  context.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
}

function drawPiece(x, y) {
  context.fillStyle = piece.color;
  for (let [dx, dy] of piece.shape) {
    context.fillRect((x + dx)*cellSize, (y + dy)*cellSize, cellSize, cellSize);
  }
}

function draw() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  drawGrid();
  if (piece) {
    drawPiece(piece.x, piece.y);
  }
}

function checkCollision(x, y) {
  for (let [dx, dy] of piece.shape) {
    const nx = x + dx;
    const ny = y + dy;
    if (nx < 0 || nx >= gridWidth || ny >= gridHeight) {
      return false;
    }
    if (grid[ny][nx] !== 0) {
      return false;
    }
  }
  return true;
}

function move(dx, dy) {
  if (checkCollision(piece.x + dx, piece.y + dy)) {
    piece.x += dx;
    piece.y += dy;
  }
}

function rotate() {
  const newShape = piece.shape.map(([dx, dy]) => [-dy, dx]);
  if (checkCollision(piece.x, piece.y)) {
    piece.shape = newShape;
  }
}

function mergePieceIntoGrid() {
  for (let [dx, dy] of piece.shape) {
    const nx = piece.x + dx;
    const ny = piece.y + dy;
    grid[ny][nx] = piece.color;
  }
}

function clearLines() {
  for (let y = gridHeight - 1; y >= 0; y--) {
    if (grid[y].every(cell => cell !== 0)) {
      grid.splice(y, 1);
      grid.unshift(new Array(gridWidth).fill(0));
    }
  }
}

function spawnNewPiece() {
  const type = getRandomTetromino();
  let shapeIndex = Math.floor(Math.random() * TETROMINOS[type].length);
  const shape = TETROMINOS[type][shapeIndex];
  
  const shapeWidth = Math.max(...shape.map(([dx]) => dx)) + 1;
  let x = Math.floor( (gridWidth - shapeWidth) / 2 );
  
  if (!checkCollision(x, 0)) {
    return null;
  }

  return { 
    type, 
    shape: shape,
    x: x, 
    y: 0,
    color: TETROMINO_COLORS[type]
  };
}

function getRandomTetromino() {
  const types = Object.keys(TETROMINOS);
  return types[Math.floor(Math.random() * types.length)];
}

document.addEventListener('keydown', (e) => {
  if (e.key === 'ArrowLeft') move(-1, 0);
  else if (e.key === 'ArrowRight') move(1, 0);
  else if (e.key === 'ArrowDown') move(0, 1);
  else if (e.key === 'ArrowUp') rotate();
});

function gameLoop() {
  if (!checkCollision(piece.x, piece.y + 1)) {
    mergePieceIntoGrid();
    clearLines();
    piece = spawnNewPiece();
    if (!piece) {
      alert('Game Over!');
      clearInterval(gameInterval);
      return;
    }
  } else {
    piece.y += 1;
  }
  draw();
}

function startGame() {
  piece = spawnNewPiece();
  if (!piece) {
    alert('Game Over!');
    return;
  }
  gameInterval = setInterval(gameLoop, 500);
}

startGame();

Qwen3-14B

あいさつ

role text
User こんにちは
Assistant(Thinking) Okay, the user said "こんにちは" which is Japanese for "Hello." I need to respond appropriately.First, I should acknowledge their greeting. Maybe say "こんにちは" back to be polite.Then, offer help. Since they might want assistance, I can ask how I can help them today.Make sure the response is friendly and open-ended. Use simple Japanese to keep it approachable.
Assistant こんにちは!どうぞ、お気軽にご相談ください。今日はどんなお手伝いができますか?

thinkタグ内は英語で、内容は問題ないですね。

javascriptプログラミング

出力が長いこともあり具体的な内容は省略します。

thinkタグ内は英語で生成に25秒ほどかかりました。基本の方針のみのシンプルでコードなどの生成はthinkタグ内では行われませんでした。

本文は日本語で生成されたコードは指示通り、javascriptを含んだ1つのHTMLファイルになりました。

生成されたコード
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>テトリス</title>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #222;
            font-family: sans-serif;
        }
        canvas {
            border: 2px solid #fff;
            background-color: #000;
        }
        #score {
            position: absolute;
            top: 20px;
            color: white;
            font-size: 24px;
        }
    </style>
</head>
<body>
    <div id="score">スコア: 0</div>
    <canvas id="tetris" width="240" height="400"></canvas>

    <script>
        const canvas = document.getElementById('tetris');
        const context = canvas.getContext('2d');
        const scoreDisplay = document.getElementById('score');

        const ROWS = 20;
        const COLS = 10;
        const BLOCK_SIZE = 24;

        const PIECES = {
            I: [[1,1,1,1]],
            O: [[1,1],[1,1]],
            T: [[0,1,0],[1,1,1]],
            S: [[0,1,1],[1,1,0]],
            Z: [[1,1,0],[0,1,1]],
            J: [[1,0,0],[1,1,1]],
            L: [[0,0,1],[1,1,1]]
        };

        const COLORS = {
            I: 'cyan',
            O: 'yellow',
            T: 'purple',
            S: 'green',
            Z: 'red',
            J: 'blue',
            L: 'orange'
        };

        let grid = createGrid();
        let currentPiece;
        let currentPosition;
        let score = 0;

        function createGrid() {
            return Array.from({length: ROWS}, () => Array(COLS).fill(0));
        }

        function drawGrid() {
            context.fillStyle = '#000';
            context.fillRect(0, 0, canvas.width, canvas.height);

            for (let y = 0; y < ROWS; y++) {
                for (let x = 0; x < COLS; x++) {
                    if (grid[y][x]) {
                        context.fillStyle = grid[y][x];
                        context.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1);
                    }
                }
            }
        }

        function drawPiece(piece, position) {
            const [y, x] = position;
            piece.shape.forEach((row, rowIndex) => {
                row.forEach((cell, colIndex) => {
                    if (cell) {
                        context.fillStyle = piece.color;
                        context.fillRect((x + colIndex) * BLOCK_SIZE, (y + rowIndex) * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1);
                    }
                });
            });
        }

        function createPiece() {
            const type = Object.keys(PIECES)[Math.floor(Math.random() * Object.keys(PIECES).length)];
            return {
                shape: PIECES[type],
                color: COLORS[type]
            };
        }

        function isValidMove(piece, position) {
            const [y, x] = position;
            return piece.shape.every((row, rowIndex) => {
                return row.every((cell, colIndex) => {
                    const newX = x + colIndex;
                    const newY = y + rowIndex;
                    return newX >= 0 && newX < COLS && newY >= 0 && grid[newY][newX] === 0;
                });
            });
        }

        function mergePiece() {
            currentPiece.shape.forEach((row, y) => {
                row.forEach((cell, x) => {
                    if (cell) {
                        grid[currentPosition[0] + y][currentPosition[1] + x] = currentPiece.color;
                    }
                });
            });
        }

        function clearLines() {
            let linesCleared = 0;
            for (let y = ROWS - 1; y >= 0; y--) {
                if (grid[y].every(cell => cell !== 0)) {
                    linesCleared++;
                    grid.splice(y, 1);
                    grid.unshift(Array(COLS).fill(0));
                }
            }
            score += linesCleared * 10;
            scoreDisplay.textContent = `スコア: ${score}`;
        }

        function gameOver() {
            if (currentPiece && !isValidMove(currentPiece, currentPosition)) {
                alert('ゲームオーバー!');
                document.location.reload();
            }
        }

        function moveDown() {
            if (isValidMove(currentPiece, [currentPosition[0] + 1, currentPosition[1]])) {
                currentPosition[0]++;
            } else {
                mergePiece();
                clearLines();
                currentPiece = createPiece();
                currentPosition = [0, Math.floor(COLS / 2) - 1];
            }
        }

        function moveLeft() {
            if (isValidMove(currentPiece, [currentPosition[0], currentPosition[1] - 1])) {
                currentPosition[1]--;
            }
        }

        function moveRight() {
            if (isValidMove(currentPiece, [currentPosition[0], currentPosition[1] + 1])) {
                currentPosition[1]++;
            }
        }

        function rotate() {
            const rotated = currentPiece.shape[0].map((_, i) => 
                currentPiece.shape.map(row => row[i]).reverse()
            );
            const newPiece = { shape: rotated, color: currentPiece.color };
            if (isValidMove(newPiece, currentPosition)) {
                currentPiece.shape = rotated;
            }
        }

        function drop() {
            while (isValidMove(currentPiece, [currentPosition[0] + 1, currentPosition[1]])) {
                currentPosition[0]++;
            }
        }

        document.addEventListener('keydown', e => {
            if (e.key === 'ArrowLeft') moveLeft();
            else if (e.key === 'ArrowRight') moveRight();
            else if (e.key === 'ArrowDown') drop();
            else if (e.key === 'ArrowUp') rotate();
        });

        function gameLoop() {
            drawGrid();
            drawPiece(currentPiece, currentPosition);
            moveDown();
            gameOver();
            requestAnimationFrame(gameLoop);
        }

        currentPiece = createPiece();
        currentPosition = [0, Math.floor(COLS / 2) - 1];
        gameLoop();
    </script>
</body>
</html>

起動時に一瞬だけブロックが落ちるのが描画されますが、こちらも全く動作しませんでした。

テトリス作成って結構難しいんですね...

まとめ

今回は日本語プロンプトにてDeepSeek-R1-0528-Qwen3-8Bを使ってみました。

元のQwen3-8Bに対して、thinkタグ内が日本語になったり日常会話のようなタスクにおいてフランクな発言をするのが特徴として見られました。

また、Qwen3-8Bより圧倒的にthinkタグ内の内容が簡潔になりつつ、指示への追従性も高まっているようにみれました。
私の主観的な評価になりますが、蒸留により性能向上していると言えます。

Discussion