im creating an mines game in javascript in which i generate an random number that is assigned to an div an this div will be an mine, but the problem is that for e.g ( i set the number of mines to 8, instead of getting 8 different mines i get 8 mines but like 2 of them are in the same place so it counts as if i had 6 mines, how can i fix it? here is the important part of the codem, also any tip on how to improve in my code it will be appreciatted as im very new to program)
let tiles = document.getElementsByClassName('tile');
let numMines = 6
for (i = 0; i < 1; i ) {
bomb = [tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))],
tiles[(Math.floor(Math.random ()* tiles.length))]]
bomb.splice(numMines)
bomb[0].style.backgroundColor = "red";
bomb[1].style.backgroundColor = "red";
bomb[2].style.backgroundColor = "red";
bomb[3].style.backgroundColor = "red";
bomb[4].style.backgroundColor = "red";
bomb[5].style.backgroundColor = "red";
bomb[6].style.backgroundColor = "red";
bomb[7].style.backgroundColor = "red";
bomb[8].style.backgroundColor = "red";
bomb[9].style.backgroundColor = "red";
bomb[10].style.backgroundColor = "red";
bomb[11].style.backgroundColor = "red";
bomb[12].style.backgroundColor = "red";
bomb[13].style.backgroundColor = "red";
bomb[14].style.backgroundColor = "red";
bomb[15].style.backgroundColor = "red";
bomb[16].style.backgroundColor = "red";
bomb[17].style.backgroundColor = "red";
bomb[18].style.backgroundColor = "red";
bomb[19].style.backgroundColor = "red";
bomb[20].style.backgroundColor = "red";
bomb[21].style.backgroundColor = "red";
bomb[22].style.backgroundColor = "red";
bomb[23].style.backgroundColor = "red";
bomb[24].style.backgroundColor = "red";
bomb[24].style.backgroundColor = "red";
} ```
CodePudding user response:
Brief explaination:
Here's some logic that solves the problem your facing, its similar to what @Hackytech said, but I chose to keep track of tile indecies instead. You can essentially create n number of bombs and select a unique tile for each bomb. The way you select a unique tile is in the getTileIndex
function, which essentially keeps calling itself until it finds an index that has not already been used and returns that index. Below is an example that doesn't involve the DOM.
For you tiles
would end up being document.getElementsByClassName("tile")
, I made it an array of random numbers just to provide some placeholder information.
You mentioned you were new to programming so here is a more detailed explaination of what's going on line by line. The first line, I'm creating an empty array of size 100 and filling it with null values, so it ends up being [null, null, null ... 100 times]
. I then call .map
which is like a for loop that creates a new array, where each item in the array depends on what's being returned from the callback. Since the callback is item => Math.random()
, I'm returning a random number for each item in the array. I then create a new array called tileIndex
which is where I keep track of what tile indexes have already been used so that we don't overlap tiles. The getTileIndex
function is really what solves your problem. It's a recursive function, it just keeps calling itself until it finds an index that hasn't already been added to the bombs array. It then returns that index. The final line of code creates an empty array of size 27 and fills it with null
. Similar to earlier, this array is then mapped, but instead of returning a random number we return a tile of a unique index by calling our getTileIndex
function which returns a unique tile index.
If you didn't understand something in here feel free to ask in the comments!
const tiles = new Array(100).fill(null).map(item => Math.random());
const tileIndex = [];
function getTileIndex() {
const i = Math.floor(Math.random() * tiles.length);
if(tileIndex.findIndex(item => i == item) < 0) {
tileIndex.push(i);
return i;
}
return getTileIndex();
}
let bombs = new Array(27).fill(null).map(bomb => tiles[getTileIndex()]);
console.log(tiles)
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
One way to be sure you get exactly the right number of randomized mines with no duplicates, is to create a temporary array with all your tiles, then remove each tile as it is randomly selected. First time you're choosing from 24, then 23, then 22, etc.
Here's some sample code:
const { floor, random } = Math;
const tiles = document.getElementsByClassName('tile');
const numMines = 6;
const t2 = [...tiles]; // clone of tiles, since we're going to mutate it
const b = [];
for (let i = 0; i < numMines; i ) {
const j = floor(random() * t2.length);
b.push(t2[j]); // add bomb tile to b array
t2.splice(j, 1); // remove tile from t2
}
// loop through all tiles, make tile red if it's a bomb, black if not
for (const tile of tiles) {
const isMine = b.indexOf(tile) >= 0;
tile.style.backgroundColor = isMine ? 'red' : 'black';
}
And a working example:
const { floor, random } = Math;
const tiles = document.getElementsByClassName('tile');
const numMines = 6;
const t2 = [...tiles];
const b = [];
for (let i = 0; i < numMines; i ) {
const j = floor(random() * t2.length);
b.push(t2[j]);
t2.splice(j, 1);
}
const bomb = [];
for (const tile of tiles) {
const isMine = b.indexOf(tile) >= 0;
bomb.push(isMine);
tile.style.backgroundColor = isMine ? 'red' : 'black';
}
.tile {
display: inline-block;
width: 1em;
height: 1em;
}
<div class='row'>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
</div>
<div class='row'>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
</div>
<div class='row'>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
</div>
<div class='row'>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
</div>
<div class='row'>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
</div>
<div class='row'>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
<span class='tile'></span>
</div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Another approach is to store the relevant IDs in an array, so they are easy to find:
let tiles = document.getElementsByClassName('tile');
let numMines = 6;
let tileIndexes = Array();
let i = 0;
while (i < 6) {
const rand = Math.floor(Math.random() * tiles.length);
if (!tileIndexes.includes(rand)) {
tileIndexes.push(rand);
i ;
}
}
tileIndexes.forEach(e => {
tiles[e].style.backgroundColor = "red";
});
To make sure you get unqiue IDs, add the numbers to an array and make sure every new number is not already in the array. The while loop will iterate until there are numMiners elements in the array.