{
  "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>Falling Blocks</title>\n<style>\n  html, body {\n    margin: 0;\n    padding: 0;\n    background: #0d0f14;\n    color: #d8dee9;\n    font-family: \"Segoe UI\", Consolas, monospace, sans-serif;\n    height: 100%;\n  }\n  body {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    min-height: 100vh;\n  }\n  #game-wrap {\n    display: flex;\n    gap: 24px;\n    padding: 24px;\n    background: #14171f;\n    border: 1px solid #262b38;\n    border-radius: 10px;\n    box-shadow: 0 10px 40px rgba(0,0,0,0.6);\n  }\n  #board-area {\n    position: relative;\n  }\n  canvas#board {\n    display: block;\n    background: #0a0c10;\n    border: 2px solid #2c3342;\n    border-radius: 6px;\n  }\n  #overlay {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    display: none;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    background: rgba(8, 10, 14, 0.82);\n    border-radius: 6px;\n    text-align: center;\n  }\n  #overlay.visible { display: flex; }\n  #overlay h1 {\n    font-size: 30px;\n    color: #ff5c5c;\n    margin: 0 0 10px 0;\n    letter-spacing: 2px;\n  }\n  #overlay p {\n    margin: 4px 0;\n    color: #aab3c5;\n    font-size: 14px;\n  }\n  #overlay .restart-hint {\n    margin-top: 18px;\n    color: #8fbe6a;\n    font-size: 15px;\n  }\n  #side {\n    width: 170px;\n    display: flex;\n    flex-direction: column;\n    gap: 18px;\n  }\n  .panel h2 {\n    font-size: 12px;\n    text-transform: uppercase;\n    letter-spacing: 2px;\n    color: #6b7488;\n    margin: 0 0 6px 0;\n  }\n  .stat-value {\n    font-size: 24px;\n    color: #e8ecf4;\n    font-variant-numeric: tabular-nums;\n  }\n  canvas#next {\n    background: #0a0c10;\n    border: 1px solid #2c3342;\n    border-radius: 6px;\n    display: block;\n  }\n  #controls {\n    font-size: 12px;\n    color: #6b7488;\n    line-height: 1.7;\n    border-top: 1px solid #262b38;\n    padding-top: 14px;\n    margin-top: auto;\n  }\n  #controls b { color: #aab3c5; }\n</style>\n</head>\n<body>\n\n<div id=\"game-wrap\">\n  <div id=\"board-area\">\n    <canvas id=\"board\" width=\"300\" height=\"600\"></canvas>\n    <div id=\"overlay\">\n      <h1>GAME OVER</h1>\n      <p id=\"final-score\">Score: 0</p>\n      <p id=\"final-lines\">Lines: 0</p>\n      <p class=\"restart-hint\">Press ENTER to restart</p>\n    </div>\n  </div>\n  <div id=\"side\">\n    <div class=\"panel\">\n      <h2>Score</h2>\n      <div class=\"stat-value\" id=\"score\">0</div>\n    </div>\n    <div class=\"panel\">\n      <h2>Lines</h2>\n      <div class=\"stat-value\" id=\"lines\">0</div>\n    </div>\n    <div class=\"panel\">\n      <h2>Level</h2>\n      <div class=\"stat-value\" id=\"level\">1</div>\n    </div>\n    <div class=\"panel\">\n      <h2>Next</h2>\n      <canvas id=\"next\" width=\"120\" height=\"90\"></canvas>\n    </div>\n    <div id=\"controls\">\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>Enter</b> restart\n    </div>\n  </div>\n</div>\n\n<script>\n(function () {\n  \"use strict\";\n\n  // ---------- Constants ----------\n  var COLS = 10, ROWS = 20, CELL = 30;\n\n  var boardCanvas = document.getElementById(\"board\");\n  var bctx = boardCanvas.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  var overlay = document.getElementById(\"overlay\");\n  var finalScoreEl = document.getElementById(\"final-score\");\n  var finalLinesEl = document.getElementById(\"final-lines\");\n\n  // Tetromino definitions: spawn-state grids + colors\n  var SHAPES = [\n    { // I\n      grid: [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],\n      color: \"#3fb8ff\"\n    },\n    { // O\n      grid: [[1,1],[1,1]],\n      color: \"#ffd23f\"\n    },\n    { // T\n      grid: [[0,1,0],[1,1,1],[0,0,0]],\n      color: \"#b96bff\"\n    },\n    { // S\n      grid: [[0,1,1],[1,1,0],[0,0,0]],\n      color: \"#5fd65f\"\n    },\n    { // Z\n      grid: [[1,1,0],[0,1,1],[0,0,0]],\n      color: \"#ff5c5c\"\n    },\n    { // J\n      grid: [[1,0,0],[1,1,1],[0,0,0]],\n      color: \"#4a7dff\"\n    },\n    { // L\n      grid: [[0,0,1],[1,1,1],[0,0,0]],\n      color: \"#ff9b3f\"\n    }\n  ];\n\n  var LINE_SCORES = [0, 100, 300, 500, 800];\n\n  // ---------- State ----------\n  var grid, cur, next, score, linesTotal, level, gameOver;\n  var dropTimer = 0, lastTime = 0, softDropping = false;\n\n  function dropInterval() {\n    // milliseconds per row; decreases with level, floor at 90ms\n    return Math.max(90, 800 - (level - 1) * 75);\n  }\n\n  // ---------- Helpers ----------\n  function makeGrid() {\n    var g = [];\n    for (var r = 0; r < ROWS; r++) {\n      var row = [];\n      for (var c = 0; c < COLS; c++) row.push(0);\n      g.push(row);\n    }\n    return g;\n  }\n\n  function rotateGridCW(m) {\n    var n = m.length, res = [];\n    for (var r = 0; r < n; r++) {\n      res.push([]);\n      for (var c = 0; c < n; c++) {\n        res[r].push(m[n - 1 - c][r]);\n      }\n    }\n    return res;\n  }\n\n  function randomPiece() {\n    var s = SHAPES[Math.floor(Math.random() * SHAPES.length)];\n    var grid = s.grid.map(function (row) { return row.slice(); });\n    // Trim I-piece's empty top row so it spawns snugly at the top\n    return {\n      grid: grid,\n      color: s.color,\n      x: Math.floor((COLS - grid[0].length) / 2),\n      y: 0\n    };\n  }\n\n  function collides(piece, offX, offY, gridShape) {\n    var g = gridShape || piece.grid;\n    for (var r = 0; r < g.length; r++) {\n      for (var c = 0; c < g[r].length; c++) {\n        if (!g[r][c]) continue;\n        var nx = piece.x + c + offX;\n        var ny = piece.y + r + offY;\n        if (nx < 0 || nx >= COLS || ny >= ROWS) return true;\n        if (ny >= 0 && grid[ny][nx]) return true;\n      }\n    }\n    return false;\n  }\n\n  // ---------- Actions ----------\n  function move(dx) {\n    if (gameOver) return;\n    if (!collides(cur, dx, 0)) cur.x += dx;\n  }\n\n  function rotate() {\n    if (gameOver) return;\n    var rotated = rotateGridCW(cur.grid);\n    // wall kicks: try offsets in order\n    var kicks = [0, -1, 1, -2, 2];\n    for (var i = 0; i < kicks.length; i++) {\n      var k = kicks[i];\n      if (!collides(cur, k, 0, rotated)) {\n        cur.grid = rotated;\n        cur.x += k;\n        return;\n      }\n      // also try kicking up one row (helps against flat floors near stack)\n      if (!collides(cur, k, -1, rotated)) {\n        cur.grid = rotated;\n        cur.x += k;\n        cur.y -= 1;\n        return;\n      }\n    }\n  }\n\n  function stepDown() {\n    if (collides(cur, 0, 1)) {\n      lockPiece();\n      return false;\n    }\n    cur.y += 1;\n    return true;\n  }\n\n  function hardDrop() {\n    if (gameOver) return;\n    var dropped = 0;\n    while (!collides(cur, 0, 1)) {\n      cur.y += 1;\n      dropped++;\n    }\n    score += dropped * 2;\n    lockPiece();\n  }\n\n  function lockPiece() {\n    for (var r = 0; r < cur.grid.length; r++) {\n      for (var c = 0; c < cur.grid[r].length; c++) {\n        if (!cur.grid[r][c]) continue;\n        var y = cur.y + r, x = cur.x + c;\n        if (y < 0) {\n          endGame();\n          return;\n        }\n        grid[y][x] = cur.color;\n      }\n    }\n    clearLines();\n    cur = next;\n    next = randomPiece();\n    if (collides(cur, 0, 0)) endGame();\n    dropTimer = 0;\n  }\n\n  function clearLines() {\n    var cleared = 0;\n    for (var r = ROWS - 1; r >= 0; r--) {\n      var full = true;\n      for (var c = 0; c < COLS; c++) {\n        if (!grid[r][c]) { full = false; break; }\n      }\n      if (full) {\n        grid.splice(r, 1);\n        var newRow = [];\n        for (var c2 = 0; c2 < COLS; c2++) newRow.push(0);\n        grid.unshift(newRow);\n        cleared++;\n        r++; // re-check same row index (shifted down)\n      }\n    }\n    if (cleared > 0) {\n      score += LINE_SCORES[cleared] * level;\n      linesTotal += cleared;\n      var newLevel = Math.floor(linesTotal / 10) + 1;\n      if (newLevel > level) level = newLevel;\n    }\n    updateHud();\n  }\n\n  function endGame() {\n    gameOver = true;\n    finalScoreEl.textContent = \"Score: \" + score;\n    finalLinesEl.textContent = \"Lines: \" + linesTotal;\n    overlay.classList.add(\"visible\");\n  }\n\n  function updateHud() {\n    scoreEl.textContent = score;\n    linesEl.textContent = linesTotal;\n    levelEl.textContent = level;\n  }\n\n  function reset() {\n    grid = makeGrid();\n    score = 0;\n    linesTotal = 0;\n    level = 1;\n    gameOver = false;\n    dropTimer = 0;\n    softDropping = false;\n    next = randomPiece();\n    cur = randomPiece();\n    overlay.classList.remove(\"visible\");\n    updateHud();\n  }\n\n  // ---------- Rendering ----------\n  function drawCell(ctx, x, y, size, color) {\n    ctx.fillStyle = color;\n    ctx.fillRect(x, y, size, size);\n    // highlight\n    ctx.fillStyle = \"rgba(255,255,255,0.22)\";\n    ctx.fillRect(x, y, size, 3);\n    ctx.fillRect(x, y, 3, size);\n    // shadow\n    ctx.fillStyle = \"rgba(0,0,0,0.30)\";\n    ctx.fillRect(x, y + size - 3, size, 3);\n    ctx.fillRect(x + size - 3, y, 3, size);\n  }\n\n  function ghostY() {\n    var gy = 0;\n    while (!collides(cur, 0, gy + 1)) gy++;\n    return cur.y + gy;\n  }\n\n  function draw() {\n    bctx.clearRect(0, 0, boardCanvas.width, boardCanvas.height);\n\n    // grid lines\n    bctx.strokeStyle = \"rgba(255,255,255,0.04)\";\n    bctx.lineWidth = 1;\n    for (var c = 1; c < COLS; c++) {\n      bctx.beginPath();\n      bctx.moveTo(c * CELL + 0.5, 0);\n      bctx.lineTo(c * CELL + 0.5, ROWS * CELL);\n      bctx.stroke();\n    }\n    for (var r = 1; r < ROWS; r++) {\n      bctx.beginPath();\n      bctx.moveTo(0, r * CELL + 0.5);\n      bctx.lineTo(COLS * CELL, r * CELL + 0.5);\n      bctx.stroke();\n    }\n\n    // stack\n    for (var r2 = 0; r2 < ROWS; r2++) {\n      for (var c2 = 0; c2 < COLS; c2++) {\n        if (grid[r2][c2]) {\n          drawCell(bctx, c2 * CELL + 1, r2 * CELL + 1, CELL - 2, grid[r2][c2]);\n        }\n      }\n    }\n\n    if (!gameOver && cur) {\n      // ghost\n      var gy = ghostY();\n      bctx.globalAlpha = 0.22;\n      for (var r3 = 0; r3 < cur.grid.length; r3++) {\n        for (var c3 = 0; c3 < cur.grid[r3].length; c3++) {\n          if (cur.grid[r3][c3]) {\n            var yy = gy + r3;\n            if (yy >= 0) {\n              drawCell(bctx, (cur.x + c3) * CELL + 1, yy * CELL + 1, CELL - 2, cur.color);\n            }\n          }\n        }\n      }\n      bctx.globalAlpha = 1;\n\n      // current piece\n      for (var r4 = 0; r4 < cur.grid.length; r4++) {\n        for (var c4 = 0; c4 < cur.grid[r4].length; c4++) {\n          if (cur.grid[r4][c4]) {\n            var py = cur.y + r4;\n            if (py >= 0) {\n              drawCell(bctx, (cur.x + c4) * CELL + 1, py * CELL + 1, CELL - 2, cur.color);\n            }\n          }\n        }\n      }\n    }\n  }\n\n  function drawNext() {\n    nctx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);\n    if (!next) return;\n    var g = next.grid;\n    // bounding box of filled cells\n    var minR = 9, maxR = -1, minC = 9, maxC = -1;\n    for (var r = 0; r < g.length; r++) {\n      for (var c = 0; c < g[r].length; c++) {\n        if (g[r][c]) {\n          if (r < minR) minR = r;\n          if (r > maxR) maxR = r;\n          if (c < minC) minC = c;\n          if (c > maxC) maxC = c;\n        }\n      }\n    }\n    var wCells = maxC - minC + 1, hCells = maxR - minR + 1;\n    var size = 20;\n    var ox = (nextCanvas.width - wCells * size) / 2;\n    var oy = (nextCanvas.height - hCells * size) / 2;\n    for (var r2 = minR; r2 <= maxR; r2++) {\n      for (var c2 = minC; c2 <= maxC; c2++) {\n        if (g[r2][c2]) {\n          drawCell(nctx, ox + (c2 - minC) * size + 1, oy + (r2 - minR) * size + 1, size - 2, next.color);\n        }\n      }\n    }\n  }\n\n  // ---------- Game loop ----------\n  function loop(time) {\n    var dt = time - lastTime;\n    lastTime = time;\n    if (dt > 250) dt = 250; // tab-away guard\n\n    if (!gameOver) {\n      dropTimer += dt;\n      var interval = softDropping ? Math.min(50, dropInterval()) : dropInterval();\n      while (dropTimer >= interval) {\n        dropTimer -= interval;\n        if (!stepDown()) break;\n        if (softDropping) score += 1;\n      }\n      updateHud();\n    }\n\n    draw();\n    drawNext();\n    requestAnimationFrame(loop);\n  }\n\n  // ---------- Input ----------\n  document.addEventListener(\"keydown\", function (e) {\n    if ([\"ArrowLeft\", \"ArrowRight\", \"ArrowUp\", \"ArrowDown\", \" \", \"Enter\"].indexOf(e.key) !== -1) {\n      e.preventDefault();\n    }\n    if (gameOver) {\n      if (e.key === \"Enter\") reset();\n      return;\n    }\n    switch (e.key) {\n      case \"ArrowLeft\":  move(-1); break;\n      case \"ArrowRight\": move(1);  break;\n      case \"ArrowUp\":    rotate(); break;\n      case \"ArrowDown\":  softDropping = true; break;\n      case \" \":          hardDrop(); break;\n    }\n  });\n\n  document.addEventListener(\"keyup\", function (e) {\n    if (e.key === \"ArrowDown\") softDropping = false;\n  });\n\n  // ---------- Start ----------\n  reset();\n  requestAnimationFrame(function (t) {\n    lastTime = t;\n    requestAnimationFrame(loop);\n  });\n})();\n</script>\n</body>\n</html>\n```",
        "reasoning_content": "Simple Tetris implementation. Let me write clean code with a canvas, an 10x20 grid, 7 tetrominoes using SRS-like kicks (simple wall kicks), a next-piece preview, score/lines/level, gravity that speeds up with level, soft-drop score, hard-drop, line clearing, game over, and restart with Enter/R.\n\nImplement pieces with a rotation state array or rotation via matrix rotation. Simpler: define pieces as coordinate sets, rotate with a matrix rotation function, and try a set of simple wall kicks [0, -1, +1, -2, +2].\n\nAlright, let's write the file.\n"
      }
    }
  ],
  "created": 1787816411,
  "model": "~/.cache/huggingface/hub/models--unsloth--Qwen3.8-Flash-Next-GGUF/snapshots/824f539b2710e5a9e47af4952cf6578cf5ee8932/UD-Q3_K_XL/Qwen3.8-Flash-Next-UD-Q3_K_XL-00001-of-00003.gguf",
  "system_fingerprint": "b10667-213df585b",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 4967,
    "prompt_tokens": 212,
    "total_tokens": 5179,
    "prompt_tokens_details": {
      "cached_tokens": 0
    }
  },
  "id": "chatcmpl-6Bg9rtt9VM0FQfX31fHciisJ8VGekAnb",
  "timings": {
    "cache_n": 0,
    "prompt_n": 212,
    "prompt_ms": 1004.684,
    "prompt_per_token_ms": 4.739075471698113,
    "prompt_per_second": 211.01162156459145,
    "predicted_n": 4967,
    "predicted_ms": 241114.949,
    "predicted_per_token_ms": 48.553151228352796,
    "predicted_per_second": 20.59598552721839
  }
}