0

I'm currently working on a Pairs game in JavaScript but I don't know how to use the picture only two times not more.

I've made an Array of objects with the pictures that have as key ID, src and a name. I loop through this array to have the id and use it in the template in a random way. So I don't have the same picture next to each other.

This is the current state of my code :

for (let index = 0 ; index < 18; index++) {
    let x = Math.round(Math.random() * 8);
    
    let template = document.getElementsByTagName('template')[0];
    let target = document.getElementById('Playground');
    let clone = template.content.cloneNode(true);

    clone.querySelector("img").src= item[x].src;
    clone.querySelector("img").alt = item[x].id;
    clone.querySelector('button').setAttribute('id', i++);
    clone.querySelector('button').setAttribute('Onclick', null);

    target.appendChild(clone);
};

I think the issue is the x variable that generate randomly a picture in the template. But the same picture return more than or less than 2 time or some pictures aren't used.

Can someone help me please ?

2
  • 1
    Create an array like [1,1,2,2,3,3...]. Then shuffle the array. Then loop through the shuffled array. Commented Nov 17, 2021 at 13:20
  • How to shuffle an array Commented Nov 17, 2021 at 13:21

2 Answers 2

1

From your posted code, I think all unique elements are in an array called items. To make sure you have 2 copies of each, you can create a new array that includes items twice:

const doubleItems = items.concat(items);

To get a random element from an array, you can write:

const randomIndex = Math.floor(Math.random() * doubleItems.length);
const randomElement = doubleItems[randomIndex];

(Notice the .floor, not .round!)

To make sure you don't accidentally keep fetching the same random element multiple times, you can remove every element you pick from your source:

doubleItems.splice(randomIndex, 1);

If you do this in a while loop, you can keep on going until all elements are placed!

const items = [
  { src: "A" },
  { src: "B" },
  { src: "C" },
  { src: "D" },
  { src: "E" },
  { src: "F" },
  { src: "G" },
  { src: "H" }
];

const doubleItems = items.concat(items);

while (doubleItems.length) {
  // Pick random element
  const randomIndex = Math.floor(Math.random() * doubleItems.length);
  const randomElement = doubleItems[randomIndex];
  
  // Remove element
  doubleItems.splice(randomIndex, 1);
  
  // Simplified for brevity
  const tile = document.createElement("div");
  tile.innerText = randomElement.src;
  document.body.appendChild(tile);
  
}
body {
  display: grid;
  grid-template-columns: 30px 30px 30px 30px;
  grid-template-rows: 30px 30px 30px 30px;
  grid-gap: 4px;
}

div {
  border: 1px solid black;
  display: flex;
  align-items: center;
  justify-content: center;
}

Sign up to request clarification or add additional context in comments.

Comments

0

The best way to think about it if you have a finite number of items is that you either create the array you need for your final solution (Option A) or to find a way to exclude items from random selection after you don't want them anymore (Option B).

First I will refactor your template copy into a function called render, and your total iterations (18) and maxIndex (8) into constants:

const render = (currItem, index) => {
    let template = document.getElementsByTagName('template')[0];
    let target = document.getElementById('Playground');
    let clone = template.content.cloneNode(true);

    clone.querySelector("img").src= currItem.src;
    clone.querySelector("img").alt = currItem.id;
    clone.querySelector('button').setAttribute('id', index);
    clone.querySelector('button').setAttribute('onClick', null);

    target.appendChild(clone);
};
// The following variables will make your code dynamic, so if you add more items to the game it will continue working correctly
const total = item.length * 2; 
const maxIndex = item.length - 1;

Option A

This option is for educational purposes and as you have a finite list it may be OK, but it is less performatic.

const items = [...item, ...item]; // this is creating a new array with all items twice

// shuffle the items in the array
for (let i = items.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [items[i], items[j]] = [items[j], items[i]];
}

// The array is shuffled with 2 instances of each item, so this will show the desired values
items.forEach(render)

Option B

In this option we will create a second array that exclude items that were used twice.

const rendered = [] // will store items rendered once
while(item.length > 0) { // will run until item has no element
    let index = Math.round(Math.random() * (item.length - 1)); // max index is based on the current length of item array
    const currItem = item[index];
    render(currItem, index);
    if (rendered.includes(currItem)) {
        item.splice(index, 1); // remove item after the second time it is rendered
    } else {
        rendered.push(currItem); // add to the rendered array after the first pass
    }
}

2 Comments

Thanks for your answer. It' help a lot
@StephanieAn Thank you. Don’t forget to mark one answer as the correct answer and upvote it. This help others looking for similar problems.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.