HTML5+JavaScript实现连连看游戏
连连看游戏连接规则:
只能连接相同图案(或图标、字符)的方块。
连线路径必须是由直线段组成的,最多可以有两个拐角。
连线路径必须是空的,不能穿过其他方块。
当两个相同图案的方块被有效连接时,它们会从游戏板上消失。当所有方块都被消除时,玩家获胜。
运行效果:
源码如下(参考https://blog.csdn.net/time1812/article/details/79513508 略有修改):
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>连连看</title>
<style>
/* 单元格边框样式 */
.border {
display: inline-block;
width: 40px;
height: 40px;
font-weight: 600;
font-size: 36px;
margin: 1px;
}
/* 显示路径时的样式 */
.border.showPath {
background-color: #99FF00;
}
/* 单元格样式 */
.cell {
display: inline-block;
width: 40px;
height: 40px;
border: 1px solid #0099FF;
text-align: center;
font-weight: 600;
font-size: 36px;
background-color: #FFFFFF;
color: #000000;
}
/* 选中单元格的样式 */
.cell.selected {
background-color: #3399FF;
color: #FFFFFF;
}
/* 显示路径的单元格样式 */
.cell.showPath {
background-color: #99FF00;
}
/* 行样式 */
.row {
display: block;
height: 42px;
}
/* 游戏区域的边框样式 */
#game {
border: 2px solid #0033FF;
width: 600px; /* 设置游戏区域的宽度 */
}
</style>
</head>
<body onselectstart="return false">
<div id="game"></div>
<script type="text/javascript">
let nx = 12, ny = 8; // 游戏网格的宽和高
let gameDiv = document.getElementById('game');
let selectedCell = null; // 当前选中的方块
let empty = ' '; // 空方块
let count = nx * ny; // 方块总数
let cellArray = []; // 存储方块的数组
// 获取特定坐标的方块
function fc(x, y) {
return (x < 0 || x > nx + 1 || y < 0 || y > ny + 1) ? null : cellArray[y][x];
}
// 获取方块的邻居
function neighbors(c) {
return [
fc(c.x - 1, c.y),
fc(c.x + 1, c.y),
fc(c.x, c.y + 1),
fc(c.x, c.y - 1)
];
}
// 查找连接路径
function findPath(c, target, pathStack, turnCount) {
if (!c) return false;
let prev = pathStack[pathStack.length - 2];
if (prev && prev.x !== c.x && prev.y !== c.y && ++turnCount > 2) return false;
if (c === target) {
pathStack.push(c);
return true;
}
if (c.innerHTML !== empty || pathStack.includes(c)) return false;
pathStack.push(c);
let nexts = neighbors(c);
for (let next of nexts) {
if (findPath(next, target, pathStack, turnCount)) return true;
}
pathStack.pop();
return false;
}
// 清除路径样式
function clearPath(path) {
for (let cell of path) {
cell.classList.remove('showPath');
}
}
// 绘制路径
function drawPath(path) {
for (let cell of path) {
cell.classList.add('showPath');
}
setTimeout(clearPath, 150, path);
}
// 尝试匹配两个方块
function tryMatch(ca, cb) {
if (ca === cb || ca.innerHTML !== cb.innerHTML) return;
let pathStack = [ca];
let found = false;
let nexts = neighbors(ca);
for (let next of nexts) {
if (findPath(next, cb, pathStack, 0)) {
found = true;
break;
}
}
if (!found) return;
ca.innerHTML = cb.innerHTML = empty;
setTimeout(drawPath, 10, pathStack);
count -= 2;
if (count < 2) alert('You win!');
}
// 处理方块点击事件
function onCellClicked() {
if (this.innerHTML === empty) return;
if (selectedCell) {
tryMatch(selectedCell, this);
selectedCell.className = 'cell';
selectedCell = null;
} else {
selectedCell = this;
selectedCell.className = 'cell selected';
}
}
// 初始化游戏网格
function init(m, n) {
for (let i = 0; i < n + 2; i++) {
let row = document.createElement('div');
row.className = 'row';
let cellArrayRow = [];
cellArray.push(cellArrayRow);
gameDiv.appendChild(row);
for (let j = 0; j < m + 2; j++) {
let cell = document.createElement('div');
cellArrayRow.push(cell);
if (i === 0 || j === 0 || i === n + 1 || j === m + 1) {
cell.className = 'border';
cell.innerHTML = empty;
} else {
cell.className = 'cell';
cell.addEventListener('click', onCellClicked);
}
cell.id = j + '_gc_' + i;
cell.x = j;
cell.y = i;
row.appendChild(cell);
}
}
}
// 随机重置游戏
function reset() {
let all = count = nx * ny;
let halfAll = all / 2;
let tmp = [];
for (let i = 0; i < halfAll; i++) {
let c = symbols.charAt(Math.floor(Math.random() * 35));
tmp.push(c, c);
}
for (let i = all - 1; i >= 0; i--) {
let r = Math.floor(Math.random() * i);
let c = tmp.splice(r, 1);
let y = Math.floor(i / nx);
let x = i - y * nx;
fc(x + 1, y + 1).innerHTML = c;
}
}
init(nx, ny);
//let symbols = 'ABCDEFGHIGKLMNOPQRSTUVWXYZ123456789'; // 可用符号
let symbols = 'πφω▽Ф#*∩+■▲▼◆●★♀∽※⊕☆㐃□◇△◎○Ω⊙Θ☮☯㊣@$θ'; // 可用符号
reset(); // 初始化游戏
</script>
</body>
</html>