As said in the comments, create an array of all values, then shuffle it
First, see this answer to another question about shuffling an array.
...All read up? Cool. You can change your data structure to be a single array (instead of separate rows and columns arrays) so one of those shuffle()
functions can be easily plugged in.
Also mentioned by others here, you will want to support multiple cell states. You don't need to define them all in the beginning, but you'll need some grouping of data that represents a cell - preferably all in one place in your code. For example, see createCell()
in the snippet below...
I don't see an immediate need for it, but you might want to identify a cell using its row and/or column number, so I've included an extra example function called buildTable()
to demonstrate how to do that (and to see the cells), just in case.
// Mostly from https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array
const shuffle = (a) => {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[a[i], a[j]] = [a[j], a[i]]
}
return a
}
const createCell = (isMine = false) => ({
isMine,
flagged: false,
revealed: false,
})
const buildCells = (width, height, mineCount = 1) => {
// Create a single array
const isMineArray = new Array(width * height)
// Place "mines" at the start of the cells array
for (let i=0; i<mineCount; i++) isMineArray[i] = true
// Randomize mines
shuffle(isMineArray)
// Map booleans to fully-fledged cell objects
return isMineArray.map(createCell)
}
// ---
// Example usage - Visualize the random cell data by building a <table>
const buildTable = (tableSelector, width, height, mineCount) => {
// Run our function from above to make the cells array
const cells = buildCells(width, height, mineCount)
// Get (and empty) the table
const tableElement = document.querySelector(tableSelector)
while (tableElement.firstChild) {
tableElement.firstChild.remove()
}
// Put cells (and rows) into the table
let rowElement
cells.forEach((cell, idx) => {
// Here's how to get col and row as separate indexes, if you need them
const colIdx = idx % width
const rowIdx = (idx - colIdx) / width
// If first cell in the row, add a new <tr>
if (colIdx === 0) {
rowElement = document.createElement('tr')
tableElement.appendChild(rowElement)
}
// Add a <td> w/ classes for visual effect
const tdElement = document.createElement('td')
tdElement.classList.add('cell')
tdElement.textContent = `${colIdx}, ${rowIdx}`
if (cell.isMine) tdElement.classList.add('mine')
rowElement.appendChild(tdElement)
})
}
buildTable('.js-table', 4, 5, 6)
.cell {
border: 1px solid black;
}
.mine {
background: red;
}
<table class="js-table"></table>