<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
<h1>Hello Plunker!</h1>
<div id='app'>
<p>SCORE: {{ snake.body_indexes.length - 1 }}</p>
<div id='map'>
<div v-for="i in grid_size * grid_size" :class="{
cell: true,
head: snake_head_index === i - 1,
body: snake.body_indexes.includes(i - 1),
fruit: fruit_index === i - 1,
}">
{{ i - 1 }}
</div>
</div>
<p v-if='is_gameover'>
GAME OVER<br>
<button onclick="location.reload()">RETRY</button>
</p>
</div>
<script src="script.js"></script>
</body>
</html>
new Vue({
el: '#app',
data: {
grid_size: 10,
fruit_index: 0,
snake: {
head_pos: {
x: 1,
y: 3,
},
body_indexes: [0],
direction: '→',
speed: 800,
},
},
created() {
this.randomize_fruit_index()
document.onkeydown = () => {
this.on_keydown(event.keyCode)
}
this.time_goes()
},
watch: {
is_eating_fruit(newValue) {
if (!newValue)
return
this.grow_up_snake()
this.randomize_fruit_index()
},
},
computed: {
snake_head_index() {
if (this.is_frameout)
return null
return this.snake.head_pos.y * this.grid_size + this.snake.head_pos.x
},
is_eating_fruit() {
return this.snake_head_index === this.fruit_index
},
is_suicided() {
return this.snake.body_indexes.includes(this.snake_head_index)
},
is_frameout() {
const head = this.snake.head_pos
return head.x < 0 || this.grid_size <= head.x || head.y < 0 || this.grid_size <= head.y
},
is_gameover() {
return this.is_suicided || this.is_frameout
},
},
methods: {
time_goes() {
if (this.is_gameover)
return
this.forward_snake()
setTimeout(this.time_goes.bind(this), this.snake.speed)
},
forward_snake() {
this.snake.body_indexes.shift()
this.snake.body_indexes.push(this.snake_head_index)
switch(this.snake.direction)
{
case '←':
this.snake.head_pos.x--;
break
case '↑':
this.snake.head_pos.y--;
break
case '→':
this.snake.head_pos.x++;
break
case '↓':
this.snake.head_pos.y++;
break
}
},
grow_up_snake() {
this.snake.body_indexes.unshift(this.snake.body_indexes[0])
},
randomize_fruit_index() {
this.fruit_index = Math.floor(Math.random() * this.grid_size * this.grid_size)
},
on_keydown(keyCode) {
switch(keyCode)
{
case 37:
if (this.snake.direction !== '→')
{
this.snake.direction = '←';
}
break
case 38:
if (this.snake.direction !== '↓')
{
this.snake.direction = '↑';
}
break
case 39:
if (this.snake.direction !== '←')
{
this.snake.direction = '→';
}
break
case 40:
if (this.snake.direction !== '↑')
{
this.snake.direction = '↓';
}
break
}
}
},
})
/* Styles go here */
#map {
--grid-size: 10;
display: grid;
grid-template-columns: repeat(var(--grid-size), 30px);
grid-template-rows: repeat(var(--grid-size), 30px);
}
.cell {
border: 1px solid white;
background: pink;
}
.cell.body {
background: silver;
}
.cell.fruit {
background: orangered;
}
.cell.head {
background: dimgray;
}