HTML实现 扫雷游戏
前言:
游戏起源与发展
-
扫雷游戏的雏形可追溯到 1973 年的 “方块(cube)” 游戏,后经改编出现了 “rlogic” 游戏,玩家需为指挥中心探出安全路线避开地雷。在此基础上,开发者汤姆・安德森编写出了扫雷游戏的雏形.
-
1992 年,微软在 windows3.1 系统中首次预装了《扫雷》,将其作为训练用户鼠标操作能力的软件.
-
虽从 windows8 开始,系统不再默认安装扫雷游戏,但它依旧凭借自身魅力受到众多玩家喜爱,甚至有玩家不断刷新时间记录来挑战自我.
游戏规则
-
胜利条件:找出所有地雷且不踩到地雷,或者在规定时间内尽可能多地找出地雷.
-
失败条件:翻开了埋有地雷的方块.
-
基础操作:左键点击未打开的格子可将其打开;右键点击可标雷 / 取消标雷;双击(左右键同时点击)已打开的数字,若该数字周围标的雷数量等于该数字,会同时打开该数字周围剩余所有格子.
游戏界面及信息
-
剩余雷数:显示在游戏界面左上角,为总雷数减去已标雷数,可据此了解雷区剩余雷的大致情况.
-
计时器:位于右上角,从本局游戏第一次操作为 0.001 秒开始计时,可用于记录游戏所用时间及玩家挑战自我时作为参考
技巧与策略
-
数字推理:数字代表其周围 8 个方格中的雷数,若数字为 0,游戏会自动打开周围所有格子;若一个方格周围已标记的雷数等于该方格上的数字,那么其周围未标记的方格都可安全打开.
-
常见阵型判断:如 “111”“22”“112” 等常见数字组合,可依据特定规律快速判断雷的位置.
-
边缘优先:可先从雷区边缘开始点击,因为边缘方格的周围方格数量相对较少,更容易判断雷的位置,降低推理难度7.
-
标记问号:当无法确定方格是否有雷时,可右键点击两次标记问号,后续再根据其他信息进一步判断.
游戏变化与创新
-
在线多人模式:如《一起来扫雷》,玩家可与世界各地的玩家合作或竞争,共同清除地图上的地雷,还能积累积分兑换装饰,装扮游戏界面.
-
VR 版扫雷:扫雷 VR 将平面的扫雷游戏搬到了 3D 场景中,增加了游戏的趣味性和沉浸感.
-
融合其他元素:《扫雷俄罗斯方块》结合了扫雷和俄罗斯方块的元素,雷区不断向上生长,找到所有地雷时行才会被清除
以上内容由ai生成
正文:
1.自己实现一个扫雷游戏
2.使用HTML实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>扫雷游戏</title>
<link
href="https://fonts.googleapis.com/css?family=Space+Mono:400,700"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css"
/>
<style>
@charset "UTF-8";
*,
*:before,
*:after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
body {
text-align: center;
color: #333;
padding: 20px 20px 100px;
background-color: #efefef;
font-family: 'Space Mono', monospace;
}
h1 {
margin: 0 0 32px;
}
p {
font-size: 1em;
line-height: 1.25em;
}
a {
color: inherit;
}
img {
height: auto;
max-width: 100%;
}
.wrap {
text-align: center;
position: relative;
display: inline-block;
}
.legend {
font-size: 14px;
margin: 0 auto 32px;
}
.legend h4,
.legend p {
margin: 0 0 6px;
}
.legend code {
background: #e3e3e3;
}
.legend code .key {
color: #ec433c;
}
.legend code .click {
color: #2a48ec;
}
.top {
border: 6px solid #e3e3e3;
background-color: #e3e3e3;
}
#scoreboard {
display: flex;
padding-bottom: 12px;
justify-content: space-between;
}
#scoreboard .select-wrap {
font-weight: 700;
vertical-align: top;
display: inline-block;
}
#scoreboard .select-wrap select {
margin: 0;
height: 36px;
border-radius: 0;
border-width: 3px;
border-style: solid;
background-color: #d1d1d1;
border-color: white #9e9e9e #9e9e9e white;
}
#scoreboard .select-wrap select:hover,
#scoreboard .select-wrap select:focus {
backgroudn-color: #ebebeb;
}
#scoreboard .reset {
padding: 0 4px;
font-size: 24px;
cursor: pointer;
font-weight: 700;
line-height: 30px;
border-width: 3px;
border-style: solid;
background-color: #d1d1d1;
border-color: white #9e9e9e #9e9e9e white;
}
#scoreboard .reset:hover,
#scoreboard .reset:focus {
outline: none;
background-color: #ebebeb;
}
#scoreboard .counter {
padding: 0 4px;
color: #ec433c;
border: 3px inset;
line-height: 30px;
letter-spacing: 0.08em;
display: inline-block;
background: #333;
text-shadow: 0 0 2px #ec433c;
}
#scoreboard .counter:first-of-type {
margin-right: 20px;
}
#scoreboard .counter:last-of-type {
margin-left: 20px;
}
#grid {
margin: 0 auto;
position: relative;
display: inline-block;
}
#grid ::-moz-selection {
background-color: transparent;
}
#grid ::selection {
background-color: transparent;
}
#grid.disabled .cell {
pointer-events: none;
}
#grid.disabled .status-indicator {
top: 50%;
left: 50%;
z-index: 11;
width: 8vw;
height: 8vw;
font-size: 4vw;
cursor: pointer;
line-height: 8vw;
position: absolute;
border-radius: 50%;
pointer-events: auto;
background-color: #d1d1d1;
transform: translate(-50%, -50%);
border: 1px solid rgba(51, 51, 51, 0.25);
}
#grid.disabled .status-indicator::after {
content: '';
}
#grid.disabled.win .status-indicator::after {
content: '😎';
}
#grid.disabled.lose .status-indicator::after {
content: '☹️';
}
#grid .row {
display: flex;
}
#grid .cell {
cursor: pointer;
width: 24px;
height: 24px;
position: relative;
background-color: #d1d1d1;
border-width: 3px;
border-style: solid;
border-color: white #9e9e9e #9e9e9e white;
}
#grid .cell i {
left: 0;
bottom: 0;
margin: 0;
width: 100%;
font-size: 14px;
font-weight: 700;
font-style: normal;
position: absolute;
line-height: 24px;
}
#grid .cell::before {
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
content: '';
position: absolute;
background-color: #d1d1d1;
}
#grid .cell::after {
top: 50%;
left: 50%;
content: '';
position: absolute;
transform: translate(-50%, -50%);
}
#grid .cell:hover::before {
background-color: #ebebeb;
}
#grid .cell.revealed {
border: 1px solid #b8b8b8;
}
#grid .cell.revealed::before {
display: none;
}
#grid .cell.revealed .flag {
display: none;
}
#grid .cell .flag {
top: 50%;
left: 50%;
z-index: 1;
width: 10px;
height: 4px;
position: absolute;
border-style: solid;
border-width: 4px 0 4px 10px;
transform: translate(-50%, -50%);
border-color: transparent transparent transparent #ec433c;
}
#grid .cell.maybe .flag {
border-color: transparent transparent transparent #d7af42;
}
#grid .cell.mine {
background-color: #ec433c;
}
#grid .cell.mine::after {
border-radius: 50%;
width: 12px;
height: 12px;
background-color: #333;
}
#grid .cell.incorrect .flag::before,
#grid .cell.incorrect .flag::after {
top: 50%;
z-index: 1;
left: -13px;
height: 2px;
width: 16px;
content: '';
position: absolute;
background-color: black;
}
#grid .cell.incorrect .flag::before {
transform: rotate(-45deg);
}
#grid .cell.incorrect .flag::after {
transform: rotate(45deg);
}
#grid .cell.mousedown {
border: none;
}
#leaderboard {
margin-top: 20px;
text-align: center;
}
#leaderboard h4 {
margin: 0 0 10px;
}
#leaderboard ul {
margin: 0;
padding: 10px;
display: inline-block;
background-color: rgba(209, 209, 209, 0.5);
}
#leaderboard ul li {
padding: 2px;
list-style: none;
}
#leaderboard ul li span {
font-weight: 900;
text-transform: capitalize;
}
#leaderboard ul li.highlight {
background-color: #fef178;
}
#leaderboard button {
border: none;
outline: none;
cursor: pointer;
font-size: 12px;
font-weight: 700;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: none;
text-decoration: underline;
}
.cell.white {
color: #fff;
}
.cell.gray-light {
color: #efefef;
}
.cell.gray-mid {
color: #e3e3e3;
}
.cell.gray {
color: #d1d1d1;
}
.cell.gray-dark {
color: #808080;
}
.cell.black {
color: #333;
}
.cell.red {
color: #ec433c;
}
.cell.maroon {
color: #a6070f;
}
.cell.purple {
color: #a42887;
}
.cell.yellow {
color: #d7af42;
}
.cell.yellow-light {
color: #fef178;
}
.cell.blue {
color: #2a48ec;
}
.cell.blue-dark {
color: #233db7;
}
.cell.green {
color: #2bb13d;
}
.cell.turquoise {
color: #28907d;
}
#devbox {
top: 10px;
right: 10px;
position: fixed;
text-align: left;
max-width: 300px;
color: #fff;
background-color: #333;
}
#devbox p {
margin: 0;
font-size: 12px;
padding: 10px 20px;
}
#devbox p + p {
padding-top: 5px;
}
</style>
</head>
<body>
<header class="site-header" role="banner">
<div class="wrap">
<h1>扫雷</h1>
<div class="legend">
<p>
<strong>插旗:</strong>
<code
><span class="key">alt</span>+<span class="click"
>click(单击)</span
></code
>
</p>
</div>
</div>
</header>
<main class="site-main" role="main">
<div class="wrap">
<div id="board">
<div class="top">
<div id="scoreboard">
<div id="minecounter" class="counter"></div>
<div>
<div class="select-wrap">
<select name="level" id="level">
<option value="beginner">初级</option>
<option value="intermediate">中级</option>
<option value="expert" selected>高级</option>
</select>
</div>
<button class="reset">🙂</button>
</div>
<div id="timer" class="counter"></div>
</div>
<div id="grid"></div>
</div>
<div class="bottom"></div>
</div>
</div>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
var $body, $document, $board, $grid, timer, time, unstarted;
function _init() {
// Cache some common DOM queries
$document = $(document);
$body = $('body');
$body.addClass('loaded');
// Start Minesweeper
$board = $('#board');
$grid = $('#grid');
var $timer = $('#timer');
var $mineCounter = $('#minecounter');
var $levelSelect = $('#level');
var levels = {
beginner: '9x9x10',
intermediate: '16x16x44',
expert: '16x30x99',
};
var level = $levelSelect.val();
var levelParams,
rows,
$rows,
columns,
cellCount,
mines,
freeCells,
mineTally,
pauseTime,
beginnerHighScore = 999,
intermediateHighScore = 999,
expertHighScore = 999;
var countColors = {
0: '',
1: 'blue',
2: 'green',
3: 'red',
4: 'blue-dark',
5: 'maroon',
6: 'turquoise',
7: 'purple',
8: 'gray-dark',
};
time = 0;
timer = false;
unstarted = true;
var statusIndicator = '<div class="status-indicator"></div>';
// Check for existing high score
// if (localStorage.getItem('beginner')) {
// beginnerHighScore = localStorage.getItem('beginner');
// populateHighScore('beginner', localStorage.getItem('beginner'));
// }
// if (localStorage.getItem('intermediate')) {
// intermediateHighScore = localStorage.getItem('intermediate');
// populateHighScore('intermediate', localStorage.getItem('intermediate'));
// }
// if (localStorage.getItem('expert')) {
// expertHighScore = localStorage.getItem('expert');
// populateHighScore('expert', localStorage.getItem('expert'));
// }
function setLevel(level) {
levelParams = levels[level];
rows = parseInt(levelParams.split('x')[0]);
columns = parseInt(levelParams.split('x')[1]);
cellCount = rows * columns;
mines = levelParams.split('x')[2];
freeCells = cellCount - mines;
}
function setBoard(level) {
// Clear Grid
$grid
.html(statusIndicator)
.removeClass('disabled lose win')
.addClass('unstarted');
// Set Up Grid
setLevel(level);
// Set unstarted
unstarted = true;
// Build Rows
for (r = 0; r < rows; r++) {
var newCells = '';
// Build Cells
for (c = 0; c < columns; c++) {
newCells += '<div class="cell"></div>';
}
$grid.append('<div class="row">' + newCells + '</div>');
}
// Set Minecounter
mineTally = mines;
$mineCounter.html(mineTally);
// Set Timer
resetTimer();
}
// Set initially
setBoard(level);
// Set on reset
$('html')
.on('mousedown', '.reset', function () {
$(this).text('😮');
})
.on('mouseup', '.reset', function () {
$(this).text('🙂');
stopTimer();
level = $levelSelect.val();
setBoard(level);
});
// Set when clicking status indicator
$('html').on('click', '.status-indicator', function () {
level = $levelSelect.val();
setBoard(level);
});
// Set on level change
$levelSelect.on('change', function () {
stopTimer();
resetTimer();
level = $levelSelect.val();
setBoard(level);
});
// Lay Mines
function layMines(level, clickedCellIndex) {
$rows = $('.row');
var freeCells = $('.cell');
var takenCells = [clickedCellIndex];
// Lay Mines
for (m = 0; m < mines; m++) {
var mineCell = Math.floor(
Math.random() * Math.floor(freeCells.length)
);
// If it happens to be the clicked cell, skip it
if ($.inArray(mineCell, takenCells) > -1) {
m--;
continue;
}
takenCells.push(mineCell);
$(freeCells[mineCell]).addClass('mine');
}
// Identify Cell Numbers
var $cells = $('.cell');
for (c = 0; c < $cells.length; c++) {
var $cell = $($cells[c]);
$cell.attr('data-cell', c);
// Skip if it's a mine
if ($cell.is('.mine')) {
continue;
}
var mineCount = 0;
var rowPos = Math.floor(c / columns);
var $currentRow = $cell.closest('.row');
$currentRow.attr('data-row', rowPos);
var rowCells = $currentRow.find('.cell');
var cellPos = c % columns;
if ($(rowCells[cellPos - 1]).is('.mine')) {
mineCount++;
}
if ($(rowCells[cellPos + 1]).is('.mine')) {
mineCount++;
}
if (rowPos > 0) {
var prevRowCells = $($rows[rowPos - 1]).find('.cell');
if ($(prevRowCells[cellPos - 1]).is('.mine')) {
mineCount++;
}
if ($(prevRowCells[cellPos]).is('.mine')) {
mineCount++;
}
if ($(prevRowCells[cellPos + 1]).is('.mine')) {
mineCount++;
}
}
if (rowPos < rows - 1) {
var nextRowCells = $($rows[rowPos + 1]).find('.cell');
if ($(nextRowCells[cellPos - 1]).is('.mine')) {
mineCount++;
}
if ($(nextRowCells[cellPos]).is('.mine')) {
mineCount++;
}
if ($(nextRowCells[cellPos + 1]).is('.mine')) {
mineCount++;
}
}
if (mineCount > 0) {
$cell.html('<i>' + mineCount + '</i>');
// Styling classes
var colorClass = countColors[mineCount];
$cell.addClass(colorClass);
} else {
$cell.addClass('zero');
}
}
}
// Click cell to start game
$('html')
.off('click', '#grid.unstarted .cell')
.on('click', '#grid.unstarted .cell', function (e) {
$grid.removeClass('unstarted');
if (unstarted && !$(e.target).is('.mine')) {
layMines(level, $('.cell').index(this));
timer = window.setInterval(startTimer, 1000);
unstarted = false;
}
});
// Timer Functions
function resetTimer() {
$timer.html('000');
time = 0;
}
function startTimer() {
time++;
if (time < 10) {
$timer.html('00' + time);
} else if (time > 9 && time < 100) {
$timer.html('0' + time);
} else {
$timer.html(time);
}
}
function stopTimer() {
window.clearInterval(timer);
}
function pauseTimer() {
stopTimer();
pauseTime = parseInt($('#timer').html());
}
function unpauseTimer() {
time = pauseTime;
timer = window.setInterval(startTimer, 1000);
pauseTime = false;
}
// Pause when window loses focus
$(window)
.on('blur', function () {
pauseTimer();
})
.on('focus', function () {
if (pauseTime) {
unpauseTimer();
}
});
// Check Cell
function checkCell($cell) {
if (!$cell.is('.mine') && !$cell.is('.revealed')) {
cellClick($cell, 'reveal');
if ($cell.is('.zero')) {
$cell.trigger('click');
}
}
}
// Clicking on a cell
function cellClick($cell, action) {
// If Flagging
if (action === 'flag' && !$cell.is('.revealed')) {
if ($cell.is('.flagged')) {
$cell.removeClass('flagged');
$cell.addClass('maybe');
mineTally++;
updateMinecounter(mineTally);
} else if ($cell.is('.maybe')) {
$cell.removeClass('maybe');
var flag = $cell.find('.flag');
flag.remove();
} else {
$cell.addClass('flagged');
$cell.append('<span class="flag"></span>');
mineTally--;
updateMinecounter(mineTally);
}
// If Revealing
} else if (action === 'reveal') {
$cell.addClass('revealed');
// If it's a mine you lose!
if ($cell.is('.mine')) {
lose();
}
statusCheck();
} else if (action === 'clear') {
if (!$cell.is('.revealed') || $cell.is('.zero')) {
return;
}
clearClick($cell);
}
}
// Update Minecounter
function updateMinecounter(mineTally) {
if (mineTally < 10) {
$mineCounter.html('0' + mineTally);
} else {
$mineCounter.html(mineTally);
}
}
// Clicking on a Zero cell
function zeroClick($cell) {
var cellPos = $cell.prevAll().length;
var $currentRow = $cell.closest('.row');
var rowPos = parseInt($currentRow.attr('data-row'));
var rowCells = $currentRow.find('.cell');
checkCell($(rowCells[cellPos - 1]));
checkCell($(rowCells[cellPos + 1]));
if (rowPos > 0) {
var prevRowCells = $($rows[rowPos - 1]).find('.cell');
checkCell($(prevRowCells[cellPos - 1]));
checkCell($(prevRowCells[cellPos]));
checkCell($(prevRowCells[cellPos + 1]));
}
if (rowPos < rows) {
var nextRowCells = $($rows[rowPos + 1]).find('.cell');
checkCell($(nextRowCells[cellPos - 1]));
checkCell($(nextRowCells[cellPos]));
checkCell($(nextRowCells[cellPos + 1]));
}
}
// Clicking on a number to clear free cells
function clearClick($cell) {
var cellPos = $cell.prevAll().length;
var $currentRow = $cell.closest('.row');
var rowPos = parseInt($currentRow.attr('data-row'));
var rowCells = $currentRow.find('.cell');
var adjacentCells = [];
var correctClear = true;
var adjacentMines = 0;
var adjacentFlags = 0;
var i;
adjacentCells.push($(rowCells[cellPos - 1]));
adjacentCells.push($(rowCells[cellPos + 1]));
if (rowPos > 0) {
var prevRowCells = $($rows[rowPos - 1]).find('.cell');
adjacentCells.push($(prevRowCells[cellPos - 1]));
adjacentCells.push($(prevRowCells[cellPos]));
adjacentCells.push($(prevRowCells[cellPos + 1]));
}
if (rowPos < rows) {
var nextRowCells = $($rows[rowPos + 1]).find('.cell');
adjacentCells.push($(nextRowCells[cellPos - 1]));
adjacentCells.push($(nextRowCells[cellPos]));
adjacentCells.push($(nextRowCells[cellPos + 1]));
}
for (i = 0; i < adjacentCells.length; i++) {
// add to mine count
if ($(adjacentCells[i]).is('.mine')) {
adjacentMines++;
}
// add to flag cout
if ($(adjacentCells[i]).is('.flagged')) {
adjacentFlags++;
}
}
if (adjacentFlags === adjacentMines) {
for (i = 0; i < adjacentCells.length; i++) {
if ($(adjacentCells[i]).is('.mine')) {
if ($(adjacentCells[i]).is('.flagged')) {
continue;
} else {
$(adjacentCells[i]).addClass('revealed');
correctClear = false;
}
} else if ($(adjacentCells[i]).is('.flagged')) {
correctClear = false;
$(adjacentCells[i]).addClass('incorrect');
lose();
}
}
if (correctClear) {
for (i = 0; i < adjacentCells.length; i++) {
if (!$(adjacentCells[i]).is('.mine')) {
if ($(adjacentCells[i]).is('.zero')) {
zeroClick($(adjacentCells[i]));
}
cellClick($(adjacentCells[i]), 'reveal');
}
}
}
} else {
return;
}
}
// Check status
function statusCheck() {
if ($('.cell.revealed').length == freeCells) {
stopTimer();
var winTime = $('#timer').html();
$grid.addClass('disabled win');
resetHighScore(level, winTime);
}
}
function lose() {
$grid.addClass('disabled lose');
stopTimer();
}
// Clicking on a cell
$('html').on('click', '.cell', function (e) {
e.preventDefault();
var action = 'reveal';
var $cell = $(this);
if (e.altKey || e.which === 3) {
action = 'flag';
} else if (
$cell.is('.revealed') ||
(e.which === 1) & (e.which === 3)
) {
action = 'clear';
}
if ($cell.is('.flagged') && !e.altKey) {
return;
}
if ($cell.is('.zero')) {
zeroClick($cell);
}
cellClick($cell, action);
});
// Mouse down on a cell
$('html')
.on('mousedown', '.cell:not(.revealed,.flagged)', function (e) {
if (!e.altKey && e.which !== 3) {
$(this).addClass('mousedown');
}
})
.on('mouseup mouseleave', '.cell.mousedown', function () {
$(this).removeClass('mousedown');
});
// Scoreboard functionality
function resetHighScore(level, winTime) {
if (localStorage.getItem(level)) {
if (winTime < localStorage.getItem(level)) {
localStorage.setItem(level, winTime);
populateHighScore(level, winTime, true);
}
} else {
localStorage.setItem(level, winTime);
populateHighScore(level, winTime, true);
}
}
function populateHighScore(level, highScore, highlight) {
if (!$('#leaderboard').length) {
$board
.find('.bottom')
.append(
'<div id="leaderboard"><h4>High Scores</h4><ul><li class="beginner"></li><li class="intermediate"></li><li class="expert"></li></ul><div><button id="score-reset" class="score-reset">Clear Scores</button></div></div>'
);
}
if (highlight === true) {
$('#leaderboard .highlight:not(.' + level + ')').removeClass(
'highlight'
);
$('#leaderboard .' + level).addClass('highlight');
}
var highScoreDisplay = parseInt(highScore, 10);
$('#leaderboard .' + level).html(
'<span>' + level + '</span>: ' + highScoreDisplay + ' seconds'
);
}
function clearScores() {
localStorage.clear();
$('#leaderboard').remove();
}
// Clicking on score reset to clear scores
$('html').on('click', '#score-reset', clearScores);
}
_init();
});
</script>
</body>
</html>
3.电脑单机就可玩耍
最后:
你今天扫雷了吗?后台回复“扫雷”获取在线连接。
扫雷游戏