solutions now really solvable

master
Ondřej Hruška 5 years ago
parent e6e23ca218
commit e8b538c8e8
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 432
      script.js

@ -57,11 +57,18 @@ class Board {
this.SCREEN_PAD = 20; this.SCREEN_PAD = 20;
// Orb grid // Orb grid
this.grid = {}; this.grid = [];
this.tiles = {}; this.tiles = [];
this.onOrbClick = (index, orb) => {}; for(let i=0; i<120;i++) {
this.onTileClick = (index) => {}; this.grid[i] = null;
this.tiles[i] = null;
}
this.onOrbClick = (index, orb) => {
};
this.onTileClick = (index) => {
};
this.initOrb(); this.initOrb();
this.initGlyphs(); this.initGlyphs();
@ -163,7 +170,7 @@ class Board {
* @returns {{x: Number, y: Number}} * @returns {{x: Number, y: Number}}
*/ */
gridIndexToXy(index) { gridIndexToXy(index) {
return { x : index % 11, y: Math.floor(index/11) }; return {x: index % 11, y: Math.floor(index / 11)};
} }
/** /**
@ -199,7 +206,7 @@ class Board {
removeOrbByIndex(index, errorIfEmpty = false) { removeOrbByIndex(index, errorIfEmpty = false) {
if (this.grid[index]) { if (this.grid[index]) {
this.$orbs.removeChild(this.grid[index].node); this.$orbs.removeChild(this.grid[index].node);
delete this.grid[index]; this.grid[index] = null;
} else { } else {
if (errorIfEmpty) { if (errorIfEmpty) {
throw new Error(`Position ${index} is already empty.`); throw new Error(`Position ${index} is already empty.`);
@ -437,7 +444,7 @@ class Board {
cx="13.229166" cx="13.229166"
fill="url(#radGradSlotBg)" /> fill="url(#radGradSlotBg)" />
</g> </g>
`, { class: 'tile' }); `, {class: 'tile'});
} }
/** /**
@ -576,7 +583,7 @@ class Rng {
* *
* @param {Number|null} seed * @param {Number|null} seed
*/ */
constructor(seed=null) { constructor(seed = null) {
if (seed === null) { if (seed === null) {
seed = +new Date; seed = +new Date;
} }
@ -603,44 +610,58 @@ class Rng {
t ^= t + Math.imul(t ^ t >>> 7, t | 61); t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296; return ((t ^ t >>> 14) >>> 0) / 4294967296;
} }
/**
* Get next int, inclusive
*
* @param {Number} max
* @return {Number}
*/
nextInt(max) {
return Math.floor((max + 1) * this.next());
}
} }
class Game { class Game {
/** /**
* Init the game * Init the game
*/ */
constructor() { constructor(seed=null) {
this.board = new Board(); this.board = new Board();
this.rng = new Rng(1); if (seed === null) {
seed = +new Date();
}
this.rng = new Rng(seed);
this.layoutTemplates = { this.layoutTemplates = {
// templates apparently all have 55 items // templates apparently all have 55 items
//'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], //'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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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], '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() this.newGame()
@ -720,7 +741,7 @@ class Game {
return tpl return tpl
.sort((a, b) => a - b) .sort((a, b) => a - b)
.map((n) => { .map((n) => {
let { x, y } = this.board.gridIndexToXy(n); let {x, y} = this.board.gridIndexToXy(n);
return this.board.xyToGridIndex(5 + y - x, y); return this.board.xyToGridIndex(5 + y - x, y);
}) })
.sort((a, b) => a - b); .sort((a, b) => a - b);
@ -767,9 +788,13 @@ class Game {
} }
isAvailable(n) { isAvailable(n) {
return this.getNeighbours(n).freeSequence >= 3;
}
getNeighbours(n) {
let {x, y} = this.board.gridIndexToXy(n); let {x, y} = this.board.gridIndexToXy(n);
let neighbors = [ let freeSpaces = [
this.isOutside(x - 1, y) || !this.board.grid[n - 1], this.isOutside(x - 1, y) || !this.board.grid[n - 1],
this.isOutside(x - 1, y - 1) || !this.board.grid[n - 12], this.isOutside(x - 1, y - 1) || !this.board.grid[n - 12],
this.isOutside(x, y - 1) || !this.board.grid[n - 11], this.isOutside(x, y - 1) || !this.board.grid[n - 11],
@ -778,235 +803,246 @@ class Game {
this.isOutside(x, y + 1) || !this.board.grid[n + 11], this.isOutside(x, y + 1) || !this.board.grid[n + 11],
]; ];
neighbors.push(neighbors[0]); let nOccupied = 0;
neighbors.push(neighbors[1]); for (let i = 0; i < 6; i++) {
if (!freeSpaces[i]) {
nOccupied++;
}
}
// if(this.debuggetneigh) console.log(`${x}×${y} #${n}, nocc ${nOccupied} `+JSON.stringify(freeSpaces));
let conseq = 0; let freeSequence = 0;
for (let i = 0; i < neighbors.length; i++) { let maxFreeSequence = 0;
if (neighbors[i]) { for (let i = 0; i < 12; i++) {
conseq++; if (freeSpaces[i % 6]) {
if (conseq === 3) { freeSequence++;
return true; if (freeSequence >= 6) {
maxFreeSequence = freeSequence;
break;
}
if (freeSequence > maxFreeSequence) {
maxFreeSequence = freeSequence;
} }
} else { } else {
conseq = 0; freeSequence = 0;
} }
} }
return false; return {neighbours: nOccupied, freeSequence: maxFreeSequence};
} }
placeOrbs() { placeOrbs(template) {
this.board.removeAllOrbs(); this.board.removeAllOrbs();
let template = this.getRandomTemplate();
let allowed = []; let allowed = [];
for(let i = 0; i <= 120; i++) { for (let i = 0; i <= 120; i++) {
allowed.push(template.includes(i)); let allo = template.includes(i);
if (!template.includes(i)) { allowed.push(allo);
// Highlight pattern shape
if (this.board.tiles[i]) { if (this.board.tiles[i]) {
this.board.$bg.removeChild(this.board.tiles[i]) if (allo) {
this.board.tiles[i].setAttribute('opacity', 1)
} else {
this.board.tiles[i].setAttribute('opacity', 0.6)
} }
} }
} }
let place = (n, symbol) => { const place = (n, symbol) => {
if (!allowed[n]) throw Error(`Position ${n} not allowed by template`); if (!allowed[n]) throw Error(`Position ${n} not allowed by template`);
if (this.board.grid[n]) throw Error(`Position ${n} is occupied`); if (this.board.grid[n]) throw Error(`Position ${n} is occupied`);
this.board.placeOrbByIndex(n, symbol); this.board.placeOrbByIndex(n, symbol);
}; };
let findAvailableIndex = () => { const findAvailableIndexWithNeighbours = (count) => {
let candidates = []; let candidates = [];
for(let i=0; i<template.length;i++) { for (let i = 0; i < template.length; i++) {
let n = template[i]; const n = template[i];
if (!this.board.grid[n] && this.isAvailable(n)) { const neigh = this.getNeighbours(n);
// console.log(neigh);
if (!this.board.grid[n] && neigh.neighbours === count && neigh.freeSequence >= 3) {
candidates.push(n); candidates.push(n);
} }
} }
if (candidates.length) { if (candidates.length) {
return candidates[Math.floor(this.rng.next()*candidates.length)] return candidates[Math.floor(this.rng.next() * candidates.length)]
} else { } else {
throw Error("Failed to find available tile"); return false;
} }
}; };
try { const findAvailableIndex = () => {
for (let i = 6; i >= 0; i--) {
const n = findAvailableIndexWithNeighbours(i);
if (n !== false) return n;
}
place(60, 'gold'); throw Error("Failed to find available tile");
};
let toPlace = [ place(60, 'gold');
// gold is placed always to 60 (5x5)
['salt', 'salt'],
['salt', 'salt'],
['mors', 'vitae'],
['mors', 'vitae'],
['mors', 'vitae'],
['mors', 'vitae'],
['air', 'air'],
['air', 'air'],
['air', 'air'],
['air', 'air'],
['fire', 'fire'],
['fire', 'fire'],
['fire', 'fire'],
['fire', 'fire'],
['water', 'water'],
['water', 'water'],
['water', 'water'],
['water', 'water'],
['earth', 'earth'],
['earth', 'earth'],
['earth', 'earth'],
['earth', 'earth'],
['lead', 'mercury'],
['tin', 'mercury'],
['iron', 'mercury'],
['copper', 'mercury'],
['silver', 'mercury'],
];
let shuffle = (a) => { const toPlace = this.buildPlacementList();
let j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(this.rng.next() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
};
shuffle(toPlace); // unpack the pairs
toPlace = toPlace.reduce((a,c) => { let toPlace_stack = toPlace.reduce((a, c) => {
a.push(c[0]); a.push(c[0]);
a.push(c[1]); a.push(c[1]);
return a; return a;
}, []); }, []);
console.log(toPlace); while (toPlace_stack.length > 0) {
place(findAvailableIndex(), toPlace_stack.pop())
let history = []; }
const maxiter = 1000;
for (let itern = 0; itern < maxiter && toPlace.length > 0; itern++) {
console.log('--- main iteration -----------');
let symbol = toPlace.pop();
console.log('take ' + symbol);
try {
console.log('?place ' + symbol);
let index = findAvailableIndex();
place(index, symbol);
console.log('placed ' + symbol + ' to ' + index);
history.push({symbol, index});
} catch (e) {
toPlace.push(symbol);
console.log('*return unplaced ' + symbol);
console.log('BACKTRACK----------- failed to place ' + symbol);
console.log('toPlace=',toPlace);
console.log('history=',history);
let depth = Math.min(1, history.length);
let backtracking = true;
// let retryAtThisLevel=0;
while (backtracking) {
// if (retryAtThisLevel > 0) {
// retryAtThisLevel = 0;
// depth++;
// console.log(`DEEPER`);
// } else {
// retryAtThisLevel++;
// }
console.log('>>> Attempt backtrack of depth ' + depth); console.info("Found a valid board!");
for (let j = 0; j < depth; j++) { console.log('Solution: ', toPlace);
let hist = history.pop();
this.board.removeOrbByIndex(hist.index, true);
toPlace.push(hist.symbol);
console.log('undo & return (hist) ' + hist.symbol + ' at ' + hist.index)
} }
console.log(`try getting back ${depth + 1} steps`); updateOrbDisabledStatus() {
let successes = 0; for (let n = 0; n < 120; n++) {
for (let j = 0; j <= depth; j++) { if (this.board.grid[n]) {
let symbol = toPlace.pop(); this.board.grid[n].node.classList.toggle('disabled', !this.isAvailable(n));
console.log('take ' + symbol); }
}
itern++;
if (itern > maxiter) {
throw Error("Exceeded max iteration depth");
} }
newGame() {
// this.board.onTileClick = (n) => {
// console.log(n, this.board.gridIndexToXy(n));
// };
const RETRY_IN_TEMPLATE = 100;
const RETRY_NEW_TEMPLATE = 15;
// retry loop, should not be needed if everything is correct
let suc = false;
let numretries = 0;
const alertOnError = false;
for (let i = 0; i < RETRY_NEW_TEMPLATE && !suc; i++) {
console.log('RNG seed is: ' + this.rng.state);
const template = this.getRandomTemplate();
for (let j = 0; j < RETRY_IN_TEMPLATE; j++) {
try { try {
console.log('?place ' + symbol); this.placeOrbs(template);
let index = findAvailableIndex(); suc = true;
place(index, symbol);
history.push({symbol, index});
console.log('placed ' + symbol + ' to ' + index);
if (j === depth) {
console.log('Backtracking success');
backtracking = false;
break; break;
}
successes++;
} catch (e) { } catch (e) {
console.log(`backtrack of depth ${depth} fail`); if (alertOnError) alert('welp');
numretries++;
toPlace.push(symbol); console.warn(e.message);
console.log('return unplaced ' + symbol);
// for(let k = 0; k < successes; k++) {
// let hist = history.pop();
// this.board.removeOrbByIndex(hist.index, true);
// toPlace.push(hist.symbol);
// console.log('undo & return (hist) ' + hist.symbol + ' at ' + hist.index);
// }
console.log(`DEEPER`);
depth++;
break;
} }
} }
if (!suc) {
console.warn("Exhausted all retries for the template, getting a new one");
}
} }
console.log('end of backtrack loop, toPlace=', toPlace); if (!suc) {
console.log('history=',history); alert(`Sorry, could not find a valid board setup after ${numretries} retries.`);
return;
} else {
console.info(`Found valid solution (with ${numretries} retries)`);
} }
this.updateOrbDisabledStatus()
} }
if (toPlace.length > 0) { shuffleArray(a) {
throw Error("Failed to place some symbols: " + JSON.stringify(toPlace)); let j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = this.rng.nextInt(i);
x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
} }
//place(findAvailableIndex(), metals.pop()); buildPlacementList() {
let toPlace = [
['air', 'air'],
['air', 'air'],
['air', 'air'],
['air', 'air'],
} catch (e) { ['fire', 'fire'],
console.error(e) ['fire', 'fire'],
['fire', 'fire'],
['fire', 'fire'],
['water', 'water'],
['water', 'water'],
['water', 'water'],
['water', 'water'],
['earth', 'earth'],
['earth', 'earth'],
['earth', 'earth'],
['earth', 'earth'],
];
for(let i=0;i<120;i++) { let newSaltedPairs = [];
if (!this.board.grid[i] && allowed[i]) { const nsalted = this.rng.nextInt(2);
console.log('Free cell '+i+' -> isAvail? '+this.isAvailable(i), this.board.gridIndexToXy(i)); for (let i = 0; i < nsalted; i++) {
while (true) {
const n = this.rng.nextInt(toPlace.length-1);
if (toPlace[n][1] !== 'salt') {
// console.log(`Pairing ${toPlace[n][1]} with salt.`);
newSaltedPairs.push([toPlace[n][1], 'salt']);
toPlace[n][1] = 'salt';
break;
} }
} }
} }
toPlace = toPlace.concat(newSaltedPairs);
// if we have some salt pairs left
for (let i = 0; i < 2 - nsalted; i++) {
toPlace.push(['salt', 'salt']);
} }
newGame() { // these are always paired like this, and don't support salt
// toPlace = toPlace.concat([
['mors', 'vitae'],
['mors', 'vitae'],
['mors', 'vitae'],
['mors', 'vitae'],
]);
this.board.onTileClick = (n) => { // shuffle the pairs that have random order (i.e. not metals)
console.log(n, this.board.gridIndexToXy(n)); this.shuffleArray(toPlace);
};
// the order here is actually significant, so let's pay attention...
const metals = [
['lead', 'mercury'],
['tin', 'mercury'],
['iron', 'mercury'],
['copper', 'mercury'],
['silver', 'mercury'],
];
let mPos = [];
for (let i = 0; i < metals.length; i++) {
let x;
// find a unique position
do {
x = this.rng.nextInt(toPlace.length + i);
} while (mPos.includes(x));
mPos.push(x)
}
mPos.sort((a, b) => a - b);
// console.log('Metal positions ', mPos);
// inject them into the array
metals.forEach((pair, i) => {
toPlace.splice(mPos[i] + i, 0, pair);
});
this.placeOrbs() return toPlace;
} }
} }

Loading…
Cancel
Save