mager-bench
← mager-bench

original subscription run / 2026-09-25

GPT-5.6 Sol through Codex CLI

One read-only headless Codex session per challenge and verdict, using a local ChatGPT sign-in. All 13 prompts finished. The same model judged its own answers.

9.0/10 mean13/13 completedjudge: codex-cli/gpt-5.6-sol

How to read this run

These scores now appear on the ChatGPT subscription board alongside GPT-6 Astra. Both runs use the same GPT-5.6 Sol judge, which graded its own answers here. Codex CLI follows an output-length instruction; it has no matching hard API token cap. The older Sonnet 5 scores remain in a separate archive.

Doom and Slots were rescored after the judge was changed to read their full saved responses.

Challenge breakdown

open a row for the answer and verdict
fizzbuzzClassic FizzBuzz — tests basic correctness and code style
correctness 10.0quality 10.0docs 10.05.7s generation
10.0

Judge note

The function is fully correct, concise and idiomatic, returns the required list, and includes a clear docstring with a valid usage example.

Raw response

```python
def fizzbuzz(n: int) -> list[str]:
    """Return FizzBuzz values for the integers from 1 through n.

    Example:
        >>> fizzbuzz(5)
        ['1', '2', 'Fizz', '4', 'Buzz']
    """
    return [
        "FizzBuzz" if number % 15 == 0
        else "Fizz" if number % 3 == 0
        else "Buzz" if number % 5 == 0
        else str(number)
        for number in range(1, n + 1)
    ]
```
debugFind and fix 3 bugs in broken Python code — tests careful reading and correctness reasoning
correctness 10.0quality 10.0docs 10.016.5s generation
10.0

Judge note

The response identifies all three genuine defects, explains their effects clearly, applies minimal clean fixes, and provides a corrected function that passes every stated test case.

Raw response

1. Buggy line:

```python
words = [w.strip('.,!?;:\'"') for w in words]
```

It retains empty strings produced from punctuation-only tokens, causing `''` to be counted as a word.

Fix:

```python
words = [cleaned for w in words if (cleaned := w.strip('.,!?;:\'"'))]
```

2. Buggy line:

```python
return [word for word, count in ranked[:n] if count > 1]
```

The `count > 1` condition incorrectly removes words occurring once, even when they belong in the top N.

Fix:

```python
return [word for word, _ in ranked[:n]]
```

3. Buggy line:

```python
return [word for word, count in ranked[:n] if count > 1]
```

For a negative `n`, `ranked[:n]` returns all but the last items rather than no words. A non-positive top-N request should return an empty list.

Fix:

```python
if n <= 0:
    return []
```

Corrected function:

```python
from collections import Counter

def top_words(text: str, n: int = 10) -> list[str]:
    if n <= 0:
        return []

    words = text.lower().split()
    words = [
        cleaned
        for word in words
        if (cleaned := word.strip('.,!?;:\'"'))
    ]

    counts = Counter(words)
    ranked = sorted(counts.items(), key=lambda item: (-item[1], item[0]))
    return [word for word, _ in ranked[:n]]
```
sqlWrite a complex SQL query with CTEs, window functions, and aggregations
correctness 10.0quality 10.0docs 10.011.1s generation
10.0

Judge note

The query fully satisfies the requirements with correct aggregation, deterministic top-customer ranking, window-based revenue percentage, appropriate rounding, clear structure, and comments for every CTE.

Raw response

```sql
WITH
-- Computes total spend per customer within each country.
customer_spend AS (
    SELECT
        c.country,
        c.name,
        SUM(o.amount) AS customer_total
    FROM customers AS c
    JOIN orders AS o
      ON o.customer_id = c.id
    GROUP BY c.country, c.id, c.name
),
-- Ranks customers by spend in each country, breaking ties by name.
ranked_customers AS (
    SELECT
        country,
        name,
        ROW_NUMBER() OVER (
            PARTITION BY country
            ORDER BY customer_total DESC, name ASC
        ) AS customer_rank
    FROM customer_spend
),
-- Computes order-level revenue statistics for each country.
country_totals AS (
    SELECT
        c.country,
        SUM(o.amount) AS total_revenue,
        COUNT(*) AS order_count,
        ROUND(AVG(o.amount), 2) AS avg_order_value
    FROM customers AS c
    JOIN orders AS o
      ON o.customer_id = c.id
    GROUP BY c.country
)
SELECT
    ct.country,
    ct.total_revenue,
    ct.order_count,
    ct.avg_order_value,
    rc.name AS top_customer,
    ROUND(
        100 * ct.total_revenue
        / NULLIF(SUM(ct.total_revenue) OVER (), 0),
        1
    ) AS revenue_pct
FROM country_totals AS ct
JOIN ranked_customers AS rc
  ON rc.country = ct.country
 AND rc.customer_rank = 1
ORDER BY ct.total_revenue DESC;
```
readme-writerWrite a README for a CLI tool — tests documentation ability directly
correctness 10.0quality 9.0docs 10.012.7s generation
9.7

Judge note

The README fully covers every required section and flag with clear examples, accurate output semantics, concise explanations, and polished Markdown structure.

Raw response

# snapdiff

`snapdiff` is a Python command-line tool that compares two directories and reports added, removed, and changed files.

## Installation

Install from PyPI using pip:

```bash
python -m pip install snapdiff
```

Verify the installation:

```bash
snapdiff --help
```

## Usage

Compare two directories:

```bash
snapdiff PATH_A PATH_B
```

### Examples

Compare two directory snapshots using text output:

```bash
snapdiff ./before ./after
```

Ignore hidden files and directories:

```bash
snapdiff ./before ./after --ignore-hidden
```

Produce machine-readable JSON:

```bash
snapdiff ./before ./after --output json
```

Limit comparison to two directory levels:

```bash
snapdiff ./before ./after --depth 2
```

Combine options:

```bash
snapdiff ./before ./after --ignore-hidden --output json --depth 3
```

### Options

- `--ignore-hidden`: Exclude hidden files and directories, such as `.git` and `.env`.
- `--output json|text`: Select the output format. The default is `text`.
- `--depth N`: Limit recursive traversal to `N` directory levels.

## Output Formats

### Text

Text output groups relative file paths by status:

```text
Added:
  assets/logo.svg

Removed:
  docs/old-guide.md

Changed:
  README.md
  src/config.py
```

- **Added** files exist only in the second directory.
- **Removed** files exist only in the first directory.
- **Changed** files exist in both directories but have different contents.

### JSON

JSON output contains arrays of relative file paths:

```json
{
  "added": ["assets/logo.svg"],
  "removed": ["docs/old-guide.md"],
  "changed": ["README.md", "src/config.py"]
}
```

## How It Works

`snapdiff` recursively scans both directories and matches files by relative path. Paths found only in the second directory are marked as added, paths found only in the first are marked as removed, and matching paths with different file contents are marked as changed. Traversal and presentation are controlled by the selected command-line options.
refactorRefactor messy code and explain each change
correctness 10.0quality 10.0docs 9.06.1s generation
9.7

Judge note

The refactor is behaviorally equivalent, idiomatic, clearly named, and fully typed, with a concise docstring and specific explanation that could only be improved by documenting parameters and the return value.

Raw response

```python
def transform_numbers(numbers: list[int]) -> list[int]:
    """Square even numbers and cube odd numbers."""
    return [
        number**2 if number % 2 == 0 else number**3
        for number in numbers
    ]
```

- Renamed `p` to `transform_numbers` to describe the function’s purpose.
- Renamed `d` to `numbers` and `x` to `number` for clarity.
- Replaced the index-based loop and repeated `append` calls with a concise list comprehension.
- Used `**2` and `**3` to express squaring and cubing directly.
- Added type hints indicating that the function accepts and returns a list of integers.
- Added a docstring summarizing the transformation.
binary-searchBinary search implementation with full docs
correctness 10.0quality 8.0docs 10.011.6s generation
9.3

Judge note

The implementation is correct and exceptionally well documented, though full type and sortedness validation makes the overall runtime O(n), undermining binary search's usual O(log n) performance.

Raw response

```python
def binary_search(arr: list[int], target: int) -> int:
    """Find a target value in a sorted list using binary search.

    Args:
        arr: A list of integers sorted in ascending order.
        target: The integer to find.

    Returns:
        The index of target if found; otherwise, -1.

    Raises:
        TypeError: If arr is not a list, target is not an integer, or an
            element of arr is not an integer.
        ValueError: If arr is not sorted in ascending order.

    Examples:
        >>> binary_search([1, 3, 5, 7, 9], 5)
        2
        >>> binary_search([1, 3, 5, 7, 9], 4)
        -1
        >>> binary_search([], 10)
        -1
    """
    if not isinstance(arr, list):
        raise TypeError("arr must be a list")
    if not isinstance(target, int):
        raise TypeError("target must be an integer")
    if any(not isinstance(value, int) for value in arr):
        raise TypeError("all elements of arr must be integers")
    if any(arr[i] > arr[i + 1] for i in range(len(arr) - 1)):
        raise ValueError("arr must be sorted in ascending order")

    left, right = 0, len(arr) - 1

    # Repeatedly discard the half that cannot contain the target.
    while left <= right:
        # This form avoids unnecessarily adding the two boundary indices.
        middle = left + (right - left) // 2

        if arr[middle] == target:
            return middle

        # Keep only the right or left half based on the middle value.
        if arr[middle] < target:
            left = middle + 1
        else:
            right = middle - 1

    # The search interval is empty, so the target is absent.
    return -1
```
elixir-testWrite idiomatic Elixir ExUnit tests — tests knowledge of Elixir testing conventions
correctness 10.0quality 9.0docs 9.012.7s generation
9.3

Judge note

The module comprehensively covers every required case with correct Unicode behavior, idiomatic assertions and grouping, though the describe labels say truncate/3 while most tests exercise the default-argument truncate/2 form.

Raw response

defmodule StringUtilsTest do
  use ExUnit.Case

  @moduledoc """
  Tests for `StringUtils.truncate/3`.

  ## Examples

      iex> StringUtils.truncate("hello", 3)
      "hel..."
  """

  describe "truncate/3 happy path" do
    test "returns a string shorter than the limit unchanged" do
      assert StringUtils.truncate("hello", 10) == "hello"
    end

    test "returns a string at the exact limit unchanged" do
      assert StringUtils.truncate("hello", 5) == "hello"
    end

    test "appends the default suffix when truncating" do
      assert StringUtils.truncate("hello world", 5) == "hello..."
    end

    test "supports a custom suffix" do
      assert StringUtils.truncate("hello world", 5, "…") == "hello…"
    end

    test "counts Unicode characters rather than bytes" do
      result = StringUtils.truncate("héllo 🌍", 5)

      assert "héllo..." = result
    end
  end

  describe "truncate/3 edge and error cases" do
    test "returns an empty string unchanged" do
      assert StringUtils.truncate("", 5) == ""
    end

    test "truncates a non-empty string when max_len is zero" do
      assert StringUtils.truncate("hello", 0) == "..."
    end

    test "returns an empty string when max_len is zero" do
      assert StringUtils.truncate("", 0) == ""
    end

    test "raises ArgumentError for a negative max_len" do
      assert_raise ArgumentError, fn ->
        StringUtils.truncate("hello", -1)
      end
    end
  end
end
api-clientWrite a small HTTP API client class with error handling and docs
correctness 9.0quality 8.0docs 9.014.1s generation
8.7

Judge note

The implementation cleanly satisfies the core requirements with proper error handling, typing, sessions, and docstrings, though it lacks timeout/session-closing support and the example may fail against the placeholder URL without catching transport errors.

Raw response

```python
from typing import Any, Mapping, Optional

import requests


class APIError(Exception):
    """Raised when the API returns a non-successful HTTP response."""

    def __init__(self, status_code: int, message: str) -> None:
        """Initialize the error with an HTTP status code and message."""
        self.status_code = status_code
        self.message = message
        super().__init__(f"API request failed ({status_code}): {message}")


class APIClient:
    """A lightweight client for making authenticated REST API requests.

    Args:
        base_url: The root URL of the API, such as
            ``"https://api.example.com/v1"``.
        api_key: Optional bearer token used to authenticate requests.
    """

    def __init__(self, base_url: str, api_key: Optional[str] = None) -> None:
        """Initialize the API client.

        Args:
            base_url: The root URL of the API.
            api_key: Optional bearer token used for authentication.
        """
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()

        if api_key is not None:
            self.session.headers["Authorization"] = f"Bearer {api_key}"

        self.session.headers["Accept"] = "application/json"

    def get(
        self,
        path: str,
        params: Optional[Mapping[str, Any]] = None,
    ) -> Any:
        """Send a GET request to an API endpoint.

        Args:
            path: Endpoint path relative to the base URL.
            params: Optional query-string parameters.

        Returns:
            The decoded JSON response.

        Raises:
            APIError: If the API returns a non-2xx response.
            requests.RequestException: If the request cannot be completed.
            requests.JSONDecodeError: If the response is not valid JSON.
        """
        response = self.session.get(self._url(path), params=params)
        self._raise_for_api_error(response)
        return response.json()

    def post(self, path: str, data: Mapping[str, Any]) -> Any:
        """Send a POST request containing JSON data to an API endpoint.

        Args:
            path: Endpoint path relative to the base URL.
            data: Mapping to serialize as the JSON request body.

        Returns:
            The decoded JSON response.

        Raises:
            APIError: If the API returns a non-2xx response.
            requests.RequestException: If the request cannot be completed.
            requests.JSONDecodeError: If the response is not valid JSON.
        """
        response = self.session.post(self._url(path), json=data)
        self._raise_for_api_error(response)
        return response.json()

    def _url(self, path: str) -> str:
        return f"{self.base_url}/{path.lstrip('/')}"

    @staticmethod
    def _raise_for_api_error(response: requests.Response) -> None:
        if 200 <= response.status_code < 300:
            return

        try:
            payload = response.json()
            message = (
                payload.get("message", response.text)
                if isinstance(payload, dict)
                else response.text
            )
        except requests.exceptions.JSONDecodeError:
            message = response.text

        raise APIError(response.status_code, message or response.reason)


# Usage example
if __name__ == "__main__":
    client = APIClient(
        base_url="https://api.example.com/v1",
        api_key="your-api-key",
    )

    try:
        users = client.get("/users", params={"limit": 10})
        created_user = client.post(
            "/users",
            data={"name": "Ada Lovelace", "email": "ada@example.com"},
        )
        print(users)
        print(created_user)
    except APIError as error:
        print(f"Request failed: {error}")
```
go-testWrite idiomatic Go table-driven tests — tests knowledge of Go testing conventions
correctness 10.0quality 10.0docs 6.013.1s generation
8.7

Judge note

The tests and benchmark fully satisfy the functional and structural requirements with idiomatic Go, but both exported test functions lack the requested explanatory comments.

Raw response

```go
package wordcount

import (
	"reflect"
	"testing"
)

func TestWordCount(t *testing.T) {
	tests := []struct {
		name, input string
		want        map[string]int
	}{
		{
			name:  "empty string",
			input: "",
			want:  map[string]int{},
		},
		{
			name:  "single word",
			input: "hello",
			want:  map[string]int{"hello": 1},
		},
		{
			name:  "mixed case",
			input: "Go go GO",
			want:  map[string]int{"go": 3},
		},
		{
			name:  "punctuation",
			input: "Hello, world! How are you?",
			want: map[string]int{
				"hello": 1,
				"world": 1,
				"how":   1,
				"are":   1,
				"you":   1,
			},
		},
		{
			name:  "repeated words",
			input: "one two one three two one",
			want: map[string]int{
				"one":   3,
				"two":   2,
				"three": 1,
			},
		},
		{
			name:  "multi-word sentence",
			input: "The quick brown fox jumps over the lazy dog",
			want: map[string]int{
				"the":   2,
				"quick": 1,
				"brown": 1,
				"fox":   1,
				"jumps": 1,
				"over":  1,
				"lazy":  1,
				"dog":   1,
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got := WordCount(tc.input)
			if !reflect.DeepEqual(got, tc.want) {
				t.Errorf("WordCount(%q) = %v; want %v", tc.input, got, tc.want)
			}
		})
	}
}

func BenchmarkWordCount(b *testing.B) {
	const input = "The quick brown fox jumps over the lazy dog, while another fox watches from the quiet forest."

	for i := 0; i < b.N; i++ {
		WordCount(input)
	}
}
```
doomBuild a Doom-style raycasting FPS engine in a single HTML file — the mager-bench signature challenge
correctness 8.7quality 9.1docs 8.494.0s generation
8.7

Judge note

Strong real DDA raycasting, corrected textured walls, shading, doors, HUD, and delta-time controls, but the spawn faces east rather than toward its first southbound corridor, collision is circle-versus-cell rather than the requested AABB, and the claimed secret area is not meaningfully secret.

Raw response

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>DDA Raycaster</title>
<style>
  html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#000}
  canvas{display:block;width:100vw;height:100vh;image-rendering:pixelated;cursor:crosshair}
</style>
</head>
<body>
<canvas id="game" width="640" height="480"></canvas>
<script>
"use strict";

const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d", { alpha:false });
ctx.imageSmoothingEnabled = false;

const state = {
  width:640,
  height:480,
  map:[],
  mapWidth:0,
  mapHeight:0,
  player:{x:0,y:0,angle:0,radius:0.2},
  keys:Object.create(null),
  textures:{},
  zBuffer:new Float32Array(640),
  openDoors:new Set(),
  nearbyDoor:null,
  exit:null,
  complete:false,
  startTime:performance.now(),
  completionTime:0,
  lastTime:performance.now(),
  fpsSamples:[],
  fps:0,
  moveSpeed:3,
  rotSpeed:2,
  fov:66*Math.PI/180
};

function initMap(s) {
  /*
    Upper-left and upper-right chambers are joined by a corridor and door.
    The central and lower chambers connect through winding corridors.
    The lower-left striped chamber is the secret area; the southeast spur
    beyond the exit corridor includes a dead end.
  */
  const encoded = [
    "1111111111111111",
    "1800001000000001",
    "1000001000000001",
    "1000002000000001",
    "1110111111041101",
    "1000100000000101",
    "1000101111100101",
    "1000001000100001",
    "1333301000103331",
    "1000000000100001",
    "1011111110101101",
    "1000000010000101",
    "1000333011110101",
    "1000300000000901",
    "1000000010000001",
    "1111111111111111"
  ];

  s.map = encoded.map(row => Array.from(row, Number));
  s.mapHeight = s.map.length;
  s.mapWidth = s.map[0].length;

  for (let y=0; y<s.mapHeight; y++) {
    for (let x=0; x<s.mapWidth; x++) {
      if (s.map[y][x] === 8) {
        s.player.x=x+0.5;
        s.player.y=y+0.5;
        s.player.angle=0;
        s.map[y][x]=0;
      } else if (s.map[y][x] === 9) {
        s.exit={x,y};
        s.map[y][x]=0;
      }
    }
  }
}

function makeTexture(draw) {
  const c=document.createElement("canvas");
  c.width=c.height=64;
  const g=c.getContext("2d");
  draw(g,64);
  return c;
}

function createTextures(s) {
  s.textures[1]=makeTexture((g,n)=>{
    for(let y=0;y<n;y+=8) for(let x=0;x<n;x+=8){
      g.fillStyle=((x+y)/8)%2===0?"#9b9b9b":"#52575b";
      g.fillRect(x,y,8,8);
    }
    g.strokeStyle="#303438";
    for(let i=0;i<=n;i+=8){
      g.beginPath();g.moveTo(i,0);g.lineTo(i,n);g.stroke();
      g.beginPath();g.moveTo(0,i);g.lineTo(n,i);g.stroke();
    }
  });

  s.textures[2]=makeTexture((g,n)=>{
    g.fillStyle="#702d24";g.fillRect(0,0,n,n);
    for(let y=0;y<n;y+=12){
      const offset=(y/12)%2 ? -8 : 0;
      for(let x=offset;x<n;x+=16){
        g.fillStyle=(x+y)%24===0?"#a34b38":"#873727";
        g.fillRect(x+1,y+1,14,10);
      }
    }
    g.fillStyle="#351d19";
    for(let y=0;y<n;y+=12)g.fillRect(0,y,64,2);
    for(let y=0;y<n;y+=12){
      const offset=(y/12)%2 ? 8 : 0;
      for(let x=offset;x<n;x+=16)g.fillRect(x,y,2,12);
    }
  });

  s.textures[3]=makeTexture((g,n)=>{
    g.fillStyle="#213842";g.fillRect(0,0,n,n);
    for(let x=0;x<n;x+=8){
      g.fillStyle=(x/8)%2?"#397788":"#285766";
      g.fillRect(x,0,6,n);
      g.fillStyle="#75a7aa";g.fillRect(x,0,1,n);
    }
    for(let y=3;y<n;y+=16){
      g.fillStyle="rgba(255,255,255,.12)";
      g.fillRect(0,y,n,2);
    }
  });

  s.textures[4]=makeTexture((g,n)=>{
    g.fillStyle="#5b351b";g.fillRect(0,0,n,n);
    for(let x=0;x<n;x+=8){
      g.fillStyle=x%16===0?"#754822":"#4a2a16";
      g.fillRect(x+1,0,6,n);
      g.fillStyle="#2a170d";g.fillRect(x,0,1,n);
    }
    g.fillStyle="#c99a38";
    g.beginPath();g.arc(51,32,3,0,Math.PI*2);g.fill();
  });
}

function cellAt(s,x,y) {
  if(x<0||y<0||x>=s.mapWidth||y>=s.mapHeight)return 1;
  return s.map[y][x];
}

function isSolid(s,x,y) {
  const v=cellAt(s,x,y);
  return v>=1 && v<=4;
}

function canOccupy(s,x,y) {
  const r=s.player.radius;
  const minX=Math.floor(x-r), maxX=Math.floor(x+r);
  const minY=Math.floor(y-r), maxY=Math.floor(y+r);
  for(let my=minY;my<=maxY;my++) for(let mx=minX;mx<=maxX;mx++){
    if(!isSolid(s,mx,my))continue;
    const nearestX=Math.max(mx,Math.min(x,mx+1));
    const nearestY=Math.max(my,Math.min(y,my+1));
    const dx=x-nearestX,dy=y-nearestY;
    if(dx*dx+dy*dy<r*r)return false;
  }
  return true;
}

function nearestDoor(s) {
  let best=null,bestD=1.5;
  for(let y=0;y<s.mapHeight;y++) for(let x=0;x<s.mapWidth;x++){
    if(s.map[y][x]!==4)continue;
    const d=Math.hypot(s.player.x-(x+.5),s.player.y-(y+.5));
    if(d<=bestD){bestD=d;best={x,y};}
  }
  return best;
}

function openNearbyDoor(s) {
  const door=nearestDoor(s);
  if(!door)return;
  s.map[door.y][door.x]=5;
  s.openDoors.add(door.x+","+door.y);
}

function castRay(s,rayAngle) {
  const rayX=Math.cos(rayAngle),rayY=Math.sin(rayAngle);
  let mapX=Math.floor(s.player.x),mapY=Math.floor(s.player.y);
  const deltaX=Math.abs(1/(rayX||1e-12));
  const deltaY=Math.abs(1/(rayY||1e-12));
  const stepX=rayX<0?-1:1,stepY=rayY<0?-1:1;
  let sideX=rayX<0?(s.player.x-mapX)*deltaX:(mapX+1-s.player.x)*deltaX;
  let sideY=rayY<0?(s.player.y-mapY)*deltaY:(mapY+1-s.player.y)*deltaY;
  let side=0,type=1,arch=null;

  for(let i=0;i<64;i++){
    let travel;
    if(sideX<sideY){
      travel=sideX;sideX+=deltaX;mapX+=stepX;side=0;
    }else{
      travel=sideY;sideY+=deltaY;mapY+=stepY;side=1;
    }
    type=cellAt(s,mapX,mapY);
    if(type===5 && !arch) arch={distance:travel,side,mapX,mapY};
    if(type>=1&&type<=4)break;
  }

  const rawDistance=side===0 ? sideX-deltaX : sideY-deltaY;
  const correctedDistance=Math.max(.0001,
    rawDistance*Math.cos(rayAngle-s.player.angle));

  let hit=side===0
    ? s.player.y+rawDistance*rayY
    : s.player.x+rawDistance*rayX;
  let textureX=hit-Math.floor(hit);
  if((side===0&&rayX>0)||(side===1&&rayY<0))textureX=1-textureX;

  if(arch){
    arch.correctedDistance=Math.max(.0001,
      arch.distance*Math.cos(rayAngle-s.player.angle));
  }

  return {distance:correctedDistance,rawDistance,side,type,textureX,arch};
}

function drawWallColumn(s,x,ray) {
  const h=s.height;
  const wallHeight=Math.min(h*4,h/ray.distance);
  const top=Math.floor(h/2-wallHeight/2);
  const texture=s.textures[ray.type]||s.textures[1];
  const tx=Math.max(0,Math.min(63,Math.floor(ray.textureX*64)));

  ctx.drawImage(texture,tx,0,1,64,x,top,1,wallHeight);

  let brightness=Math.max(.18,Math.min(1,1.25/ray.distance));
  if(ray.side===1)brightness*=.78;
  ctx.fillStyle=`rgba(0,0,0,${1-brightness})`;
  ctx.fillRect(x,top,1,wallHeight);

  /* A passable opened door is drawn as an overhead arch lintel. */
  if(ray.arch && ray.arch.correctedDistance<ray.distance){
    const d=ray.arch.correctedDistance;
    const fullHeight=Math.min(h*4,h/d);
    const archTop=h/2-fullHeight/2;
    const lintelHeight=Math.max(2,fullHeight*.16);
    if(d<s.zBuffer[x]){
      ctx.drawImage(s.textures[4],0,0,64,10,x,archTop,1,lintelHeight);
      const shade=Math.max(.2,Math.min(1,1.2/d));
      ctx.fillStyle=`rgba(0,0,0,${1-shade})`;
      ctx.fillRect(x,archTop,1,lintelHeight);
    }
  }
}

function renderWorld(s) {
  ctx.fillStyle="#141820";
  ctx.fillRect(0,0,s.width,s.height/2);
  ctx.fillStyle="#292a28";
  ctx.fillRect(0,s.height/2,s.width,s.height/2);

  const halfFov=s.fov/2;
  const tanHalf=Math.tan(halfFov);

  for(let x=0;x<s.width;x++){
    const cameraX=2*(x+.5)/s.width-1;
    const rayAngle=s.player.angle+Math.atan(cameraX*tanHalf);
    const ray=castRay(s,rayAngle);
    s.zBuffer[x]=ray.distance;
    drawWallColumn(s,x,ray);
  }
}

/* Future sprites should project to a screen column, then draw only when
   spriteDistance < state.zBuffer[column], preserving correct wall occlusion. */

function drawMinimap(s) {
  const scale=6,ox=10,oy=10;
  ctx.fillStyle="rgba(0,0,0,.65)";
  ctx.fillRect(ox-4,oy-4,s.mapWidth*scale+8,s.mapHeight*scale+8);

  const colors=["#20252a","#8e9296","#934434","#357487","#a76a2c","#4c6a45"];
  for(let y=0;y<s.mapHeight;y++) for(let x=0;x<s.mapWidth;x++){
    const v=s.map[y][x];
    ctx.fillStyle=v===0?"#20252a":colors[v]||"#20252a";
    ctx.fillRect(ox+x*scale,oy+y*scale,scale-1,scale-1);
  }

  if(s.exit){
    ctx.fillStyle="#4cff6b";
    ctx.fillRect(ox+s.exit.x*scale+1,oy+s.exit.y*scale+1,scale-2,scale-2);
  }

  const px=ox+s.player.x*scale,py=oy+s.player.y*scale;
  ctx.fillStyle="#ffe45c";
  ctx.beginPath();ctx.arc(px,py,2.5,0,Math.PI*2);ctx.fill();
  ctx.strokeStyle="#ffe45c";ctx.lineWidth=1.5;
  ctx.beginPath();
  ctx.moveTo(px,py);
  ctx.lineTo(px+Math.cos(s.player.angle)*9,py+Math.sin(s.player.angle)*9);
  ctx.stroke();
}

function formatTime(ms) {
  const total=Math.floor(ms/1000);
  return String(Math.floor(total/60)).padStart(2,"0")+":"+
         String(total%60).padStart(2,"0");
}

function drawHUD(s) {
  drawMinimap(s);
  ctx.font="bold 14px monospace";
  ctx.textAlign="right";
  ctx.fillStyle="rgba(0,0,0,.65)";
  ctx.fillRect(s.width-100,10,90,24);
  ctx.fillStyle="#fff";
  ctx.fillText(`${s.fps.toFixed(0)} FPS`,s.width-18,27);

  ctx.textAlign="center";
  if(s.nearbyDoor&&!s.complete){
    const text="Press E to open door";
    ctx.font="bold 16px sans-serif";
    ctx.fillStyle="rgba(0,0,0,.7)";
    ctx.fillRect(s.width/2-105,s.height-55,210,30);
    ctx.fillStyle="#ffe8a3";
    ctx.fillText(text,s.width/2,s.height-34);
  }

  if(document.pointerLockElement!==canvas&&!s.complete){
    ctx.font="14px sans-serif";
    ctx.fillStyle="rgba(0,0,0,.65)";
    ctx.fillRect(s.width/2-105,16,210,28);
    ctx.fillStyle="#fff";
    ctx.fillText("Click to enable mouse-look",s.width/2,35);
  }

  if(s.complete){
    ctx.fillStyle="rgba(0,0,0,.72)";
    ctx.fillRect(0,0,s.width,s.height);
    ctx.font="bold 32px sans-serif";
    ctx.fillStyle="#7dff8d";
    ctx.fillText("LEVEL COMPLETE — "+formatTime(s.completionTime),
                 s.width/2,s.height/2);
  }
}

function handleInput(s,dt) {
  if(s.complete)return;

  let forward=0,strafe=0;
  if(s.keys.KeyW)forward++;
  if(s.keys.KeyS)forward--;
  if(s.keys.KeyD)strafe++;
  if(s.keys.KeyA)strafe--;

  if(s.keys.ArrowLeft)s.player.angle-=s.rotSpeed*dt;
  if(s.keys.ArrowRight)s.player.angle+=s.rotSpeed*dt;

  const length=Math.hypot(forward,strafe)||1;
  forward/=length;strafe/=length;

  const ca=Math.cos(s.player.angle),sa=Math.sin(s.player.angle);
  const dx=(ca*forward-sa*strafe)*s.moveSpeed*dt;
  const dy=(sa*forward+ca*strafe)*s.moveSpeed*dt;

  if(canOccupy(s,s.player.x+dx,s.player.y))s.player.x+=dx;
  if(canOccupy(s,s.player.x,s.player.y+dy))s.player.y+=dy;

  s.player.angle=(s.player.angle+Math.PI*2)%(Math.PI*2);
  s.nearbyDoor=nearestDoor(s);

  if(s.exit &&
     Math.hypot(s.player.x-(s.exit.x+.5),s.player.y-(s.exit.y+.5))<.42){
    s.complete=true;
    s.completionTime=performance.now()-s.startTime;
  }
}

function gameLoop(now) {
  const dt=Math.min(.05,(now-state.lastTime)/1000);
  state.lastTime=now;

  handleInput(state,dt);
  renderWorld(state);
  drawHUD(state);

  const instantaneous=dt>0?1/dt:60;
  state.fpsSamples.push(instantaneous);
  if(state.fpsSamples.length>30)state.fpsSamples.shift();
  state.fps=state.fpsSamples.reduce((a,b)=>a+b,0)/state.fpsSamples.length;

  requestAnimationFrame(gameLoop);
}

addEventListener("keydown",e=>{
  state.keys[e.code]=true;
  if(e.code==="KeyE"&&!e.repeat)openNearbyDoor(state);
  if(["KeyW","KeyA","KeyS","KeyD","ArrowLeft","ArrowRight","Space"].includes(e.code))
    e.preventDefault();
});

addEventListener("keyup",e=>state.keys[e.code]=false);

canvas.addEventListener("click",()=>{
  if(!state.complete)canvas.requestPointerLock();
});

addEventListener("mousemove",e=>{
  if(document.pointerLockElement===canvas&&!state.complete)
    state.player.angle+=e.movementX*.0025;
});

initMap(state);
createTextures(state);
requestAnimationFrame(gameLoop);
</script>
</body>
</html>
test-writingWrite pytest tests for a provided function — tests edge-case thinking and assertion quality
correctness 8.0quality 9.0docs 8.010.9s generation
8.3

Judge note

The suite is accurate, concise, and covers many useful boundary and invalid inputs, but it misses a dedicated valid minutes-plus-seconds case and lacks explicit meaningful parametrization IDs.

Raw response

```python
import pytest

from your_module import parse_duration


@pytest.mark.parametrize(
    ("duration", "expected_seconds"),
    [
        ("1h", 3600),
        ("30m", 1800),
        ("45s", 45),
        ("1h30m", 5400),
        ("2h15m30s", 8130),
        ("0h", 0),
        ("0m", 0),
        ("0s", 0),
        ("24h60m60s", 90060),
        ("001h002m003s", 3723),
        ("  1h30m  ", 5400),
        ("\t45s\n", 45),
    ],
)
def test_parse_duration_returns_total_seconds(duration, expected_seconds):
    assert parse_duration(duration) == expected_seconds


@pytest.mark.parametrize(
    "invalid_duration",
    [
        "",
        "   ",
        "1",
        "1d",
        "1H",
        "30M",
        "45S",
        "1h 30m",
        "30m1h",
        "1h30m45s extra",
        "-1h",
        "+30m",
        "1.5h",
        "hm",
        "abc",
    ],
)
def test_parse_duration_rejects_unrecognized_input(invalid_duration):
    with pytest.raises(ValueError, match=r"^Invalid duration:"):
        parse_duration(invalid_duration)
```
slotsBuild a Vegas-style slot machine in a single HTML file — reels, pay table, betting, wins
correctness 9.0quality 9.0docs 7.096.7s generation
8.3

Judge note

The game is complete and mechanically correct, including payout precedence, reel order, state handling, effects, and credit animation, but reel timings and animation intervals are embedded as literals rather than named constants and the code has little explicit documentation.

Raw response

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lucky Sevens</title>
<style>
  :root {
    --gold: #ffd45c;
    --deep-gold: #a86b00;
    --red: #b80f22;
    --dark-red: #510711;
    --cream: #fff4c7;
    --panel: #18070b;
  }

  * {
    box-sizing: border-box;
  }

  body {
    min-height: 100vh;
    margin: 0;
    display: grid;
    place-items: center;
    padding: 24px;
    color: var(--cream);
    font-family: Impact, Haettenschweiler, "Arial Narrow Bold", sans-serif;
    background:
      radial-gradient(circle at 50% 15%, #8d1522 0, #35070d 36%, #100205 72%),
      #100205;
  }

  button {
    font: inherit;
  }

  .casino {
    width: min(940px, 100%);
    border: 5px solid var(--gold);
    border-radius: 28px;
    padding: 22px;
    background: linear-gradient(145deg, #860e1e, #37050c);
    box-shadow:
      0 0 0 8px #7b4200,
      0 0 35px rgba(255, 196, 47, .5),
      inset 0 0 30px rgba(0, 0, 0, .65);
  }

  h1 {
    margin: 0;
    text-align: center;
    color: var(--gold);
    font-size: clamp(2.5rem, 8vw, 5.4rem);
    line-height: .9;
    letter-spacing: .08em;
    text-shadow: 0 4px #7a0a18, 0 0 18px #ffb000;
  }

  .tagline {
    margin: 10px 0 20px;
    text-align: center;
    letter-spacing: .25em;
    color: white;
  }

  .layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 250px;
    gap: 20px;
  }

  .machine,
  .paytable {
    border: 3px solid #d39118;
    border-radius: 18px;
    background: linear-gradient(#25070d, #100205);
    box-shadow: inset 0 0 18px #000;
  }

  .machine {
    padding: 18px;
  }

  .win-name {
    height: 38px;
    display: grid;
    place-items: center;
    color: var(--gold);
    font-size: clamp(1.25rem, 4vw, 2rem);
    letter-spacing: .08em;
    text-shadow: 0 0 12px #ff9d00;
  }

  .reels-frame {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    padding: 13px;
    border: 5px solid var(--gold);
    border-radius: 18px;
    background: #8a4e05;
    box-shadow: inset 0 0 15px #2b1000;
  }

  .reel {
    height: clamp(112px, 19vw, 170px);
    overflow: hidden;
    border: 4px solid #28040a;
    border-radius: 12px;
    background: linear-gradient(#d7d7d7, #fff 35%, #fff 65%, #c6c6c6);
    box-shadow: inset 0 0 22px rgba(0, 0, 0, .4);
  }

  .strip {
    height: 100%;
    will-change: transform;
  }

  .symbol {
    height: 100%;
    display: grid;
    place-items: center;
    font-family: system-ui, sans-serif;
    font-size: clamp(3.4rem, 10vw, 7rem);
    line-height: 1;
    user-select: none;
  }

  .symbol.winner {
    animation: winnerFlash .22s ease-in-out 6 alternate;
  }

  .reels-frame.shake {
    animation: shake .35s ease-in-out;
  }

  @keyframes winnerFlash {
    from { filter: brightness(1); transform: scale(1); }
    to {
      filter: brightness(1.5) drop-shadow(0 0 15px #ffd600);
      transform: scale(1.12);
      background: rgba(255, 222, 0, .42);
    }
  }

  @keyframes shake {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-9px) rotate(-1deg); }
    40% { transform: translateX(8px) rotate(1deg); }
    60% { transform: translateX(-6px); }
    80% { transform: translateX(4px); }
  }

  .status {
    min-height: 39px;
    margin: 13px 0 5px;
    display: grid;
    place-items: center;
    color: #ddd;
    font-family: Arial, sans-serif;
    font-size: 1.15rem;
    font-weight: 800;
  }

  .status.win {
    color: var(--gold);
    animation: pulse .5s ease-in-out 2 alternate;
    text-shadow: 0 0 12px #ff9700;
  }

  @keyframes pulse {
    to { transform: scale(1.12); }
  }

  .credits {
    margin: 8px 0 16px;
    text-align: center;
    color: white;
    font-size: clamp(1.5rem, 5vw, 2.4rem);
  }

  #creditValue {
    display: inline-block;
    min-width: 3ch;
    color: var(--gold);
    font-variant-numeric: tabular-nums;
  }

  .controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    flex-wrap: wrap;
  }

  .bets {
    display: flex;
    gap: 7px;
  }

  .bet-btn,
  .spin-btn,
  .again-btn {
    border: 3px solid var(--gold);
    color: white;
    cursor: pointer;
    box-shadow: 0 4px 0 #542600;
    transition: transform .12s, filter .12s;
  }

  .bet-btn {
    min-width: 48px;
    padding: 8px 12px;
    border-radius: 9px;
    background: #5d0b15;
  }

  .bet-btn.active {
    color: #2a0800;
    background: var(--gold);
    transform: translateY(2px);
    box-shadow: 0 2px 0 #542600, 0 0 12px #ffc400;
  }

  button:not(:disabled):active {
    transform: translateY(3px);
  }

  button:disabled {
    cursor: not-allowed;
    filter: grayscale(1) brightness(.55);
  }

  .spin-btn {
    min-width: 155px;
    padding: 13px 25px;
    border-radius: 999px;
    background: linear-gradient(#ff354d, #a90018);
    font-size: 1.65rem;
    letter-spacing: .08em;
  }

  .paytable {
    padding: 13px;
  }

  .paytable h2 {
    margin: 2px 0 10px;
    color: var(--gold);
    text-align: center;
    letter-spacing: .1em;
  }

  .pay-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 8px;
    align-items: center;
    margin: 5px 0;
    padding: 8px;
    border: 1px solid #72202a;
    border-radius: 8px;
    font-family: Arial, sans-serif;
    background: rgba(255,255,255,.04);
  }

  .pay-row span:first-child {
    font-size: 1.13rem;
    white-space: nowrap;
  }

  .pay-row strong {
    color: var(--gold);
  }

  .pay-row.hit {
    color: #270500;
    background: var(--gold);
    animation: rowGlow .4s ease-in-out 4 alternate;
  }

  .pay-row.hit strong {
    color: #7b0012;
  }

  @keyframes rowGlow {
    to { box-shadow: 0 0 18px var(--gold); }
  }

  .game-over {
    position: fixed;
    inset: 0;
    z-index: 10;
    display: grid;
    place-items: center;
    padding: 20px;
    background: rgba(8, 0, 2, .88);
  }

  .game-over[hidden] {
    display: none;
  }

  .game-over-card {
    padding: 38px;
    border: 5px solid var(--gold);
    border-radius: 22px;
    text-align: center;
    background: #650b18;
    box-shadow: 0 0 35px #ffae00;
  }

  .game-over h2 {
    margin: 0 0 22px;
    color: var(--gold);
    font-size: clamp(3rem, 12vw, 6rem);
  }

  .again-btn {
    padding: 13px 24px;
    border-radius: 10px;
    background: #198a36;
    font-size: 1.35rem;
  }

  @media (max-width: 720px) {
    body { padding: 10px; }
    .casino { padding: 13px; }
    .layout { grid-template-columns: 1fr; }
    .paytable { order: 2; }
    .pay-row { margin: 3px 0; padding: 6px 8px; }
  }
</style>
</head>
<body>
<main class="casino">
  <h1>LUCKY 7s</h1>
  <p class="tagline">VEGAS SLOTS</p>

  <div class="layout">
    <section class="machine" aria-label="Slot machine">
      <div class="win-name" id="winName" aria-live="polite"></div>

      <div class="reels-frame" id="reelsFrame">
        <div class="reel"><div class="strip" id="reel0"></div></div>
        <div class="reel"><div class="strip" id="reel1"></div></div>
        <div class="reel"><div class="strip" id="reel2"></div></div>
      </div>

      <div class="status" id="status" aria-live="polite">PLACE YOUR BET</div>
      <div class="credits">CREDITS: <span id="creditValue">100</span></div>

      <div class="controls">
        <div class="bets" aria-label="Choose bet">
          <button class="bet-btn active" data-bet="1" type="button">BET 1</button>
          <button class="bet-btn" data-bet="5" type="button">BET 5</button>
          <button class="bet-btn" data-bet="10" type="button">BET 10</button>
        </div>
        <button class="spin-btn" id="spinButton" type="button">SPIN</button>
      </div>
    </section>

    <aside class="paytable" aria-label="Pay table">
      <h2>PAY TABLE</h2>
      <div class="pay-row" data-win="seven"><span>7️⃣ 7️⃣ 7️⃣</span><strong>100×</strong></div>
      <div class="pay-row" data-win="star"><span>⭐ ⭐ ⭐</span><strong>50×</strong></div>
      <div class="pay-row" data-win="bell"><span>🔔 🔔 🔔</span><strong>20×</strong></div>
      <div class="pay-row" data-win="grape"><span>🍇 🍇 🍇</span><strong>15×</strong></div>
      <div class="pay-row" data-win="orange"><span>🍊 🍊 🍊</span><strong>10×</strong></div>
      <div class="pay-row" data-win="lemon"><span>🍋 🍋 🍋</span><strong>5×</strong></div>
      <div class="pay-row" data-win="cherry"><span>🍒 🍒 🍒</span><strong>3×</strong></div>
      <div class="pay-row" data-win="two-cherries"><span>🍒 🍒 ANY</span><strong>2×</strong></div>
    </aside>
  </div>
</main>

<div class="game-over" id="gameOver" hidden>
  <div class="game-over-card">
    <h2>GAME OVER</h2>
    <button class="again-btn" id="playAgain" type="button">PLAY AGAIN</button>
  </div>
</div>

<script>
  const symbols = ["🍒", "🍋", "🍊", "🍇", "🔔", "⭐", "7️⃣"];

  const wins = {
    "7️⃣": { multiplier: 100, name: "JACKPOT!", key: "seven" },
    "⭐":  { multiplier: 50,  name: "THREE STARS", key: "star" },
    "🔔":  { multiplier: 20,  name: "THREE BELLS", key: "bell" },
    "🍇":  { multiplier: 15,  name: "THREE GRAPES", key: "grape" },
    "🍊":  { multiplier: 10,  name: "THREE ORANGES", key: "orange" },
    "🍋":  { multiplier: 5,   name: "THREE LEMONS", key: "lemon" },
    "🍒":  { multiplier: 3,   name: "THREE CHERRIES", key: "cherry" }
  };

  let state;
  let displayedCredits = 100;

  const reelElements = [...document.querySelectorAll(".strip")];
  const betButtons = [...document.querySelectorAll(".bet-btn")];
  const spinButton = document.getElementById("spinButton");
  const creditValue = document.getElementById("creditValue");
  const statusElement = document.getElementById("status");
  const winName = document.getElementById("winName");
  const reelsFrame = document.getElementById("reelsFrame");
  const gameOver = document.getElementById("gameOver");

  function initState() {
    state = {
      credits: 100,
      bet: 1,
      spinning: false,
      reels: ["🍒", "🍋", "7️⃣"],
      result: null
    };
    displayedCredits = 100;
    gameOver.hidden = true;
    statusElement.textContent = "PLACE YOUR BET";
    statusElement.className = "status";
    winName.textContent = "";
    clearEffects();
    renderStaticReels();
    updateUI();
  }

  function randomSymbol() {
    return symbols[Math.floor(Math.random() * symbols.length)];
  }

  function renderStaticReels() {
    state.reels.forEach((symbol, index) => {
      reelElements[index].style.transition = "none";
      reelElements[index].style.transform = "translateY(0)";
      reelElements[index].innerHTML =
        '<div class="symbol">' + symbol + "</div>";
    });
  }

  function checkWin(result) {
    if (result[0] === result[1] && result[1] === result[2]) {
      return wins[result[0]];
    }
    if (result[0] === "🍒" && result[1] === "🍒") {
      return {
        multiplier: 2,
        name: "DOUBLE CHERRIES",
        key: "two-cherries"
      };
    }
    return null;
  }

  function animateReels(result) {
    const promises = reelElements.map((strip, index) => {
      const duration = [800, 1200, 1600][index] +
        Math.floor(Math.random() * 161) - 80;
      const steps = Math.max(8, Math.round(duration / 83));
      const sequence = [state.reels[index]];

      for (let i = 1; i < steps; i++) {
        sequence.push(randomSymbol());
      }
      sequence.push(result[index]);

      strip.style.transition = "none";
      strip.style.transform = "translateY(0)";
      strip.innerHTML = sequence.map(symbol =>
        '<div class="symbol">' + symbol + "</div>"
      ).join("");

      return new Promise(resolve => {
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            strip.style.transition =
              "transform " + duration + "ms linear";
            strip.style.transform =
              "translateY(-" + ((sequence.length - 1) * 100) + "%)";
          });
        });
        setTimeout(resolve, duration);
      });
    });

    return Promise.all(promises);
  }

  function animateCredits(from, to) {
    displayedCredits = from;
    const start = performance.now();
    const duration = 400;

    function frame(now) {
      const progress = Math.min((now - start) / duration, 1);
      const eased = 1 - Math.pow(1 - progress, 3);
      displayedCredits = Math.round(from + (to - from) * eased);
      creditValue.textContent = displayedCredits;
      if (progress < 1) {
        requestAnimationFrame(frame);
      } else {
        displayedCredits = to;
        creditValue.textContent = to;
      }
    }

    requestAnimationFrame(frame);
  }

  function clearEffects() {
    reelsFrame.classList.remove("shake");
    document.querySelectorAll(".winner").forEach(element =>
      element.classList.remove("winner")
    );
    document.querySelectorAll(".pay-row.hit").forEach(row =>
      row.classList.remove("hit")
    );
  }

  function showWin(win, amount) {
    winName.textContent = win.name;
    statusElement.textContent = "+" + amount + " CREDITS";
    statusElement.className = "status win";

    const winningCount = win.key === "two-cherries" ? 2 : 3;
    reelElements.forEach((strip, index) => {
      const visibleSymbol = strip.lastElementChild;
      if (index < winningCount && visibleSymbol) {
        visibleSymbol.classList.add("winner");
      }
    });

    const row = document.querySelector(
      '.pay-row[data-win="' + win.key + '"]'
    );
    if (row) row.classList.add("hit");
  }

  function showLoss() {
    winName.textContent = "";
    statusElement.textContent = "NO WIN — TRY AGAIN";
    statusElement.className = "status";
    void reelsFrame.offsetWidth;
    reelsFrame.classList.add("shake");
  }

  async function spin() {
    if (state.spinning || state.credits < state.bet) return;

    clearEffects();
    state.spinning = true;
    state.result = null;
    winName.textContent = "";
    statusElement.textContent = "GOOD LUCK!";
    statusElement.className = "status";

    state.credits -= state.bet;
    displayedCredits = state.credits;
    creditValue.textContent = state.credits;
    updateUI();

    const result = [randomSymbol(), randomSymbol(), randomSymbol()];
    await animateReels(result);

    state.reels = result;
    state.result = checkWin(result);

    if (state.result) {
      const beforePrize = state.credits;
      const prize = state.bet * state.result.multiplier;
      state.credits += prize;
      showWin(state.result, prize);
      animateCredits(beforePrize, state.credits);
    } else {
      showLoss();
    }

    state.spinning = false;
    updateUI();

    if (state.credits === 0) {
      setTimeout(() => {
        gameOver.hidden = false;
      }, 650);
    }
  }

  function updateUI() {
    betButtons.forEach(button => {
      const value = Number(button.dataset.bet);
      button.classList.toggle("active", value === state.bet);
      button.disabled = state.spinning || value > state.credits;
    });

    spinButton.disabled =
      state.spinning || state.credits < state.bet || state.credits === 0;
    spinButton.textContent = state.spinning ? "SPINNING…" : "SPIN";

    if (!state.spinning && displayedCredits === state.credits) {
      creditValue.textContent = state.credits;
    }
  }

  betButtons.forEach(button => {
    button.addEventListener("click", () => {
      const selectedBet = Number(button.dataset.bet);
      if (!state.spinning && selectedBet <= state.credits) {
        state.bet = selectedBet;
        updateUI();
      }
    });
  });

  spinButton.addEventListener("click", spin);
  document.getElementById("playAgain").addEventListener("click", initState);

  initState();
</script>
</body>
</html>
async-fetchWrite async Python for concurrent HTTP fetching with timeout and retry
correctness 9.0quality 8.0docs 2.018.0s generation
6.3

Judge note

Concurrency, per-attempt timeouts, retry targeting, exponential backoff, shared session use, and runnable main are strong, but dict types are unparameterized, Optional is unused, and the docstring omits required parameter, units, result-key, and backoff documentation.

Raw response

```python
import asyncio
from typing import Optional

import aiohttp


async def fetch_all(
    urls: list[str],
    timeout: float = 5.0,
    retries: int = 2,
) -> list[dict]:
    """Fetch URLs concurrently with per-request timeouts and retries."""

    async def fetch_one(session: aiohttp.ClientSession, url: str) -> dict:
        attempts = max(0, retries) + 1

        for attempt in range(attempts):
            try:
                async with session.get(
                    url,
                    timeout=aiohttp.ClientTimeout(total=timeout),
                ) as response:
                    body = await response.text()

                    if response.status < 500:
                        return {
                            "url": url,
                            "status": response.status,
                            "body": body,
                            "error": None,
                        }

                    if attempt == attempts - 1:
                        return {
                            "url": url,
                            "status": response.status,
                            "body": body,
                            "error": f"HTTP {response.status}",
                        }

            except (aiohttp.ClientError, asyncio.TimeoutError) as exc:
                if attempt == attempts - 1:
                    return {
                        "url": url,
                        "status": None,
                        "body": None,
                        "error": str(exc) or type(exc).__name__,
                    }
            except Exception as exc:
                return {
                    "url": url,
                    "status": None,
                    "body": None,
                    "error": str(exc) or type(exc).__name__,
                }

            await asyncio.sleep(0.5 * (2**attempt))

        return {
            "url": url,
            "status": None,
            "body": None,
            "error": "Unknown error",
        }

    try:
        async with aiohttp.ClientSession() as session:
            tasks = [fetch_one(session, url) for url in urls]
            return await asyncio.gather(*tasks)
    except Exception as exc:
        error: str = str(exc) or type(exc).__name__
        return [
            {"url": url, "status": None, "body": None, "error": error}
            for url in urls
        ]


if __name__ == "__main__":
    async def main() -> None:
        urls: list[str] = [
            "https://example.com",
            "https://httpbin.org/status/500",
            "https://httpbin.org/get",
        ]
        results: list[dict] = await fetch_all(urls)
        for result in results:
            print(result)

    asyncio.run(main())
```

Inspect the saved JSON run → · Read the note →