JSFiddle - React, Tailwind, and code Playground

by Santiago J

CSS

canvas {
  border: 1px solid;
}

JavaScript

const gridWidth = 20;
const gridHeight = 20;
const GRID_TO_PX = 8;
const px = x => x * GRID_TO_PX;
const canvas = document.createElement('canvas');
canvas.width = px(gridWidth);
canvas.height = px(gridHeight);
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const point = (x, y) => ({x, y});
const snake = [];
const UP = 1, DOWN = 2, LEFT = 3, RIGHT = 4;
let direction, nextDirection;
let speed;
let food;
function init() {
  direction = nextDirection = RIGHT;
  speed = 2;
  food = genFood();
  snake.length = 0;
  for (let i = 0; i < 3; i++) {
    snake.unshift(point(i, 3));
  }
}
init();
function pointEq(a, b) {
  return a.x === b.x && a.y === b.y;
}
function isInSnake(point) {
  for (const seg of snake) {
    if (pointEq(point, seg)) return true;
  }
  return false;
}
function isInSnakeTail(point) {
  for (let i = 1; i < snake.length; i++) {
    if (pointEq(point, snake[i])) return true;
  }
  return false;
}
function genFood() {
  while (true) {
    const food = point(
      Math.floor(Math.random() * gridWidth),
      Math.floor(Math.random() * gridHeight));
    if (!isInSnake(food)) return food;
  }
}
function drawPoint(point) {
  ctx.fillRect(px(point.x), px(point.y), px(1), px(1));
}
function loop() {
  direction = nextDirection;
  const oldHead = snake[0];
  const dx = direction === LEFT ? -1 :direction === RIGHT ? 1 : 0;
  const dy = direction === UP ? -1 :direction === DOWN ? 1 : 0;
  const newHead = point(oldHead.x + dx, oldHead.y + dy);
  snake.unshift(newHead);
  const lost = isInSnakeTail(newHead) ||
    newHead.x < 0 || newHead.x >= gridWidth ||
    newHead.y < 0 || newHead.y >= gridHeight;
  if (pointEq(newHead, food)) {
    food = genFood();
    speed++;
  } else {
    snake.pop();
  }
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (const seg of snake) drawPoint(seg);
  drawPoint(food);
  if (lost) {
    alert('you lose!');
    init();
  }
  setTimeout(loop, 1000 /...