Grid Stacking Game Issues

Joined
Jun 30, 2023
Messages
1
Reaction score
0
I have a basic grid stacking game. It's not completed yet, but here's what I have so far - Red blocks fall from the top of a 7x7 grid. When the blocks land, they should stay there, and another block spawns. However, this is not the case. When a block lands, another spawns on top of the first. Any ideas?

// JavaScript code

const gridSize = 7;
const cellSize = 30;
const dropInterval = 1000; // Time interval for the square to drop (in milliseconds)

let currentSquare = null;
let intervalId = null;
let blockPositions = [];

// Create a 2D array for the grid
const grid = new Array(gridSize);
for (let i = 0; i < gridSize; i++) {
grid = new Array(gridSize);
}

// Fill the grid with numbers and grid pieces
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (i === 0 && j === 0) {
// Top-left corner
grid[j] = '';
} else if (i === 0) {
// First row (numbers)
grid[j] = j.toString();
} else if (j === 0) {
// First column (numbers)
grid[j] = i.toString();
} else {
// Grid pieces
grid[j] = '';
}
}
}

// Create the HTML grid
const table = document.getElementById('grid');
for (let i = 0; i < gridSize; i++) {
const row = document.createElement('tr');
for (let j = 0; j < gridSize; j++) {
const cell = document.createElement('td');
cell.textContent = grid[j];
row.appendChild(cell);
}
table.appendChild(row);
}

// Spawn a new square
function spawnSquare() {
const spawnX = Math.floor(Math.random() * (gridSize - 1)) + 1; // Randomize the spawn position within the grid
currentSquare = { x: spawnX, y: 1 }; // Initial position of the square

// Check if the position above the current square is available
if (collidesWithBlock()) {
stopFalling();
resetGame();
return; // Stop the game if the position above is occupied
}

drawSquare();
startFalling();
}

// Draw the current square on the grid
function drawSquare() {
const row = table.children[currentSquare.y];
const cell = row.children[currentSquare.x];
cell.classList.add('square');
}

// Remove the current square from the grid
function clearSquare() {
const row = table.children[currentSquare.y];
const cell = row.children[currentSquare.x];
cell.classList.remove('square');
}

// Move the current square down
function moveDown() {
clearSquare();
currentSquare.y++;
drawSquare();

if (collidesWithGrid() || collidesWithBottom() || collidesWithBlock()) {
currentSquare.y--;
stackSquare();
removeCompletedRows();
stopFalling();
spawnSquare();
}
}

// Check if the current square collides with the grid or bottom
function collidesWithGrid() {
const row = table.children[currentSquare.y];
const cell = row.children[currentSquare.x];
return cell.textContent !== '';
}

function collidesWithBottom() {
return currentSquare.y === gridSize - 1;
}

// Check if the current square collides with a block in place
function collidesWithBlock() {
const row = table.children[currentSquare.y + 1];
const cell = row.children[currentSquare.x];
return cell.classList.contains('square');
}

// Stack the current square on top of the grid
function stackSquare() {
const row = table.children[currentSquare.y];
const cell = row.children[currentSquare.x];
cell.classList.add('square');
cell.textContent = 'X'; // Update the cell's text content to 'X'
blockPositions.push({ x: currentSquare.x, y: currentSquare.y });
}

// Remove completed rows from the grid
function removeCompletedRows() {
const completedRows = [];

for (let i = 1; i < gridSize; i++) {
let isRowComplete = true;

for (let j = 1; j < gridSize; j++) {
const row = table.children;
const cell = row.children[j];

if (!cell.classList.contains('square')) {
isRowComplete = false;
break;
}
}

if (isRowComplete) {
completedRows.push(i);
}
}

// Remove completed rows and shift the rows above
for (let i = 0; i < completedRows.length; i++) {
const rowIndex = completedRows;
const row = table.children[rowIndex];
row.remove();
}

// Add new rows at the top
for (let i = 0; i < completedRows.length; i++) {
const newRow = document.createElement('tr');

for (let j = 0; j < gridSize; j++) {
const cell = document.createElement('td');
cell.textContent = grid[0][j];
newRow.appendChild(cell);
}

table.prepend(newRow);
}
}

// Reset the game
function resetGame() {
clearGrid();
currentSquare = null;
blockPositions = [];
}

// Clear the grid
function clearGrid() {
for (let i = 1; i < gridSize; i++) {
const row = table.children;

for (let j = 1; j < gridSize; j++) {
const cell = row.children[j];
cell.classList.remove('square');
cell.textContent = '';
}
}
}

// Start the falling interval
function startFalling() {
intervalId = setInterval(moveDown, dropInterval);
}

// Stop the falling interval
function stopFalling() {
clearInterval(intervalId);
}

// Event listener for keyboard inputs
document.addEventListener('keydown', (event) => {
if (event.key === 'ArrowLeft') {
// Move the square to the left (if there's an empty space)
if (currentSquare.x > 1 && !collidesWithLeftBlock()) {
clearSquare();
currentSquare.x--;
drawSquare();
}
} else if (event.key === 'ArrowRight') {
// Move the square to the right (if there's an empty space)
if (currentSquare.x < gridSize - 1 && !collidesWithRightBlock()) {
clearSquare();
currentSquare.x++;
drawSquare();
}
} else if (event.key === 'ArrowDown') {
// Move the square down instantly
moveDown();
}
});

// Check if the current square collides with a block on the left
function collidesWithLeftBlock() {
const row = table.children[currentSquare.y];
const cell = row.children[currentSquare.x - 1];
return cell.classList.contains('square');
}

// Check if the current square collides with a block on the right
function collidesWithRightBlock() {
const row = table.children[currentSquare.y];
const cell = row.children[currentSquare.x + 1];
return cell.classList.contains('square');
}

// Start the game by spawning the first square
spawnSquare();

Thanks so much!
 
Joined
Jul 4, 2023
Messages
474
Reaction score
58
JavaScript:
const gridSize = 7;
// ...

// Create a 2D array for the grid
const grid = new Array(gridSize);
for (let i = 0; i < gridSize; i++) {
  grid = new Array(gridSize);
}

This part of code not create 2D array. You missed index notation [ ]
Code:
grid[i] = new Array(gridSize);

in this part is the same problem missing index [] notation for 1st dimension.
JavaScript:
// Fill the grid with numbers and grid pieces
for (let i = 0; i < gridSize; i++) {
  for (let j = 0; j < gridSize; j++) {
    if (i === 0 && j === 0) {
      // Top-left corner
      grid[j] = '';
    } else if (i === 0) {
      // First row (numbers)
      grid[j] = j.toString();
    } else if (j === 0) {
      // First column (numbers)
      grid[j] = i.toString();
    } else {
      // Grid pieces
      grid[j] = '';
    }
  }
}

JavaScript:
grid[i][j] = ...
 
Joined
Jul 4, 2023
Messages
474
Reaction score
58
BTW, check this self, may it'll be useful

HTML:
<div class="grid-container">
  <div class="grid"></div>
  <div class="buttons">
    <button data-grid-pattern-name="line">Line</button>
    <button data-grid-pattern-name="cell">Cell</button>
    <button data-grid-pattern-name="full">Full</button>
  </div>
</div>
CSS:
:root {
  --main-padding-025: .25rem;
}
html,
body {
  width: 100%;
  height: 100vh;
  margin: 0;

  display: grid;
  place-content: center;
}
.grid-container {
  width: clamp(300px, 70vw, 600px);
  height: clamp(320px, 80vh, 620px);
  background-color: hsl(0 0% 0% / .5);
  box-sizing: border-box;

  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr 2rem;
  gap: .5rem;
  padding: var(--main-padding-025);
}
.grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-template-rows: repeat(7, 1fr);
  gap: var(--main-padding-025);
  padding: var(--main-padding-025);
  overflow: hidden;
}
.grid div {
  border: 2px solid hsl(0 0% 80% / 1);
  animation: zoom-in-grid 1s ease-out;
}
@keyframes zoom-in-grid {
  from {
    transform: scale(2);
    border-color: hsl(0 0% 100% / 1);
  }
  to {
    transform: scale(1);
    border-color: hsl(0 0% 80% / 1);  
  }
}

.buttons {
  padding: var(--main-padding-025);
  user-select: none;
}
.buttons button {
  width: 4rem;
  border-radius: .25rem;
  font: 500 clamp(.8rem, 1.05vw, .95rem)/1.4 system-ui, monospace;
  font-variant: small-caps;
  cursor: pointer;
  background-color: hsl(0, 100%, 100%); /* white */
  box-shadow: 1px 1px 2px black;
  transition:
    box-shadow 150ms,
    background-color 250ms;
}
.buttons button + button {
  margin-left: var(--main-padding-025);
}
.buttons button:disabled {
  pointer-events: none;
}
.buttons button:active {
  box-shadow: none;
}
.buttons button:hover {
  background-color: hsl(40, 5%, 89%); /* platinum */
}
JavaScript:
const GRID_SIZE = 7,
      CSS_ANIMATION_DELTA = 380, // (milliseconds)
      HTML_GRID_PATTERN = { line:1, cell:2, full:3 },
      TOGGLE_BUTTON = { on:true, off:false };

let mainContainer, container;

// Create a 2D array for the grid ...
const gridArray = new Array(GRID_SIZE)
// ... fill the grid with numbers 0 - means empty cell
                .fill(new Array(GRID_SIZE).fill(0));
//console.log(gridArray);

window.onload = main;

async function main() {
  // Setting the main container
  mainContainer = document.querySelector('.grid-container');
  container = mainContainer.querySelector('.grid');

  // Setting the buttons - Event Delegation
  mainContainer.onclick = menu;
}

async function menu(e) {
  if (e.target.matches('button')) {
    toggleMenuButton({ toggle:TOGGLE_BUTTON.off });
    clearHtmlGrid({ container:container });
    const pattern = HTML_GRID_PATTERN[e.target.dataset.gridPatternName];
    await createHtmlGrid({ pattern:pattern, container:container });
    toggleMenuButton({ toggle:TOGGLE_BUTTON.on });
  }
}

function toggleMenuButton({ toggle }) {
  for (const button of mainContainer.querySelectorAll('button')) {
    if (toggle == TOGGLE_BUTTON.on)
      button.removeAttribute('disabled');
    else
      button.setAttribute('disabled', '');
  }  
}

function createHtmlGrid({ pattern=1, container }) {
  return new Promise((resolve) => {
    let wait = 0;

    if (pattern == 3) { // HTML_GRID_PATTERN.full
      container.innerHTML = '<div></div>'.repeat(GRID_SIZE * GRID_SIZE);
      wait = 1000;
    }

    if (pattern == 2) { // HTML_GRID_PATTERN.cell
      var place_grid = setInterval(() => {
        container.appendChild(document.createElement('div'));
        if (++index == GRID_SIZE * GRID_SIZE) clearInterval(place_grid);
      }, 50, index=0);
      wait = (GRID_SIZE * GRID_SIZE) * 50;
    }

    if (pattern == 1) { // HTML_GRID_PATTERN.line
      var place_grid = setInterval(() => {
        container.insertAdjacentHTML('beforeend', '<div></div>'.repeat(GRID_SIZE));
        if (++index == GRID_SIZE) clearInterval(place_grid);
      }, 250, index=0);
      wait = GRID_SIZE * 250;
    }  

    setTimeout(() => { resolve(); }, wait + CSS_ANIMATION_DELTA);
  });
}

function clearHtmlGrid({ container }) {
  container.innerHTML = null;
}
 
Joined
Jul 4, 2023
Messages
474
Reaction score
58

You should consider to use in your game feature called: "long click". ;)

Check below ...
[ click once on the button or keep mouse button down for a while on the button ]

HTML:
<div class="box">
  <div class="counter">0</div>
  <div class="buttons">
    <button>+1</button>
    <button>-1</button>
  </div>
</div>
CSS:
.box {
  display: grid;
  width: 15rem;
  margin-inline: auto;
  font: 900 3rem/1 system-ui, monospace;
  border: 1px solid hsl(0 0% 0% / .1);
  border-radius: .25rem;
  padding: 1rem;
  user-select: none;
}
.counter {
  text-align: center;
  pointer-events: none;
}
.buttons {
  display: flex;
  justify-content: center;
  gap: 1em;
  margin-top: .5em;
}
button {
  width: 3.5em;
  padding: .25em;
  font: inherit;
  font-size: .4em;
  box-shadow: 1px 1px hsl(0 0% 0% / .6);
  transition: all 120ms;
}
button:active {
  box-shadow: none;
  border-color: hsl(0 0% 0% / .45);
}
JavaScript:
window.onload = load;

function load() {
  const box = document.querySelector('.box'),
        counter = box.querySelector('.counter');

  for (event of ['mousedown', 'mouseup', 'contextmenu'])
    box.addEventListener(event, longClick);

  let intervalID = null, timeoutID = null;

  function longClick(e) {
    if (e.which === 1 && e.target.matches('button')) {
      const delta = (e.target.matches('button:first-child')) ? +1 : -1;

      if (e.type === 'mousedown') {
        counter.textContent = parseInt(counter.textContent) + delta;

        timeoutID = setTimeout(() => {
          intervalID = setInterval(() => {       
            counter.textContent = parseInt(counter.textContent) + delta;
          }, 50); // time in milliseconds between next clicks
        }, 200); // the time in milliseconds after which a long click takes effect
      }

      if (e.type === 'mouseup') {
        clearInterval(intervalID);
        clearInterval(timeoutID)
      }
    }

    if (e.which === 3) { // no contextmenu
      e.preventDefault();
      return false;
    }
  }
}
 
Last edited:

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top