Przejdź do głównej zawartości

33. Gra Snake

Klasyczna gra Snake — wąż rośnie zjadając jedzenie, koniec gry gdy uderzy w ścianę lub siebie. Na planszy grid, sterowanie strzałkami.

const GRID_SIZE = 20;
const INITIAL_SNAKE = [{ x: 10, y: 10 }];
const INITIAL_DIRECTION = { x: 1, y: 0 };
function useSnakeGame() {
const [snake, setSnake] = useState(INITIAL_SNAKE);
const [food, setFood] = useState(generateFood(INITIAL_SNAKE));
const [direction, setDirection] = useState(INITIAL_DIRECTION);
const [gameOver, setGameOver] = useState(false);
const [score, setScore] = useState(0);
const directionRef = useRef(direction); // ref dla aktualnego kierunku w setInterval
useEffect(() => { directionRef.current = direction; }, [direction]);
useEffect(() => {
if (gameOver) return;
const interval = setInterval(() => {
setSnake((prev) => {
const newHead = {
x: prev[0].x + directionRef.current.x,
y: prev[0].y + directionRef.current.y,
};
// Kolizja ze ścianą
if (newHead.x < 0 || newHead.x >= GRID_SIZE || newHead.y < 0 || newHead.y >= GRID_SIZE) {
setGameOver(true);
return prev;
}
// Kolizja z samym sobą
if (prev.some((seg) => seg.x === newHead.x && seg.y === newHead.y)) {
setGameOver(true);
return prev;
}
const ateFood = newHead.x === food.x && newHead.y === food.y;
if (ateFood) {
setScore((s) => s + 10);
setFood(generateFood([newHead, ...prev]));
return [newHead, ...prev];
}
return [newHead, ...prev.slice(0, -1)];
});
}, 150);
return () => clearInterval(interval);
}, [gameOver, food]);
function handleKey(e) {
const keys = {
ArrowUp: { x: 0, y: -1 },
ArrowDown: { x: 0, y: 1 },
ArrowLeft: { x: -1, y: 0 },
ArrowRight: { x: 1, y: 0 },
};
if (keys[e.key]) {
e.preventDefault();
setDirection(keys[e.key]);
}
}
return { snake, food, score, gameOver, handleKey };
}
function generateFood(snake) {
let pos;
do {
pos = { x: Math.floor(Math.random() * GRID_SIZE), y: Math.floor(Math.random() * GRID_SIZE) };
} while (snake.some((s) => s.x === pos.x && s.y === pos.y));
return pos;
}
  • Plansza grid 20×20
  • Wąż poruszający się, rośnie po zjedzeniu jedzenia
  • Kolizja ze ścianą i sobą
  • Wynik (ile jedzenia zjedzono)
  • Restart gry
Ocena: 3.0

Powodzenia!

Gra Snake to projekt wymagający, ale satysfakcjonujący — gdy zadziała, możesz w nią grać! Kluczowe pułapki: używaj useRef dla kierunku wewnątrz setInterval (inaczej closure zapamiętuje stary kierunek), i nie zapomnij o return () => clearInterval(interval) w cleanup useEffect. Bez tego gra przyspiesza przy każdym renderze!