<html>
<head>
<title>Minesweeper</title>
<style>
.unknown {
background-color: grey;
color: grey;
}
.marked {
background-color: black;
color: black;
}
.mine {
background-color: red;
color: black;
}
.open {
background-color: white;
color: black;
}
td {
border: 1px solid black;
height: 2.5em;
width: 2.2em;
text-align: center;
}
</style>
</head>
<body>
<div>
<p>Width: <input id="width" /></p>
<p>Height: <input id="height" /></p>
<p>Mines: <input id="mines" /></p>
<button id="gen">Generate</button>
</div>
<table id="table">
</table>
<script>
let wd = document.getElementById("width");
let hd = document.getElementById("height");
let md = document.getElementById("mines");
let gb = document.getElementById("gen");
let tb = document.getElementById("table");
let mp = [];
let dx = [-1, 0, 1, -1, 1, -1, 0, 1];
let dy = [-1, -1, -1, 0, 0, 1, 1, 1];
function rf(s)
{
return Math.floor(Math.random() * s);
}
let style = ["unknown", "marked", "mine", "open"];
let mkd = 0, st = 0, ed = 0, gmc = 0;
function end()
{
ed = Date.now();
let p = document.createElement("p");
p.innerHTML = `Finished. You used ${(ed - st) / 1000} seconds.`;
document.body.appendChild(p);
}
class Block
{
td;
mine;
_state; // unknown, marked, mine, open
around;
get state()
{
return this._state;
}
set state(v)
{
if (this._state == 1 || this._state == 2)
{
mkd--;
}
if (v == 1 || v == 2)
{
mkd++;
}
if (mkd == gmc)
{
end();
}
this.td.classList = style[v];
this._state = v;
if (v == 2)
{
this.td.innerHTML = "M";
}
else if (v == 3)
{
this.td.innerHTML = this.around ? this.around : "";
}
else
{
this.td.innerHTML = "";
}
}
constructor()
{
this.td = document.createElement("td");
this.mine = 0;
this.state = 0;
}
}
function gen(w, h, m)
{
mkd = 0;
gmc = m;
tb.innerHTML = "";
mp = [];
for (let i = 0; i < w; i++)
{
let tr = document.createElement("tr");
mp[i] = [];
for (let j = 0; j < h; j++)
{
let bl = new Block();
mp[i][j] = bl;
tr.appendChild(bl.td);
}
tb.appendChild(tr);
}
for (let i = 0; i < m; i++)
{
let x = rf(w), y = rf(h);
while (mp[x][y].mine)
{
x = rf(w), y = rf(h);
}
mp[x][y].mine = 1;
}
function chk(x, y)
{
return x >= 0 && x < w && y >= 0 && y < h;
}
function opendfs(x, y)
{
mp[x][y].state = 3;
if (mp[x][y].around)
{
return;
}
for (let k = 0; k < 8; k++)
{
let i = x + dx[k], j = y + dy[k];
if (chk(i, j) && mp[i][j].state == 0)
{
opendfs(i, j);
}
}
}
function tryopen(i, j)
{
if (mp[i][j].state == 0)
{
if (mp[i][j].mine)
{
mp[i][j].state = 2;
}
else
{
opendfs(i, j);
}
}
else if (mp[i][j].state == 3)
{
let ct = 0;
for (let k = 0; k < 8; k++)
{
let x = i + dx[k], y = j + dy[k];
if (chk(x, y) && (mp[x][y].state == 1 || mp[x][y].state == 2))
{
ct++;
}
}
if (ct != mp[i][j].around)
{
return;
}
for (let k = 0; k < 8; k++)
{
let x = i + dx[k], y = j + dy[k];
if (chk(x, y) && mp[x][y].state == 0)
{
tryopen(x, y);
}
}
}
}
for (let i = 0; i < w; i++)
{
for (let j = 0; j < h; j++)
{
mp[i][j].td.addEventListener("click", () => { // open
tryopen(i, j);
});
mp[i][j].td.addEventListener("contextmenu", (e) => { // mark
e.preventDefault();
if (mp[i][j].state == 0)
{
mp[i][j].state = 1;
}
else if (mp[i][j].state == 1)
{
mp[i][j].state = 0;
}
});
if (mp[i][j].mine)
{
continue;
}
let res = 0;
for (let k = 0; k < 8; k++)
{
let x = i + dx[k], y = j + dy[k];
if (chk(x, y) && mp[x][y].mine)
{
res++;
}
}
mp[i][j].around = res;
}
}
st = Date.now();
}
gb.addEventListener("click", () => {
let w = Number(wd.value);
let h = Number(hd.value);
let m = Number(md.value);
if (!isNaN(w) && !isNaN(h) && !isNaN(m) && 1 <= w && w <= 30 && 1 <= h && h <= 30 && m < h * w)
{
gen(w, h, m);
}
});
gen(10, 10, 10);
</script>
</body>
</html>
Width:
Height:
Mines: