0

I am just doing some practice with a color alignment game. I want to run a function after every move to see if it's winner but I can't figure out the best way to check if 3 colors are in a row or diagonal. I guess I could iterate down the DOM tree each time and see if certain multiples are fulfilled but I don't know if that's the best option.

(() => {
  let counter = 0;
  let conts = document.querySelectorAll('.parent div')
 
  conts.forEach(x => {
     x.addEventListener('click', (evt) => {  
        if (!!evt.target.style.backgroundColor) {
           return; 
        }
        counter++ 
        if (counter % 2 == 0) {
          evt.target.style.backgroundColor = "red"
        } else {
          evt.target.style.backgroundColor = "green"
        }
     })  
  });
})();
html, body {
  height: 100%;
}
.parent {
  height: 100%;
  display: grid;
  grid-template-columns: 50px 50px 50px;
  grid-template-rows: 50px 50px 50px;
  grid-gap: 2px;
  width: 40%;

}
.parent div {
  border: 1px solid #000;
}
<div class="parent">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>
RicardoAlvveroa
  • 226
  • 1
  • 8
  • check my answer to same question -> https://stackoverflow.com/questions/65463817/how-can-i-check-winners-by-using-jquery/65464363#65464363 – barzin.A Jan 19 '21 at 05:15

2 Answers2

2

you can try something like this.

let counter = 0;
let winner = null;

const match = (player, row, col) => {
  const div = document.querySelector(`.parent #r${row}c${col}`);
  const bgColor = div.style.backgroundColor;
  return bgColor === player;
}

const win_hori = (player, row) => {
  return ![1, 2, 3].some(col => !match(player, row, col));
}

const win_verti = (player, col) => {
  return ![1, 2, 3].some(row => !match(player, row, col));
}

const win_slash = (player) => {
  return match(player, 1, 1) && match(player, 2, 2) && match(player, 3, 3);
}

const win_backslash = (player) => {
  return match(player, 3, 1) && match(player, 2, 2) && match(player, 1, 3);
}

const win = (player, row, col) => {
  return win_hori(player, row) || win_verti(player, col) || win_slash(player) || win_backslash(player);
}

document.querySelectorAll('.parent div').forEach(x => {
  x.addEventListener('click', (evt) => {
    const cell = evt.target;
    if (winner || !!cell.style.backgroundColor) {
      return;
    }
    const player = (++counter % 2 == 0) ? "red" : "green";
    cell.style.backgroundColor = player;
    const row = parseInt(cell.id.substring(1, 2));
    const col = parseInt(cell.id.substring(3, 4));
    if (win(player, row, col)) {
      winner = player;
      document.getElementById("winner").innerText = `${player} wins`;
    }
  })
});
html, body {
  height: 100%;
}
.parent {
  height: 100%;
  display: grid;
  grid-template-columns: 50px 50px 50px;
  grid-template-rows: 50px 50px 50px;
  grid-gap: 2px;
  width: 40%;

}
.parent div {
  border: 1px solid #000;
}
<div id="winner">nobody wins</div>

<div class="parent">
  <div id="r1c1"></div>
  <div id="r1c2"></div>
  <div id="r1c3"></div>
  <div id="r2c1"></div>
  <div id="r2c2"></div>
  <div id="r2c3"></div>
  <div id="r3c1"></div>
  <div id="r3c2"></div>
  <div id="r3c3"></div>
</div>
D. Seah
  • 4,472
  • 1
  • 12
  • 20
2

You could solve this problem traversing the grid horizontally, vertically, and diagonally, in search of a sequence of "boxes" with the same color.

// used to walk the grid
const size = Math.sqrt(boxes.length);

// used to check grid columns, rows, and diagonal
const check = (color, index, inc = 1) => {
  for (let y = 0; y < size; y += inc) {
    let score = 0;
    for (let x = 0; x < size; x++) {
      // index callback to retrieve the next box
      if (boxes[index(x, y)].style.backgroundColor === color)
        score++;
      else
        break;
    }
    if (score === size)
      return true;
  }
  return false;
};

Whenever a color is set, you can perform such check via:

  if (
    // horizontally
    //    0 1 2,
    //    3 4 5,
    //    6 7 8
    check(color, (x, y) => (y * size) + x) ||

    // vertically
    //    0 3 6,
    //    1 4 7,
    //    2 5 8
    check(color, (x, y) => (y + (size * x))) ||

    // diagonally (y loops only once)
    //    0 4 8
    check(color, (x, y) => x * (size + 1), size) ||

    // diagonally (y loops only once)
    //    2 4 6
    check(color, (x, y) => ((x + 1) * (size - 1)), size)
  )
    alert(color + ' won!');

Differently from other answers, this logic is independent of the grid size.

You can see the 3x3 live and play around, but you can also see that without changing code you can have a 4x4 grid too.

The whole code in a nutshell:

(() => {
  let counter = 0;
  const boxes = document.querySelectorAll('.parent div');
  const size = Math.sqrt(boxes.length);

  const check = (color, index, inc = 1) => {
    for (let y = 0; y < size; y += inc) {
      let score = 0;
      for (let x = 0; x < size; x++) {
        if (boxes[index(x, y)].style.backgroundColor === color)
          score++;
        else
          break;
      }
      if (score === size)
        return true;
    }
    return false;
  };

  boxes.forEach(x => {
    x.addEventListener('click', (evt) => {
      if (!!evt.target.style.backgroundColor)
        return;
      counter++;
      const color = counter % 2 ? "green" : "red";
      evt.target.style.backgroundColor = color;
      if (
        check(color, (x, y) => (y * size) + x) ||
        check(color, (x, y) => (y + (size * x))) ||
        check(color, (x, y) => x * (size + 1), size) ||
        check(color, (x, y) => ((x + 1) * (size - 1)), size)
      )
        alert(color + ' won!');
    })
  });
})();

I hope this helps

Andrea Giammarchi
  • 3,038
  • 15
  • 25