let height = 10 let width = 10 let mine_rate = 0.10 let current_time = undefined let interval = undefined let mines = [] let visited = [] let running = false function isValidPos(y, x) { return y >= 0 && y < height && x >= 0 && x < width } function* neighboursGenerator(y, x) { for (let i = -1; i <= 1; i++) { for (let j = -1; j <= 1; j++) { if (i === 0 && j === 0) continue let ret = [y + i, x + j] if (!isValidPos(...ret)) continue yield ret } } } let cells = [] function floodFill(y, x) { visited[y][x] = true let cell = cells[y][x] cell.classList.add("visited") if (mines[y][x] !== 0) cell.textContent = mines[y][x] switch (mines[y][x]) { case 1: cell.style.color = "blue"; break case 2: cell.style.color = "green"; break case 3: cell.style.color = "red"; break case 4: cell.style.color = "darkblue"; break case 5: cell.style.color = "brown"; break case 6: cell.style.color = "cyan"; break case 7: cell.style.color = "black"; break case 8: cell.style.color = "grey"; break } if (mines[y][x] !== 0) return for (let [ny, nx] of neighboursGenerator(y, x)) if (!visited[ny][nx]) floodFill(ny, nx) } const timer_node = document.getElementById("minesweeper-timer") const flash_message_node = document.getElementById("flash-message") flash_message_node.addEventListener("click", () => { flash_message_node.hidden = true }) function endFlashMessage(won) { flash_message_node.textContent = `You ${won ? "won" : "lost"} in ${timer_node.textContent} seconds` flash_message_node.classList.remove("flash-won") flash_message_node.classList.remove("flash-lost") flash_message_node.classList.add(won ? "flash-won" : "flash-lost") flash_message_node.hidden = false; setTimeout(() => { flash_message_node.hidden = true }, 5000) } function initGrid() { let table = document.getElementById("minesweeper-table") table.innerHTML = "" width = document.getElementById("minesweeper-width").value height = document.getElementById("minesweeper-height").value mine_rate = document.getElementById("minesweeper-mine-rate").value / 100 mines = [] visited = [] cells = [] for (let i = 0; i < height; i++) { mines.push(new Array(width)) visited.push(new Array(width)) for (let j = 0; j < width; j++) { mines[i][j] = Math.random() < mine_rate ? -1 : 0; visited[i][j] = false; } } // count neighbour mines for (let i = 0; i < height; i++) { for (let j = 0; j < width; j++) { if (mines[i][j] === -1) continue mines[i][j] = [...neighboursGenerator(i, j)] .filter(([y, x]) => mines[y][x] === -1) .length } } for (let i = 0; i < height; i++) { cells.push([]) let table_row = document.createElement("tr") for (let j = 0; j < width; j++) { let table_cell = document.createElement("td") let cell = document.createElement("button") cell.textContent = "" cell.addEventListener("click", () => { if (!running) toggleRunning() if (cell.classList.contains("flagged")) { cell.classList.remove("flagged") return } if (mines[i][j] === -1) { toggleRunning() endFlashMessage(false) } else { floodFill(i, j) won = true; for (let y = 0; y < width; y++) { for (let x = 0; x < width; x++) { if (mines[y][x] !== -1 && !cells[y][x].classList.contains("visited")) won = false } } if (won) { toggleRunning() endFlashMessage(true) } } }) cell.addEventListener("contextmenu", event => { event.preventDefault() if (!running) toggleRunning() if (cell.classList.contains("visited")) return if (cell.classList.contains("flagged")) cell.classList.remove("flagged") else cell.classList.add("flagged") }) cells[i].push(cell) table_cell.appendChild(cell) table_row.appendChild(table_cell) } table.appendChild(table_row) } } for (input of document.querySelectorAll("#minesweeper-settings input")) { input.addEventListener("input", () => initGrid()) } function toggleRunning() { flash_message_node.hidden = true; if (running) { clearInterval(interval) initGrid() } else { current_time = 0 timer_node.textContent = current_time interval = setInterval(() => { timer_node.textContent = current_time current_time++ }, 1000) } running = !running let settings = document.getElementById("minesweeper-settings") let info = document.getElementById("minesweeper-game-info") settings.hidden = !settings.hidden info.hidden = !info.hidden } document.getElementById("minesweeper-stop").addEventListener("click", toggleRunning) initGrid()