{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Blocks</title>\n<style>\n  html, body {\n    margin: 0;\n    padding: 0;\n    background: #0d0f14;\n    color: #d8dde6;\n    font-family: \"Segoe UI\", Arial, sans-serif;\n    height: 100%;\n  }\n  #wrap {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    min-height: 100vh;\n    gap: 24px;\n  }\n  #board {\n    background: #12151c;\n    border: 2px solid #2a3040;\n    border-radius: 6px;\n    box-shadow: 0 0 30px rgba(0,0,0,.8), inset 0 0 40px rgba(0,0,0,.5);\n  }\n  #side {\n    width: 160px;\n  }\n  .panel {\n    background: #161a23;\n    border: 1px solid #2a3040;\n    border-radius: 6px;\n    padding: 12px 14px;\n    margin-bottom: 14px;\n  }\n  .panel h2 {\n    margin: 0 0 8px 0;\n    font-size: 12px;\n    letter-spacing: 2px;\n    text-transform: uppercase;\n    color: #6f7c92;\n    font-weight: 600;\n  }\n  .stat {\n    font-size: 24px;\n    font-variant-numeric: tabular-nums;\n    color: #eef2fa;\n  }\n  .stat.small { font-size: 20px; }\n  #next {\n    display: block;\n    margin: 4px auto 0 auto;\n    background: #10131a;\n    border-radius: 4px;\n  }\n  #help {\n    font-size: 12px;\n    line-height: 1.9;\n    color: #8a94a8;\n  }\n  #help b { color: #c7cfdd; font-weight: 600; }\n</style>\n</head>\n<body>\n<div id=\"wrap\">\n  <canvas id=\"board\" width=\"300\" height=\"600\"></canvas>\n  <div id=\"side\">\n    <div class=\"panel\">\n      <h2>Next</h2>\n      <canvas id=\"next\" width=\"120\" height=\"80\"></canvas>\n    </div>\n    <div class=\"panel\">\n      <h2>Score</h2>\n      <div class=\"stat\" id=\"score\">0</div>\n    </div>\n    <div class=\"panel\">\n      <h2>Lines</h2>\n      <div class=\"stat small\" id=\"lines\">0</div>\n    </div>\n    <div class=\"panel\">\n      <h2>Level</h2>\n      <div class=\"stat small\" id=\"level\">1</div>\n    </div>\n    <div class=\"panel\" id=\"help\">\n      <b>&larr; &rarr;</b> move<br>\n      <b>&uarr;</b> rotate<br>\n      <b>&darr;</b> soft drop<br>\n      <b>Space</b> hard drop<br>\n      <b>R</b> restart\n    </div>\n  </div>\n</div>\n\n<script>\n(function () {\n  \"use strict\";\n\n  var COLS = 10, ROWS = 20, SIZE = 30;\n  var canvas = document.getElementById(\"board\");\n  var ctx = canvas.getContext(\"2d\");\n  var nextCanvas = document.getElementById(\"next\");\n  var nctx = nextCanvas.getContext(\"2d\");\n\n  var scoreEl = document.getElementById(\"score\");\n  var linesEl = document.getElementById(\"lines\");\n  var levelEl = document.getElementById(\"level\");\n\n  // --- Pieces --------------------------------------------------------------\n  var PIECES = [\n    { name: \"I\", color: \"#31c7e8\", shape: [\n        [0,0,0,0],\n        [1,1,1,1],\n        [0,0,0,0],\n        [0,0,0,0] ] },\n    { name: \"O\", color: \"#e8c531\", shape: [\n        [1,1],\n        [1,1] ] },\n    { name: \"T\", color: \"#a85fe8\", shape: [\n        [0,1,0],\n        [1,1,1],\n        [0,0,0] ] },\n    { name: \"S\", color: \"#4fd16a\", shape: [\n        [0,1,1],\n        [1,1,0],\n        [0,0,0] ] },\n    { name: \"Z\", color: \"#e85555\", shape: [\n        [1,1,0],\n        [0,1,1],\n        [0,0,0] ] },\n    { name: \"J\", color: \"#4f7fe8\", shape: [\n        [1,0,0],\n        [1,1,1],\n        [0,0,0] ] },\n    { name: \"L\", color: \"#e8923f\", shape: [\n        [0,0,1],\n        [1,1,1],\n        [0,0,0] ] }\n  ];\n\n  // --- State ----------------------------------------------------------------\n  var board, current, next, score, linesCleared, level, gameOver;\n  var dropTimer, lastTime, speed;\n  var LINE_SCORES = [0, 40, 100, 300, 1200];\n\n  function newBoard() {\n    var b = [], r, c;\n    for (r = 0; r < ROWS; r++) {\n      var row = [];\n      for (c = 0; c < COLS; c++) row.push(0);\n      b.push(row);\n    }\n    return b;\n  }\n\n  function randomPiece() {\n    var def = PIECES[Math.floor(Math.random() * PIECES.length)];\n    var shape = def.shape.map(function (row) { return row.slice(); });\n    var piece = {\n      color: def.color,\n      shape: shape,\n      x: Math.floor((COLS - shape.length) / 2),\n      y: def.name === \"I\" ? -1 : 0\n    };\n    return piece;\n  }\n\n  function reset() {\n    board = newBoard();\n    score = 0;\n    linesCleared = 0;\n    level = 1;\n    gameOver = false;\n    dropTimer = 0;\n    lastTime = performance.now();\n    setSpeed();\n    current = randomPiece();\n    next = randomPiece();\n    if (collides(current, 0, 0)) {\n      gameOver = true;\n    }\n    updateHud();\n    drawNext();\n  }\n\n  function setSpeed() {\n    // milliseconds per cell of gravity, faster each level\n    speed = Math.max(70, 800 - (level - 1) * 70);\n  }\n\n  function collides(piece, dx, dy, shape) {\n    shape = shape || piece.shape;\n    var y, x, row;\n    for (y = 0; y < shape.length; y++) {\n      for (x = 0; x < shape[y].length; x++) {\n        if (!shape[y][x]) continue;\n        var nx = piece.x + x + dx;\n        var ny = piece.y + y + dy;\n        if (nx < 0 || nx >= COLS || ny >= ROWS) return true;\n        if (ny >= 0 && board[ny][nx]) return true;\n      }\n    }\n    return false;\n  }\n\n  function rotateMatrix(m) {\n    var n = m.length;\n    var out = [], i, j, row;\n    for (i = 0; i < n; i++) {\n      row = [];\n      for (j = 0; j < n; j++) row.push(m[n - 1 - j][i]);\n      out.push(row);\n    }\n    return out;\n  }\n\n  function tryRotate() {\n    if (current.shape.length === 2) return; // O piece: rotation is a no-op\n    var rotated = rotateMatrix(current.shape);\n    var kicks = [0, -1, 1, -2, 2];\n    var kicksY = [0, 0, 0, 0, 0, -1];\n    var i, j;\n    for (i = 0; i < kicks.length; i++) {\n      for (j = 0; j < kicksY.length; j++) {\n        var dy = (j === 5) ? -1 : 0;\n        var test = { x: current.x + kicks[i], y: current.y + dy, shape: current.shape };\n        var saved = current.shape;\n        current.shape = rotated;\n        var bad = collides(current, kicks[i], dy, rotated);\n        current.shape = saved;\n        if (!bad) {\n          current.shape = rotated;\n          current.x += kicks[i];\n          current.y += dy;\n          return;\n        }\n      }\n    }\n  }\n\n  function move(dx) {\n    if (!collides(current, dx, 0)) {\n      current.x += dx;\n    }\n  }\n\n  function moveDown(reward) {\n    if (!collides(current, 0, 1)) {\n      current.y++;\n      if (reward) score += 1;\n      return true;\n    }\n    lockPiece();\n    return false;\n  }\n\n  function hardDrop() {\n    var dist = 0;\n    while (!collides(current, 0, 1)) {\n      current.y++;\n      dist++;\n    }\n    score += dist * 2;\n    lockPiece();\n  }\n\n  function lockPiece() {\n    var y, x;\n    for (y = 0; y < current.shape.length; y++) {\n      for (x = 0; x < current.shape[y].length; x++) {\n        if (current.shape[y][x]) {\n          var by = current.y + y;\n          var bx = current.x + x;\n          if (by < 0) {\n            // piece locked above the ceiling\n            gameOver = true;\n            updateHud();\n            return;\n          }\n          board[by][bx] = current.color;\n        }\n      }\n    }\n    clearLines();\n    updateHud();\n    current = next;\n    next = randomPiece();\n    dropTimer = 0;\n    if (collides(current, 0, 0)) {\n      gameOver = true;\n    }\n    drawNext();\n  }\n\n  function clearLines() {\n    var cleared = 0;\n    var y, x, full, rowIsFull;\n    for (y = ROWS - 1; y >= 0; y--) {\n      full = true;\n      for (x = 0; x < COLS; x++) {\n        if (!board[y][x]) { full = false; break; }\n      }\n      if (full) {\n        board.splice(y, 1);\n        var empty = [];\n        for (x = 0; x < COLS; x++) empty.push(0);\n        board.unshift(empty);\n        cleared++;\n        y++; // re-check same row index after shifting\n      }\n    }\n    if (cleared > 0) {\n      score += LINE_SCORES[cleared] * level;\n      linesCleared += cleared;\n      var newLevel = Math.floor(linesCleared / 10) + 1;\n      if (newLevel !== level) {\n        level = newLevel;\n        setSpeed();\n      }\n    }\n  }\n\n  function updateHud() {\n    scoreEl.textContent = score;\n    linesEl.textContent = linesCleared;\n    levelEl.textContent = level;\n  }\n\n  // --- Rendering ------------------------------------------------------------\n  function drawCell(c, pieceX, pieceY, color, x, y, size) {\n    var px = (pieceX + x) * size;\n    var py = (pieceY + y) * size;\n    c.fillStyle = color;\n    c.fillRect(px + 1, py + 1, size - 2, size - 2);\n    // highlight edge\n    c.fillStyle = \"rgba(255,255,255,0.25)\";\n    c.fillRect(px + 1, py + 1, size - 2, 3);\n    c.fillRect(px + 1, py + 1, 3, size - 2);\n    // shadow edge\n    c.fillStyle = \"rgba(0,0,0,0.30)\";\n    c.fillRect(px + 1, py + size - 4, size - 2, 3);\n    c.fillRect(px + size - 4, py + 1, 3, size - 2);\n  }\n\n  function draw() {\n    var w = canvas.width, h = canvas.height;\n    ctx.fillStyle = \"#12151c\";\n    ctx.fillRect(0, 0, w, h);\n\n    // grid lines\n    ctx.strokeStyle = \"rgba(255,255,255,0.05)\";\n    ctx.lineWidth = 1;\n    var i;\n    ctx.beginPath();\n    for (i = 1; i < COLS; i++) {\n      ctx.moveTo(i * SIZE + 0.5, 0);\n      ctx.lineTo(i * SIZE + 0.5, h);\n    }\n    for (i = 1; i < ROWS; i++) {\n      ctx.moveTo(0, i * SIZE + 0.5);\n      ctx.lineTo(w, i * SIZE + 0.5);\n    }\n    ctx.stroke();\n\n    // stack\n    var y, x;\n    for (y = 0; y < ROWS; y++) {\n      for (x = 0; x < COLS; x++) {\n        if (board[y][x]) {\n          drawCell(ctx, 0, 0, board[y][x], x, y, SIZE);\n        }\n      }\n    }\n\n    // current piece (skip rows above the ceiling)\n    if (!gameOver && current) {\n      for (y = 0; y < current.shape.length; y++) {\n        for (x = 0; x < current.shape[y].length; x++) {\n          if (current.shape[y][x] && current.y + y >= 0) {\n            drawCell(ctx, 0, 0, current.color, current.x + x, current.y + y, SIZE);\n          }\n        }\n      }\n    }\n\n    if (gameOver) {\n      ctx.fillStyle = \"rgba(8,10,14,0.82)\";\n      ctx.fillRect(0, 0, w, h);\n      ctx.fillStyle = \"#eef2fa\";\n      ctx.textAlign = \"center\";\n      ctx.font = \"bold 30px 'Segoe UI', Arial, sans-serif\";\n      ctx.fillText(\"GAME OVER\", w / 2, h / 2 - 30);\n      ctx.font = \"15px 'Segoe UI', Arial, sans-serif\";\n      ctx.fillStyle = \"#8a94a8\";\n      ctx.fillText(\"Press R to restart\", w / 2, h / 2 + 10);\n    }\n  }\n\n  function drawNext() {\n    var w = nextCanvas.width, h = nextCanvas.height;\n    nctx.fillStyle = \"#10131a\";\n    nctx.fillRect(0, 0, w, h);\n    if (!next) return;\n\n    // compute bounding box of filled cells for centering\n    var minX = 99, maxX = -1, minY = 99, maxY = -1;\n    var y, x;\n    for (y = 0; y < next.shape.length; y++) {\n      for (x = 0; x < next.shape[y].length; x++) {\n        if (next.shape[y][x]) {\n          if (x < minX) minX = x;\n          if (x > maxX) maxX = x;\n          if (y < minY) minY = y;\n          if (y > maxY) maxY = y;\n        }\n      }\n    }\n    var cw = (maxX - minX + 1), ch = (maxY - minY + 1);\n    var cell = 20;\n    var offX = (w - cw * cell) / 2 - minX * cell;\n    var offY = (h - ch * cell) / 2 - minY * cell;\n\n    for (y = 0; y < next.shape.length; y++) {\n      for (x = 0; x < next.shape[y].length; x++) {\n        if (next.shape[y][x]) {\n          var px = offX + x * cell, py = offY + y * cell;\n          nctx.fillStyle = next.color;\n          nctx.fillRect(px + 1, py + 1, cell - 2, cell - 2);\n          nctx.fillStyle = \"rgba(255,255,255,0.25)\";\n          nctx.fillRect(px + 1, py + 1, cell - 2, 2);\n          nctx.fillRect(px + 1, py + 1, 2, cell - 2);\n          nctx.fillStyle = \"rgba(0,0,0,0.30)\";\n          nctx.fillRect(px + 1, py + cell - 3, cell - 2, 2);\n          nctx.fillRect(px + cell - 3, py + 1, 2, cell - 2);\n        }\n      }\n    }\n  }\n\n  // --- Game loop ------------------------------------------------------------\n  function loop(now) {\n    var delta = now - lastTime;\n    lastTime = now;\n    if (!gameOver) {\n      dropTimer += delta;\n      if (dropTimer >= speed) {\n        dropTimer = 0;\n        moveDown(false);\n      }\n    }\n    draw();\n    requestAnimationFrame(loop);\n  }\n\n  // --- Input -----------------------------------------------------------------\n  document.addEventListener(\"keydown\", function (e) {\n    var key = e.key;\n    if (key === \" \" || key === \"ArrowUp\" || key === \"ArrowDown\" ||\n        key === \"ArrowLeft\" || key === \"ArrowRight\") {\n      e.preventDefault();\n    }\n\n    if ((gameOver && (key === \"r\" || key === \"R\")) || key === \"F5\") {\n      if (key === \"r\" || key === \"R\") { reset(); }\n      return;\n    }\n    if (gameOver) return;\n\n    switch (key) {\n      case \"ArrowLeft\":  move(-1); break;\n      case \"ArrowRight\": move(1);  break;\n      case \"ArrowUp\":    tryRotate(); break;\n      case \"ArrowDown\":  moveDown(true); dropTimer = 0; break;\n      case \" \":          hardDrop(); break;\n    }\n    updateHud();\n  });\n\n  // --- Start -----------------------------------------------------------------\n  reset();\n  requestAnimationFrame(loop);\n})();\n</script>\n</body>\n</html>\n```",
        "reasoning_content": "Need to write a complete Tetris-like game. Single HTML file, canvas, dark theme. Let me write clean code.\n\nPieces: I, O, T, S, Z, J, L \u2014 four squares each (tetrominoes), different colors.\n\nRotation: rotation by offset model. Define rotation states or use matrix rotation. Simplest: define each piece as matrix and rotate by matrix rotation, then do wall-kick attempts for x offset (0, -1, +1, -2, +2). For I piece which is 4x4 matrix rotation.\n\nLet me define pieces as matrices:\n- I: 4x4\n- O: 2x2 (rotation effectively does nothing)\n- T, S, Z, J, L: 3x3\n\nRotate matrix clockwise: newMatrix[x][y] = old[N-1-y][x].\n\nWall kicks: try offsets [0,-1,1,-2,2], check validity. Also small upward kick (dy -1) maybe. Keep simple.\n\nGame loop: requestAnimationFrame and drop timer. Level speed: dropInterval = max(100, 800 - (level-1)*70) or use standard formula.\n\nScoring: 40, 100, 300, 1200 per level times line-clear multiplier.\n\nNext-piece preview: separate small canvas.\n\nGame over: overlay text on canvas, restart with R key.\n\nHard drop: instant drop, lock, plus score. Soft drop speeds up.\n\nLet me write it. Clean dark style. Canvas 10x20, cell 30px \u2192 300x600.\n\nImplement ghost? Not required. Keep simple but polished.\n\nDraw blocks with slight bevel highlight.\n\nPrevent key repeat issues with arrow keys \u2014 use e.preventDefault.\n\nSpawn piece at top center. Game over if spawn collides.\n\nLock: when piece can't move down, gravity-timer handles. On hard-drop, lock immediately.\n\nLet me code:\n\n```js\nconst COLS=10, ROWS=20, SIZE=30;\nconst pieces = { I:{color:'#00d9f5',shape:[[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]]}, O:{...}, T, S, Z, J, L }\n```\n\nValid check: iterate cells of matrix, if filled and (x<0 || x>=COLS || y>=ROWS) return false; if y>=0 && board[y][x] return false.\n\nRotate: rotate matrix clockwise, try kicks.\n\nSoft drop: move down; if invalid, lock. Add points on soft/hard drop.\n\nLevel: lines / 10 + 1.\n\nNext piece: random.\n\nDraw grid, blocks, piece.\n\nGame over state: boolean, draw overlay.\n\nRestart: R key or Enter.\n\nLet me write complete file.\n\nFor keyboard handling: keydown listener, check keys based on state.\n\nAlright, code carefully.\n\nDrop timer based: lastTime, currentTime, accum dropInterval. Use fixed step maybe: setInterval style with delta in rAF.\n\nAlso handle piece lock-out: when piece locks, if any of cells is at y<0 (spawn area above), game over? Standard: if spawn collides game over. Let's do this: if new piece collides at spawn, game over.\n\nRows above come down when line is cleared: standard splice.\n\nFlash effect on line clear? Optional; skip for simplicity, or add short flash. Let's keep simple \u2014 immediate clear.\n\nLet me write the HTML now.\n"
      }
    }
  ],
  "created": 1787805204,
  "model": "~/.cache/huggingface/hub/models--unsloth--Qwen3.8-Flash-Next-GGUF/snapshots/178b998806b7b406c311a3e6174aa99cf6304eaf/UD-Q2_K_XL/Qwen3.8-Flash-Next-UD-Q2_K_XL-00001-of-00003.gguf",
  "system_fingerprint": "b10667-213df585b",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 5415,
    "prompt_tokens": 212,
    "total_tokens": 5627,
    "prompt_tokens_details": {
      "cached_tokens": 0
    }
  },
  "id": "chatcmpl-pqRFwkeFnH5fOhAOlMwTUFy4y3Ik2BwO",
  "timings": {
    "cache_n": 0,
    "prompt_n": 212,
    "prompt_ms": 1618.392,
    "prompt_per_token_ms": 7.633924528301887,
    "prompt_per_second": 130.9942214247228,
    "predicted_n": 5415,
    "predicted_ms": 225879.129,
    "predicted_per_token_ms": 41.721301994828224,
    "predicted_per_second": 23.968571261845092
  }
}