2017-04-04 01:58:52 +03:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<style>
|
|
|
|
|
|
|
|
|
|
.snake.field {
|
|
|
|
|
width: 500px;
|
|
|
|
|
height: 500px;
|
|
|
|
|
border: solid 1px silver;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
2017-04-05 19:03:33 +03:00
|
|
|
// XXX add random apple and snake placement + avoid walls and other stuff...
|
2017-04-05 19:06:13 +03:00
|
|
|
// XXX normalize x/y everywhere we input coordinates...
|
2017-04-04 01:58:52 +03:00
|
|
|
var Snake = {
|
|
|
|
|
config: {
|
|
|
|
|
apple_color: 'red',
|
2017-04-05 05:13:22 +03:00
|
|
|
wall_color: 'gray',
|
2017-04-04 01:58:52 +03:00
|
|
|
interval: 200,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
_field: null,
|
|
|
|
|
_cells: null,
|
2017-04-05 05:19:55 +03:00
|
|
|
players: null,
|
|
|
|
|
field_size: null,
|
2017-04-04 01:58:52 +03:00
|
|
|
|
2017-04-06 01:21:32 +03:00
|
|
|
// utils...
|
|
|
|
|
random_point: function(){
|
|
|
|
|
var cells = this._cells
|
|
|
|
|
var l = cells.length
|
|
|
|
|
var w = this.field_size.width
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
var i = Math.floor(Math.random() * l)
|
|
|
|
|
} while(cells[i].style.backgroundColor != '')
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
x: i%w,
|
|
|
|
|
y: Math.floor(i/w),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2017-04-04 02:11:41 +03:00
|
|
|
// XXX BUG: going down into bottom left corner breaks the snake...
|
|
|
|
|
// ...check other corner cases!
|
|
|
|
|
// ...looks like the only corner case...
|
2017-04-04 01:58:52 +03:00
|
|
|
_tick: function(){
|
|
|
|
|
var that = this
|
|
|
|
|
|
|
|
|
|
var l = this._cells.length
|
|
|
|
|
var w = this.field_size.width
|
|
|
|
|
var h = this.field_size.height
|
|
|
|
|
|
|
|
|
|
var tick = this.__tick = (this.__tick + 1 || 0)
|
|
|
|
|
var directions = 'neswn'
|
|
|
|
|
|
|
|
|
|
this._cells.forEach(function(cell, i){
|
|
|
|
|
var color = cell.style.backgroundColor
|
|
|
|
|
|
|
|
|
|
// skip cells we touched...
|
|
|
|
|
if(cell.tick == tick){
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// snake...
|
|
|
|
|
if(cell.age != null){
|
|
|
|
|
// handle cell age...
|
|
|
|
|
if(cell.age == 0){
|
|
|
|
|
delete cell.age
|
|
|
|
|
cell.style.backgroundColor = ''
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
cell.age -= 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// head...
|
|
|
|
|
var direction = cell.direction
|
|
|
|
|
var next =
|
|
|
|
|
direction == 'n' ?
|
|
|
|
|
(i < w ? l - w + i : i - w)
|
|
|
|
|
: direction == 's' ?
|
|
|
|
|
(i > (l-w) ? i - (l-w) : i + w)
|
|
|
|
|
: direction == 'e' ?
|
|
|
|
|
((i+1)%w == 0 ? i - (w-1) : i + 1)
|
|
|
|
|
: direction == 'w' ?
|
|
|
|
|
(i%w == 0 ? i + (w-1) : i - 1)
|
|
|
|
|
: null
|
|
|
|
|
|
|
|
|
|
if(next != null){
|
|
|
|
|
next = that._cells[next]
|
|
|
|
|
|
|
|
|
|
// turn...
|
|
|
|
|
var turn = that.players[color] || ''
|
|
|
|
|
if(turn != ''){
|
|
|
|
|
var j = turn == 'left' ? directions.indexOf(direction) - 1
|
|
|
|
|
: directions.indexOf(direction) + 1
|
|
|
|
|
j = j < 0 ? 3 : j
|
|
|
|
|
direction = directions[j]
|
|
|
|
|
that.players[color] = ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var age = cell.age
|
2017-04-05 05:13:22 +03:00
|
|
|
var move = false
|
2017-04-04 01:58:52 +03:00
|
|
|
// special case: other snake's head -- kill both...
|
2017-04-05 05:13:22 +03:00
|
|
|
if(next.direction){
|
2017-04-04 01:58:52 +03:00
|
|
|
next.style.backgroundColor = ''
|
|
|
|
|
// NOTE: we are not deleteing .direction here as
|
|
|
|
|
// we can have upto 4 snakes colliding...
|
2017-04-04 02:11:41 +03:00
|
|
|
next.direction = ''
|
2017-04-04 01:58:52 +03:00
|
|
|
delete next.age
|
|
|
|
|
|
2017-04-05 05:13:22 +03:00
|
|
|
// apples...
|
|
|
|
|
} else if(next.style.backgroundColor == that.config.apple_color){
|
|
|
|
|
age += 1
|
|
|
|
|
move = true
|
|
|
|
|
|
2017-04-04 01:58:52 +03:00
|
|
|
// empty...
|
|
|
|
|
// NOTE: anything but an apple will kill the snake...
|
|
|
|
|
} else if(next.style.backgroundColor == ''){
|
2017-04-05 05:13:22 +03:00
|
|
|
move = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(move){
|
2017-04-04 01:58:52 +03:00
|
|
|
next.tick = tick
|
|
|
|
|
next.style.backgroundColor = color
|
|
|
|
|
next.age = age + 1
|
|
|
|
|
next.direction = direction
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete cell.direction
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cell.tick = tick
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
setup: function(field){
|
|
|
|
|
field = field || this._field
|
|
|
|
|
field = this._field = typeof(field) == typeof('str') ? document.querySelector(field)
|
|
|
|
|
: field
|
|
|
|
|
this._cells = [].slice.call(field.querySelectorAll('td'))
|
|
|
|
|
this.field_size = {
|
|
|
|
|
width: field.querySelector('tr').querySelectorAll('td').length,
|
|
|
|
|
height: field.querySelectorAll('tr').length,
|
|
|
|
|
}
|
|
|
|
|
this.players = {}
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
|
2017-04-05 05:13:22 +03:00
|
|
|
// constructors...
|
2017-04-05 19:06:13 +03:00
|
|
|
// XXX normalize input x/y...
|
2017-04-06 01:21:32 +03:00
|
|
|
snake: function(color, point, direction, age){
|
|
|
|
|
point = point || this.random_point()
|
|
|
|
|
var head = this._cells[point.x + point.y * this.field_size.width]
|
2017-04-04 01:58:52 +03:00
|
|
|
head.style.backgroundColor = color
|
|
|
|
|
head.direction = direction
|
|
|
|
|
head.age = (age || 5) - 1
|
|
|
|
|
|
|
|
|
|
this.players[color] = ''
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
},
|
2017-04-06 01:21:32 +03:00
|
|
|
apple: function(point){
|
|
|
|
|
point = point || this.random_point()
|
|
|
|
|
this._cells[point.x + point.y * this.field_size.width]
|
2017-04-05 05:13:22 +03:00
|
|
|
.style.backgroundColor = this.config.apple_color
|
|
|
|
|
return this
|
|
|
|
|
},
|
2017-04-06 01:21:32 +03:00
|
|
|
wall: function(point, direction, length){
|
|
|
|
|
point = point || this.random_point()
|
|
|
|
|
var x = point.x
|
|
|
|
|
var y = point.y
|
2017-04-05 05:13:22 +03:00
|
|
|
length = length || 1
|
|
|
|
|
while(length > 0){
|
|
|
|
|
this._cells[x + y * this.field_size.width]
|
|
|
|
|
.style.backgroundColor = this.config.wall_color
|
|
|
|
|
|
|
|
|
|
x += direction == 'e' ? 1
|
|
|
|
|
: direction == 'w' ? -1
|
|
|
|
|
: 0
|
|
|
|
|
x = x < 0 ? this.field_size.width + x
|
|
|
|
|
: x % this.field_size.width
|
|
|
|
|
y += direction == 'n' ? -1
|
|
|
|
|
: direction == 's' ? 1
|
|
|
|
|
: 0
|
|
|
|
|
y = y < 0 ? this.field_size.height + y
|
|
|
|
|
: y % this.field_size.height
|
|
|
|
|
|
|
|
|
|
length -= 1
|
|
|
|
|
}
|
|
|
|
|
return this
|
|
|
|
|
},
|
2017-04-04 01:58:52 +03:00
|
|
|
|
|
|
|
|
// actions...
|
2017-04-06 01:21:32 +03:00
|
|
|
start: function(t){
|
2017-04-04 01:58:52 +03:00
|
|
|
this.__timer = this.__timer
|
2017-04-06 01:21:32 +03:00
|
|
|
|| setInterval(this._tick.bind(this), t || this.config.interval || 200)
|
2017-04-04 01:58:52 +03:00
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
stop: function(){
|
|
|
|
|
clearInterval(this.__timer)
|
|
|
|
|
delete this.__timer
|
|
|
|
|
delete this.__tick
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
left: function(color){
|
|
|
|
|
this.players[color || Object.keys(this.players)[0]] = 'left'
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
right: function(color){
|
|
|
|
|
this.players[color || Object.keys(this.players)[0]] = 'right'
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-04-05 05:18:22 +03:00
|
|
|
function test(game){
|
2017-04-05 05:13:22 +03:00
|
|
|
return Snake
|
2017-04-05 05:18:22 +03:00
|
|
|
.setup(game || '.snake')
|
2017-04-05 05:13:22 +03:00
|
|
|
|
|
|
|
|
// XXX BUG: this will break as soon as it reaches the corner...
|
2017-04-06 01:21:32 +03:00
|
|
|
.snake('blue', {x:0, y:0}, 's', 5)
|
2017-04-05 05:13:22 +03:00
|
|
|
|
|
|
|
|
// hit an apple...
|
2017-04-06 01:21:32 +03:00
|
|
|
.snake('green', {x:5, y:3}, 's', 5)
|
|
|
|
|
.apple({x:5, y:5})
|
2017-04-05 05:13:22 +03:00
|
|
|
|
|
|
|
|
// hit a wall...
|
2017-04-06 01:21:32 +03:00
|
|
|
.snake('silver', {x:14, y:3}, 'w', 5)
|
|
|
|
|
.wall({x:2, y:14}, 's', 7)
|
2017-04-05 05:13:22 +03:00
|
|
|
|
|
|
|
|
.start()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-04-05 18:53:07 +03:00
|
|
|
// XXX keyboard controller...
|
|
|
|
|
// - press two buttons in under N milliseconds to launch a snake
|
|
|
|
|
// - first button pressed is left, second is right
|
2017-04-05 18:57:19 +03:00
|
|
|
// XXX should the cfg be global or local to Snake???
|
|
|
|
|
// ...at this point global seems simpler...
|
|
|
|
|
var KEY_CONFIG = {
|
|
|
|
|
}
|
2017-04-05 18:54:14 +03:00
|
|
|
function kbHandler(event){
|
|
|
|
|
// XXX
|
|
|
|
|
}
|
2017-04-05 18:53:07 +03:00
|
|
|
|
|
|
|
|
|
2017-04-06 01:21:32 +03:00
|
|
|
var Levels = {
|
|
|
|
|
basicLevel: function(){
|
|
|
|
|
return this
|
|
|
|
|
.wall({x:3, y:10}, 's', 11)
|
|
|
|
|
.wall({x:3, y:5}, 'e', 5)
|
|
|
|
|
.wall({x:3+5, y:5}, 's', 5)
|
|
|
|
|
.wall({x:3+5, y:5+4}, 'e', 12) },
|
|
|
|
|
randomLevel: function(){
|
|
|
|
|
return this
|
|
|
|
|
.wall(null, 's', 11)
|
|
|
|
|
.wall(null, 's', 11)
|
|
|
|
|
.wall(null, 'e', 11)
|
|
|
|
|
.wall(null, 'e', 11) },
|
|
|
|
|
}
|
|
|
|
|
Snake.__proto__ = Levels
|
|
|
|
|
|
2017-04-05 18:53:07 +03:00
|
|
|
|
|
|
|
|
function setup(){
|
2017-04-05 18:58:22 +03:00
|
|
|
Snake
|
|
|
|
|
.setup('.snake')
|
2017-04-06 01:21:32 +03:00
|
|
|
|
|
|
|
|
//.randomLevel()
|
|
|
|
|
.basicLevel()
|
|
|
|
|
|
|
|
|
|
.apple()
|
|
|
|
|
.apple()
|
2017-04-05 18:53:07 +03:00
|
|
|
|
|
|
|
|
.start()
|
|
|
|
|
|
|
|
|
|
// XXX setup kb handler...
|
|
|
|
|
// XXX
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-04 01:58:52 +03:00
|
|
|
</script>
|
|
|
|
|
|
2017-04-05 18:53:07 +03:00
|
|
|
<body onload="setup()">
|
2017-04-04 01:58:52 +03:00
|
|
|
|
2017-04-05 04:38:11 +03:00
|
|
|
<table class="snake field" cellspacing="0">
|
2017-04-04 01:58:52 +03:00
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
<tr> <td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td>
|
|
|
|
|
<td></td><td></td><td></td><td></td> <td></td><td></td><td></td><td></td> </tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2017-04-05 18:53:07 +03:00
|
|
|
<p>
|
|
|
|
|
XXX basic instructions...
|
|
|
|
|
</p>
|
|
|
|
|
|
2017-04-04 01:58:52 +03:00
|
|
|
</body>
|
|
|
|
|
</html>
|
2017-04-05 18:53:07 +03:00
|
|
|
<!-- vim:set ts=4 sw=4 spell : -->
|