17:31Testing Gemini 3.8 Flash on Real Coding Challenges (vs. other models)
Sep 2, 2026 · YouTube
17:37Tencent Hy4 Preview Tested: Is It Really Better Than Kimi K3 and GLM 5.3?
Aug 28, 2026 · YouTube
23:58Qwen 3.8 Flash Tested: UI, Coding, Physics & more - compared to Opus 5, Sol Pro, Grok
Aug 27, 2026 · YouTube
Build a memory game — also called concentration or pairs — as one self-contained HTML page.
The theme is fascinating animals. You choose the eight animals and the art style.
The player must be able to see how many moves they have made and how many pairs are left.
When a pair is matched, show that animal's fact.
When the game is won, say so clearly.
Clicking a card is the main interaction.
You may add exactly one button: New game.
Do not add:
Choose 8 animals that are genuinely fascinating — something about each one is strange, extreme, or surprising.
For each animal, provide:
The facts must be true. They will be checked.
Do not use the eight most obvious zoo animals. Lion, tiger, elephant, giraffe, panda, penguin, dolphin and zebra are all forbidden.
You do not create the artwork yourself. You write the prompts, and an image model draws them afterwards.
The art is delivered as one PNG:
art/cards.png
The file is a 2048×2048 3×3 contact sheet:
The grid layout, numbering, and background are fixed. Your job is to specify what belongs in each cell.
The path art/cards.png must appear exactly once in your HTML.
You may slice it with CSS background positioning, a canvas, clipped elements, or another technique.
Because the PNG has a black background, make sure the artwork sits properly on your card design.
The game must be playable before art/cards.png exists.
If the image cannot be loaded, each face-up card must show a readable fallback — the animal's name, a drawn shape, or a styled placeholder. Blank cards are not acceptable, because a memory game with blank faces cannot be played at all.
You may request one additional image, such as a table surface or background.
It must use the form art/<id>.png and it must be optional decoration — the page must look finished without it.
The page must support one URL mode.
#demoDeal a board where 7 of the 8 pairs are already matched and only one pair is left to find.
This exists so the win can be filmed without playing a whole game.
It must be a real board state that the normal game logic plays out — not a fake animation.
The game must be a single HTML file.
It must:
art/ filesDo not use:
localStoragesessionStorageindexedDBThe page must have no scrollbars at any window size. The board should scale to fit whatever space it is given.
Sound is optional. If you add any, generate it with the Web Audio API — no audio files.
Your response must contain exactly these four sections.
One line: what the game is called and what it looks like.
Provide exactly one fenced html code block containing the complete HTML page and nothing else.
Provide exactly one fenced json code block using this structure:
{
"game": { "name": "TURN TWO CARDS" },
"sheet": {
"style": "One paragraph, appended to every cell prompt below: medium, palette, lighting, line quality. This is what makes nine separate images look like one deck.",
"cells": [
{
"cell": 0,
"id": "axolotl",
"name": "Axolotl",
"role": "animal",
"prompt": "What to draw in this cell. One animal.",
"fact": "It regrows lost limbs, and even parts of its brain, without scarring."
},
{ "cell": 8, "id": "back", "name": "Card back", "role": "back", "prompt": "The pattern on the back of every card." }
]
},
"extras": [{ "id": "table", "aspect": "16:9", "prompt": "..." }]
}
Rules:
cells contains exactly 9 entries.cell values are 0–8 with no gaps or duplicates."role": "animal" and a fact."role": "back" and no fact.id is a single lowercase token and is unique.fact is 120 characters or fewer.extras may contain at most one entry, or be left out.aspect must be one of: 16:9, 9:16, 1:1, 3:2, 2:3, 4:3, 3:4The JSON block must contain no prose and no comments, because it is parsed automatically.
Four bullets:
art/cards.png is unavailable.Turn Two Cards: a dark bioluminescent gallery of Earth's most bizarre organisms, brought to life through responsive cards and reactive audio.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Turn Two Cards</title>
<style>
:root {
--bg: #07090e;
--panel-bg: rgba(14, 20, 32, 0.75);
--border: rgba(64, 150, 255, 0.18);
--accent: #38bdf8;
--accent-glow: rgba(56, 189, 248, 0.35);
--text: #e2e8f0;
--text-dim: #94a3b8;
--gold: #f59e0b;
--cards-url: url('art/cards.png');
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
-webkit-user-select: none;
}
html, body {
width: 100vw;
height: 100vh;
overflow: hidden;
background: radial-gradient(circle at 50% 40%, #0d1527 0%, var(--bg) 85%);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
}
#app {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 1.2vmin 2vmin;
max-width: 1200px;
}
header {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5vmin 1vmin;
flex: 0 0 auto;
}
.title-group {
display: flex;
flex-direction: column;
}
h1 {
font-size: min(3.4vmin, 22px);
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: #f8fafc;
text-shadow: 0 0 16px var(--accent-glow);
line-height: 1.1;
}
.subtitle {
font-size: min(1.8vmin, 11px);
letter-spacing: 0.22em;
color: var(--accent);
text-transform: uppercase;
opacity: 0.85;
}
.stats-bar {
display: flex;
align-items: center;
gap: min(2.5vmin, 18px);
background: var(--panel-bg);
border: 1px solid var(--border);
padding: 0.6vmin 1.8vmin;
border-radius: 999px;
backdrop-filter: blur(8px);
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
}
.stat {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1.1;
}
.stat-label {
font-size: min(1.5vmin, 9px);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--text-dim);
}
.stat-val {
font-size: min(2.8vmin, 17px);
font-weight: 700;
color: #fff;
}
.btn-new {
background: rgba(56, 189, 248, 0.12);
border: 1px solid rgba(56, 189, 248, 0.4);
color: #e0f2fe;
font-size: min(1.8vmin, 12px);
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
padding: min(1vmin, 7px) min(2.2vmin, 16px);
border-radius: 999px;
cursor: pointer;
transition: all 0.2s ease;
outline: none;
}
.btn-new:hover {
background: rgba(56, 189, 248, 0.25);
border-color: var(--accent);
box-shadow: 0 0 12px var(--accent-glow);
transform: translateY(-1px);
}
.btn-new:active {
transform: translateY(0);
}
/* Main Board Area */
main {
flex: 1 1 auto;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
min-height: 0;
position: relative;
padding: 0.5vmin;
}
#grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: min(1.5vmin, 12px);
width: min(78vh, 85vw);
height: min(78vh, 85vw);
max-height: calc(100vh - 120px);
max-width: calc(100vh - 120px);
perspective: 1000px;
}
.card-slot {
position: relative;
width: 100%;
height: 100%;
cursor: pointer;
perspective: 800px;
}
.card-inner {
position: absolute;
inset: 0;
border-radius: min(1.4vmin, 10px);
transform-style: preserve-3d;
transition: transform 0.45s cubic-bezier(0.2, 0.85, 0.32, 1.15), box-shadow 0.3s;
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
}
.card-slot.flipped .card-inner,
.card-slot.matched .card-inner {
transform: rotateY(180deg);
}
.card-slot.matched .card-inner {
box-shadow: 0 0 16px rgba(56, 189, 248, 0.45), 0 0 0 1px rgba(56, 189, 248, 0.6);
}
.card-slot:not(.flipped):not(.matched):hover .card-inner {
transform: translateY(-2px) scale(1.02);
box-shadow: 0 6px 16px rgba(56, 189, 248, 0.25);
}
.card-face {
position: absolute;
inset: 0;
border-radius: min(1.4vmin, 10px);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
overflow: hidden;
background-color: #05070c;
}
/* Card Back */
.face-back {
border: 1px solid rgba(56, 189, 248, 0.25);
background: radial-gradient(circle at 50% 50%, #0f1c30 0%, #060a12 100%);
display: flex;
align-items: center;
justify-content: center;
}
.back-art {
position: absolute;
inset: 0;
background-image: var(--cards-url);
background-size: 300% 300%;
background-position: 100% 100%; /* Cell 8 */
opacity: 0;
transition: opacity 0.3s;
}
.has-art .back-art {
opacity: 1;
}
.back-fallback {
position: absolute;
inset: 6%;
border: 1px solid rgba(56, 189, 248, 0.35);
border-radius: min(1vmin, 6px);
display: flex;
align-items: center;
justify-content: center;
background: radial-gradient(circle at center, rgba(56,189,248,0.1) 0%, transparent 70%);
}
.back-symbol {
width: 44%;
height: 44%;
border: 2px solid var(--accent);
transform: rotate(45deg);
position: relative;
box-shadow: 0 0 10px var(--accent-glow), inset 0 0 8px var(--accent-glow);
display: flex;
align-items: center;
justify-content: center;
}
.back-symbol::after {
content: '';
width: 50%;
height: 50%;
background: var(--accent);
opacity: 0.6;
transform: rotate(45deg);
}
/* Card Front */
.face-front {
transform: rotateY(180deg);
border: 1px solid rgba(56, 189, 248, 0.4);
background-color: #000000;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
position: relative;
}
.front-art {
position: absolute;
inset: 0;
background-image: var(--cards-url);
background-size: 300% 300%;
opacity: 0;
transition: opacity 0.3s;
}
.has-art .front-art {
opacity: 1;
}
.front-fallback {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 8%;
text-align: center;
background: radial-gradient(circle at 50% 40%, rgba(30, 58, 102, 0.5) 0%, #050811 80%);
}
.fallback-icon {
width: 45%;
height: 45%;
stroke: var(--accent);
stroke-width: 1.5;
fill: rgba(56, 189, 248, 0.12);
margin-bottom: 6%;
filter: drop-shadow(0 0 6px var(--accent-glow));
}
.fallback-name {
font-size: min(1.8vmin, 12px);
font-weight: 700;
letter-spacing: 0.08em;
color: #f8fafc;
text-transform: uppercase;
text-shadow: 0 1px 4px rgba(0,0,0,0.8);
}
.front-label {
position: relative;
z-index: 2;
width: 100%;
padding: 0.6vmin 0;
background: linear-gradient(to top, rgba(0,0,0,0.92) 0%, rgba(0,0,0,0.65) 75%, transparent 100%);
font-size: min(1.7vmin, 11px);
font-weight: 600;
letter-spacing: 0.08em;
text-align: center;
color: #e2e8f0;
text-shadow: 0 1px 3px rgba(0,0,0,0.9);
}
/* Slicing positions for cells 0-7 */
.cell-0 .front-art { background-position: 0% 0%; }
.cell-1 .front-art { background-position: 50% 0%; }
.cell-2 .front-art { background-position: 100% 0%; }
.cell-3 .front-art { background-position: 0% 50%; }
.cell-4 .front-art { background-position: 50% 50%; }
.cell-5 .front-art { background-position: 100% 50%; }
.cell-6 .front-art { background-position: 0% 100%; }
.cell-7 .front-art { background-position: 50% 100%; }
/* Bottom Banner / Fact Area */
footer {
width: 100%;
min-height: min(7.5vmin, 46px);
max-height: min(10vmin, 60px);
display: flex;
justify-content: center;
align-items: center;
padding: 0.4vmin 1vmin;
flex: 0 0 auto;
}
.banner {
width: 100%;
max-width: 720px;
height: 100%;
background: var(--panel-bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.6vmin 1.8vmin;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
backdrop-filter: blur(8px);
box-shadow: 0 4px 16px rgba(0,0,0,0.35);
transition: all 0.3s ease;
}
.banner.highlight {
border-color: var(--accent);
box-shadow: 0 0 18px var(--accent-glow);
}
.banner-text {
font-size: min(2.1vmin, 13px);
color: var(--text);
line-height: 1.3;
}
.banner-text .animal-tag {
font-weight: 700;
color: var(--accent);
margin-right: 6px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Win Modal Overlay */
#win-modal {
position: absolute;
inset: 0;
background: rgba(5, 8, 15, 0.82);
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 50;
opacity: 0;
pointer-events: none;
transition: opacity 0.4s ease;
padding: 3vmin;
}
#win-modal.active {
opacity: 1;
pointer-events: auto;
}
.win-card {
background: linear-gradient(145deg, rgba(16, 26, 44, 0.95), rgba(7, 12, 22, 0.98));
border: 1px solid rgba(56, 189, 248, 0.4);
box-shadow: 0 0 40px rgba(56, 189, 248, 0.25), inset 0 0 20px rgba(56, 189, 248, 0.1);
padding: min(4vmin, 28px) min(6vmin, 44px);
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
max-width: 90vw;
animation: popIn 0.4s cubic-bezier(0.18, 0.89, 0.32, 1.25);
}
@keyframes popIn {
0% { transform: scale(0.85); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
.win-badge {
font-size: min(2.2vmin, 13px);
font-weight: 700;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(--gold);
margin-bottom: 0.8vmin;
}
.win-title {
font-size: min(5.5vmin, 36px);
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
color: #ffffff;
text-shadow: 0 0 20px var(--accent-glow);
margin-bottom: 1.5vmin;
}
.win-stats {
font-size: min(2.6vmin, 16px);
color: var(--text-dim);
margin-bottom: 3vmin;
}
.win-stats strong {
color: var(--accent);
}
</style>
</head>
<body>
<div id="app">
<header>
<div class="title-group">
<h1>Turn Two Cards</h1>
<span class="subtitle">Fascinating Creatures</span>
</div>
<div class="stats-bar">
<div class="stat">
<span class="stat-label">Moves</span>
<span class="stat-val" id="move-count">0</span>
</div>
<div class="stat">
<span class="stat-label">Remaining</span>
<span class="stat-val" id="pairs-left">8</span>
</div>
</div>
<button class="btn-new" id="btn-new">New Game</button>
</header>
<main>
<div id="grid"></div>
<div id="win-modal">
<div class="win-card">
<span class="win-badge">Exploration Complete</span>
<h2 class="win-title">All Pairs Matched!</h2>
<p class="win-stats" id="win-summary">Completed in <strong>0</strong> moves.</p>
<button class="btn-new" id="btn-modal-new">Play Again</button>
</div>
</div>
</main>
<footer>
<div class="banner" id="fact-banner">
<span class="banner-text" id="banner-text">Turn over two matching cards to reveal their secret.</span>
</div>
</footer>
</div>
<script>
(() => {
const ANIMALS = [
{
id: "axolotl",
name: "Axolotl",
fact: "It can regenerate lost limbs, heart tissue, spinal cord, and parts of its brain without any scarring.",
icon: '<path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm-3 8a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm6 0a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm-3 7c-2.3 0-4-1.5-4-1.5s1.7-1 4-1 4 1 4 1-1.7 1.5-4 1.5z"/>'
},
{
id: "mantis_shrimp",
name: "Mantis Shrimp",
fact: "Its club strike accelerates as fast as a .22 caliber bullet, vaporizing water into light and heat.",
icon: '<path d="M19 4L5 18M5 4l14 14M9 12h6M12 9v6"/>'
},
{
id: "tardigrade",
name: "Tardigrade",
fact: "It can survive outer space, boiling water, absolute zero, and decades without food or moisture.",
icon: '<ellipse cx="12" cy="12" rx="7" ry="9"/><circle cx="12" cy="7" r="2"/><path d="M5 10c-2 1-3 3-3 5M19 10c2 1 3 3 3 5"/>'
},
{
id: "mimic_octopus",
name: "Mimic Octopus",
fact: "It shifts color, skin texture, and posture to impersonate sea snakes, lionfish, and flatfish.",
icon: '<circle cx="12" cy="8" r="5"/><path d="M7 12c-2 3-1 7-4 8M17 12c2 3 1 7 4 8M10 13c-1 3-1 6-1 8M14 13c1 3 1 6 1 8"/>'
},
{
id: "immortal_jellyfish",
name: "Immortal Jellyfish",
fact: "When injured or starving, it reverts its adult cells back into a juvenile polyp, avoiding death.",
icon: '<path d="M12 3C7 3 4 7 4 12c0 1 2 2 4 1s4 1 4 1 2-1 4-1 4 0 4-1c0-5-3-9-8-9z"/><path d="M7 14v6M10 14v7M14 14v7M17 14v6"/>'
},
{
id: "lyrebird",
name: "Lyrebird",
fact: "Its complex syrinx can flawlessly mimic chainsaws, camera shutters, car alarms, and other birds.",
icon: '<path d="M6 19c2-5 3-12 1-15 4 2 5 7 5 15M18 19c-2-5-3-12-1-15-4 2-5 7-5 15M12 9v10"/>'
},
{
id: "pangolin",
name: "Pangolin",
fact: "Its overlapping keratin scales make up 20% of its weight, rolling into an impenetrable sphere.",
icon: '<path d="M4 16c2-8 8-12 14-8s2 12-4 12c-4 0-8-1-10-4z"/><circle cx="15" cy="10" r="1"/>'
},
{
id: "platypus",
name: "Platypus",
fact: "It hunts with electroreception in its bill, lays eggs, glows under UV, and males have venomous spurs.",
icon: '<ellipse cx="10" cy="12" rx="6" ry="4"/><ellipse cx="18" cy="12" rx="4" ry="2"/><path d="M4 12c-2 0-3 2-3 4s2 3 3 1"/>'
}
];
// Web Audio Synthesizer
let audioCtx = null;
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
}
function playSound(type) {
if (!audioCtx) return;
try {
const now = audioCtx.currentTime;
if (type === 'flip') {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(320, now);
osc.frequency.exponentialRampToValueAtTime(600, now + 0.08);
gain.gain.setValueAtTime(0.12, now);
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.08);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now);
osc.stop(now + 0.08);
} else if (type === 'match') {
[523.25, 659.25, 783.99, 1046.50].forEach((freq, idx) => {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(freq, now + idx * 0.06);
gain.gain.setValueAtTime(0, now + idx * 0.06);
gain.gain.linearRampToValueAtTime(0.18, now + idx * 0.06 + 0.02);
gain.gain.exponentialRampToValueAtTime(0.001, now + idx * 0.06 + 0.28);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now + idx * 0.06);
osc.stop(now + idx * 0.06 + 0.3);
});
} else if (type === 'mismatch') {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(240, now);
osc.frequency.exponentialRampToValueAtTime(140, now + 0.22);
gain.gain.setValueAtTime(0.15, now);
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.22);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now);
osc.stop(now + 0.22);
} else if (type === 'win') {
const notes = [440, 554.37, 659.25, 880, 1108.73];
notes.forEach((freq, idx) => {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(freq, now + idx * 0.1);
gain.gain.setValueAtTime(0.2, now + idx * 0.1);
gain.gain.exponentialRampToValueAtTime(0.001, now + idx * 0.1 + 0.6);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(now + idx * 0.1);
osc.stop(now + idx * 0.1 + 0.65);
});
}
} catch (e) {}
}
// Preload test image for fallback switching
const testImg = new Image();
testImg.src = 'art/cards.png';
testImg.onload = () => {
document.body.classList.add('has-art');
};
// State
let deck = [];
let flippedIndices = [];
let matchedIndices = new Set();
let moves = 0;
let isLocked = false;
const gridEl = document.getElementById('grid');
const moveCountEl = document.getElementById('move-count');
const pairsLeftEl = document.getElementById('pairs-left');
const bannerEl = document.getElementById('fact-banner');
const bannerTextEl = document.getElementById('banner-text');
const winModal = document.getElementById('win-modal');
const winSummaryEl = document.getElementById('win-summary');
function setBanner(animalName, factText, isHighlighted = false) {
if (animalName && factText) {
bannerTextEl.innerHTML = `<span class="animal-tag">${animalName}:</span> ${factText}`;
} else {
bannerTextEl.textContent = 'Turn over two matching cards to reveal their secret.';
}
if (isHighlighted) {
bannerEl.classList.add('highlight');
} else {
bannerEl.classList.remove('highlight');
}
}
function updateStats() {
moveCountEl.textContent = moves;
pairsLeftEl.textContent = 8 - (matchedIndices.size / 2);
}
function createBoard() {
gridEl.innerHTML = '';
deck.forEach((animalIndex, slotIndex) => {
const animal = ANIMALS[animalIndex];
const slot = document.createElement('div');
slot.className = `card-slot cell-${animalIndex}`;
slot.dataset.index = slotIndex;
slot.innerHTML = `
<div class="card-inner">
<div class="card-face face-back">
<div class="back-fallback">
<div class="back-symbol"></div>
</div>
<div class="back-art"></div>
</div>
<div class="card-face face-front">
<div class="front-fallback">
<svg class="fallback-icon" viewBox="0 0 24 24">${animal.icon}</svg>
<div class="fallback-name">${animal.name}</div>
</div>
<div class="front-art"></div>
<div class="front-label">${animal.name}</div>
</div>
</div>
`;
slot.addEventListener('click', () => handleCardClick(slotIndex));
gridEl.appendChild(slot);
});
}
function initGame() {
const isDemo = window.location.hash === '#demo';
moves = 0;
flippedIndices = [];
matchedIndices.clear();
isLocked = false;
winModal.classList.remove('active');
setBanner(null, null, false);
if (isDemo) {
// 7 pairs matched, 1 pair hidden
// Sequential pairs: animals 0..7
const pairs = [0,0, 1,1, 2,2, 3,3, 4,4, 5,5, 6,6, 7,7];
deck = pairs;
createBoard();
// Match first 14 cards (cells for animals 0 to 6)
for (let i = 0; i < 14; i++) {
matchedIndices.add(i);
const el = gridEl.children[i];
el.classList.add('matched');
}
const lastAnimal = ANIMALS[6];
setBanner(lastAnimal.name, lastAnimal.fact, true);
} else {
// Shuffled deck
const pairs = [];
for (let i = 0; i < 8; i++) {
pairs.push(i, i);
}
// Fisher-Yates shuffle
for (let i = pairs.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[pairs[i], pairs[j]] = [pairs[j], pairs[i]];
}
deck = pairs;
createBoard();
}
updateStats();
}
function handleCardClick(index) {
initAudio();
if (isLocked) return;
if (matchedIndices.has(index)) return;
if (flippedIndices.includes(index)) return;
if (flippedIndices.length >= 2) return;
const slotEl = gridEl.children[index];
slotEl.classList.add('flipped');
flippedIndices.push(index);
playSound('flip');
if (flippedIndices.length === 2) {
moves++;
updateStats();
const [idx1, idx2] = flippedIndices;
const animal1 = deck[idx1];
const animal2 = deck[idx2];
if (animal1 === animal2) {
// Matched
matchedIndices.add(idx1);
matchedIndices.add(idx2);
flippedIndices = [];
const el1 = gridEl.children[idx1];
const el2 = gridEl.children[idx2];
el1.classList.add('matched');
el2.classList.add('matched');
const animal = ANIMALS[animal1];
setBanner(animal.name, animal.fact, true);
playSound('match');
updateStats();
if (matchedIndices.size === 16) {
setTimeout(() => {
playSound('win');
winSummaryEl.innerHTML = `Discovered all 8 species in <strong>${moves}</strong> moves.`;
winModal.classList.add('active');
}, 600);
}
} else {
// Mismatch
isLocked = true;
setTimeout(() => {
playSound('mismatch');
}, 350);
setTimeout(() => {
const el1 = gridEl.children[idx1];
const el2 = gridEl.children[idx2];
if (el1) el1.classList.remove('flipped');
if (el2) el2.classList.remove('flipped');
flippedIndices = [];
isLocked = false;
}, 900);
}
}
}
document.getElementById('btn-new').addEventListener('click', () => {
initAudio();
initGame();
});
document.getElementById('btn-modal-new').addEventListener('click', () => {
initAudio();
initGame();
});
window.addEventListener('hashchange', () => {
initGame();
});
initGame();
})();
</script>
</body>
</html>
{
"game": { "name": "TURN TWO CARDS" },
"sheet": {
"style": "Hyper-detailed scientific illustration with luminous neon bio-luminescence against a pure pitch-black background #000000. Clean silhouettes, vivid crystalline cyan, teal, electric purple, and amber accents, intricate textures, rim lighting, centered square framing, high contrast modern natural history plate aesthetic.",
"cells": [
{
"cell": 0,
"id": "axolotl",
"name": "Axolotl",
"role": "animal",
"prompt": "An ethereal pink and translucent white axolotl floating in dark water, showcasing glowing feathery external gills with vibrant magenta frills, black eyes, and delicate regenerative limbs.",
"fact": "It can regenerate lost limbs, heart tissue, spinal cord, and parts of its brain without any scarring."
},
{
"cell": 1,
"id": "mantis_shrimp",
"name": "Mantis Shrimp",
"role": "animal",
"prompt": "A peacock mantis shrimp displaying intense iridescent rainbow armor of turquoise, scarlet, and gold, with coiled predatory dactyl clubs and complex multi-faceted optical stalk eyes.",
"fact": "Its club strike accelerates as fast as a .22 caliber bullet, vaporizing water into light and heat."
},
{
"cell": 2,
"id": "tardigrade",
"name": "Tardigrade",
"role": "animal",
"prompt": "A microscopic water bear tardigrade with eight stout clawed legs, translucent barrel-shaped body, and subtle glowing cyan microscopic rim illumination.",
"fact": "It can survive outer space, boiling water, absolute zero, and decades without food or moisture."
},
{
"cell": 3,
"id": "mimic_octopus",
"name": "Mimic Octopus",
"role": "animal",
"prompt": "A dynamic mimic octopus with swirling banded tentacles in contrasting dark brown, white, and electric yellow stripes, mid-transformation across a black void.",
"fact": "It shifts color, skin texture, and posture to impersonate sea snakes, lionfish, and flatfish."
},
{
"cell": 4,
"id": "immortal_jellyfish",
"name": "Immortal Jellyfish",
"role": "animal",
"prompt": "Turritopsis dohrnii hydrozoan jellyfish featuring a luminous scarlet bell-shaped stomach core surrounded by delicate, transparent trailing bioluminescent tendrils.",
"fact": "When injured or starving, it reverts its adult cells back into a juvenile polyp, avoiding death."
},
{
"cell": 5,
"id": "lyrebird",
"name": "Lyrebird",
"role": "animal",
"prompt": "A magnificent male Superb Lyrebird seen in profile displaying its vast fan of intricate lyre-shaped tail feathers and silvery gossamer plumage.",
"fact": "Its complex syrinx can flawlessly mimic chainsaws, camera shutters, car alarms, and other birds."
},
{
"cell": 6,
"id": "pangolin",
"name": "Pangolin",
"role": "animal",
"prompt": "A pangolin curled into a protective spiral armor ball, showing overlapping polished amber and bronze keratin scales with rim lighting.",
"fact": "Its overlapping keratin scales make up 20% of its weight, rolling into an impenetrable sphere."
},
{
"cell": 7,
"id": "platypus",
"name": "Platypus",
"role": "animal",
"prompt": "A duck-billed platypus swimming gracefully with sleek waterproof brown pelt, paddle tail, broad sensory leathery bill, and subtle cyan UV bio-fluorescence.",
"fact": "It hunts with electroreception in its bill, lays eggs, glows under UV, and males have venomous spurs."
},
{
"cell": 8,
"id": "back",
"name": "Card back",
"role": "back",
"prompt": "An intricate symmetrical occult geometric seal featuring a glowing cyan astrolabe compass and bioluminescent nautilus spiral framed by fine ornate silver linework on pitch black."
}
]
}
}