I want to move a block to the empty space (swap the block with the empty block) when clicked.
But before doing so, I have to verify if the block is movable.
I'm confused with how to apply the logic here. I've seen many solutions but I don't really understand and others used jQuery plus I don't still get it :-/
I need some help please.
Here's what I tried doing:
randomOrder.forEach((el, i) => {
const block = document.createElement('div')
block.classList.add('block')
block.id = "b" + el
block.style.left = (4 + 98 * (i % 3)) + 'px'
block.style.top = (4 + 98 * Math.floor(i / 3)) + 'px'
block.innerText = el == 9 ? "" : el
if(el == 9){ freeBlock = block }
container.appendChild(block)
}) //////////////
// get clicked block
container.addEventListener("click", (e) => {
var clickedBlock = e.target
if (e.target.id === "b9") {
return 0
}
else {
moveBlock(clickedBlock)
}
}) ///////////////
// Function To Move Block
function moveBlock(clickedBlock) {
// check if the block is movable before moving it
if (isMovable(clickedBlock)) {
console.log(clickedBlock.offsetLeft)
}
} /////////////// end moveBlock()
// check if block is movable
function isMovable(clickedBlock) {
if(clickedBlock.offsetLeft === freeBlock.offsetLeft){
if(clickedBlock.offsetTop === freeBlock.offsetTop){
return true
}
}
} //////////// end check if movable
Anyone knows how I can do this ?
I would first change two things in your setup:
Don't create a block in the DOM for value 9. Just only make blocks for the other values (8 blocks)
Keep the array from which you created the blocks updated with every move. It is good practice to keep the state of the game in variables and treat the display as a sort of side effect.
Here is how you could do it:
const container = document.querySelector(".container");
const state = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function moveBlockTo(block, i) {
block.style.left = (4 + 98*(i % 3)) + 'px';
block.style.top = (4 + 98*Math.floor(i / 3)) + 'px';
}
state.forEach((el, i) => {
if (el == 9) return; // No block is created for 9
const block = document.createElement('div');
block.classList.add('block');
moveBlockTo(block, i);
block.innerText = el;
container.appendChild(block);
});
container.addEventListener("click", e => {
const block = e.target;
if (!block.classList.contains('block')) return; // Click was not on a block
// Get the position of the block as an index in the array
const i = state.indexOf(+block.innerText);
const gap = state.indexOf(9);
// Check that the gap is neighboring the clicked tile
if (i % 3 > 0 && i - 1 === gap ||
i % 3 < 2 && i + 1 === gap ||
i - 3 === gap ||
i + 3 === gap) {
// It is a valid move, so now swap the gap with the clicked tile
// 1. Adapt state to reflect the move
[state[i], state[gap]] = [state[gap], state[i]];
// 2. Move the block in the gap
moveBlockTo(block, gap);
}
});
.container{
width: 300px;
padding: 3px;
aspect-ratio: 1;
position: relative;
margin: 100px auto;
background: #777;
border-radius: 10px;
box-shadow: 0 0 0 5px #000;
}
.block{
width: 31%;
height: 31%;
margin: 1.5px;
display: flex;
font-size: 3rem;
cursor: pointer;
user-select: none;
background: #eee;
position: absolute;
border-radius: 10px;
align-items: center;
justify-content: center;
border: 2px solid #000;
box-shadow: 0 0 20px #555 inset;
}
<div class="container"></div>