node-ai-boilerplate-nahid/src/games/SlidingBlockPuzzle.js
"use strict";
module.exports.SlidingBlockPuzzle = class
{
/**
* @param {number} width
* @param {number} height
*/
constructor(width, height)
{
this.width = width;
this.height = height;
}
createRandomInitialState()
{
let state = this.createGoalState();
let steps = 50;
while (steps-- > 0)
{
let actions = this.getActions(state);
let action = actions[Math.floor(actions.length * Math.random())];
state = this.performAction(state, action);
}
return state;
}
createGoalState()
{
let state = {
width: this.width,
height: this.height,
length: this.width * this.height
};
for (let index = 0; index < state.length; index++)
{
state[index] = index;
}
return state;
}
print(state)
{
let output = [];
for (let yIndex = 0; yIndex < state.height; yIndex++)
{
let line = [];
for (let xIndex = 0; xIndex < state.width; xIndex++)
{
line.push(state[yIndex * state.width + xIndex]);
}
output.push(line.join(' '))
}
return output.join('\n')
}
isGoalState(state, goal)
{
for (let index = 0; index < state.length; index++)
{
if (state[index] !== goal[index])
{
return false;
}
}
return true;
}
getActions(state)
{
const actions = [];
const emptyIndex = Array.prototype.indexOf.call(state, 0)
const emptyX = emptyIndex % state.width;
const emptyY = (emptyIndex - emptyX) / state.width;
if (emptyX > 0)
{
actions.push(emptyIndex - 1)
}
if (emptyX + 1 < state.width)
{
actions.push(emptyIndex + 1)
}
if (emptyY > 0)
{
actions.push(emptyIndex - state.width)
}
if (emptyY + 1 < state.height)
{
actions.push(emptyIndex + state.width)
}
return actions;
}
performAction(state, action)
{
state = Object.assign({}, state);
const emptyIndex = Array.prototype.indexOf.call(state, 0)
state[emptyIndex] = state[action];
state[action] = 0;
return state;
}
stateSerialise(state)
{
return Array.prototype.join.call(state, ',');
}
}