memoscan
← all memos

Memo 0xb625abee…235b53 on Ethereum

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Matrix ASCII Snakes</title> <style> :root { --bg-color: #0a0a0a; } * { margin: 0; padding: 0; box-sizing: border-box; } body { background: var(--bg-color); overflow: hidden; font-family: 'Courier New', monospace; } #puzzle-container { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; } .matrix-canvas { display: block; width: 100%; height: 100%; } </style> </head> <body> <div id="puzzle-container" data-x="0" data-y="0" data-width="1" data-height="1" data-grid-size="60" data-speed-min="0.015" data-speed-max="0.09" data-length-min="5" data-length-max="18" data-spawn-rate="0.08" data-max-snakes="60" data-turn-chance="0.15" data-burst-interval-min="400" data-burst-interval-max="2000" data-burst-amount-min="2" data-burst-amount-max="5" data-decay-rate="0.02"> <canvas class="matrix-canvas"></canvas> </div> <script> (function() { // Full ASCII symbol pool const ALL_SYMBOLS = [ '░', '▒', '▓', '█', '▄', '▀', '■', '□', '▪', '▫', '◊', '○', '●', '◐', '◑', '◒', '◓', '◔', '◕', '◖', '◗', '☆', '★', '✦', '✧', '✶', '✷', '✸', '✹', '✺', '╱', '╲', '╳', '┃', '━', '┏', '┓', '┗', '┛', '╋', '△', '▲', '▽', '▼', '◁', '◀', '▷', '▶', '◇', '◆', '⌂', '⌐', '¬', '½', '¼', '¾', '±', '≡', '≈', '≠', 'ア', 'イ', 'ウ', 'エ', 'オ', 'カ', 'キ', 'ク', 'ケ', 'コ', '∞', '∑', '∏', '√', '∫', '∂', '∆', '∇', '∈', '∉', '⟨', '⟩', '⟪', '⟫', '⌘', '⌥', '⌦', '⌫', '⏎', '⏏', '♠', '♣', '♥', '♦', '♪', '♫', '♬', '♭', '♮', '♯' ]; // Color palettes const COLORS = [ { name: 'white', head: '#ffffff', body: [200, 200, 200], glow: '#ffffff' }, { name: 'pink', head: '#ffccee', body: [255, 120, 180], glow: '#ff69b4' }, { name: 'grey', head: '#dddddd', body: [140, 140, 140], glow: '#aaaaaa' }, { name: 'blue', head: '#aaddff', body: [80, 160, 255], glow: '#4488ff' }, { name: 'green', head: '#aaffaa', body: [80, 220, 80], glow: '#00ff41' } ]; // Randomly select symbols for this instance const symbolCount = Math.floor(Math.random() * ALL_SYMBOLS.length) + 1; const shuffled = [...ALL_SYMBOLS].sort(() => Math.random() - 0.5); const SYMBOLS = shuffled.slice(0, symbolCount); console.log(`Using ${SYMBOLS.length} symbols:`, SYMBOLS.join(' ')); const container = document.getElementById('puzzle-container'); const canvas = container.querySelector('.matrix-canvas'); const ctx = canvas.getContext('2d'); // Read config from data attributes const config = { gridSize: parseInt(container.dataset.gridSize), speedMin: parseFloat(container.dataset.speedMin), speedMax: parseFloat(container.dataset.speedMax), lengthMin: parseInt(container.dataset.lengthMin), lengthMax: parseInt(container.dataset.lengthMax), spawnRate: parseFloat(container.dataset.spawnRate), maxSnakes: parseInt(container.dataset.maxSnakes), turnChance: parseFloat(container.dataset.turnChance), burstIntervalMin: parseInt(container.dataset.burstIntervalMin), burstIntervalMax: parseInt(container.dataset.burstIntervalMax), burstAmountMin: parseInt(container.dataset.burstAmountMin), burstAmountMax: parseInt(container.dataset.burstAmountMax), decayRate: parseFloat(container.dataset.decayRate) }; // Directions: 0=right, 1=down, 2=left, 3=up const DIR = { RIGHT: 0, DOWN: 1, LEFT: 2, UP: 3 }; const dirVectors = [ { dx: 1, dy: 0 }, // right { dx: 0, dy: 1 }, // down { dx: -1, dy: 0 }, // left { dx: 0, dy: -1 } // up ]; let snakes = []; let grid = []; let initialized = false; function randomSymbol() { return SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)]; } function randomColor() { return COLORS[Math.floor(Math.random() * COLORS.length)]; } function initGrid() { grid = []; for (let x = 0; x < config.gridSize; x++) { grid[x] = []; for (let y = 0; y < config.gridSize; y++) { grid[x][y] = false; } } } function isValidCell(x, y) { return x >= 0 && x < config.gridSize && y >= 0 && y < config.gridSize; } function isCellFree(x, y) { if (!isValidCell(x, y)) return false; return !grid[x][y]; } function occupyCell(x, y) { if (isValidCell(x, y)) grid[x][y] = true; } function freeCell(x, y) { if (isValidCell(x, y)) grid[x][y] = false; } function createSnake() { const length = config.lengthMin + Math.floor(Math.random() * (config.lengthMax - config.lengthMin + 1)); // Pick random edge and direction const edge = Math.floor(Math.random() * 4); let startX, startY, direction; switch (edge) { case 0: // Top edge, going down startX = Math.floor(Math.random() * config.gridSize); startY = 0; direction = DIR.DOWN; break; case 1: // Right edge, going left startX = config.gridSize - 1; startY = Math.floor(Math.random() * config.gridSize); direction = DIR.LEFT; break; case 2: // Bottom edge, going up startX = Math.floor(Math.random() * config.gridSize); startY = config.gridSize - 1; direction = DIR.UP; break; case 3: // Left edge, going right startX = 0; startY = Math.floor(Math.random() * config.gridSize); direction = DIR.RIGHT; break; } // Check if starting cell is free if (!isCellFree(startX, startY)) return null; const segments = [{ x: startX, y: startY, char: randomSymbol(), opacity: 1 }]; occupyCell(startX, startY); // Random speed from min to 6x min const speed = config.speedMin + Math.random() * (config.speedMin * 6 - config.speedMin); return { segments: segments, targetLength: length, direction: direction, speed: speed, moveProgress: 0, nextBurstTime: performance.now() + config.burstIntervalMin + Math.random() * (config.burstIntervalMax - config.burstIntervalMin), alive: true, decaying: false, color: randomColor() }; } function getTurnDirections(currentDir) { if (currentDir === DIR.RIGHT || currentDir === DIR.LEFT) { return [DIR.UP, DIR.DOWN]; } else { return [DIR.LEFT, DIR.RIGHT]; } } function canMove(snake) { const head = snake.segments[0]; const vec = dirVectors[snake.direction]; // Check straight const straightX = head.x + vec.dx; const straightY = head.y + vec.dy; if (isCellFree(straightX, straightY)) return true; // Check turns const turns = getTurnDirections(snake.direction); for (const newDir of turns) { const turnVec = dirVectors[newDir]; const turnX = head.x + turnVec.dx; const turnY = head.y + turnVec.dy; if (isCellFree(turnX, turnY)) return true; } return false; } function moveSnake(snake) { if (snake.decaying) return; const head = snake.segments[0]; const vec = dirVectors[snake.direction]; let newX = head.x + vec.dx; let newY = head.y + vec.dy; // Check if we should turn or must turn let turned = false; const shouldTurn = Math.random() < config.turnChance; const mustTurn = !isCellFree(newX, newY); if (shouldTurn || mustTurn) { const turns = getTurnDirections(snake.direction); const shuffledTurns = Math.random() < 0.5 ? turns : [turns[1], turns[0]]; for (const newDir of shuffledTurns) { const turnVec = dirVectors[newDir]; const turnX = head.x + turnVec.dx; const turnY = head.y + turnVec.dy; if (isCellFree(turnX, turnY)) { snake.direction = newDir; newX = turnX; newY = turnY; turned = true; break; } } // If we had to turn but couldn't, try straight if (mustTurn && !turned) { // Can't go anywhere - start decaying snake.decaying = true; return; } } // Final validation if (!isCellFree(newX, newY)) { snake.decaying = true; return; } // Add new head snake.segments.unshift({ x: newX, y: newY, char: randomSymbol(), opacity: 1 }); occupyCell(newX, newY); // Remove tail if at target length if (snake.segments.length > snake.targetLength) { const tail = snake.segments.pop(); freeCell(tail.x, tail.y); } } function decaySnake(snake) { // Fade all segments let allFaded = true; for (const seg of snake.segments) { seg.opacity -= config.decayRate; if (seg.opacity > 0) allFaded = false; } // Remove fully faded tail segments while (snake.segments.length > 0 && snake.segments[snake.segments.length - 1].opacity <= 0) { const tail = snake.segments.pop(); freeCell(tail.x, tail.y); } // Mark as dead when fully decayed if (snake.segments.length === 0 || allFaded) { snake.alive = false; } } function init() { initGrid(); snakes = []; // Spawn initial snakes const initialCount = Math.floor(config.maxSnakes * 0.4); for (let i = 0; i < initialCount; i++) { const snake = createSnake(); if (snake) snakes.push(snake); } initialized = true; } function resize() { const dpr = window.devicePixelRatio || 1; const width = window.innerWidth; const height = window.innerHeight; canvas.width = width * dpr; canvas.height = height * dpr; canvas.style.width = width + 'px'; canvas.style.height = height + 'px'; ctx.scale(dpr, dpr); if (!initialized) { init(); } } function trySpawnSnake() { if (snakes.length < config.maxSnakes && Math.random() < config.spawnRate) { const snake = createSnake(); if (snake) snakes.push(snake); } } function draw() { const width = window.innerWidth; const height = window.innerHeight; const now = performance.now(); const cellWidth = width / config.gridSize; const cellHeight = height / config.gridSize; const fontSize = Math.min(cellWidth, cellHeight) * 0.85; // Clear canvas ctx.fillStyle = '#0a0a0a'; ctx.fillRect(0, 0, width, height); ctx.font = `${fontSize}px 'Courier New', monospace`; ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; // Process snakes for (let i = snakes.length - 1; i >= 0; i--) { const snake = snakes[i]; if (snake.decaying) { // Handle decay decaySnake(snake); } else { // Update movement snake.moveProgress += snake.speed; while (snake.moveProgress >= 1 && snake.alive && !snake.decaying) { snake.moveProgress -= 1; moveSnake(snake); } // Burst character change if (now > snake.nextBurstTime && !snake.decaying) { const burstAmount = config.burstAmountMin + Math.floor(Math.random() * (config.burstAmountMax - config.burstAmountMin + 1)); for (let b = 0; b < burstAmount && b < snake.segments.length; b++) { const idx = Math.floor(Math.random() * snake.segments.length); snake.segments[idx].char = randomSymbol(); } snake.nextBurstTime = now + config.burstIntervalMin + Math.random() * (config.burstIntervalMax - config.burstIntervalMin); } } // Remove dead snakes if (!snake.alive) { for (const seg of snake.segments) { freeCell(seg.x, seg.y); } snakes.splice(i, 1); continue; } const color = snake.color; // Draw segments for (let j = 0; j < snake.segments.length; j++) { const seg = snake.segments[j]; const screenX = (seg.x + 0.5) * cellWidth; const screenY = (seg.y + 0.5) * cellHeight; // Gradient from bright at head to dimmer at tail const positionBrightness = 1 - (j / snake.segments.length) * 0.7; const opacity = seg.opacity; if (j === 0 && !snake.decaying) { ctx.fillStyle = color.head; ctx.globalAlpha = opacity; ctx.shadowColor = color.glow; ctx.shadowBlur = 8; } else { const r = Math.floor(color.body[0] * positionBrightness); const g = Math.floor(color.body[1] * positionBrightness); const b = Math.floor(color.body[2] * positionBrightness); ctx.fillStyle = `rgb(${r}, ${g}, ${b})`; ctx.globalAlpha = opacity; ctx.shadowBlur = 0; } ctx.fillText(seg.char, screenX, screenY); } ctx.shadowBlur = 0; ctx.globalAlpha = 1; } // Try to spawn new snakes trySpawnSnake(); requestAnimationFrame(draw); } // Initial setup resize(); window.addEventListener('resize', resize); requestAnimationFrame(draw); })(); </script> </body> </html>