效果图:
game.vue
<template>
<view>
<view class="main">
<view class="foot">
<view v-if="!isTip" class="sudoku_area">
<view v-for="(row, index) of rowList" :key="index" class="sudoku_row"
:style="{borderRight:(index==2 || index==5)?'2px solid #ae7a36':index==8?'none':'1px solid #dab88a'}">
<view v-for="(value, cellIndex) of row" :key="cellIndex" class="sudoku_cell"
:style="{borderBottom:(cellIndex==2 || cellIndex==5)?'2px solid #ae7a36':cellIndex==8?'none':'1px solid #dab88a'}">
<input v-if="isOkNew(index, cellIndex)" :value="value" style="background: #fffae1;"
:disabled="isOkNew(index, cellIndex)" />
<input v-else maxlength="1" type="number" v-model="inputValue"
style="background: #f8e7a4; font-weight: bold;"
@input="changeValue(index, cellIndex)" />
</view>
</view>
</view>
<view v-else class="sudoku_area">
<view v-for="(row, index) of rightList" :key="index" class="sudoku_row">
<view v-for="(value, cellIndex) of row" :key="cellIndex" class="sudoku_cell">
<input style="height:5vh;color: black;" :value="value"
:disabled="isOkNew(index, cellIndex)" />
</view>
</view>
</view>
</view>
<view style="margin-top: 64rpx; display: flex;">
<button class="button-class" @click="goRestart()">
重开
</button>
<button class="button-class" @click="showTip()">
答案<span v-if="isTip">{{ count }}s</span>
</button>
<button class="button-class" @click="checkShudu()">
提交
</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
rowList: [], // 数独二维数组
rowListNew: [], // 题目数组
rightList: [], // 正确的数独数组
inputValue: '', // input数值
isTip: false, // 是否展示提示
count: 5, // 倒计时
myTimer: ''
}
},
onLoad() {
this.generateShuDu()
},
methods: {
// 初始化数独
generateArr() {
const arr = []
for (let i = 0; i < 9; i++) {
arr[i] = []
for (let j = 0; j < 9; j++) {
arr[i][j] = 0
}
}
console.log(arr);
return arr
},
// 生成1-9的随机整数
generateRandom() {
return Math.floor(Math.random() * 9 + 1)
},
//判断重开
goRestart() {
uni.showModal({
title: '提示',
content: '重开后将清空已填的数,并且不会保存',
showCancel: true,
cancelText: '继续',
confirmText: '重开',
success: res => {
if (res.confirm) {
this.generateShuDu()
} else if (res.cancel) {
return
}
},
fail: () => {},
complete: () => {}
});
},
// 生成数独
generateShuDu() {
const arr = this.generateArr()
//生成数独
for (let i = 0; i < 9; i++) {
let time = 0
for (let j = 0; j < 9; j++) {
arr[i][j] = time === 9 ? 0 : this.generateRandom()
if (arr[i][j] === 0) { // 不是第一列,则倒退一列
if (j > 0) {
j -= 2
continue
} else { // 是第一列,则倒退到上一行的最后一列
i--
j = 8
continue
}
}
if (this.isCorret(arr, i, j)) {
time = 0 // 初始化time,为下一次填充做准备
} else {
time++ // 次数增加1
j-- // 继续填充当前格
}
}
}
this.rightList = JSON.parse(JSON.stringify(arr))
// 随机删除部分数独内容
this.delSomeList(arr)
},
// 是否满足行、列和3X3区域不重复的要求
isCorret(arr, row, col) {
return (this.checkRow(arr, row) && this.checkLine(arr, col) && this.checkNine(arr, row, col))
},
// 检测行是否符合标准
checkRow(arr, row) {
for (let j = 0; j < 8; j++) {
if (arr[row][j] === 0) {
continue
}
for (let k = j + 1; k < 9; k++) {
if (arr[row][j] === arr[row][k]) {
return false
}
}
}
return true
},
// 检测列是否符合标准
checkLine(arr, col) {
for (let j = 0; j < 8; j++) {
if (arr[j][col] === 0) {
continue
}
for (let k = j + 1; k < 9; k++) {
if (arr[j][col] === arr[k][col]) {
return false
}
}
}
return true
},
// 检测3X3是否符合标准
checkNine(arr, row, col) {
// 获得左上角的坐标
const j = Math.floor(row / 3) * 3
const k = Math.floor(col / 3) * 3
// 循环比较
for (let i = 0; i < 8; i++) {
if (arr[j + Math.floor(i / 3)][k + i % 3] === 0) {
continue
}
for (let m = i + 1; m < 9; m++) {
if (arr[j + Math.floor(i / 3)][k + Math.round(i % 3)] === arr[j + Math.floor(m / 3)][k + Math
.round(m % 3)
]) {
return false
}
}
}
return true
},
// 随机删除部分数独内容
delSomeList(arr) {
const randomNum = Math.floor(Math.random() * 30) + 50
for (let a = 0; a < randomNum; a++) {
const i = Math.floor(Math.random() * 9)
const j = Math.floor(Math.random() * 9)
arr[i][j] = ''
}
this.rowList = JSON.parse(JSON.stringify(arr))
this.rowListNew = JSON.parse(JSON.stringify(arr))
},
// 是否为题目数字
isOkNew(index, cellIndex) {
if (this.rowListNew[index][cellIndex] === '') {
return false
}
return true
},
// 填写数独
changeValue(index, cellIndex) {
this.inputValue === '' ? this.rowList[index][cellIndex] = '' : this.rowList[index][cellIndex] =
parseInt(this.inputValue)
this.inputValue = ''
},
// 提交数独
checkShudu() {
const answer = this.rowList
for (let i = 0; i < answer.length; i++) {
for (let j = 0; j < answer[i].length; j++) {
if (answer[i][j] === '') {
uni.showToast({
title: '数独未填完',
icon: 'none'
});
return
}
}
}
if (answer.toString() === this.rightList.toString()) {
uni.showToast({
title: '答案正确',
icon: 'none'
});
} else {
uni.showToast({
title: '答案错误',
icon: 'none'
});
}
},
// 提示
showTip() {
this.isTip = true
this.countDown()
},
// 倒计时
countDown() {
// 有定时器 -> 清除定时器(避免重复设置)
if (this.myTimer) {
clearInterval(this.myTimer)
}
// 设置定时器
this.myTimer = setInterval(() => {
if (this.count > 0) {
this.count--
if (this.count === 0) {
// 倒计时到0时发送请求 并清除定时器
this.isTip = false
this.count = 5
clearInterval(this.myTimer)
}
}
}, 1000)
}
}
}
</script>
<style lang="scss">
.main {
display: flex;
flex-direction: column;
}
.sudoku_area {
border: 2px solid #ae7a36;
display: flex;
justify-content: center;
border-radius: 16rpx;
overflow: hidden;
}
.button-class {
width: 184rpx;
height: 88rpx;
background-color: #fffae1;
font-size: 32rpx;
line-height: 88rpx;
color: #6c4e27;
border-radius: 24rpx;
}
.sudoku_cell {
width: 76rpx;
box-sizing: border-box;
text-align: center;
line-height: 76rpx;
color: #6c4e27;
}
.sudoku_cell input {
width: 76rpx;
height: 76rpx;
}
.foot {
display: flex;
justify-content: center;
cursor: pointer;
margin-top: 32rpx;
}
</style>