-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathdev.html
112 lines (111 loc) · 3.88 KB
/
dev.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<body>
<script>
let d = document,
b = d.body,
M = Math,
body = `<meta name="viewport" content="width=device-width,initial-scale=1" />Space: start/reset. Arrows: Move<div><canvas id=a><style>#a{border:solid;width:80vmin}*{background:tan;display:flex;flex-direction:column;align-items:center;font-size:1.2rem;}</style>`,
gridSize = 40,
canvasSize = 400,
// let alive initialise itself to undefined which is falsy
alive,
// don't worry about initialising score, heading, etc, we'll reset on start
score,
snake = [[3, 3]],
// valid array key codes
headings = { 37: 1, 38: 1, 39: 1, 40: 1 },
// heading stores the current heading key code - 38
// -1=left, 0=up, 1=right, 2=down
heading,
defaultSnake = snake,
// default the fruit to the pos of the snakes tail to save a coord
fruit = snake[0],
black = "#000",
red = "red",
addEventListener = "addEventListener",
rect = (x, y, col = black, w = gridSize, h = gridSize) => {
(ctx.fillStyle = col), ctx.fillRect(x, y, w, h);
},
text = (str, x, y = x, align = "center") => {
ctx.fillStyle = black;
ctx.textAlign = align;
ctx.fillText(str, x, y);
},
rand = () => M.floor(M.random() * 9),
update = () => {
if (!alive) return;
let [headX, headY] = snake[0],
nextX = headX + (heading % 2),
nextY = headY + ((heading - 1) % 2),
collides = ([x, y]) => x == nextX && y == nextY;
// remove current tail
snake.pop();
// check death
if (
nextX < 0 ||
nextX > 9 ||
nextY < 0 ||
nextY > 9 ||
snake.some(collides)
)
return die();
// and increment the new head
snake = [[nextX, nextY], ...snake];
// check fruit
if (collides(fruit)) {
// make a copy of the tail, using the score as a proxy for snake.length-1
snake.push([...snake[score]]);
score++;
fruit = [rand(), rand()];
}
// clear screen
rect(0, 0, "tan", canvasSize, canvasSize);
// draw snake
for ([x, y] of snake) {
rect(x * gridSize, y * gridSize);
}
// draw fruit
rect(fruit[0] * gridSize, fruit[1] * gridSize, red);
// draw score
text(score, 9, 30, "left");
},
die = () => {
alive = 0;
rect(0, 0, red, canvasSize, canvasSize);
// rely on default y position and text align
text(":(", canvasSize / 2);
},
reset = () => {
(alive = 1),
(snake = [...defaultSnake]),
(heading = 2), // heading = 40 - 38
(score = 0);
};
b.innerHTML = body;
let ctx = a.getContext("2d");
// a is the id of the injected canvas, available globally
a.height = a.width = canvasSize;
// invalid font family defaults to auto
ctx.font = "30px f";
// minifier willl minify these names correctly
let {
width: canvasWidth,
height: canvasHeight,
x: canvasX,
y: canvasY,
} = a.getBoundingClientRect();
d[addEventListener]("keyup", ({ which: w }) => {
headings[w] ? (heading = w - 38) : w == 32 && !alive && reset();
});
a[addEventListener]("touchstart", ({ touches: [{ clientX, clientY }] }) => {
if (!alive) reset();
const touchX = clientX - canvasX,
touchY = clientY - canvasY;
if (touchY < canvasHeight / 3) heading = 0;
if (touchY > (canvasHeight / 3) * 2) heading = 2;
if (touchX < canvasWidth / 3) heading = -1;
if (touchX > (canvasWidth / 3) * 2) heading = 1;
});
// if key is space and we're dead, reset. Otherwise, check if key is a valid
// arrow key and store that key - 38 (saves a couple of bytes calculating later)
setInterval(update, canvasSize);
</script>