console
(function (Sweep) {
const W = 20, H = 20, SIZE = 20, difficulty = .1
const WIDTH = W * SIZE, HEIGHT = H * SIZE
const { points, openPoint } = new Sweep(W, H, difficulty)
const canvas = document.createElement("canvas")
canvas.width = WIDTH
canvas.height = HEIGHT
const ctx = canvas.getContext("2d")
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
const render = function () {
ctx.clearRect(0, 0, WIDTH, HEIGHT)
for (let x = 0; x < W; x++) {
let line = points[x]
for (let y = 0; y < H; y++) {
const { n, isMine, isOpen, flag } = line[y]
if (flag) {
ctx.fillStyle = 'green'
ctx.fillText('✅', (x + .5) * SIZE, (y + .5) * SIZE)
} else if (isOpen) {
ctx.fillStyle = '#fff'
ctx.fillRect(x * W, y * H, SIZE, SIZE)
if (n > 0) {
ctx.fillStyle = ['#fff', '#333', 'green', 'yellow', 'orange', '#c00', '#f00', '#f00'][n]
ctx.fillText(n, (x + .5) * SIZE, (y + .5) * SIZE)
}
if (isMine) {
ctx.fillStyle = 'red'
ctx.fillText('雷', (x + .5) * SIZE, (y + .5) * SIZE)
}
} else {
ctx.fillStyle = '#d2d2d2'
ctx.fillRect(x * W, y * H, SIZE, SIZE)
}
ctx.strokeStyle = '#ccc'
ctx.strokeRect(x * W, y * H, SIZE, SIZE)
}
}
}
let clicking
canvas.addEventListener('click', function (e) {
const { offsetX, offsetY } = e
openPoint(offsetX / SIZE | 0, offsetY / SIZE | 0)
render();
if (clicking) {
const eve = new CustomEvent('dblclick')
Object.assign(eve, { offsetX, offsetY })
canvas.dispatchEvent(eve)
}
clicking = true
setTimeout(function() {
clicking = false
}, 300);
})
canvas.addEventListener('dblclick', function (e) {
const { offsetX, offsetY } = e
const x = offsetX / SIZE | 0
const y = offsetY / SIZE | 0
const a = points;
[
a[x - 1] && a[x - 1][y - 1],
a[x - 1] && a[x - 1][y],
a[x - 1] && a[x - 1][y + 1],
a[x][y - 1],
a[x][y + 1],
a[x + 1] && a[x + 1][y - 1],
a[x + 1] && a[x + 1][y],
a[x + 1] && a[x + 1][y + 1]
].map(p => {
if (p && !p.flag) {
openPoint(p.x, p.y)
}
})
render()
})
canvas.addEventListener('contextmenu', function (e) {
e.preventDefault()
const { offsetX, offsetY } = e
const point = points[offsetX / SIZE | 0][offsetY / SIZE | 0]
point.flag = !point.flag
render();
})
document.body.appendChild(canvas)
render();
})(function () {
class Point {
constructor(x, y, isMine) {
this.x = x
this.y = y
this.isMine = !!isMine
this.n = 0
this.isOpen = false
this.flag = false
}
}
class Sweep {
constructor(W, H, difficulty) {
this.W = W
this.H = H
this.dt = difficulty || .1
this.openPoint = this.openPoint.bind(this)
this.getNum = this.getNum.bind(this)
this._init()
}
openPoint(x, y) {
const { points, pointList, openPoint } = this
const a = points
const p = a[x] && a[x][y]
this.beginTime = this.beginTime || Date.now()
if (p && !p.isOpen) {
p.isOpen = true
if (p.isMine) {
setTimeout(function() {
alert('game over!')
window.location.reload()
}, 0);
} else if (!pointList.find((pt) => pt.isMine === pt.isOpen)) {
alert(`success! \n time: ${(Date.now() - this.beginTime) / 1000 | 0}s`)
} else if (!p.isMine && !p.n) {
openPoint(x - 1, y)
openPoint(x + 1, y)
openPoint(x, y - 1)
openPoint(x, y + 1)
}
}
}
getNum(p, a) {
const { x, y } = p
if (!p.isMine) {
p.n = [
a[x - 1] && a[x - 1][y - 1],
a[x - 1] && a[x - 1][y],
a[x - 1] && a[x - 1][y + 1],
a[x][y - 1],
a[x][y + 1],
a[x + 1] && a[x + 1][y - 1],
a[x + 1] && a[x + 1][y],
a[x + 1] && a[x + 1][y + 1]
].filter(m => m && m.isMine).length
}
}
_init() {
const { W, H, dt, getNum } = this
let points = []
for (let x = 0; x < W; x++) {
let line = []
for (let y = 0; y < H; y++) {
line.push(new Point(x, y, Math.random() < dt))
}
points.push(line)
}
for (let x = 0; x < W; x++) {
let line = points[x]
for (let y = 0; y < H; y++) {
getNum(line[y], points)
}
}
this.pointList = points.reduce((a, line) => a.concat(line), [])
this.points = points
}
}
return Sweep
}())
<html>
<body>
</body>
</html>