add rng and layout templates

master
Ondřej Hruška 5 years ago
parent a11e674226
commit c0e38a4f31
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 2
      index.html
  2. 324
      script.js
  3. 28
      style.css

@ -74,6 +74,6 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="script.js"></script> <script src="script.js"></script>
</body> </body>
</html> </html>

@ -57,7 +57,11 @@ class Board {
this.SCREEN_PAD = 20; this.SCREEN_PAD = 20;
// Orb grid // Orb grid
this.gameGrid = {}; this.grid = {};
this.tiles = {};
this.onOrbClick = (index, orb) => {};
this.onTileClick = (index) => {};
this.initOrb(); this.initOrb();
this.initGlyphs(); this.initGlyphs();
@ -66,8 +70,12 @@ class Board {
this.buildBackground(); this.buildBackground();
this.initAutoScaling(); this.initAutoScaling();
}
// XXX test content /**
* Show all orbs for graphics debugging
*/
testGraphics() {
let o; let o;
this.placeOrb(0, 0, 'salt'); this.placeOrb(0, 0, 'salt');
@ -101,7 +109,6 @@ class Board {
initAutoScaling() { initAutoScaling() {
this.rescaleTimeout = null; this.rescaleTimeout = null;
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
console.log('window resize');
if (this.rescaleTimeout === null) { if (this.rescaleTimeout === null) {
this.rescaleTimeout = setTimeout(() => this.rescaleCanvas(), 60) this.rescaleTimeout = setTimeout(() => this.rescaleCanvas(), 60)
} }
@ -141,19 +148,29 @@ class Board {
/** /**
* Convert grid coordinates to gameGrid array index * Convert grid coordinates to gameGrid array index
* *
* @param x * @param {Number} x
* @param y * @param {Number} y
* @returns {Number} * @returns {Number}
*/ */
gridXyToArrayIndex(x, y) { xyToGridIndex(x, y) {
return y * 11 + x return y * 11 + x
} }
/**
* Convert grid index to X, Y
*
* @param {Number} index
* @returns {{x: Number, y: Number}}
*/
gridIndexToXy(index) {
return { x : index % 11, y: Math.floor(index/11) };
}
/** /**
* Convert grid coordinates to graphic coordinates * Convert grid coordinates to graphic coordinates
* *
* @param x * @param {Number} x
* @param y * @param {Number} y
* @returns {{rx: number, ry: number}} * @returns {{rx: number, ry: number}}
*/ */
gridXyToCoord(x, y) { gridXyToCoord(x, y) {
@ -165,29 +182,51 @@ class Board {
/** /**
* Remove an orb from the grid at the given coordinates. * Remove an orb from the grid at the given coordinates.
* *
* @param x - board X * @param {Number} x - board X
* @param y - board Y * @param {Number} y - board Y
*/ */
removeOrb(x, y) { removeOrb(x, y) {
const arrayIndex = this.gridXyToArrayIndex(x, y); const index = this.xyToGridIndex(x, y);
if (this.gameGrid[arrayIndex]) { this.removeOrbByIndex(index)
this.$orbs.removeChild(this.gameGrid[arrayIndex].node);
this.gameGrid[arrayIndex] = null;
} }
/**
* Remove orb by array index
*
* @param {Number} index
*/
removeOrbByIndex(index) {
if (this.grid[index]) {
this.$orbs.removeChild(this.grid[index].node);
delete this.grid[index];
}
}
/**
* Place an orb by array index
*
* @param {Number} index
* @param {String} symbol
* @return {{node: Node, symbol: String}}
*/
placeOrbByIndex(index, symbol) {
const {x, y} = this.gridIndexToXy(index);
return this.placeOrb(x, y, symbol);
} }
/** /**
* Place an orb on the grid * Place an orb on the grid
* *
* @param x - board X * @param {Number} x - board X
* @param y - board Y * @param {Number} y - board Y
* @param symbol - alchemical symbol name * @param {String} symbol - alchemical symbol name
* @returns {object} - orb object * @returns {{node : Node, symbol: String}} - orb object
*/ */
placeOrb(x, y, symbol) { placeOrb(x, y, symbol) {
const {rx, ry} = this.gridXyToCoord(x, y); const {rx, ry} = this.gridXyToCoord(x, y);
const arrayIndex = this.xyToGridIndex(x, y);
this.removeOrb(x, y); this.removeOrbByIndex(arrayIndex);
let template; let template;
if (this.metals.includes(symbol)) { if (this.metals.includes(symbol)) {
@ -196,23 +235,27 @@ class Board {
template = this.orbTpl; template = this.orbTpl;
} }
let o = template.cloneNode(true); let orb = template.cloneNode(true);
o.classList.add(`element-${symbol}`); orb.classList.add(`symbol-${symbol}`);
o.setAttribute('transform', `translate(${rx},${ry})`); orb.setAttribute('transform', `translate(${rx},${ry})`);
o.querySelector('.orb-fill') orb.querySelector('.orb-fill')
.setAttribute('fill', this.orbColors[symbol]); .setAttribute('fill', this.orbColors[symbol]);
o.appendChild(this.symbolTpls[symbol].cloneNode(true)); orb.appendChild(this.symbolTpls[symbol].cloneNode(true));
this.$orbs.appendChild(o); orb.dataset.index = arrayIndex;
orb.dataset.symbol = symbol;
this.$orbs.appendChild(orb);
const arrayIndex = this.gridXyToArrayIndex(x, y); orb.addEventListener('click', () => {
this.onOrbClick(arrayIndex, orb);
});
let object = { let object = {
node: o, node: orb,
symbol symbol
}; };
this.gameGrid[arrayIndex] = object; this.grid[arrayIndex] = object;
return object; return object;
} }
@ -389,7 +432,7 @@ class Board {
cx="13.229166" cx="13.229166"
fill="url(#radGradSlotBg)" /> fill="url(#radGradSlotBg)" />
</g> </g>
`); `, { class: 'tile' });
} }
/** /**
@ -416,9 +459,16 @@ class Board {
polygon_shadow.setAttribute('transform', `translate(${rx},${ry}),scale(1.1)`); polygon_shadow.setAttribute('transform', `translate(${rx},${ry}),scale(1.1)`);
this.buf0.push(polygon_shadow); this.buf0.push(polygon_shadow);
const index = this.xyToGridIndex(x, y);
let tile = this.tileTpl.cloneNode(true); let tile = this.tileTpl.cloneNode(true);
tile.setAttribute('transform', `translate(${rx},${ry})`); tile.setAttribute('transform', `translate(${rx},${ry})`);
this.buf1.push(tile); this.buf1.push(tile);
tile.addEventListener('click', () => {
this.onTileClick(index);
});
this.tiles[index] = tile;
} }
/** /**
@ -442,7 +492,13 @@ class Board {
vitae: 'm 11.975898,274.8189 0.358077,0.35808 v 2.86461 H 9.4693607 v 1.79038 h 2.8646143 v 3.93885 H 6.9628227 l 6.4453823,8.95192 6.087306,-8.95192 H 14.12436 v -3.93885 h 2.864613 v -1.79038 H 14.12436 v -2.86461 l 0.358076,-0.35808 z m -1.790384,10.38422 h 6.445383 l -3.222692,4.65501 z', vitae: 'm 11.975898,274.8189 0.358077,0.35808 v 2.86461 H 9.4693607 v 1.79038 h 2.8646143 v 3.93885 H 6.9628227 l 6.4453823,8.95192 6.087306,-8.95192 H 14.12436 v -3.93885 h 2.864613 v -1.79038 H 14.12436 v -2.86461 l 0.358076,-0.35808 z m -1.790384,10.38422 h 6.445383 l -3.222692,4.65501 z',
}; };
this.metals = ['mercury', 'lead', 'tin', 'iron', 'copper', 'silver', 'gold']; this.symbols = [
'salt', 'air', 'fire', 'water', 'earth', 'mercury', 'lead',
'tin', 'iron', 'copper', 'silver', 'gold', 'mors', 'vitae'
];
this.metals = [
'mercury', 'lead', 'tin', 'iron', 'copper', 'silver', 'gold'
];
this.orbColors = { this.orbColors = {
salt: '#f2e7b4', salt: '#f2e7b4',
@ -483,8 +539,214 @@ class Board {
} }
} }
} }
/**
* Remove all boards on the board
*/
removeAllOrbs() {
Object.keys(this.grid).forEach((n) => {
this.removeOrbByIndex(n);
})
}
/**
* Get orb by array index
*
* @param {Number} n
* @returns {object} grid object
*/
getOrbByIndex(n) {
return this.grid[n];
}
}
/**
* Random number generator
*
* Uses Mullbery32 from https://stackoverflow.com/a/47593316/2180189
*/
class Rng {
/**
* Construct with a given or random seed
*
* @param {Number|null} seed
*/
constructor(seed=null) {
if (seed === null) {
seed = Math.random();
}
this.seed(seed);
}
/**
* Set seed for following rolls
*
* @param {Number} seed
*/
seed(seed) {
this.state = seed;
}
/**
* Get a pseudo-random number
*
* @returns {Number}
*/
next() {
let t = this.state += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
}
class Game {
/**
* Init the game
*/
constructor() {
this.board = new Board();
this.rng = new Rng();
this.layoutTemplates = {
'wheel': [0,1,2,3,4,5,11,12,13,14,15,16,17,22,23,24,27,28,29,33,34,36,38,40,41,44,45,48,49,52,53,55,56,57,58,59,60,61,62,63,64,65,67,68,71,72,75,76,79,80,82,84,86,87,91,92,93,96,97,98,103,104,105,106,107,108,109,115,116,117,118,119,120],
'beyblade': [0,1,2,3,4,5,12,14,15,23,26,34,35,37,38,39,40,46,47,48,50,51,52,55,58,60,61,64,65,67,68,69,70,71,73,76,79,80,83,84,85,86,87,91,94,95,97,98,103,104,105,106,109,115,120],
'tulip': [4,14,15,16,23,24,25,26,27,28,34,35,36,37,39,40,45,46,47,48,49,50,51,52,56,57,59,60,61,62,63,67,68,69,70,71,72,73,74,80,81,82,83,85,86,93,94,95,96,97,105,106,107,108,109],
'alien': [3,4,14,15,16,22,25,26,27,28,34,35,36,37,38,39,40,41,45,46,48,49,51,56,57,58,59,60,61,62,67,68,69,70,71,72,73,74,79,80,81,82,84,85,86,94,95,96,97,98,106,107,108,109,117],
'cube': [1,5,12,13,14,15,16,17,23,27,29,33,34,35,36,37,38,40,44,48,49,51,52,55,56,57,58,59,60,61,62,64,68,70,71,72,73,75,76,80,82,84,86,92,94,96,97,103,104,105,106,108,118,119,120],
'star': [3,14,15,22,23,24,25,26,27,34,35,36,37,38,39,40,41,46,47,48,49,50,51,52,57,58,59,60,61,62,63,68,69,70,71,72,73,74,79,80,81,82,83,84,85,86,93,94,95,96,97,98,105,106,117],
'flower': [3,11,12,13,14,15,23,25,27,28,34,36,37,38,39,40,45,46,47,48,49,50,52,53,57,58,59,60,61,62,64,68,70,71,72,73,74,75,79,80,81,82,83,84,86,92,95,96,97,98,104,105,106,107,116],
'windmill': [4,11,12,13,14,15,16,23,24,25,27,28,34,37,39,40,45,46,47,48,49,50,52,53,56,57,59,60,61,63,64,67,68,70,71,72,73,74,75,80,81,83,86,92,93,95,96,97,104,105,106,107,108,109,116],
'propeller': [1,2,3,4,13,14,15,16,22,25,28,34,36,37,38,39,40,41,45,46,47,48,50,56,58,60,61,62,67,68,70,71,73,74,75,76,79,80,81,82,83,84,86,87,91,92,95,97,98,103,106,107,108,109,117],
'garden': [0,1,2,3,4,5,11,12,13,14,15,16,17,22,23,28,29,33,34,40,41,44,45,52,53,55,56,60,64,65,67,68,75,76,79,80,86,87,91,92,97,98,103,104,105,106,107,108,109,115,116,117,118,119,120],
'windmill2': [1,12,13,14,15,16,17,23,24,26,27,28,34,35,36,37,38,40,44,45,47,50,51,52,56,57,58,60,62,63,64,68,69,70,73,75,76,80,82,83,84,85,86,92,93,94,96,97,103,104,105,106,107,108,119],
'bird': [2,3,4,14,15,25,27,28,29,33,34,35,36,37,38,39,40,45,46,47,48,49,50,51,57,58,59,60,61,62,67,68,70,71,72,73,74,79,80,81,82,83,84,86,87,91,94,95,96,97,98,106,107,109,118],
'strider': [1,2,3,4,11,12,13,14,15,24,25,26,36,37,38,47,48,49,50,53,58,59,60,61,62,63,64,67,68,69,70,71,72,73,74,75,76,79,80,81,82,83,84,85,86,87,91,92,93,97,98,103,104,109,116],
'campfire': [0,1,2,3,4,5,11,15,17,22,23,24,25,26,27,29,33,35,39,41,44,46,51,52,53,55,57,60,63,65,67,68,69,74,76,79,81,85,87,91,93,94,95,96,97,98,103,105,109,115,116,117,118,119,120],
'skillet': [0,1,2,5,11,13,17,22,25,26,27,28,29,33,37,39,41,44,45,46,47,48,49,50,53,55,57,59,60,61,65,69,70,71,72,73,74,75,76,81,83,85,87,91,92,95,96,103,107,115,116,117,118,119,120],
'digger': [2,3,4,5,16,17,22,27,28,29,33,34,35,36,37,38,39,40,41,44,45,46,47,48,50,51,55,56,57,58,60,61,62,67,70,71,73,79,82,83,84,87,91,94,95,96,98,106,107,108,109,117,118,119,120],
'chestnut': [3,4,12,13,14,15,16,23,25,27,28,34,36,38,39,40,45,46,47,48,49,50,52,56,57,58,59,60,61,62,64,67,68,71,72,74,75,79,80,81,82,83,84,86,92,95,96,97,98,104,105,106,107,108,109],
'manta': [0,5,11,16,22,23,24,25,26,27,28,29,33,34,35,39,40,44,45,46,47,51,52,56,57,58,59,60,61,62,63,64,68,69,73,74,75,76,80,81,85,86,87,91,92,93,94,95,96,97,98,104,109,115,120],
'pyramids': [3,4,14,15,16,23,25,26,28,34,35,36,37,38,39,40,45,46,47,48,49,50,51,52,56,58,59,60,61,62,67,68,69,70,71,72,73,74,79,80,81,82,83,84,85,86,94,95,97,98,105,106,107,108,109],
'bigwheel': [0,1,2,3,4,5,11,13,17,22,25,28,29,33,36,37,38,39,41,44,45,46,47,50,53,55,58,60,62,65,67,70,73,74,75,76,79,81,82,83,84,87,91,92,95,98,103,107,109,115,116,117,118,119,120],
'handshake': [0,1,2,3,4,5,11,12,13,15,16,22,23,24,25,27,33,34,35,36,38,46,48,49,50,58,59,60,61,62,70,71,72,74,82,84,85,86,87,93,95,96,97,98,104,105,107,108,109,115,116,117,118,119,120],
'thinwheel': [0,1,2,3,4,5,11,12,16,17,22,24,27,29,33,36,38,41,44,48,49,53,55,56,57,58,59,60,61,62,63,64,65,67,71,72,76,79,82,84,87,91,93,96,98,103,104,108,109,115,116,117,118,119,120],
'heavywheel':[12,13,14,15,16,23,24,25,27,28,34,36,37,38,39,40,45,46,47,48,49,50,52,56,57,58,59,60,61,62,63,64,68,70,71,72,73,74,75,80,81,82,83,84,86,92,93,95,96,97,104,105,106,107,108],
'virus': [2,3,13,14,15,22,24,26,27,34,35,36,37,38,39,40,41,46,47,48,49,50,51,57,58,59,60,61,62,63,68,69,70,71,72,73,75,79,80,82,83,84,85,86,87,91,92,93,94,95,96,97,98,106,117],
'frisbee': [0,11,12,13,14,15,22,25,26,27,28,29,33,34,36,38,39,40,41,45,46,47,49,50,53,57,58,59,60,62,64,65,68,69,72,74,75,80,81,82,83,84,85,86,92,95,96,97,104,106,107,115,116,117,118],
};
this.newGame()
}
/**
* Show a selected template, for debug
*
* @param {String} name - template name
* @param {boolean} flip - flip horizontally
*/
showTemplate(name, flip = false) {
this.board.removeAllOrbs();
this.getTemplate(name, flip).forEach((n) => {
this.board.placeOrbByIndex(n, 'lead');
})
}
/**
* Show a random template, for debug
*/
showRandomTemplate() {
this.board.removeAllOrbs();
this.getRandomTemplate().forEach((n) => {
this.board.placeOrbByIndex(n, 'lead');
})
}
/**
* Get a template - a sequence of numbers that are allowed as orb positions
*
* @param {String} name
* @param {boolean} flipped
* @returns {number[]}
*/
getTemplate(name, flipped) {
let tpl = this.layoutTemplates[name].slice(0); // this slice takes a copy so the array is not corrupted by later manipulations
if (flipped) {
tpl = this.flipTemplate(tpl);
}
return tpl;
}
/**
* Get a random and randomly flipped template
*
* @return {{template: number[], name: string, flipped: boolean}}
*/
getRandomTemplate() {
let names = Object.keys(this.layoutTemplates);
let name = names[Math.floor(this.rng.next() * names.length)];
let flipped = this.rng.next() > 0.5;
return {
template: this.getTemplate(name, flipped),
name,
flipped
};
}
/**
* Flip a template array.
*
* The array is modified in place!
*
* @param tpl
* @returns {Uint16Array}
*/
flipTemplate(tpl) {
return tpl.sort((a,b) => a-b).map((n) => {
let { x, y } = this.board.gridIndexToXy(n);
return this.board.xyToGridIndex(5 + y - x, y);
});
}
/**
* Print array of all occupied orbs as a layout template
*
* @returns {number[]}
*/
toTemplate() {
return Object.keys(game.board.grid)
.map((x) => +x)
.sort((a, b) => a - b);
}
/**
* Run a template editor
*
* - click on tiles to toggle orbs
* - call `game.showTemplate('wheel')` to show an existing template on the board
* - call `JSON.stringify(game.toTemplate())` to print the current template array to console
*/
templateBuilder() {
this.board.removeAllOrbs();
this.board.onTileClick = (n) => {
let symbol = 'lead';
this.board.placeOrbByIndex(n, symbol);
};
this.board.onOrbClick = (n, orb) => {
this.board.removeOrbByIndex(n)
};
}
newGame() {
//
}
} }
/* Start */ /* Start */
window.board = new Board(); window.game = new Game();

@ -22,20 +22,20 @@ html,body {
opacity: 0.6; opacity: 0.6;
} }
.highlight-salt .element-salt .orb-fill, .highlight-salt .symbol-salt .orb-fill,
.highlight-air .element-air .orb-fill, .highlight-air .symbol-air .orb-fill,
.highlight-fire .element-fire .orb-fill, .highlight-fire .symbol-fire .orb-fill,
.highlight-water .element-water .orb-fill, .highlight-water .symbol-water .orb-fill,
.highlight-earth .element-earth .orb-fill, .highlight-earth .symbol-earth .orb-fill,
.highlight-mercury .element-mercury .orb-fill, .highlight-mercury .symbol-mercury .orb-fill,
.highlight-lead .element-lead .orb-fill, .highlight-lead .symbol-lead .orb-fill,
.highlight-tin .element-tin .orb-fill, .highlight-tin .symbol-tin .orb-fill,
.highlight-iron .element-iron .orb-fill, .highlight-iron .symbol-iron .orb-fill,
.highlight-copper .element-copper .orb-fill, .highlight-copper .symbol-copper .orb-fill,
.highlight-silver .element-silver .orb-fill, .highlight-silver .symbol-silver .orb-fill,
.highlight-gold .element-gold .orb-fill, .highlight-gold .symbol-gold .orb-fill,
.highlight-vitae .element-vitae .orb-fill, .highlight-vitae .symbol-vitae .orb-fill,
.highlight-mors .element-mors .orb-fill { .highlight-mors .symbol-mors .orb-fill {
stroke: yellow; stroke: yellow;
stroke-width: 7px; stroke-width: 7px;
} }

Loading…
Cancel
Save