本帖最后由 mr88fang 于 2024-3-19 12:05 编辑
[数字华容道] Html+css+js 实现小游戏
预览&代码地址
代码在线预览 效果预览
增加指定数列
代码示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>数字华容道</title>
<style>
h1 {
text-align: center;
}
.box {
border: 1px solid #cfcfcf;
margin: 0 auto;
max-width: 647px;
padding: 20px;
border-radius: 20px;
display: flex;
}
.fun {
display: flex;
justify-content: space-between;
}
td {
width: 100px;
height: 100px;
text-align: center;
background-color: #f1c385;
user-select: none;
}
.current {
background-color: #fff !important;
transition: all .3s;
}
#error {
color: red;
}
</style>
</head>
<body>
<div class="box">
<div style="flex: 1;">
<h1>数字华容道</h1>
<p><strong>规则:</strong>移动方块依次出现1、2、3、4、5、6、7、8就可通关!不能对角线移动,不能跳格子移动。只能相邻上下或左右</p>
<hr />
<div class="fun">
<div><span>计次:</span><span id="num">0</span></div>
<div><span>提示:</span><span id="error"></span></div>
<div><span>功能:</span><button id="reset">重开</button></div>
</div>
<hr />
<div class="fun">
<div><label>指定序列:</label><input type="text" placeholder="指定数列,例如:1,2,3,4,6,7,8" /></div>
<div><button id="confirm">确定</button></div>
</div>
<hr />
<table border="1">
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>5</td>
<td>6</td>
</tr>
<tr>
<td>7</td>
<td>8</td>
<td class="current"></td>
</tr>
</table>
</div>
<div style="flex: 1;padding: 10px;">
<p>
<strong>逆序数:</strong>
是指,在一个数列中,任取两个数字,如果前面的数字大于后面的数字,则这两个数字形成一个逆序。在数字华容道中,忽略空位,将盘面上的数字从上到下、从左到右排列成一个序列,然后计算这个序列的逆序数总和。如果逆序数的总和是偶数,那么这个布局是有解的;如果是奇数,则这个布局是无解的
</p>
<p>
<strong>例如:</strong>如果一个布局的数字序列(空格忽略不计)是12345678,那么其逆序数为0(因为它已经是顺序排列),这是一个有解的布局。如果布局是12345687,其逆序数为1(因为只有数字8和7是逆序的),所以这个布局是无解的
</p>
<div id="desc" style="color: red;visibility: hidden;">
逆序数为“奇数”,次局无解,建议重开<br>
[1,2,3]<br>
[4,5,6]<br>
[8,7, ]<br>
</div>
</div>
</div>
<script>
const step = document.getElementById('num');
const error = document.getElementById('error');
const desc = document.getElementById('desc');
const input = document.querySelector('input')
const seed = [1, 2, 3, 4, 5, 6, 7, 8];
let custom_seed = [];
// 计算逆序数总和
const countInversions = (arr) => {
// 定义变量inversions用于计数
let inversions = 0;
// 遍历数组arr,从第一个元素开始,到最后一个元素结束
for (let i = 0; i < arr.length - 1; i++) {
// 内层循环,从i+1开始,到最后一个元素结束
for (let j = i + 1; j < arr.length; j++) {
// 如果arr[i]比arr[j]大,则计数加1
if (arr[i] > arr[j]) {
inversions++;
}
}
}
// 返回计数结果
return inversions;
}
// 随机数组
const shuffle = (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// 检查结果
const check = () => {
let flag = true;
document.querySelectorAll('td').forEach((item, i) => {
if (i + 1 !== parseInt(item.innerText)) {
flag = false;
}
})
if (flag) {
error.innerText = '恭喜你通关啦!👌';
}
}
// 更新 td 数据
const init = () => {
desc.style.visibility = 'hidden'
const series = []; // 数列
const data = custom_seed.length ? custom_seed : shuffle(seed);
const tds = document.querySelectorAll('td');
for (let i = 0; i < tds.length - 1; i++) {
let td = tds[i];
td.innerText = data[i];
td.className = ''
series.push(data[i]);
}
error.innerText = '';
step.innerText = 0;
const last = tds[tds.length - 1];
last.className = 'current'
last.innerText = '';
// 数列(逆序数)计算,次局是否有解
const total = countInversions(series)
if (total % 2 !== 0) desc.style.visibility = 'visible';
custom_seed = []; // 清空
seed.sort(); // 恢复
}
init()
// 重开
document.getElementById('reset').addEventListener('click', () => {
input.value = '';
init();
});
// 自定义数列
document.getElementById('confirm').addEventListener('click', () => {
const value = input.value;
if (value) {
custom_seed = value.split(',').filter(item => {
if (item) return item
}).map(item => {
return parseInt(item)
});
let sort_seed = [];
sort_seed = [...custom_seed]
if (seed.toString() !== sort_seed.sort().toString()) {
alert('指定数列错误,请输入1~8英文逗号分隔');
return;
}
init();
}
});
// 监听点击事件,移动方块处理
document.querySelector('table').addEventListener('click', (event) => {
const target = event.target;
const current = document.querySelector('.current');
const {
x: cx,
y: cy
} = current.getBoundingClientRect();
const {
x: tx,
y: ty
} = target.getBoundingClientRect();
const w = Math.abs(cx - tx);
const h = Math.abs(cy - ty);
if ((cx === tx || ty === cy) && (w < 200 && h < 200)) {
if (target.nodeName === 'TD' && target !== current) {
const innerText = target.innerText;
target.classList = 'current';
target.innerText = '';
// 当前空白块
current.innerText = innerText
current.classList.remove('current');
// 更新步骤
let num = step.innerText || 0;
num++;
step.innerText = num;
error.innerText = '';
check();
}
} else {
error.innerText = '不能这样哦😀';
}
})
</script>
</body>
</html>
|