A simple puzzle solver for Garland Assemble mini-game found in Christmas Town in Torn.com
Click on any puzzle piece to rotate it. Use the solve button to automatically solve the puzzle using the GarlandSolver library.
The GarlandSolver uses a constraint satisfaction algorithm to:
The execution time will be higher in Torn.com's live environment because of other javascript code and other resources also running on the page. So far in my testing, the maximum time taken in live environment is yet to exceed 700ms.
If the puzzle sample you are using is taking much longer, try increasing the argument in
this.#createAdjsNreducePossibilities(3); in "solve" method in the lib's code to 4
or 5.
async solve() {
return new Promise((resolve, reject) => {
try {
this.#createPossibleOptions();
this.#createAdjsNreducePossibilities(3); // increase this to 4 or 5 if your puzzle sample is taking too long to solve.
const solution = this.#generateCombinations();
if (solution) {
resolve(solution);
} else {
reject("No solution found.");
}
} catch (err) {
reject(err);
}
});
}
I have set it by default to 3 because in my testing I found that on increasing it after the 3rd time, it did not reduce the possible solutions dramatically.
Running it 3 times was enough to get possibilities from 2 million to less than
40 thousand. Running it more than that, did not contribute much in terms of
performance.
// Create solver instance with puzzle data
const solver = new GarlandSolver(puzzleData);
// Solve the puzzle
solver.solve()
.then(solution => {
console.log("Solution found!!");
console.log(solution);
})
.catch(err => {
console.error("Error solving:", err);
});
Garland Assemble game is
started.
The response of this call in JSON form, is the input puzzleData of the
GarlandSolver.js
function calculateClicks(originalGrid, solutionGrid) {
let a = 0;
let b = 0;
const array = [];
for (let i = 0; i < 25; i++) {
let clicks = 0;
const orig_cell = originalGrid.tails[a][b];
const sol_cell = solutionGrid.tails[a][b];
if (orig_cell !== null && sol_cell !== null) {
const img = orig_cell.imageName;
if (!img.includes("cross")) {
const orig_rot = normaliseRotationValue(orig_cell.rotation, img);
const sol_rot = normaliseRotationValue(sol_cell.rotation, img);
if (orig_rot !== sol_rot) {
if (img.includes("straight")) {
clicks += 1;
} else if (img.includes("angle")) {
if (orig_rot > sol_rot) {
clicks += ((360 - orig_rot) / 90) + (sol_rot / 90)
} else {
clicks += (sol_rot - orig_rot) / 90;
}
}
}
}
}
if (clicks > 0) {
array.push([a, b, clicks]);
}
b += 1;
if (b === 5) {
b = 0;
a += 1;
}
}
return array;
}
function normaliseRotationValue(rotation, img) {
let rot = rotation;
if (rot >= 360) {
while (rot >= 360) {
rot -= 360;
}
}
if (img.includes("straight")) {
if (rot === 0 || rot === 180) {
rot = 0;
} else if (rot === 270 || rot === 90) {
rot = 90;
}
} else if (img.includes("angle")) {
if (rot === 0) {
rot = 360;
}
}
return rot;
}
// The calculateClicks function will return an array of arrays that looks like the one below.
// It only contains cells that need to be rotated in order to reach the solution state.
//First 2 elements of each array represent the x and y coordinates of the cell.
//The 3rd element of each array represents the number of clicks required to rotate the cell to its final/required state.
[
[
0,
3,
3
],
[
0,
4,
1
],
[
1,
0,
1
],
[
1,
2,
1
],
[
1,
4,
3
],
[
2,
0,
3
],
[
3,
0,
1
],
[
3,
2,
3
],
[
3,
3,
1
],
[
4,
0,
3
]
]