snake!
This commit is contained in:
parent
cea3fa32ed
commit
0ec9edd595
106
app/nose/bin/snake.ts
Normal file
106
app/nose/bin/snake.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
/// <reference lib="dom" />
|
||||||
|
export const game = true
|
||||||
|
|
||||||
|
import type { GameContext, InputState } from "@/shared/game"
|
||||||
|
import { rng } from "@/shared/utils.ts"
|
||||||
|
|
||||||
|
const CELL = 20
|
||||||
|
const WIDTH = 30
|
||||||
|
const HEIGHT = 20
|
||||||
|
const TICK = 5
|
||||||
|
|
||||||
|
let snake: { x: number; y: number }[] = [{ x: 5, y: 5 }]
|
||||||
|
let dir = { x: 1, y: 0 }
|
||||||
|
let food = { x: rng(WIDTH) - 1, y: rng(HEIGHT) - 1 }
|
||||||
|
let dead = false
|
||||||
|
let tick = 0
|
||||||
|
|
||||||
|
export function init() {
|
||||||
|
snake = [{ x: 5, y: 5 }]
|
||||||
|
dir = { x: 1, y: 0 }
|
||||||
|
food = { x: rng(WIDTH) - 1, y: rng(HEIGHT) - 1 }
|
||||||
|
dead = false
|
||||||
|
tick = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export function update(_delta: number, input: InputState) {
|
||||||
|
if (dead) return
|
||||||
|
|
||||||
|
const keys = input.pressed
|
||||||
|
|
||||||
|
if (keys.has("ArrowUp") || keys.has("w")) dir = { x: 0, y: -1 }
|
||||||
|
if (keys.has("ArrowDown") || keys.has("s")) dir = { x: 0, y: 1 }
|
||||||
|
if (keys.has("ArrowLeft") || keys.has("a")) dir = { x: -1, y: 0 }
|
||||||
|
if (keys.has("ArrowRight") || keys.has("d")) dir = { x: 1, y: 0 }
|
||||||
|
|
||||||
|
// move every $TICK ticks
|
||||||
|
if (++tick % TICK !== 0) return
|
||||||
|
|
||||||
|
const head = { x: snake[0]!.x + dir.x, y: snake[0]!.y + dir.y }
|
||||||
|
|
||||||
|
// death checks
|
||||||
|
if (
|
||||||
|
head.x < 0 || head.x >= WIDTH ||
|
||||||
|
head.y < 0 || head.y >= HEIGHT ||
|
||||||
|
snake.some(s => s.x === head.x && s.y === head.y)
|
||||||
|
) {
|
||||||
|
dead = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
snake.unshift(head)
|
||||||
|
|
||||||
|
// eat
|
||||||
|
if (head.x === food.x && head.y === food.y) {
|
||||||
|
food = { x: rng(WIDTH) - 1, y: rng(HEIGHT) - 1 }
|
||||||
|
} else {
|
||||||
|
snake.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function draw(ctx: GameContext) {
|
||||||
|
ctx.clear()
|
||||||
|
ctx.rectfill(0, 0, ctx.width, ctx.height, "black")
|
||||||
|
|
||||||
|
const boardW = WIDTH * CELL
|
||||||
|
const boardH = HEIGHT * CELL
|
||||||
|
|
||||||
|
// move board center → canvas center
|
||||||
|
const offsetX = (ctx.width - boardW) / 2
|
||||||
|
const offsetY = (ctx.height - boardH) / 2
|
||||||
|
|
||||||
|
console.log("X", offsetX)
|
||||||
|
console.log("Y", offsetY)
|
||||||
|
|
||||||
|
const c = ctx.ctx
|
||||||
|
c.save()
|
||||||
|
c.translate(offsetX, offsetY)
|
||||||
|
|
||||||
|
// board background (now local 0,0 is board top-left)
|
||||||
|
ctx.rectfill(0, 0, boardW, boardH, "green")
|
||||||
|
|
||||||
|
// food
|
||||||
|
ctx.rectfill(
|
||||||
|
food.x * CELL, food.y * CELL,
|
||||||
|
(food.x + 1) * CELL, (food.y + 1) * CELL,
|
||||||
|
"lime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// snake
|
||||||
|
for (const s of snake) {
|
||||||
|
ctx.rectfill(
|
||||||
|
s.x * CELL, s.y * CELL,
|
||||||
|
(s.x + 1) * CELL, (s.y + 1) * CELL,
|
||||||
|
"magenta"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// score!
|
||||||
|
ctx.text(`Score: ${snake.length - 1}`, 5, boardH - 25, "cyan")
|
||||||
|
|
||||||
|
c.restore()
|
||||||
|
|
||||||
|
if (dead) {
|
||||||
|
ctx.centerText("GAME OVER", ctx.height / 2, "red", 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user