0xb8952959…ccf4sent to0xb8952959…ccf4·#23,950,947·view on Etherscan
<!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>