Skip to content

Commit cff85a4

Browse files
committed
feat: add Battlesnake Easter Egg with Pixel Snake mini-game
1 parent 10f4777 commit cff85a4

File tree

3 files changed

+408
-0
lines changed

3 files changed

+408
-0
lines changed

app/battlesnake/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
CardTitle,
99
} from '@/components/ui/card'
1010
import { Code2, Brain, Zap, Shield } from 'lucide-react'
11+
import BattlesnakeEasterEgg from '@/components/battlesnake-easter-egg'
1112

1213
export const metadata: Metadata = {
1314
title: 'Battlesnake Stats - Sayfullah Eid',
@@ -18,6 +19,8 @@ export const metadata: Metadata = {
1819
export default function BattlesnakePage() {
1920
return (
2021
<div className="flex flex-col items-center space-y-8">
22+
{/* Hidden Easter Egg listener: Konami code opens a dialog with a Pixel Snake mini-game */}
23+
<BattlesnakeEasterEgg />
2124
{/* Header Section */}
2225
<div className="max-w-3xl space-y-4 text-center">
2326
<h1 className="text-4xl font-bold tracking-tighter sm:text-5xl md:text-6xl">
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
'use client'
2+
3+
import React, { useEffect, useMemo, useState } from 'react'
4+
import {
5+
Dialog,
6+
DialogContent,
7+
DialogDescription,
8+
DialogHeader,
9+
DialogTitle,
10+
} from '@/components/ui/dialog'
11+
import PixelSnakeGame from '@/components/pixel-snake-game'
12+
13+
// A small wrapper that listens for a secret sequence and opens a dialog with the snake game.
14+
// New code: s n a k e Enter
15+
const SECRET = ['s', 'n', 'a', 'k', 'e', 'Enter']
16+
17+
export default function BattlesnakeEasterEgg() {
18+
const [open, setOpen] = useState(false)
19+
const [ix, setIx] = useState(0)
20+
21+
const instructions = useMemo(
22+
() => 'Hint: Type S N A K E then Enter anywhere on this page.',
23+
[],
24+
)
25+
26+
useEffect(() => {
27+
const normalize = (k: string) => (k.length === 1 ? k.toLowerCase() : k)
28+
const onKey = (e: KeyboardEvent) => {
29+
const key = normalize(e.key)
30+
const expected = normalize(SECRET[ix])
31+
if (key === expected) {
32+
const next = ix + 1
33+
if (next >= SECRET.length) {
34+
// Prevent the Enter key (final step) from interacting with the dialog immediately.
35+
e.preventDefault()
36+
e.stopPropagation()
37+
// Open on next frame to let the key event fully resolve first.
38+
requestAnimationFrame(() => setOpen(true))
39+
setIx(0)
40+
return
41+
}
42+
setIx(next)
43+
} else {
44+
// If mismatch but current key could restart the sequence
45+
if (key === normalize(SECRET[0])) setIx(1)
46+
else setIx(0)
47+
}
48+
}
49+
const opts: AddEventListenerOptions = { capture: true }
50+
window.addEventListener('keydown', onKey, opts)
51+
return () => window.removeEventListener('keydown', onKey, opts)
52+
}, [ix])
53+
54+
// One-time console hints to tease the easter egg (non-intrusive, styled).
55+
useEffect(() => {
56+
try {
57+
const title = '🕹 Hidden Fun'
58+
const sTitle =
59+
'background:#111;color:#B19EEF;padding:2px 8px;border-radius:6px;font-weight:700;'
60+
const sLine = 'color:#9ca3af'
61+
const sHot =
62+
'background:#1f2937;color:#e5e7eb;padding:1px 6px;border-radius:4px'
63+
// Grouped to keep console tidy; collapsed so it doesn’t spam.
64+
// Shown in both dev and prod—this is intentional for the easter egg.
65+
// Feel free to gate via NODE_ENV if you want it dev-only.
66+
console.groupCollapsed('%c' + title, sTitle)
67+
console.log('%cThere’s a tiny easter egg on this page.', sLine)
68+
console.log('Hint: type: %cS N A K E Enter', sHot)
69+
console.log('%cPsst: share the fun, not the secret 😉', sLine)
70+
console.groupEnd()
71+
} catch {
72+
// Ignore if console is unavailable
73+
}
74+
}, [])
75+
76+
return (
77+
<>
78+
{/* Accessible-only hint for screen readers; visually hidden to keep it an easter egg */}
79+
<p className="sr-only" aria-live="polite">
80+
{instructions}
81+
</p>
82+
<Dialog open={open} onOpenChange={setOpen}>
83+
<DialogContent
84+
className="max-w-xl p-4 sm:p-6"
85+
onOpenAutoFocus={(e) => e.preventDefault()}
86+
>
87+
<DialogHeader>
88+
<DialogTitle>Pixel Snake</DialogTitle>
89+
<DialogDescription>
90+
Use arrow keys or WASD to move. Press Space to pause.
91+
</DialogDescription>
92+
</DialogHeader>
93+
<div className="mt-2">
94+
<PixelSnakeGame className="relative mx-auto aspect-square w-full max-w-[520px]" />
95+
</div>
96+
</DialogContent>
97+
</Dialog>
98+
</>
99+
)
100+
}

0 commit comments

Comments
 (0)