--- /dev/null
+<!doctype html>
+<meta charset="utf-8">
+<html>
+ <head>
+ <script src="grid.js"></script>
+ <script src="cell.js"></script>
+ <script src="play.js"></script>
+ <script src="rps.js"></script>
+ <script src="draw.js"></script>
+ <script>
+ function run() {
+ lc();
+ }
+ </script>
+ <style>
+ span {
+ margin=left: 15px;
+ }
+ input[type=number] {
+ width: 30px;
+ margin-left: 15px;
+ }
+ select {
+ margin-left: 15px;
+ }
+ #es {
+ margin-left: 15px;
+ }
+ </style>
+ <link rel="icon" type="image/png" href="icn.png"/>
+ <title>hexagonal grids</title>
+ </head>
+ <body onload="run()" style="background-color: #f8f8ff;">
+ <div class="ctrl">
+ <span>
+ <input type="button" id="step" value="step"/>
+ <input type="button" id="playpause" value="play"/>
+ <input type="range" id="tempo" value="150" min="20" max="500"/>
+ <input type="button" id="clear" value="clear"/>
+ <input type="button" id="reset" value="reset"/>
+ </span>
+ <span>
+ <input type="number" id="rad" value="9" min="1" max="30" step="1"/>
+ <input type="button" id="rot" value="rotate hex"/>
+ <input type="button" id="flp" value="flip hex"/>
+ <input type="button" id="rfl" value="reflect hex"/>
+ <input type="button" id="copy" value="copy hex"/>
+ <input type="button" id="paste" value="paste hex"/>
+ </span>
+ <span>
+ <input type="number" id="slot" value="1" min="1" max="9" step="1"/>
+ <input type="button" id="save" value="save state"/>
+ <input type="button" id="load" value="load state"/>
+ </span>
+ <span>
+ <input type="button" id="es" value="export state"/>
+ <input type="button" id="is" value="import state"/>
+ </span>
+ <details><summary></summary><span id="outzone"></span></details>
+ </div>
+ <div><canvas width="2000" height="2000" id="c"></canvas></div>
+ </body>
+</html>
--- /dev/null
+// rps automata, seen via https://twitter.com/matthen2/status/1543226575604355072?s=20&t=a35j4jJSqZ6gY8BjybAFDw
+
+let NEIGHBOR_LIMIT = 2;
+
+const RPSState = {
+ OFF: 'off',
+ ROCK: 'rock',
+ PAPER: 'paper',
+ SCISSORS:'scissors'
+}
+Object.freeze(RPSState);
+
+function nextKey(k,fwd=true) {
+ switch(k) {
+ case RPSState.OFF:
+ return fwd? RPSState.ROCK : RPSState.SCISSORS;
+ case RPSState.ROCK:
+ return fwd? RPSState.PAPER: RPSState.OFF;
+ case RPSState.PAPER:
+ return fwd? RPSState.SCISSORS : RPSState.ROCK;
+ case RPSState.SCISSORS:
+ default:
+ return fwd? RPSState.OFF : RPSState.PAPER;
+ }
+}
+
+class RPSHex extends Hex {
+ constructor(state,q,r,s) {
+ super(q,r,s);
+ this.state = state;
+ }
+ clone() {
+ return new RPSHex(this.state,this.q,this.r,this.s);
+ }
+ static fromJSON(o) {
+ return new RPSHex(o.state, o.q, o.r);
+ }
+ drawState(ctx,x,y,sz) {
+ switch(this.state) {
+ case RPSState.ROCK:
+ drawHexagon(ctx,x,y,sz,'rgb(255,0,0)');
+ break;
+ case RPSState.PAPER:
+ drawHexagon(ctx,x,y,sz,'rgb(0,255,0)');
+ break;
+ case RPSState.SCISSORS:
+ drawHexagon(ctx,x,y,sz,'rgb(0,0,255)');
+ break;
+ case RPSState.OFF:
+ default:
+ return;
+ }
+ }
+ drawStateBad(ctx, x, y, sz) {
+ ctx.save();
+ switch(this.state) {
+ case RPSState.OFF:
+ ctx.fillStyle = 'rgb(0,0,0)';
+ ctx.restore();
+ return;
+ break;
+ case RPSState.ROCK:
+ ctx.fillStyle = 'rgb(255,0,0)';
+ break;
+ case RPSState.PAPER:
+ ctx.fillStyle = 'rgb(0,255,0)';
+ break;
+ case RPSState.SCISSORS:
+ default:
+ ctx.fillStyle = 'rgb(0,0,255)';
+ break;
+ }
+ ctx.beginPath();
+ let r = sz * Math.sqrt(3)/2 * 0.9;
+ ctx.arc(x,y,r,0,Math.PI*2,true);
+ ctx.fill();
+ ctx.stroke();
+ ctx.restore();
+ }
+};
+
+class RPSGrid extends Grid {
+ constructor() {
+ super(RPSHex, RPSState.OFF);
+ }
+ step() {
+ let cellcp = this.cells.map(c=>c.clone());
+ function stateAtClone(cl,q,r) {
+ let cells = cl.filter(c => c.q==q && c.r==r);
+ if(cells.length == 0) return RPSState.OFF;
+ return cells[0].state;
+ }
+ for(var i=0; i<cellcp.length;i++) {
+ //console.log(i);
+ let nbrs = cellcp[i].neighbors();
+ switch(cellcp[i].state) {
+ case RPSState.ROCK:
+ var papers = 0;
+ for(var j=0;j<6;j++) {
+ var st8 = stateAtClone(cellcp, nbrs[j].q, nbrs[j].r);
+ if(st8 == RPSState.PAPER) papers++;
+ //console.log(st8);
+ }
+ if(papers >= NEIGHBOR_LIMIT) {
+ this.cells[i].state = RPSState.PAPER;
+ }
+ break;
+ case RPSState.PAPER:
+ var scissors = 0;
+ for(var j=0;j<6;j++) {
+ var st8 = stateAtClone(cellcp, nbrs[j].q, nbrs[j].r);
+ if(st8 == RPSState.SCISSORS) scissors++;
+ }
+ if(scissors >= NEIGHBOR_LIMIT) {
+ this.cells[i].state = RPSState.SCISSORS;
+ }
+ break;
+ case RPSState.SCISSORS:
+ var rocks = 0;
+ for(var j=0;j<6;j++) {
+ var st8 = stateAtClone(cellcp, nbrs[j].q, nbrs[j].r);
+ if(st8 == RPSState.ROCK) rocks++;
+ }
+ if(rocks >= NEIGHBOR_LIMIT) {
+ this.cells[i].state = RPSState.ROCK;
+ }
+ break;
+ case RPSState.OFF:
+ default:
+ break;
+ }
+ }
+ }
+}
+
+g = new RPSGrid();
+
+
+function randomize(q,r,g) {
+ // puts random elements in the grid
+ for(let i=0;i<q;i++) {
+ for(let j=0; j<r;j++) {
+ let rv = Math.floor(Math.random()*3);
+ if(rv < 1) {
+ g.change(RPSState.ROCK,i,j);
+ }
+ else if(rv<2) {
+ g.change(RPSState.PAPER,i,j);
+ }
+ else {
+ g.change(RPSState.SCISSORS,i,j);
+ }
+ }
+ }
+}