Search code examples
javascripthtml-tablegrid

How to Ensure that no Two Adjacent Cells Have Same Color?


I am trying to build a small shuffle the question book app thing. Where I take the input of number of rows and columns along with quantity of each color question book.

What I want is to distribute the colors in a way that no two adjacent cells have same colors and are shuffled enough. What I have now won't be considering the adjacent cells before placing any color. Can someone please help?

Here is the Fiddle Link https://jsfiddle.net/8h4erzpu/

Here is the JS Code

function distributeBooks() {
  const rows = parseInt(document.getElementById("rows").value);
  const cols = parseInt(document.getElementById("cols").value);
  const whiteCount = parseInt(document.getElementById("whiteCount").value);
  const pinkCount = parseInt(document.getElementById("pinkCount").value);
  const greenCount = parseInt(document.getElementById("greenCount").value);
  const yellowCount = parseInt(document.getElementById("yellowCount").value);

  const totalStudents = rows * cols;
  const colors = [];

  for (let i = 0; i < whiteCount; i++) colors.push("white");
  for (let i = 0; i < pinkCount; i++) colors.push("pink");
  for (let i = 0; i < greenCount; i++) colors.push("green");
  for (let i = 0; i < yellowCount; i++) colors.push("yellow");

  if (colors.length < totalStudents) {
    alert("Not enough colors to distribute to all students!");
    return;
  }

  // Shuffle the colors array to add randomness
  for (let i = colors.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [colors[i], colors[j]] = [colors[j], colors[i]];
  }

  const table = document.getElementById("distributionTable");
  table.innerHTML = "";

  for (let row = 0; row < rows; row++) {
    const tr = document.createElement("tr");

    for (let col = 0; col < cols; col++) {
      const td = document.createElement("td");
      td.className = colors.pop();
      tr.appendChild(td);
    }

    table.appendChild(tr);
  }
}

Solution

  • Pseudocode:

    1. Before placing a color into a cell, check if it's the first cell (i.e., row = 0 and col = 0). If it's not the first cell, I proceed with the check for adjacent cells. This condition is added to avoid the alert issue during the first iteration.

      if (row > 0 || col > 0) {
      // Check adjacent cells for color
      }
      
    2. Inside the condition, I retrieve the colors of the left and top adjacent cells, if they exist. If there is no left or top cell, I set the respective variable to null.

      let leftColor = col > 0 ? tr.children[col - 1].className : null;
      let topColor = row > 0 ? table.children[row - 1].children[col].className : null;
      
    3. I created a new array called availableColors by filtering out the left and top adjacent colors from the original colors array. This ensures that we only place colors that are not the same as the left or top adjacent cells.

       availableColors = colors.filter(color => color !== leftColor && color !== topColor);
      
    4. I then checked if the availableColors array is empty.
      4.1. If it is, it means it's not possible to distribute the books without adjacent cells having the same color. In that case, I show an alert and return from the function.

       if (availableColors.length === 0) {
               alert("Not possible to distribute books without adjacent colors!");
               return;
             }
      

      4.2. If there are available colors, I randomly select one of them and set it as the current cell's color. Now here is the place you can randomize the shuffle. There are some randome libraries as well.

      let randomIndex = Math.floor(Math.random() * availableColors.length);
            let selectedColor = availableColors[randomIndex];
            td.className = selectedColor;
      
    5. Lastely, I removed the selected color from the colors array to ensure that it's not used again.

       colors.splice(colors.indexOf(selectedColor), 1);
      

    As per you second follow-up question, I have added a shuffle function(temporary one)Step 4.2 to provide more randomness which you can optimize as well for your purpose.

    function shuffleArray(array) {
      for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
      }
      return array;
    }
    

    There are some cool libraries for shuffling and randomizing which you may want to take a look at:

    CODE DEMO