Revive and add recordings and README.
This commit is contained in:
commit
4186f80a09
23
README.md
Normal file
23
README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Cellular automata experiments
|
||||||
|
|
||||||
|
Cellular automata experiments and game ideas from 2014.
|
||||||
|
|
||||||
|
|
||||||
|
## Hexagonal
|
||||||
|
|
||||||
|
Cellular automata on a hexagonal grid and a home-cooked rule set.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Interactive
|
||||||
|
|
||||||
|
Not really an automata since this one is interactive. Control the thing
|
||||||
|
with the arrow keys.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Broken
|
||||||
|
|
||||||
|
This one is not working. Not bothering to fix it, got no git history.
|
||||||
35
broken/ca.html
Executable file
35
broken/ca.html
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CA</title>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
#viewport {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
height: 640px;
|
||||||
|
width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#board {
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: '#bbb';
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="viewport">
|
||||||
|
<canvas id="board" height="600" width="800"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="ca.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
877
broken/ca.js
Executable file
877
broken/ca.js
Executable file
@ -0,0 +1,877 @@
|
|||||||
|
/*
|
||||||
|
* Michael Soukup, 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//var DIM = [window.innerWidth, window.innerHeight];
|
||||||
|
|
||||||
|
function zeros(m, n) {
|
||||||
|
var mat = [];
|
||||||
|
for (var i=0; i<m; i++) {
|
||||||
|
mat.push([]);
|
||||||
|
for (var j=0; j<n; j++) {
|
||||||
|
mat[i].push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
function nearestMod0(f, n) {
|
||||||
|
var mod = f % n;
|
||||||
|
if (mod < n/2) { // floor
|
||||||
|
return f-mod
|
||||||
|
}
|
||||||
|
else { // ceil
|
||||||
|
return f+n-mod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Vector(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
Vector.prototype.add = function(v) {
|
||||||
|
this.x += v.x;
|
||||||
|
this.y += v.y;
|
||||||
|
}
|
||||||
|
Vector.prototype.sub = function(v) {
|
||||||
|
this.x -= v.x;
|
||||||
|
this.y -= v.y;
|
||||||
|
}
|
||||||
|
Vector.prototype.dot = function(v) {
|
||||||
|
return this.x*v.x + this.y*v.y
|
||||||
|
}
|
||||||
|
Vector.prototype.setLength = function(newlen) {
|
||||||
|
var len = this.getLength();
|
||||||
|
this.x = (this.x*newlen)/len;
|
||||||
|
this.y = (this.y*newlen)/len;
|
||||||
|
}
|
||||||
|
Vector.prototype.getLength = function() {
|
||||||
|
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Frame(coords, dim) {
|
||||||
|
this.coords = coords;
|
||||||
|
this.dim = dim;
|
||||||
|
}
|
||||||
|
Frame.prototype.empty = function() {
|
||||||
|
return (this.dim.x <= 0 || this.dim.y <= 0)
|
||||||
|
}
|
||||||
|
Frame.prototype.move = function(u) {
|
||||||
|
this.coords.add(u);
|
||||||
|
}
|
||||||
|
Frame.prototype.align = function(mod) {
|
||||||
|
this.coords.x = nearestMod0(this.coords.x, mod);
|
||||||
|
this.coords.y = nearestMod0(this.coords.y, mod);
|
||||||
|
this.dim.x = nearestMod0(this.dim.x, mod);
|
||||||
|
this.dim.y = nearestMod0(this.dim.y, mod);
|
||||||
|
}
|
||||||
|
Frame.prototype.overlaps = function(f) {
|
||||||
|
var dx = f.coords.x - this.coords.x,
|
||||||
|
dy = f.coords.y - this.coords.y,
|
||||||
|
inter_x = ((dx > 0 && this.dim.x > dx) || (dx < 0 && f.dim.x < -dx)),
|
||||||
|
inter_y = ((dy > 0 && this.dim.y > dy) || (dy < 0 && f.dim.y < -dy));
|
||||||
|
|
||||||
|
return inter_x && inter_y
|
||||||
|
}
|
||||||
|
Frame.prototype.join = function(f) {
|
||||||
|
// Expands frame to also fit f
|
||||||
|
var x_min = Math.min(this.coords.x, f.coords.x),
|
||||||
|
y_min = Math.min(this.coords.y, f.coords.y),
|
||||||
|
x_max = Math.max(this.coords.x+this.dim.x, f.coords.x+f.dim.x),
|
||||||
|
y_max = Math.max(this.coords.y+this.dim.y, f.coords.y+f.dim.y);
|
||||||
|
|
||||||
|
this.coords.x = x_min;
|
||||||
|
this.coords.y = y_min;
|
||||||
|
this.dim.x = x_max - x_min;
|
||||||
|
this.dim.y = y_max - y_min;
|
||||||
|
}
|
||||||
|
Frame.prototype.intersect = function(f) {
|
||||||
|
// this AND f
|
||||||
|
var dx = f.coords.x - this.coords.x,
|
||||||
|
dy = f.coords.y - this.coords.y;
|
||||||
|
|
||||||
|
if (dx > 0) {
|
||||||
|
this.coords.x = f.coords.x;
|
||||||
|
this.dim.x = Math.min(this.dim.x - dx, f.dim.x);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.dim.x = Math.min(f.dim.x + dx, this.dim.x);
|
||||||
|
}
|
||||||
|
if (dy > 0) {
|
||||||
|
this.coords.y = f.coords.y;
|
||||||
|
this.dim.y = Math.min(this.dim.y - dy, f.dim.y);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.dim.y = Math.min(f.dim.y + dy, this.dim.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var CELL_COL = {
|
||||||
|
0: '#000',
|
||||||
|
1: '#222',
|
||||||
|
2: '#484',
|
||||||
|
3: '#a44',
|
||||||
|
4: '#8a8a8a'
|
||||||
|
},
|
||||||
|
CELL_T = {
|
||||||
|
'empty': 0,
|
||||||
|
'wall': 1,
|
||||||
|
'player': 2,
|
||||||
|
'enemy': 3,
|
||||||
|
'conway': 4
|
||||||
|
},
|
||||||
|
CELL_CONFIGS = {
|
||||||
|
'player0': [
|
||||||
|
[0,2,2,0,0,0],
|
||||||
|
[0,0,0,0,0,0],
|
||||||
|
[0,0,0,0,0,0],
|
||||||
|
[0,0,0,4,4,0],
|
||||||
|
[0,0,4,0,4,0],
|
||||||
|
[0,0,0,4,0,0]
|
||||||
|
],
|
||||||
|
'enemy0': [
|
||||||
|
[0,3,3,3,3,0],
|
||||||
|
[3,4,4,0,3,3],
|
||||||
|
[3,4,0,0,0,3],
|
||||||
|
[3,0,0,4,4,3],
|
||||||
|
[3,0,4,0,3,3],
|
||||||
|
[0,3,3,3,0,0]
|
||||||
|
],
|
||||||
|
'organism0': [
|
||||||
|
[0,0,0,0,0,0,4,4],
|
||||||
|
[0,0,4,4,4,4,4,4],
|
||||||
|
[0,0,0,4,0,0,0,0],
|
||||||
|
[0,4,0,0,0,0,0,0],
|
||||||
|
[0,4,4,4,0,4,0,4],
|
||||||
|
[4,4,4,0,4,4,4,4],
|
||||||
|
[0,0,4,4,4,0,0,0]
|
||||||
|
],
|
||||||
|
'box': [
|
||||||
|
[0,0,0,0],
|
||||||
|
[0,4,4,0],
|
||||||
|
[0,4,4,0],
|
||||||
|
[0,0,0,0]
|
||||||
|
],
|
||||||
|
'diamond': [
|
||||||
|
[0,4,0,0],
|
||||||
|
[4,0,4,0],
|
||||||
|
[0,4,0,0],
|
||||||
|
[0,0,0,0]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function Ndist(N, r) {
|
||||||
|
var dist = [0,0,0,0,0];
|
||||||
|
for (var x=0; x<N.length; x++) {
|
||||||
|
for (var y=0; y<N.length; y++) {
|
||||||
|
if (x != r && y != r) {
|
||||||
|
dist[N[x][r]] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dist
|
||||||
|
}
|
||||||
|
function cell_0_update(N, r) {
|
||||||
|
// count neighbours for now
|
||||||
|
var dist = Ndist(N, r);
|
||||||
|
if (dist[4] == 3) {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function cell_1_update(N, r) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
function cell_2_update(N, r) {
|
||||||
|
// player cell
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
function cell_3_update(N, r) {
|
||||||
|
// enemy cell
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
function cell_4_update(N, r) {
|
||||||
|
// conway live cells
|
||||||
|
var dist = Ndist(N, r);
|
||||||
|
if (dist[2] == 2 && dist[4] > 2) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
else if (dist[4] == 2 || dist[4] == 3) {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function cellNext(N) {
|
||||||
|
// Size of neighbourhood N is nxn where n is (r*2 + 1), r in {1,2,3...}:
|
||||||
|
// 3x3, 5x5, 7x7, ...
|
||||||
|
|
||||||
|
var r = (N.length - 1) / 2, // must be int
|
||||||
|
cell = N[r][r];
|
||||||
|
|
||||||
|
switch (cell) {
|
||||||
|
case 0:
|
||||||
|
return cell_0_update(N, r);
|
||||||
|
case 1:
|
||||||
|
return cell_1_update(N, r);
|
||||||
|
case 2:
|
||||||
|
return cell_2_update(N, r);
|
||||||
|
case 3:
|
||||||
|
return cell_3_update(N, r);
|
||||||
|
case 4:
|
||||||
|
return cell_4_update(N, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Every organism has a pointer to the environment
|
||||||
|
|
||||||
|
// dim_cam, coords_cam, px_tile, dim_frame, coords_frame, tiles_frame
|
||||||
|
|
||||||
|
// function Organism(configuration, x, y, tilesize) {
|
||||||
|
// // Tiles can be of any size px_tile_min*n, n in N
|
||||||
|
|
||||||
|
// this.p = new Vector(x, y); // continuous
|
||||||
|
// this.v = new Vector(0, 0);
|
||||||
|
|
||||||
|
// this.px_tile = tilesize;
|
||||||
|
// this.tiles = new Vector(configuration[0].length, configuration.length);
|
||||||
|
|
||||||
|
// this.config = configuration;
|
||||||
|
// this.config_buf = zeros(this.tiles.x, this.tiles.y);
|
||||||
|
|
||||||
|
// this.frame = new Frame(new Vector(nearestMod0(x, tilesize), nearestMod0(y, tilesize)),
|
||||||
|
// new Vector(tilesize*this.tiles.x, tilesize*this.tiles.y));
|
||||||
|
|
||||||
|
// //this.coords_center = null // keep track of center for steering and battle
|
||||||
|
|
||||||
|
// }
|
||||||
|
// Organism.prototype.getNeighbourhood = function(cx, cy, r) {
|
||||||
|
// var n = (r*2)+1,
|
||||||
|
// N = zeros(n,n);
|
||||||
|
|
||||||
|
// for (var x=cx-r; x<=cx+r; x++) {
|
||||||
|
// for (var y=cy-r; y<=cy+r; y++) {
|
||||||
|
// if ((x < 0 || x >= this.tiles.x) || (y < 0 || y >= this.tiles.y)) {
|
||||||
|
// N[x+r-cx][y+r-cy] = 0; // Tiles outside constraints are 0 anyways
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// N[x+r-cx][y+r-cy] = this.config[x][y];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return N
|
||||||
|
// }
|
||||||
|
// Organism.prototype.updateCells = function() {
|
||||||
|
// // find player cell center
|
||||||
|
// for (var x=0; x<this.tiles.x; x++) {
|
||||||
|
// for (var y=0; y<this.tiles.y; y++) {
|
||||||
|
// this.config_buf[x][y] = cellNext(this.getNeighbourhood(x, y, 1));
|
||||||
|
// // update cell according to neighborhood
|
||||||
|
// // (x', y') = f(x, y)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// this.config = this.config_buf;
|
||||||
|
// }
|
||||||
|
// Organism.prototype.updateFrame = function() {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// Organism.prototype.playerMove = function(dF, dt) {
|
||||||
|
// this.v.x = dF.x/dt;
|
||||||
|
// this.v.y = dF.y/dt;
|
||||||
|
|
||||||
|
// this.p.add(dF); // Move the whole box. dF controls position
|
||||||
|
// }
|
||||||
|
// Organism.prototype.playerUpdate = function(dF, dt) {
|
||||||
|
// this.frame.move(dF);
|
||||||
|
// this.frame.fitCoords(this.px_tile);
|
||||||
|
// this.updateCells();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Organism.prototype.framesOverlap = function(o) {
|
||||||
|
// xoutside || youtside
|
||||||
|
// return (this.coords.x < o.coords.x+o.dim.x) && (this.dim.x )
|
||||||
|
// }
|
||||||
|
// Organism.prototype.mergeOrganism = function(o) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
function Organism(configuration, x, y, tilesize) {
|
||||||
|
// Tiles can be of any size px_tile_min*n, n in N
|
||||||
|
|
||||||
|
//this.pos = new Vector(x, y); // continuous
|
||||||
|
//this.v = new Vector(0, 0);
|
||||||
|
|
||||||
|
this.px_tile = tilesize;
|
||||||
|
this.tiles = new Vector(configuration[0].length, configuration.length);
|
||||||
|
|
||||||
|
this.frame_tilemargin = 2;
|
||||||
|
this.frame = new Frame(new Vector(x, y), new Vector(tilesize*this.tiles.x, tilesize*this.tiles.y));
|
||||||
|
|
||||||
|
this.config = configuration;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
Organism.prototype.getNeighbourhood = function(cx, cy, r) {
|
||||||
|
var n = (r*2)+1,
|
||||||
|
N = zeros(n,n);
|
||||||
|
|
||||||
|
for (var x=cx-r; x<=cx+r; x++) {
|
||||||
|
for (var y=cy-r; y<=cy+r; y++) {
|
||||||
|
if ((x < 0 || x >= this.tiles.x) || (y < 0 || y >= this.tiles.y)) {
|
||||||
|
N[x+r-cx][y+r-cy] = 0; // Tiles outside constraints are 0 anyways
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
N[x+r-cx][y+r-cy] = this.config[x][y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return N
|
||||||
|
}
|
||||||
|
Organism.prototype.updateCells = function() {
|
||||||
|
var config_buf = zeros(this.tiles.x, this.tiles.y);
|
||||||
|
|
||||||
|
for (var x=0; x<this.tiles.x; x++) {
|
||||||
|
for (var y=0; y<this.tiles.y; y++) {
|
||||||
|
this.config_buf[x][y] = cellNext(this.getNeighbourhood(x, y, 1));
|
||||||
|
// update cell according to neighborhood
|
||||||
|
// (x', y') = f(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.config = config_buf;
|
||||||
|
}
|
||||||
|
Organism.prototype.move = function(dF, dt) {
|
||||||
|
if (dF.x <= 0) {
|
||||||
|
this.frame.coords.x += dF.x;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.frame.dim.x += dF.x;
|
||||||
|
}
|
||||||
|
if (dF.y <= 0) {
|
||||||
|
this.frame.coords.y += dF.y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.frame.dim.y += dF.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Organism.prototype.setConfig = function(new_tiles) {
|
||||||
|
var diffx = new_tiles.x - this.tiles.x,
|
||||||
|
diffy = new_tiles.y - this.tiles.y,
|
||||||
|
config = zeros(new_tiles.x, new_tiles.y),
|
||||||
|
x_2 = Math.floor(Math.min(this.tiles.x / 2 - 1, new_tiles.x / 2 - 1)),
|
||||||
|
y_2 = Math.floor(Math.min(this.tiles.y / 2 - 1, new_tiles.y / 2 - 1));
|
||||||
|
|
||||||
|
for (var x=0; x<x_2; x++) {
|
||||||
|
for (var y=0; y<y_2; y++) {
|
||||||
|
config[x][y] = this.config[x][y];
|
||||||
|
config[new_tiles.x-1-x][new_tiles.y-1-y] = this.config[this.tiles.x-1-x][this.tiles.y-1-y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.config = config;
|
||||||
|
this.tiles = new_tiles;
|
||||||
|
}
|
||||||
|
Organism.prototype.step = function(dF, dt) {
|
||||||
|
|
||||||
|
var x = this.frame.dim.x + this.K_p*(this.a_ref - this.frame.dim.x),
|
||||||
|
y = this.frame.dim.y + this.K_p*(this.a_ref - this.frame.dim.y),
|
||||||
|
dx = x - this.frame.dim.x, // shrinking: < 0, expanding: > 0
|
||||||
|
dy = y - this.frame.dim.y;
|
||||||
|
|
||||||
|
this.frame.coords.x += -dx/2;
|
||||||
|
this.frame.coords.y += -dy/2;
|
||||||
|
this.frame.dim.x = x;
|
||||||
|
this.frame.dim.y = y;
|
||||||
|
|
||||||
|
this.move(dF, dt);
|
||||||
|
|
||||||
|
// fit frame and simulate
|
||||||
|
this.frame.align(this.px_tile);
|
||||||
|
var new_tiles = new Vector(this.frame.dim.x / this.px_tile, this.frame.dim.y / this.px_tile);
|
||||||
|
this.setConfig(new_tiles);
|
||||||
|
this.updateCells();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var Game = (function(canvas_id) {
|
||||||
|
|
||||||
|
var canvas = document.getElementById(canvas_id),
|
||||||
|
ctx = canvas.getContext('2d'),
|
||||||
|
|
||||||
|
// Set up camera and simulation frame
|
||||||
|
// Camera moves continuously, frame size and coords must fit tiles.
|
||||||
|
|
||||||
|
dim_cam = new Vector(canvas.width, canvas.height),
|
||||||
|
coords_cam = new Vector(0.0, 0.0), // Initial view: coords_cam -> coords_cam+dim_cam
|
||||||
|
|
||||||
|
px_tile_min = 5, // 5x5 pixels
|
||||||
|
dim_frame = new Vector(nearestMod0(2*dim_cam.x, px_tile_min), nearestMod0(2*dim_cam.y, px_tile_min)),
|
||||||
|
coords_frame = new Vector(nearestMod0(coords_cam.x - 0.25*dim_frame.x, px_tile_min), nearestMod0(coords_cam.y - 0.25*dim_frame.y, px_tile_min)),
|
||||||
|
tiles_frame = new Vector(dim_frame.x/px_tile_min, dim_frame.y/px_tile_min),
|
||||||
|
|
||||||
|
player, enemies = [], organisms = [],
|
||||||
|
|
||||||
|
level0 = [
|
||||||
|
'player0,55,60',
|
||||||
|
'enemy0,120,24',
|
||||||
|
'organism0,-5,0',
|
||||||
|
'box,120,32',
|
||||||
|
'diamond,87,32'
|
||||||
|
];
|
||||||
|
|
||||||
|
//cells = zeros(tiles_frame.x, tiles_frame.y),
|
||||||
|
//cells_buf = (tiles_frame.x, tiles_frame.y),
|
||||||
|
|
||||||
|
|
||||||
|
// function detectNearbyEnemies(organism) {
|
||||||
|
// // Returns a list of indices of nearby enemies
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function _setOrganismBox(organism, nearby) {
|
||||||
|
// // assume all coords are tile fitted
|
||||||
|
|
||||||
|
// // pos_o = box_coord + 0.5*box
|
||||||
|
// var pos_o = Vector(organism.box_coords.x + 0.5*organism.box.x, organism.box_coords.y + 0.5*organism.box.y),
|
||||||
|
// old_coords = Vector(organism.box_coords.x, organism.box_coords.y),
|
||||||
|
// old_box = Vector(organism.box.x, organism.box.y)
|
||||||
|
|
||||||
|
// for (var i=0; i<nearby.length; i++) {
|
||||||
|
// // Expand boxes x
|
||||||
|
// // Expand boxes y
|
||||||
|
|
||||||
|
// // diff_x =
|
||||||
|
|
||||||
|
// organism.box_coords.x = Math.min(organism.box_coords.x, nearby[i].box_coords.x);
|
||||||
|
// organism.box_coords.y = Math.min(organism.box_coords.y, nearby[i].box_coords.y);
|
||||||
|
// organism.box.x = Math.max(organism.box.x+organism.box_coords.x, nearby[i].box.x + nearby[i].box_coords.x - organism.box_coords.x)
|
||||||
|
// if ()
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function playerSetBox(player) {
|
||||||
|
// // e[] <- find nearby enemies
|
||||||
|
// // o[] <- find nearby organisms
|
||||||
|
// // new_coords, new_box =
|
||||||
|
|
||||||
|
// var new_enemies = [], new_organisms = [], d, r;
|
||||||
|
|
||||||
|
// // Detect nearby enemies
|
||||||
|
// // Check distances player-to-all
|
||||||
|
// for (var i=0; i<enemies.length; i++) {
|
||||||
|
// d = veclen(player.pos.sub(enemies[i].pos));
|
||||||
|
// r =
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function playerSetBox(player) {
|
||||||
|
// // e[] <- find nearby enemies (pops e[])
|
||||||
|
// // o[] <- find nearby organisms (pops o[])
|
||||||
|
|
||||||
|
|
||||||
|
// // new_coords, new_box, = findBox(e + o, )
|
||||||
|
|
||||||
|
// // player <- new_coords, new_box
|
||||||
|
// // updatePlayerConfig(player)
|
||||||
|
// player.
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// function setOrganismBox(organism) {
|
||||||
|
// // This has to be a global method because we have to see outside the organisms configuration.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
function addOrganism(configuration, x, y) {
|
||||||
|
var o = new Organism(configuration, x, y);
|
||||||
|
|
||||||
|
// go through tiles TODO
|
||||||
|
if (o.isPlayer()) {
|
||||||
|
player = o;
|
||||||
|
}
|
||||||
|
else if (i.isEnemy()) {
|
||||||
|
enemies.push(i);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
organisms.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// function drawConfiguration(configuration, x, y) {
|
||||||
|
// var tx = Math.min(configuration[0].length, tiles_x-x),
|
||||||
|
// ty = Math.min(configuration.length, tiles_y-y);
|
||||||
|
|
||||||
|
// for (var i=0; i<tx; i++) {
|
||||||
|
// for (var j=0; j<ty; j++) {
|
||||||
|
// if (configuration[j][i] > 0) {
|
||||||
|
// ctx.fillStyle = cellcol[configuration[j][i]];
|
||||||
|
// ctx.beginPath();
|
||||||
|
// ctx.rect((i+x)*tilesize+0.5, (j+y)*tilesize+0.5, tilesize-0.5, tilesize-0.5);
|
||||||
|
// ctx.closePath();
|
||||||
|
// ctx.fill();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// function update(vx, vy) {
|
||||||
|
// updateCells(vx, vy);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function drawCells() {
|
||||||
|
// for (var i=0; i<tiles_x; i++) {
|
||||||
|
// for (var j=0; j<tiles_y; j++) {
|
||||||
|
// if (cells[i][j] > 0) {
|
||||||
|
// ctx.fillStyle = cellcol[cells[i][j]];
|
||||||
|
// ctx.beginPath();
|
||||||
|
// ctx.rect(i*tilesize+0.5, j*tilesize+0.5, tilesize-0.5, tilesize-0.5);
|
||||||
|
// ctx.closePath();
|
||||||
|
// ctx.fill();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function drawGrid() {
|
||||||
|
// ctx.translate(0.5, 0.5); // Transform canvas to get sharp lines
|
||||||
|
// ctx.strokeStyle = 'black';
|
||||||
|
// ctx.lineWidth = 0.1;
|
||||||
|
|
||||||
|
// for (var i=1; i<tiles_x; i++) {
|
||||||
|
// ctx.beginPath();
|
||||||
|
// ctx.moveTo(i*tilesize, 0);
|
||||||
|
// ctx.lineTo(i*tilesize, height);
|
||||||
|
// ctx.closePath();
|
||||||
|
// ctx.stroke();
|
||||||
|
// }
|
||||||
|
// for (var i=1; i<tiles_y; i++) {
|
||||||
|
// ctx.beginPath();
|
||||||
|
// ctx.moveTo(0, i*tilesize);
|
||||||
|
// ctx.lineTo(width, i*tilesize);
|
||||||
|
// ctx.closePath();
|
||||||
|
// ctx.stroke();
|
||||||
|
// }
|
||||||
|
// ctx.setTransform(1, 0, 0, 1, 0, 0); // Transform back
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function draw() {
|
||||||
|
// ctx.clearRect(0, 0, width, height);
|
||||||
|
// drawGrid();
|
||||||
|
// drawCells();
|
||||||
|
// }
|
||||||
|
// function draw2() {
|
||||||
|
// ctx.clearRect(0, 0, width, height);
|
||||||
|
// drawGrid();
|
||||||
|
// for (var i=0; i<organisms.length; i++) {
|
||||||
|
// drawConfiguration(organisms[i].config, organisms[i].offset.x, organisms[i].offset.y);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function drawOrganism(organism) {
|
||||||
|
// //
|
||||||
|
|
||||||
|
// var x_t0 = tile_min - (camera.x % tile_min), // x diff from camera to start tile
|
||||||
|
// y_t0 = tile_min - (camera.y % tile_min), // x diff from camera to start tile
|
||||||
|
// tiles_x = Math.min(organism.config[0].length, tiles_frame.x - x),
|
||||||
|
// tiles_y = Math.min(organism.config.length, tiles_frame.y - y);
|
||||||
|
|
||||||
|
// for (var i=0; i<tiles_x; i++) {
|
||||||
|
// for (var j=0; j<tiles_y; j++) {
|
||||||
|
// if (organism.config[j][i] > 0) {
|
||||||
|
// ctx.fillStyle = CELL_COL[organism.config[j][i]];
|
||||||
|
// ctx.beginPath();
|
||||||
|
|
||||||
|
// // Draw every 5x5 tile at x = (i+x)*tile_min + x_t0, y = (j+y)*tile_min + y_t0
|
||||||
|
// ctx.rect((i+x)*tile_min+x_t0+0.5, (j+y)*tile_min+y_t0+0.5, tile_min-0.5, tile_min-0.5);
|
||||||
|
// ctx.closePath();
|
||||||
|
// ctx.fill();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
function framesOverlap(o1, o2) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function minimumDist(o1, o2, d_gap, cell_sep) {
|
||||||
|
// returns true if o1 and o2 are touching or have a layer of maximum width d_gap consisting only of celltype cell_sep.
|
||||||
|
|
||||||
|
// try finding a layer. If no layer, return true
|
||||||
|
// try expanding the layer. If d_layer > d_gap return false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePlayer() {
|
||||||
|
var new_enemies = [], new_organisms = [];
|
||||||
|
|
||||||
|
// Split player? Implement later
|
||||||
|
|
||||||
|
var gap = 2;
|
||||||
|
for (var i=0; i<enemies.length; i++) {
|
||||||
|
if (framesOverlap(player, enemies[i]) && minimumGap(player, enemies[i], 2)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enemies[i].move(player.prev_dF);
|
||||||
|
}
|
||||||
|
for (var j=0; j<enemies.length; j++) {
|
||||||
|
organisms[i].move(dh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(dF) {
|
||||||
|
//set time
|
||||||
|
player.move(dF, dh);
|
||||||
|
for (var i=0; i<enemies.length; i++) {
|
||||||
|
enemies[i].move(player.prev_dF);
|
||||||
|
}
|
||||||
|
//for (var j=0; j<enemies.length; j++) {
|
||||||
|
// organisms[i].move(dh);
|
||||||
|
//}
|
||||||
|
|
||||||
|
updatePlayer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function initLevel(level) {
|
||||||
|
/*
|
||||||
|
level = {
|
||||||
|
organisms: ['label1,x1,y1', 'label2,x2,y2', ..., 'labeln,xn,yn']
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
function shouldMerge(o1, o2, d_gap, cell_t) {
|
||||||
|
var frame_union = frameUnion(o1.frame, o2.frame);
|
||||||
|
if (frame_union.isEmpty()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// relative speed v_12
|
||||||
|
var v_12 = new Vector(o1.v.x, o1.v.y);
|
||||||
|
v_12.sub(o2.v);
|
||||||
|
if (v_12.dot(o1.v) < 0) {
|
||||||
|
// Organisms are moving apart
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var d_min = Infinity;
|
||||||
|
// check all-to-all in overlapping region
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Find layer
|
||||||
|
// find two nearest cells both not of cell cell_t
|
||||||
|
// expand layer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function playerMerge() {
|
||||||
|
var new_enemies = [], new_organisms = [], gap = 2;
|
||||||
|
|
||||||
|
for (var i=0; i<enemies.length; i++) {
|
||||||
|
if (player.frame.overlaps(enemies[i].frame) && shouldMerge(player, enemies[i], gap, 0)) {
|
||||||
|
player.mergeOrganism(enemies[i]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new_enemies.push(enemies[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var j=0; j<organisms.length; j++) {
|
||||||
|
if (player.frame.overlaps(organisms[i].frame) && shouldMerge(player, organisms[i], gap, 0)) {
|
||||||
|
player.mergeOrganism(organisms[i]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new_enemies.push(organisms[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete new_enemies;
|
||||||
|
delete organisms;
|
||||||
|
enemies = new_enemies;
|
||||||
|
organisms = new_organisms;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//var t = Date.now(), h = 0, dh = 1; // Start at 1
|
||||||
|
function step(dF) {
|
||||||
|
// var t_now = Date.now();
|
||||||
|
// dt = (t_now - t)/1000;
|
||||||
|
// t = t_now;
|
||||||
|
// h += dh;
|
||||||
|
|
||||||
|
var dh = 1; // tmp
|
||||||
|
|
||||||
|
// simulatePlayer
|
||||||
|
player.move(dF, dh);
|
||||||
|
player.simulate(dh);
|
||||||
|
|
||||||
|
for (var i=0; i<enemies.length; i++) {
|
||||||
|
enemies[i].move(player.v, dh);
|
||||||
|
enemies[i].simulate(dh);
|
||||||
|
}
|
||||||
|
for (var j=0; j<organisms.length; j++) {
|
||||||
|
organisms[i].move(dF, dh);
|
||||||
|
organisms[j].simulate(dh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
drawOrganism(player); // We have ensured no overlap
|
||||||
|
for (var i=0; i<enemies.length; i++) {
|
||||||
|
drawOrganism(enemies[i]);
|
||||||
|
}
|
||||||
|
for (var j=0; j<organisms.length; j++) {
|
||||||
|
drawOrganism(organisms[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//function update() {
|
||||||
|
// playerMerge();
|
||||||
|
|
||||||
|
//setCamera(player.p, player.v);
|
||||||
|
//updateFrame();
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
runSimulation: function(timestep) {
|
||||||
|
_initCells(900);
|
||||||
|
//insertConfiguration(skintest, 10, 10);
|
||||||
|
draw();
|
||||||
|
setInterval(function (){
|
||||||
|
update();
|
||||||
|
draw();
|
||||||
|
}, timestep);
|
||||||
|
},
|
||||||
|
testphysics: function() {
|
||||||
|
// Set events
|
||||||
|
//locatePlayer();
|
||||||
|
//_initCells(900);
|
||||||
|
draw2();
|
||||||
|
|
||||||
|
addEventListener('keydown', function(e) {
|
||||||
|
var vx = vy = 0,
|
||||||
|
key = e.keyCode;
|
||||||
|
//locatePlayer();
|
||||||
|
if (key == 37) {
|
||||||
|
vx--;
|
||||||
|
}
|
||||||
|
else if (key == 39){
|
||||||
|
vx++
|
||||||
|
}
|
||||||
|
else if (key == 40){
|
||||||
|
vy++;
|
||||||
|
}
|
||||||
|
else if (key == 38) {
|
||||||
|
vy--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//movePlayer(vx, vy);
|
||||||
|
//updateCells(vx, vy);
|
||||||
|
draw2();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
run: function() {
|
||||||
|
var running = false,
|
||||||
|
dF = new Vector(0, 0);
|
||||||
|
|
||||||
|
initLevel(level0);
|
||||||
|
draw();
|
||||||
|
|
||||||
|
addEventListener('keydown', function(e) {
|
||||||
|
if (running) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
var key = e.keyCode;
|
||||||
|
dF.x = 0;
|
||||||
|
dF.y = 0;
|
||||||
|
if (key == 37) {
|
||||||
|
dF.x--;
|
||||||
|
}
|
||||||
|
else if (key == 39){
|
||||||
|
dF.x++
|
||||||
|
}
|
||||||
|
else if (key == 40){
|
||||||
|
dF.y++;
|
||||||
|
}
|
||||||
|
else if (key == 38) {
|
||||||
|
dF.y--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
update(dF);
|
||||||
|
draw();
|
||||||
|
running = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addOrganism: addOrganism
|
||||||
|
//insertConfiguration: insertConfiguration
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// conway
|
||||||
|
// static rule
|
||||||
|
|
||||||
|
// CELL MAP
|
||||||
|
/**
|
||||||
|
0: dead or empty tile
|
||||||
|
1: solid wall
|
||||||
|
2: skin cell (player)
|
||||||
|
3: skin cell (enemy)
|
||||||
|
4: living cell
|
||||||
|
4:
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
var game = Game('board');
|
||||||
|
//game.insertConfiguration(skintest, 10, 10);
|
||||||
|
//game.runSimulation(1000);
|
||||||
|
|
||||||
|
// game.insertConfiguration(player, 100, 100);
|
||||||
|
// game.insertConfiguration(enemy, 10, 10);
|
||||||
|
|
||||||
|
// game.insertConfiguration(box, 56, 12);
|
||||||
|
// game.insertConfiguration(box, 123, 94);
|
||||||
|
// game.insertConfiguration(box, 35, 67);
|
||||||
|
|
||||||
|
// game.insertConfiguration(diamond, 43, 122);
|
||||||
|
// game.insertConfiguration(diamond, 86, 12);
|
||||||
|
// game.insertConfiguration(diamond, 25, 75);
|
||||||
|
|
||||||
|
// game.insertConfiguration(organism, 94, 55);
|
||||||
|
// game.run();
|
||||||
|
|
||||||
|
game.addOrganism(CELL_CONFIGS['organism0'], 94, 55);
|
||||||
|
game.testphysics();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
35
hex/ca-hex.html
Executable file
35
hex/ca-hex.html
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CA</title>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
#viewport {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
height: 640px;
|
||||||
|
width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#board {
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: '#bbb';
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="viewport">
|
||||||
|
<canvas id="board" height="600" width="800"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="ca-hex.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
529
hex/ca-hex.js
Executable file
529
hex/ca-hex.js
Executable file
@ -0,0 +1,529 @@
|
|||||||
|
/*
|
||||||
|
Cellular automata, polar coordinates.
|
||||||
|
|
||||||
|
Michael Soukup, 2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function nearestMod0(f, n) {
|
||||||
|
var mod = f % n;
|
||||||
|
if (mod < n/2) { // floor
|
||||||
|
return f-mod
|
||||||
|
}
|
||||||
|
else { // ceil
|
||||||
|
return f+n-mod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Cartesian(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cartesian.prototype.r = function() {
|
||||||
|
return Math.sqrt(Math.pow(this.x,2) + Math.pow(this.y, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
Cartesian.prototype.theta = function() {
|
||||||
|
return Math.atan2(this.y, this.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Polar(r, theta) {
|
||||||
|
this.r = r;
|
||||||
|
this.theta = theta;
|
||||||
|
}
|
||||||
|
|
||||||
|
Polar.prototype.x = function() {
|
||||||
|
return this.r*Math.cos(this.theta);
|
||||||
|
}
|
||||||
|
|
||||||
|
Polar.prototype.y = function() {
|
||||||
|
return this.r*Math.sin(this.theta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Vector(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector.prototype.add = function(v) {
|
||||||
|
this.x += v.x;
|
||||||
|
this.y += v.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector.prototype.sub = function(v) {
|
||||||
|
this.x -= v.x;
|
||||||
|
this.y -= v.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function Frame(o, v) {
|
||||||
|
this.o = o;
|
||||||
|
this.v = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame.prototype.center = function() {
|
||||||
|
return new Cartesian(this.o.x + this.v.x/2, this.o.y + this.v.y/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var CELL_COL = {
|
||||||
|
0: '#eee',
|
||||||
|
1: '#222',
|
||||||
|
2: '#484',
|
||||||
|
3: '#a44',
|
||||||
|
4: '#8a8a8a'
|
||||||
|
},
|
||||||
|
CELL_STROKE = {
|
||||||
|
0: '#bbb',
|
||||||
|
1: '#222',
|
||||||
|
2: '#484',
|
||||||
|
3: '#a44',
|
||||||
|
4: '#8a8a8a'
|
||||||
|
},
|
||||||
|
CELL_LINEWIDTH = {
|
||||||
|
0: 0.1,
|
||||||
|
1: 0.8,
|
||||||
|
2: 0.5,
|
||||||
|
3: 0.5,
|
||||||
|
4: 0.2
|
||||||
|
},
|
||||||
|
CELL_T = {
|
||||||
|
'empty': 0,
|
||||||
|
'wall': 1,
|
||||||
|
'player': 2,
|
||||||
|
'enemy': 3,
|
||||||
|
'conway': 4
|
||||||
|
},
|
||||||
|
CELL_CONFIGS = {
|
||||||
|
'player0': [2,2,2,2,2,2],
|
||||||
|
'enemy0': [3,3,3,3,3,3],
|
||||||
|
'organism0': [4,0,4,0,0,0,4,0,0,4,4,4],
|
||||||
|
'box': [4,4,0,0,0,0],
|
||||||
|
'diamond': [4,0,0,4,0,0]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var Game = (function(canvas_id) {
|
||||||
|
var canvas = document.getElementById(canvas_id),
|
||||||
|
ctx = canvas.getContext('2d'),
|
||||||
|
|
||||||
|
view = new Frame(new Cartesian(0,0), new Vector(canvas.width, canvas.height)),
|
||||||
|
|
||||||
|
n_poly, r_poly, alpha_poly;
|
||||||
|
|
||||||
|
|
||||||
|
function Organism(cell_seq, x, y) {
|
||||||
|
this.cells = cell_seq; // Must be of length % n == 0. When big enough, cell sequence becomes a new cell. Cells expand recursively
|
||||||
|
this.o = new Cartesian(x, y); // Continuous. Fit to r_sim before simulation
|
||||||
|
|
||||||
|
this.layers = cell_seq.length/world.n_cell;// find radius = f(length sequence)
|
||||||
|
//this.r = this.layers*world.r_cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadWorld(w) {
|
||||||
|
n_poly = w.n_poly;
|
||||||
|
r_poly = w.r_poly;
|
||||||
|
alpha_poly = Math.PI / n_poly;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawPolygon(N, R, phi, x, y, fill) {
|
||||||
|
var vertice = new Polar(R, phi - Math.PI / N),
|
||||||
|
dtheta = 2*Math.PI / N;
|
||||||
|
|
||||||
|
ctx.fillStyle = fill;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x+vertice.x(), y+vertice.y());
|
||||||
|
for (var i=0; i<N; i++) {
|
||||||
|
vertice.theta += dtheta;
|
||||||
|
ctx.lineTo(x+vertice.x(), y+vertice.y());
|
||||||
|
}
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawPolySeq(seq, N, R, phi, x, y) {
|
||||||
|
var l = 1, incr = 0, c_0l = 1, dtheta = 2*Math.PI / N,
|
||||||
|
pol = new Polar(0,0);
|
||||||
|
|
||||||
|
drawPolygon(N, R, phi, x, y, CELL_COL[seq[0]]);
|
||||||
|
for (var i=1; i<seq.length; i++) {
|
||||||
|
if (++incr > N*l) {
|
||||||
|
l++;
|
||||||
|
incr = 0;
|
||||||
|
c_0l = i;
|
||||||
|
dtheta = (2*Math.PI) / (l*N);
|
||||||
|
}
|
||||||
|
pol.theta = (i - c_0l)*dtheta + phi;
|
||||||
|
pol.r = Math.sqrt(3*Math.pow(R*l, 2) + Math.pow((l/2 - (i - c_0l) % l)*2*R, 2));
|
||||||
|
|
||||||
|
drawPolygon(N, R, phi, x+pol.x(), y+pol.y(), CELL_COL[seq[i]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNeighbourhood(i, l, N) {
|
||||||
|
var X = [];
|
||||||
|
if (i == 0) { // origo is special
|
||||||
|
for (var _i=0; _i<N; _i++) {
|
||||||
|
X.push(_i+1);
|
||||||
|
}
|
||||||
|
return X;
|
||||||
|
}
|
||||||
|
|
||||||
|
var c_0l = 3*l*(l-1)+1, // c_0l sequence expression :)
|
||||||
|
_diff = N*(l-1) + Math.ceil((i-c_0l)/l),
|
||||||
|
_add = N*l + Math.ceil((i-c_0l)/l);
|
||||||
|
|
||||||
|
if ((i - c_0l) % l == 0) { // crown cell
|
||||||
|
if (i != c_0l) {
|
||||||
|
X.push(i+_add-1);
|
||||||
|
}
|
||||||
|
X.push(i+_add);
|
||||||
|
X.push(i+_add+1);
|
||||||
|
X.push(i+1);
|
||||||
|
if (l == 1) { // watch out for origo
|
||||||
|
X.push(i-_diff-1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
X.push(i-_diff);
|
||||||
|
}
|
||||||
|
if (i == c_0l) {
|
||||||
|
X.push(i + N*l - 1);
|
||||||
|
X.push(i + N*l + N*(l+1) - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
X.push(i-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((i - N*l + 1) == c_0l) {
|
||||||
|
X.push(i+_add);
|
||||||
|
X.push(c_0l);
|
||||||
|
X.push(c_0l-N*(l-1));
|
||||||
|
X.push(i-_diff);
|
||||||
|
X.push(i-1);
|
||||||
|
X.push(i+_add-1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
X.push(i+_add-1);
|
||||||
|
X.push(i+_add);
|
||||||
|
X.push(i+1);
|
||||||
|
X.push(i-_diff+1);
|
||||||
|
X.push(i-_diff);
|
||||||
|
X.push(i-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return X;
|
||||||
|
}
|
||||||
|
|
||||||
|
function neighbours(org, i, l, N) {
|
||||||
|
var rv = [], X = getNeighbourhood(i, l, N);
|
||||||
|
|
||||||
|
for (var n=0; n<X.length; n++) {
|
||||||
|
if (n < org.length) {
|
||||||
|
rv.push(org[X[n]]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rv.push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
function neighbourDist(X) {
|
||||||
|
var dist={0:0, 1:0, 2:0, 3:0, 4:0};
|
||||||
|
for (var n=0; n<X.length; n++) {
|
||||||
|
dist[X[n]]++;
|
||||||
|
}
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_0_update(X) {
|
||||||
|
// count neighbours for now
|
||||||
|
var nd = neighbourDist(X);
|
||||||
|
if (nd[2] >= 2 && nd[4] > 0) { // Inside of player
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else if (nd[4] == 2) { // rebirth rule
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else if (nd[1] > 2) { // wall rule
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_1_update(X) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_2_update(X) {
|
||||||
|
// player cell
|
||||||
|
var nd = neighbourDist(X);
|
||||||
|
if (nd[4] > 2 || nd[2] > 4) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_3_update(X) {
|
||||||
|
// enemy cell
|
||||||
|
var nd = neighbourDist(X);
|
||||||
|
if (nd[4] > 3 || nd[3] > 4) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_4_update(X) {
|
||||||
|
// conway live cells
|
||||||
|
var nd = neighbourDist(X);
|
||||||
|
if (nd[2] > 2 && nd[4] >= 2) { // Inside corner of player
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
else if (nd[4] == 2){
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else if (nd[1] > 2) { // wall rule
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cellNext(c, X) {
|
||||||
|
// Size of neighbourhood N is nxn where n is (r*2 + 1), r in {1,2,3...}:
|
||||||
|
// 3x3, 5x5, 7x7, ...
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 0:
|
||||||
|
return cell_0_update(X);
|
||||||
|
case 1:
|
||||||
|
return cell_1_update(X);
|
||||||
|
case 2:
|
||||||
|
return cell_2_update(X);
|
||||||
|
case 3:
|
||||||
|
return cell_3_update(X);
|
||||||
|
case 4:
|
||||||
|
return cell_4_update(X);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function simulate(org, dt) {
|
||||||
|
var org_buf = [],
|
||||||
|
l = 0,
|
||||||
|
incr = 0,
|
||||||
|
X,
|
||||||
|
N = 6;
|
||||||
|
|
||||||
|
X = neighbours(org, 0, l, N);
|
||||||
|
org_buf.push(cellNext(org[0], X))
|
||||||
|
for (var i=1; i<org.length; i++) {
|
||||||
|
if (++incr > N*l) {
|
||||||
|
l++;
|
||||||
|
}
|
||||||
|
X = neighbours(org, i, l, N);
|
||||||
|
org_buf.push(cellNext(org[i], X));
|
||||||
|
}
|
||||||
|
return org_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawOrganism(organism, depth) {
|
||||||
|
// recursive
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGrid() {
|
||||||
|
var o = view.center(),
|
||||||
|
o_polar = new Polar(o.r(),-o.theta());
|
||||||
|
|
||||||
|
o_polar.r = nearestMod0(o_polar.r, world.r_cell); // set r to fit nearest
|
||||||
|
o_polar.theta = nearestMod0(o_polar.theta, world.dtheta) + world.theta_cell;// set theta to nearest
|
||||||
|
|
||||||
|
// Set relative to canvas
|
||||||
|
o.x = o_polar.x() - view.o.x;
|
||||||
|
o.y = o_polar.y() - view.o.y;
|
||||||
|
o_polar.r = 0; o_polar.theta = world.theta_cell;
|
||||||
|
var i = 0,
|
||||||
|
r_max = Math.sqrt(Math.pow(o.x, 2), Math.pow(o.y, 2)); // Upper left corner
|
||||||
|
|
||||||
|
console.log(o);
|
||||||
|
console.log(o_polar.r);
|
||||||
|
console.log(r_max);
|
||||||
|
|
||||||
|
console.log(o_polar.x());
|
||||||
|
var dtheta;
|
||||||
|
while (o_polar.r < r_max) {
|
||||||
|
o_polar.r = Math.ceil(i/world.n_cell)*2*world.r_cell;
|
||||||
|
dtheta = 2*Math.PI/(Math.ceil(i/world.n_cell)*world.n_cell);
|
||||||
|
o_polar.theta = Math.ceil(i/world.n_cell)*world.n_cell*i + world.theta_cell;
|
||||||
|
drawPolygon(o_polar.x()+o.x, o_polar.y()+o.y,
|
||||||
|
world.r_cell, world.n_cell, 0,
|
||||||
|
'#bbb');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
// move and update physics;
|
||||||
|
// fit origo to tiles
|
||||||
|
// check for collision, merge and expand radius
|
||||||
|
// simulate
|
||||||
|
|
||||||
|
// update camera
|
||||||
|
// load simulation frame from world config?
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadWorld: loadWorld,
|
||||||
|
testDraw: function() {
|
||||||
|
//drawGrid();
|
||||||
|
|
||||||
|
//drawPolygon(N, R, phi, x, y, fill)
|
||||||
|
drawPolygon(6, 10, 0, 40, 80, '#aaa');
|
||||||
|
drawPolygon(6, 15, (2*Math.PI/6)/10, 100, 80, '#aaa', '#ddd', 0.4);
|
||||||
|
drawPolygon(3, 15, 0, 180, 80, '#aaa', '#ddd', 0.4);
|
||||||
|
drawPolygon(3, 15, 2*Math.PI/12, 240, 80, '#aaa', '#ddd', 0.4);
|
||||||
|
drawPolygon(8, 15, 0, 300, 80, '#aaa', '#ddd', 0.4);
|
||||||
|
|
||||||
|
//drawPolySeq(seq, N, R, phi, x, y)
|
||||||
|
var n=6, px=10, phi=Math.PI/n;
|
||||||
|
drawPolySeq([2,2,3,3,3,2,4,2,2,1,2,2,2,3,2,2,2,2,2,2,3,4,1,1,1,1,2,2,2,2,2,3,3,3,2,2,2,4], n, px, phi, 101.5, 201.5);
|
||||||
|
drawPolySeq([2,2,3,3,3,2,4,2,2,1,2,2,2,3,2,2,2,2,2,2,3,4,1,1,1,1,2,2,2,2,2,3,3,3,2,2,2,4], n, px/2, phi, 301.5, 201.5);
|
||||||
|
|
||||||
|
n=4; px=5; phi=Math.PI/n;
|
||||||
|
drawPolySeq([2,2,2,3,3,2,2,2,2,3,3,3,3,2,2,4,4,2,2,4,2,3,2,2,3,3,2], n, px, phi, 501.5, 201.5);
|
||||||
|
|
||||||
|
},
|
||||||
|
testLogic: function() {
|
||||||
|
var n=6,l=2,i=7;;
|
||||||
|
console.log('c_02 neighbourhood: (i=7)');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=1;l=1;n=6
|
||||||
|
console.log('c_01 neighbourhood: (i=1)');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=22;l=3;n=6
|
||||||
|
console.log('crown 1 l=3 neighbourhood (i=22):');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=34;l=3;n=6
|
||||||
|
console.log('crown 5 l=3 neighbourhood (i=34):');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=8;l=2;n=6
|
||||||
|
console.log('middle 1 l=2 neighbourhood (i=8):');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=60;l=4;n=6
|
||||||
|
console.log('middle step layer 6 l=4 neighbourhood (i=60):');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=4;l=1;n=6
|
||||||
|
console.log('crown 3 l=1 (i=4):');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=3;l=1;n=6
|
||||||
|
console.log('crown 3 l=1 (i=3):');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=13;l=2;n=6
|
||||||
|
console.log('crown 3 l=2 (i=13):');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
|
||||||
|
i=0;l=0;n=6
|
||||||
|
console.log('center cell (i=0):');
|
||||||
|
X = getNeighbourhood(i, l, n);
|
||||||
|
console.log(X);
|
||||||
|
},
|
||||||
|
testSimulation: function() {
|
||||||
|
var n=6, px=10, phi=Math.PI/n, dt=1000,
|
||||||
|
player=[4,4,4,0,0,0,0,4,0,0,0,4,0,0,4,4,4,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
|
||||||
|
enemy1=[4,0,0,0,4,4,0,0,4,0,0,4,4,4,4,0,0,0,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3];
|
||||||
|
enemy2=[4,4,4,0,4,4,0,0,4,0,0,4,4,4,4,0,0,0,4,4,0,0,0,0,4,4,4,0,0,4,4,0,0,4,4,0,0,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3];
|
||||||
|
wallor=[1,0,1,0,1,0,4,4,4,0,1,0,1,0,1,0,0,4,4,4,4,4,0,0,0,4,4,4,0,0,4,4,0,0,4,4,4,0]
|
||||||
|
|
||||||
|
drawPolySeq(player, n, px, phi, canvas.width*0.25, canvas.height*0.25);
|
||||||
|
drawPolySeq(enemy1, n, px, phi, canvas.width*0.75, canvas.height*0.75);
|
||||||
|
drawPolySeq(enemy2, n, px, phi, canvas.width*0.8, canvas.height*0.2);
|
||||||
|
drawPolySeq(wallor, n, px, phi, canvas.width*0.2, canvas.height*0.8);
|
||||||
|
setInterval(function (){
|
||||||
|
player = simulate(player, dt);
|
||||||
|
enemy1 = simulate(enemy1, dt);
|
||||||
|
enemy2 = simulate(enemy2, dt);
|
||||||
|
wallor = simulate(wallor, dt);
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
drawPolySeq(player, n, px, phi, canvas.width*0.25, canvas.height*0.25);
|
||||||
|
drawPolySeq(enemy1, n, px, phi, canvas.width*0.75, canvas.height*0.75);
|
||||||
|
drawPolySeq(enemy2, n, px, phi, canvas.width*0.8, canvas.height*0.2);
|
||||||
|
drawPolySeq(wallor, n, px, phi, canvas.width*0.2, canvas.height*0.8);
|
||||||
|
}, dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// conway
|
||||||
|
// static rule
|
||||||
|
|
||||||
|
// CELL MAP
|
||||||
|
/**
|
||||||
|
0: dead or empty tile
|
||||||
|
1: solid wall
|
||||||
|
2: skin cell (player)
|
||||||
|
3: skin cell (enemy)
|
||||||
|
4: living cell
|
||||||
|
4:
|
||||||
|
|
||||||
|
**/
|
||||||
|
var WORLD0 = {
|
||||||
|
"n_poly": 6, // Must be 3, 6, ..
|
||||||
|
"r_poly": 15,
|
||||||
|
"alpha_poly": 0,
|
||||||
|
"dtheta": 2*Math.PI / 6,
|
||||||
|
|
||||||
|
"r_sim": 2000,
|
||||||
|
"o_f_init": new Cartesian(0, 0),
|
||||||
|
|
||||||
|
"level": 0,
|
||||||
|
"config": [] // sequence of cell configs
|
||||||
|
};
|
||||||
|
|
||||||
|
var game = Game('board');
|
||||||
|
//game.loadWorld(WORLD0);
|
||||||
|
//game.testDraw();
|
||||||
|
//testLogic();
|
||||||
|
game.testSimulation();
|
||||||
BIN
hex/recording.gif
Normal file
BIN
hex/recording.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
35
interactive/ca-interactive.html
Executable file
35
interactive/ca-interactive.html
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CA</title>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
#viewport {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
height: 640px;
|
||||||
|
width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#board {
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: '#bbb';
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="viewport">
|
||||||
|
<canvas id="canvas" height="600" width="800"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="ca-interactive.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
580
interactive/ca-interactive.js
Executable file
580
interactive/ca-interactive.js
Executable file
@ -0,0 +1,580 @@
|
|||||||
|
/**
|
||||||
|
Interactive cellular automata.
|
||||||
|
|
||||||
|
Michael Soukup 2014.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function zeros(m, n) {
|
||||||
|
var mat = [];
|
||||||
|
for (var i=0; i<m; i++) {
|
||||||
|
mat.push([]);
|
||||||
|
for (var j=0; j<n; j++) {
|
||||||
|
mat[i].push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mat
|
||||||
|
}
|
||||||
|
|
||||||
|
function nearestMod0(f, n) {
|
||||||
|
var mod = f % n;
|
||||||
|
if (mod < n/2) { // floor
|
||||||
|
return f-mod
|
||||||
|
}
|
||||||
|
else { // ceil
|
||||||
|
return f+n-mod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var CELL_T = {
|
||||||
|
'empty': 0,
|
||||||
|
'wall': 1,
|
||||||
|
'player': 2,
|
||||||
|
'enemy': 3,
|
||||||
|
'conway': 4
|
||||||
|
};
|
||||||
|
|
||||||
|
var CELLCOL = {
|
||||||
|
0: '#fff',
|
||||||
|
1: '#222',
|
||||||
|
2: '#484',
|
||||||
|
3: '#a44',
|
||||||
|
4: '#8a8a8a'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: Cellsets appear transposed.
|
||||||
|
var CELLSETS = {
|
||||||
|
"dead_4": [
|
||||||
|
[4,4],
|
||||||
|
[4,4]
|
||||||
|
],
|
||||||
|
"dead_9": [
|
||||||
|
[0,4,0],
|
||||||
|
[4,0,4],
|
||||||
|
[0,4,0]
|
||||||
|
],
|
||||||
|
"finite14_64": [
|
||||||
|
[0,0,0,0,0,0,0,0],
|
||||||
|
[0,4,0,4,4,4,0,0],
|
||||||
|
[0,0,0,0,4,4,0,0],
|
||||||
|
[0,0,4,4,0,4,0,0],
|
||||||
|
[0,0,4,4,0,0,0,0],
|
||||||
|
[0,0,0,4,4,4,0,0],
|
||||||
|
[0,4,4,0,4,4,0,0],
|
||||||
|
[0,4,4,0,0,0,0,0]
|
||||||
|
],
|
||||||
|
"infinite_64": [
|
||||||
|
[0,0,0,4,0,0,0,0],
|
||||||
|
[0,4,4,4,4,4,4,0],
|
||||||
|
[4,0,0,0,4,4,0,0],
|
||||||
|
[0,4,4,4,0,4,0,0],
|
||||||
|
[0,4,0,0,4,4,0,4],
|
||||||
|
[0,0,0,4,0,0,0,0],
|
||||||
|
[0,4,0,4,0,4,0,4],
|
||||||
|
[0,4,4,4,0,0,0,0]
|
||||||
|
],
|
||||||
|
"player_0": [
|
||||||
|
[2,2,2,2],
|
||||||
|
[2,0,4,2],
|
||||||
|
[2,0,0,2],
|
||||||
|
[2,2,2,2]
|
||||||
|
],
|
||||||
|
"enemy_0": [
|
||||||
|
[3,3,3,3],
|
||||||
|
[3,0,0,3],
|
||||||
|
[3,0,0,3],
|
||||||
|
[3,3,3,3]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
var WORLD_0 = {
|
||||||
|
"width": 80, // tilewidth
|
||||||
|
"heigth": 60, // tileheight
|
||||||
|
"walls": true, // sets boundaries if true
|
||||||
|
"player": ["player_0", 20, 20], // tileset, x, y
|
||||||
|
"enemies": [
|
||||||
|
["enemy_0", 50, 40]
|
||||||
|
],
|
||||||
|
"organisms": [
|
||||||
|
["finite14_64", 25, 25],
|
||||||
|
["infinite_64", 65, 40],
|
||||||
|
["dead_4", 10, 5],
|
||||||
|
["dead_4", 5, 10],
|
||||||
|
["dead_4", 30, 5],
|
||||||
|
["dead_9", 17, 40],
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Update rules
|
||||||
|
|
||||||
|
c - cell identifier
|
||||||
|
X - neighbourhood of c. Array of length 8 starting in upper left corner.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function _ndist(X) {
|
||||||
|
var dist = [0,0,0,0,0];
|
||||||
|
for (var i=0; i<X.length; i++) {
|
||||||
|
dist[X[i]]++;
|
||||||
|
}
|
||||||
|
return dist
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_0_update(X) {
|
||||||
|
// count neighbours for now
|
||||||
|
var dist = _ndist(X);
|
||||||
|
if (dist[4] == 3) {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_1_update(X) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_2_update(X) {
|
||||||
|
// Player cell
|
||||||
|
var dist = _ndist(X);
|
||||||
|
if (dist[2] < 1 || dist[2] > 7) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (dist[2] == 1 && dist[4] < 3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_3_update(X) {
|
||||||
|
// enemy cell
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_4_update(X) {
|
||||||
|
// conway live cells
|
||||||
|
var dist = _ndist(X);
|
||||||
|
if (dist[2] == 3 && dist[4] > 2) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
else if (dist[2] > 0 && (dist[4] != 5 || dist[4] == 4)) {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else if (dist[4] == 2 || dist[4] == 3) {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cell_next(c, X) {
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 0:
|
||||||
|
return cell_0_update(X);
|
||||||
|
case 1:
|
||||||
|
return cell_1_update(X);
|
||||||
|
case 2:
|
||||||
|
return cell_2_update(X);
|
||||||
|
case 3:
|
||||||
|
return cell_3_update(X);
|
||||||
|
case 4:
|
||||||
|
return cell_4_update(X);
|
||||||
|
case 5:
|
||||||
|
return cell_0_update(X);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Board holds the world variables and a complete map over all tiles.
|
||||||
|
All tiles on the board are simlulated in each step.
|
||||||
|
*/
|
||||||
|
function Board() {
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Board.prototype.on_board = function(x, y) {
|
||||||
|
// TODO check walls
|
||||||
|
return (x >= this.x && y >= this.y && x < this.x+this.w && y < this.y+this.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
Board.prototype.set_tile = function(val, x, y) {
|
||||||
|
if (this.on_board(x, y)) {
|
||||||
|
this.map[x][y] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Board.prototype.get_tile = function(x, y) {
|
||||||
|
if (this.on_board(x, y)) {
|
||||||
|
return this.map[x][y];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Board.prototype.load_tileset = function(tileset, x, y) {
|
||||||
|
for (var i=0; i<tileset[0].length; i++) {
|
||||||
|
for (var j=0; j<tileset.length; j++) {
|
||||||
|
this.set_tile(tileset[j][i], x+i, y+j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Board.prototype.load_world = function(world) {
|
||||||
|
this.w = world.width;
|
||||||
|
this.h = world.heigth;
|
||||||
|
this.map = zeros(this.w, this.h);
|
||||||
|
this.map_buf = zeros(this.w, this.h); // Buffer for simulation
|
||||||
|
|
||||||
|
this.load_tileset(CELLSETS[world.player[0]], world.player[1], world.player[2]);
|
||||||
|
for (var e=0; e<world.enemies.length; e++) {
|
||||||
|
var _enemy = world.enemies[e];
|
||||||
|
this.load_tileset(CELLSETS[_enemy[0]], _enemy[1], _enemy[2]);
|
||||||
|
}
|
||||||
|
for (var o=0; o<world.organisms.length; o++) {
|
||||||
|
var _organism = world.organisms[o];
|
||||||
|
this.load_tileset(CELLSETS[_organism[0]], _organism[1], _organism[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world.walls) {
|
||||||
|
for (var i=this.x; i<this.x+this.w; i++) {
|
||||||
|
this.set_tile(1, i, 0);
|
||||||
|
this.set_tile(1, i, this.h-1);
|
||||||
|
}
|
||||||
|
for (var j=this.y; j<this.y+this.h; j++) {
|
||||||
|
this.set_tile(1, 0, j);
|
||||||
|
this.set_tile(1, this.w-1, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Move the set of tiles defined by frame to (x+dx, y+dy).
|
||||||
|
Not responsible for checking if the move is a legal operation.
|
||||||
|
*/
|
||||||
|
Board.prototype.move_frame = function(frame, dx, dy) {
|
||||||
|
var x_start, x_end, x_step,
|
||||||
|
y_start, y_end, y_step,
|
||||||
|
x, y;
|
||||||
|
|
||||||
|
if (dx < 0) {
|
||||||
|
x_start = frame.x;
|
||||||
|
x_end = frame.x+frame.w;
|
||||||
|
x_step = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x_start = frame.x+frame.w-1;
|
||||||
|
x_end = frame.x-1;
|
||||||
|
x_step = -1;
|
||||||
|
}
|
||||||
|
if (dy < 0) {
|
||||||
|
y_start = frame.y;
|
||||||
|
y_end = frame.y+frame.h;
|
||||||
|
y_step = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
y_start = frame.y+frame.h-1;
|
||||||
|
y_end = frame.y-1;
|
||||||
|
y_step = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = x_start;
|
||||||
|
while (x != x_end) {
|
||||||
|
y = y_start;
|
||||||
|
while (y != y_end) {
|
||||||
|
this.set_tile(this.map[x][y], x+dx, y+dy);
|
||||||
|
this.set_tile(0, x, y);
|
||||||
|
y += y_step;
|
||||||
|
}
|
||||||
|
x += x_step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Board.prototype.neighbourhood = function(cx, cy) {
|
||||||
|
return [
|
||||||
|
this.get_tile(cx-1, cy-1), // upper left corner
|
||||||
|
this.get_tile(cx, cy-1),
|
||||||
|
this.get_tile(cx+1, cy-1),
|
||||||
|
this.get_tile(cx-1, cy),
|
||||||
|
this.get_tile(cx+1, cy),
|
||||||
|
this.get_tile(cx-1, cy+1),
|
||||||
|
this.get_tile(cx, cy+1),
|
||||||
|
this.get_tile(cx+1, cy+1),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Board.prototype.simulate = function(frame) {
|
||||||
|
var X;
|
||||||
|
for (var i=this.x; i<this.x+this.w; i++) {
|
||||||
|
for (var j=this.y; j<this.y+this.h; j++) {
|
||||||
|
X = this.neighbourhood(i, j);
|
||||||
|
this.map_buf[i][j] = cell_next(this.map[i][j], X);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.map = this.map_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
TileFrame defines a square area of tiles with the upper left corner
|
||||||
|
in (x, y) and lower right corner in (x+w, y+h).
|
||||||
|
|
||||||
|
Coordinates in tile space.
|
||||||
|
*/
|
||||||
|
function TileFrame(x, y, w, h) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.w = w;
|
||||||
|
this.h = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
TileFrame.prototype.move = function(dx, dy) {
|
||||||
|
this.x += dx;
|
||||||
|
this.y += dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
TileFrame.prototype.contains = function(frame) {
|
||||||
|
return (this.x < frame.x && this.y < frame.y && this.x+this.w > frame.x+frame.w && this.y+this.h > frame.y+frame.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
TileFrame.prototype.intersects = function(frame) {
|
||||||
|
return (((this.x > frame.x && this.x < frame.x+frame.w) || (frame.x > this.x && frame.x < this.x+this.w)) &&
|
||||||
|
((this.y > frame.y && this.y < frame.y+frame.h) || (frame.y > this.y && frame.y < this.y+this.h)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Holds the canvas context and camera coordinates. Implements methods for
|
||||||
|
rendering tile sets.
|
||||||
|
|
||||||
|
Coordinates are continuous and in tile space.
|
||||||
|
*/
|
||||||
|
function Camera(canvas_id) {
|
||||||
|
this.canvas = document.getElementById(canvas_id);
|
||||||
|
this.ctx = this.canvas.getContext('2d');
|
||||||
|
this.width = this.canvas.width;
|
||||||
|
this.height = this.canvas.height;
|
||||||
|
|
||||||
|
this.tilesize = 5; // Minimum tilesize (px).
|
||||||
|
this.tw = this.width/this.tilesize;
|
||||||
|
this.th = this.height/this.tilesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera.prototype.zoom = function(tilesize) {
|
||||||
|
var factor = tilesize/this.tilesize,
|
||||||
|
_tw = this.width/tilesize,
|
||||||
|
_th = this.height/tilesize,
|
||||||
|
diff_x = (this.tw - _tw)/2,
|
||||||
|
diff_y = (this.th - _th)/2;
|
||||||
|
|
||||||
|
this.x += diff_x;
|
||||||
|
this.tw = _tw;
|
||||||
|
this.y += diff_y;
|
||||||
|
this.th = _th;
|
||||||
|
this.tilesize = tilesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera.prototype.center = function(x, y) {
|
||||||
|
this.x = x - this.tw/2;
|
||||||
|
this.y = y - this.th/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera.prototype.draw_grid = function() {
|
||||||
|
var i = (Math.ceil(this.x) - this.x)*this.tilesize,
|
||||||
|
j = (Math.ceil(this.y) - this.y)*this.tilesize;
|
||||||
|
|
||||||
|
this.ctx.strokestyle = 'black';
|
||||||
|
this.ctx.lineWidth = 0.1;
|
||||||
|
|
||||||
|
// Vertical lines
|
||||||
|
while (i < this.width) {
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(i, 0);
|
||||||
|
this.ctx.lineTo(i, this.height);
|
||||||
|
this.ctx.closePath();
|
||||||
|
this.ctx.stroke();
|
||||||
|
i += this.tilesize;
|
||||||
|
}
|
||||||
|
// Horizontal lines
|
||||||
|
while (j < this.height) {
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(0, j);
|
||||||
|
this.ctx.lineTo(this.width, j);
|
||||||
|
this.ctx.closePath();
|
||||||
|
this.ctx.stroke();
|
||||||
|
j += this.tilesize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera.prototype.draw_tiles = function(board) {
|
||||||
|
var x, y, i, j, tile_margin = this.tilesize*0.01;
|
||||||
|
|
||||||
|
x = Math.floor(this.x);
|
||||||
|
i = (x - this.x) * this.tilesize;
|
||||||
|
while (i < this.width) {
|
||||||
|
y = Math.floor(this.y);
|
||||||
|
j = (y - this.y)*this.tilesize;
|
||||||
|
|
||||||
|
while (j < this.height) {
|
||||||
|
if (board.get_tile(x,y) != 0) {
|
||||||
|
this.ctx.fillStyle = CELLCOL[board.get_tile(x, y)];
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.rect(i+tile_margin, j+tile_margin, this.tilesize-2*tile_margin, this.tilesize-2*tile_margin);
|
||||||
|
this.ctx.closePath();
|
||||||
|
this.ctx.fill();
|
||||||
|
}
|
||||||
|
y++;
|
||||||
|
j += this.tilesize;
|
||||||
|
}
|
||||||
|
x++;
|
||||||
|
i += this.tilesize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera.prototype.clear = function() {
|
||||||
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function testsim(canvas_id, world, dt) {
|
||||||
|
var board = new Board(),
|
||||||
|
player = new TileFrame(world.player[1], world.player[2], CELLSETS[world.player[0]][0].length, CELLSETS[world.player[0]].length),
|
||||||
|
cam = new Camera(canvas_id),
|
||||||
|
tilesize = 8;
|
||||||
|
|
||||||
|
board.load_world(world);
|
||||||
|
cam.set(world.player[1]-1.5, world.player[1]-1.5, tilesize); // cam.center(...,
|
||||||
|
cam.draw_grid();
|
||||||
|
cam.draw_tiles(board);
|
||||||
|
setInterval(function (){
|
||||||
|
board.simulate();
|
||||||
|
cam.clear();
|
||||||
|
cam.draw_grid();
|
||||||
|
cam.draw_tiles(board);
|
||||||
|
}, dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testmove(canvas_id, world, dt) {
|
||||||
|
var board = new Board(),
|
||||||
|
player = new TileFrame(world.player[1], world.player[2], CELLSETS[world.player[0]][0].length, CELLSETS[world.player[0]].length),
|
||||||
|
cam = new Camera(canvas_id),
|
||||||
|
tilesize = 8.8;
|
||||||
|
|
||||||
|
board.load_world(world);
|
||||||
|
cam.zoom(tilesize);
|
||||||
|
cam.center(player.x, player.y);
|
||||||
|
cam.draw_grid();
|
||||||
|
cam.draw_tiles(board);
|
||||||
|
|
||||||
|
function expand_player() {
|
||||||
|
var X;
|
||||||
|
console.log('exp');
|
||||||
|
for (var x=player.x; x<player.x+player.w; x++) {
|
||||||
|
X = board.neighbourhood(x, player.y);
|
||||||
|
console.log(X);
|
||||||
|
if (X[0] == 2 || X[1] == 2 || X[2] == 2) {
|
||||||
|
player.y--;
|
||||||
|
player.h++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
X = board.neighbourhood(x, player.y+player.h-1);
|
||||||
|
if (X[4] == 2 || X[5] == 2 || X[6] == 2) {
|
||||||
|
player.h++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var y=player.y; y<player.y+player.h; y++) {
|
||||||
|
X = board.neighbourhood(player.x, y);
|
||||||
|
if (X[6] == 2 || X[7] == 2 || X[0] == 2) {
|
||||||
|
player.x--;
|
||||||
|
player.w++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
X = board.neighbourhood(player.x+player.w-1, y);
|
||||||
|
if (X[2] == 2 || X[3] == 2 || X[4] == 2) {
|
||||||
|
player.w++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var i=0;
|
||||||
|
addEventListener('keydown', function(e) {
|
||||||
|
var vx = vy = 0,
|
||||||
|
key = e.keyCode;
|
||||||
|
|
||||||
|
if (key == 37) {
|
||||||
|
vx--;
|
||||||
|
}
|
||||||
|
else if (key == 39){
|
||||||
|
vx++
|
||||||
|
}
|
||||||
|
else if (key == 40){
|
||||||
|
vy++;
|
||||||
|
}
|
||||||
|
else if (key == 38) {
|
||||||
|
vy--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
board.move_frame(player, vx, vy);
|
||||||
|
player.move(vx, vy);
|
||||||
|
board.simulate();
|
||||||
|
|
||||||
|
cam.center(player.x, player.y);
|
||||||
|
cam.clear();
|
||||||
|
cam.draw_grid();
|
||||||
|
cam.draw_tiles(board);
|
||||||
|
|
||||||
|
// Expand frames
|
||||||
|
expand_player();
|
||||||
|
|
||||||
|
i++;
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener('wheel', function(e) {
|
||||||
|
if (e.deltaY > 0) {
|
||||||
|
tilesize *= 0.9;
|
||||||
|
cam.zoom(tilesize);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tilesize *= 1.1;
|
||||||
|
cam.zoom(tilesize);
|
||||||
|
}
|
||||||
|
cam.clear();
|
||||||
|
cam.draw_grid();
|
||||||
|
cam.draw_tiles(board);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Run
|
||||||
|
//testsim("canvas", WORLD_0, 1000);
|
||||||
|
testmove("canvas", WORLD_0, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
interactive/recording.gif
Normal file
BIN
interactive/recording.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 MiB |
Loading…
x
Reference in New Issue
Block a user