前言
继上次JAVA版的九宫格数独,这几天把java版的迁移到了小程序这边,写一篇记录一下。
核心还是在算法上,话不多说,直接干代码
一、核心算法
先把81个格子填满,填充之前需要先判断当前格子填的数字是否合适?再去挖洞,避免出现无法填充的bug。代码如下【挖洞在后面给出】
1、填满格子
fill(){
if(this.fillCount == 81){
return; //表示填满返回
}
if(this.squareNums[parseInt(this.fillCount / 9)][this.fillCount % 9] != 0) {
++this.fillCount;
this.fill(); //位置不为0,填充下一个
return;
} else {
//从1到9里面选择数字填进去
for(var x = 1; x != 11; x++) {
if(this.fillCount == 81){
return; //表示填满返回
}
if(x == 10 && this.fillCount != 0) {
--this.fillCount;
this.squareNums[parseInt(this.fillCount / 9)][this.fillCount % 9] = 0;
return;
}
if(this.checkFillabled(parseInt(this.fillCount / 9), (this.fillCount % 9), x)){
this.squareNums[parseInt(this.fillCount / 9)][(this.fillCount % 9)] = x;
++this.fillCount;
this.fill();
}
}
}
},
2、判断格子是否能填充指定数值
checkFillabled(X, Y, FillNum){
for(var i=0;i<9;i++){
if(this.squareNums[X][i]==FillNum || this.squareNums[i][Y]==FillNum){
return false;
}
}
var D_X = parseInt(X / 3) * 3; // [0,3,6]
var D_Y = parseInt(Y / 3) * 3;
for(var count = 0; count < 9; count++) {
if(D_X == X && D_Y == Y)
continue; //count / 3 [0,1,2] count % 3 [0,1,2]
if(this.squareNums[D_X + parseInt(count/3)][D_Y + count%3] == FillNum)
return false;
}
return true;
},
3,格子填满就开始挖洞
digholes(count){
for(var i=0;i<count;i++){
var rand = parseInt(Math.random()*81)
this.squareNums[parseInt(rand/9)][rand%9] = 0
}
}
二、完整代码
核心算法写完就是自由发挥的时候了。游戏页面的完整代码
1、模板
<view class="" style="filter: grayscale(0%);font-family:mianfeiziti;">
<view class="" style="position: fixed;top: 0;left: 0;z-index: 99;width: 100vw;">
<gui-top-message ref="guitopmsg1">
<view class="gui-bg-green">
<text
class="message-text gui-block-text gui-color-white gui-icons"> 还没选格子呢</text>
</view>
</gui-top-message>
<gui-top-message ref="guitopmsg2">
<view class="gui-bg-yellow">
<text
class="message-text gui-block-text gui-color-black gui-icons"> 行或列已存在相同的填充数</text>
</view>
</gui-top-message>
<view class="buttons">
<button type="default" @tap="navigateTo('../../pages/shudu/guanqia')">关卡</button>
<button type="default" @tap="back_step">退一步</button>
<button type="default" @tap="pauseTemp()">暂停</button>
<button type="default" @tap="navigateTo('../../pages/shudu/login')">退出</button>
</view>
<view class="remarks">
<view class="rank">
等级:{{rankText}}
</view>
<view class="interval">
耗时:{{intervalText}}
</view>
</view>
<view class="select-display">
已选中格子:{{arial.X}} & {{arial.Y}}
</view>
<view class="center-container">
<view class="square-row" v-for="(item,index) in squareNums" :key="index">
<view :class="[
{
'bg-yelow':(parseInt(index/3)==0&&(parseInt(ind/3)==0||parseInt(ind/6)==1))||(parseInt(index/6)==1&&(parseInt(ind/3)==0||parseInt(ind/6)==1)),
'bg-white':parseInt(index/3)==1&&parseInt(ind/3)==1
},
'row-'+index+''+ind
]" v-for="(num,ind) in item" :key="ind" @tap="selectCheck($event,index,ind)"
:data-num="num"
>
{{num==0?'':num}}
</view>
</view>
</view>
<view class="bottom-num">
<button @tap="selectFillNum(1)">1</button>
<button @tap="selectFillNum(2)">2</button>
<button @tap="selectFillNum(3)">3</button>
<button @tap="selectFillNum(4)">4</button>
<button @tap="selectFillNum(5)">5</button>
<button @tap="selectFillNum(6)">6</button>
<button @tap="selectFillNum(7)">7</button>
<button @tap="selectFillNum(8)">8</button>
<button @tap="selectFillNum(9)">9</button>
</view>
<gui-popup ref="guipopup1">
<view class="gui-relative gui-box-shadow gui-img-in">
<view class="win-note">
好厉害,再接再厉
</view>
<image
style="width:580rpx;"
mode="widthFix"
src="../../static/image/victory.jpeg"></image>
<!-- 关闭按钮 -->
<text class="gui-block demo-close gui-icons gui-color-white gui-absolute-rt"
@tap.stop="close1"></text>
<view class="win-btn">
<button type="default" @tap="afterWinFun(0)">关卡</button>
<button type="default" @tap="afterWinFun(1)">下一关</button>
</view>
</view>
</gui-popup>
</view>
<view class="" :style="{background :'url(' + picUrl + ')',backgroundSize:'100% 100%',backgroundRepeat:'no-repeat;',backgroundPosition:'0 100%', filter: 'blur(0px)',height:'100vh',zIndex:-1}">
</view>
</view>
2、js
const innerAudioContext1 = uni.createInnerAudioContext();
innerAudioContext1.src = uni.graceJS.cdnUrl+'music/fill.mp4';
const innerAudioContext2 = uni.createInnerAudioContext();
innerAudioContext2.src = uni.graceJS.cdnUrl+'music/finish.mp4';
;
import api from '../../js/api.js'
export default{
data(){
return {
picUrl:uni.graceJS.cdnUrl+'images/bgpost2.jpeg',
squareNums:[],
x:0,
y:0,
arial:{X:0,Y:0},
fillCount:0,
intervalText:"00时00分00秒",
interval:null,
second:0,
minute:0,
hour:0,
isPause:false, // 暂停
backStep:0 ,// 退一步
pre_arial:{x:0,y:0},
holesNum:20,// 简单 20 一般 30 困难 40
rank:0, // 简单 一般 困难
guanqia:0, // 当前关卡
isRedirect:false,
rankText:"",
voicePlaying:false,
userInfo: uni.getStorageSync('userinfo')?JSON.parse(uni.getStorageSync('userinfo')):uni.getStorageSync('userinfo'),
}
},
methods:{
pauseTemp(){
uni.buttonVoice();
if(this.isPause)return;
this.isPause = true
clearInterval(this.interval)
},
navigateTo(url){
uni.buttonVoice();
this.isRedirect = true
clearInterval(this.interval)
uni.redirectTo({
url
})
},
open1 : function () {this.$refs.guipopup1.open();},
close1 : function () {this.$refs.guipopup1.close();},
afterWinFun(i){
uni.buttonVoice();
this.isRedirect = true
if(i){
uni.redirectTo({
url:'../../pages/shudu/main_page?guanqia='+(this.guanqia+1)+'&rank='+this.rank
})
}else{
uni.redirectTo({
url:'../../pages/shudu/guanqia'
})
}
},
selectFillNum(num){
if(!this.arial.X){
this.openmsg1()
return;
}
if(this.checkFillabled(this.x,this.y,num)){
this.voicePlay()
this.backStep = 0
this.pre_arial = {x:this.x,y:this.y} // 上一步的格子
this.squareNums[this.x][this.y] = num
this.$set(this.$data,'squareNums',this.squareNums)
if(!/0/.test(this.squareNums.join())){
uni.graceJS.post(
api.saveGuanQiaData(),
{w_id:this.userInfo.w_id,time:this.second,rank:this.rank}, {},{},
(res) => {
console.log(res)
}
);
clearInterval(this.interval)
setTimeout(()=>{
this.open1()
this.voicePlay2()
},300)
}
}else{
this.openmsg2()
}
},
fill(){
if(this.fillCount == 81){
return; //表示填满返回
}
if(this.squareNums[parseInt(this.fillCount / 9)][this.fillCount % 9] != 0) {
++this.fillCount;
this.fill(); //位置不为0,填充下一个
return;
} else {
//从1到9里面选择数字填进去
for(var x = 1; x != 11; x++) {
if(this.fillCount == 81){
return; //表示填满返回
}
if(x == 10 && this.fillCount != 0) {
--this.fillCount;
this.squareNums[parseInt(this.fillCount / 9)][this.fillCount % 9] = 0;
return;
}
if(this.checkFillabled(parseInt(this.fillCount / 9), (this.fillCount % 9), x)){
this.squareNums[parseInt(this.fillCount / 9)][(this.fillCount % 9)] = x;
++this.fillCount;
this.fill();
}
}
}
},
back_step(){
uni.buttonVoice();
if(!this.arial.X){
return;
}
if(this.backStep==0){
this.backStep = 1
this.squareNums[this.pre_arial.x][this.pre_arial.y] = 0
this.$set(this.$data,'squareNums',this.squareNums)
}else{
uni.showToast({
title:'都说了退一步你还想退几步?是不是玩不起?',
icon:'none'
})
}
},
squareInit(){
let first_layer = [];
for(var i=0;i<=8;i++){
let sec_layer = [];
for(var j=0;j<=8;j++){
sec_layer.push(0)
}
first_layer.push(sec_layer)
}
this.squareNums = first_layer
this.fill()
this.digholes(this.holesNum)
},
selectCheck(e,x,y){
if(e.target.dataset.num)return;
uni.buttonVoice();
var that = this
if(this.isPause){
this.isPause = false
this.interval = setInterval(function(){
that.second++;
var s = (that.second%60).toString().padStart(2,0);
var m = parseInt(that.second/60)==60?'00':(parseInt(that.second/60)).toString().padStart(2,0)
var h = (parseInt(that.second/3600)).toString().padStart(2,0)
that.$set(that.$data,'intervalText',h+"时"+m+"分"+s+"秒")
},1000)
}
const query = uni.createSelectorQuery().in(this);
query.select('.row-'+x+''+y).boundingClientRect(function(data){
console.log(data)
this.background = 'gray'
}).exec();
// query.select('.row-'+x+''+y).backgroundColor = 'gray';
this.$set(this.$data,'arial',{X:x+1,Y:y+1})
this.x = x
this.y = y
},
checkFillabled(X, Y, FillNum){
for(var i=0;i<9;i++){
if(this.squareNums[X][i]==FillNum || this.squareNums[i][Y]==FillNum){
return false;
}
}
var D_X = parseInt(X / 3) * 3; // [0,3,6]
var D_Y = parseInt(Y / 3) * 3;
for(var count = 0; count < 9; count++) {
if(D_X == X && D_Y == Y)
continue; //count / 3 [0,1,2] count % 3 [0,1,2]
if(this.squareNums[D_X + parseInt(count/3)][D_Y + count%3] == FillNum)
return false;
}
return true;
},
digholes(count){
for(var i=0;i<count;i++){
var rand = parseInt(Math.random()*81)
this.squareNums[parseInt(rand/9)][rand%9] = 0
}
},
openmsg2 : function () {
this.$refs.guitopmsg2.open();
},
openmsg1 : function () {
this.$refs.guitopmsg1.open();
},
voicePlay(){
if(!this.voicePlaying){
this.voicePlaying = true
innerAudioContext1.play();
setTimeout(()=>{
this.voicePlaying = false
innerAudioContext1.stop();
},200)
}
},
voicePlay2(){
innerAudioContext2.play()
setTimeout(function(){
innerAudioContext2.stop()
},4500)
},
switchTime(time){
var str = ''
if(time>=3600){
str += (parseInt(time/3600)).toString().padStart(2,0)+'时'
}else{
str += '00时'
}
if(time>=60){
str += (parseInt((time%3600)/60)).toString().padStart(2,0)+'分'
}else{
str += '00分'
}
str += (parseInt(time%60)).toString().padStart(2,0)+'秒'
return str;
}
},
onLoad(option){
console.log(option)
this.guanqia = parseInt(option.guanqia)
this.rank = option.rank
uni.setNavigationBarTitle({
title:'第'+this.guanqia+'关',
})
this.rankText = this.rank==0?'简单':(this.rank==1?'一般':'困难')
this.holesNum = this.rank==0?20:(this.rank==1?30:40)
this.squareInit()
var that = this
this.interval = setInterval(function(){
that.second++;
that.$set(that.$data,'intervalText',that.switchTime(that.second))
},1000)
},
onUnload() {
clearInterval(this.interval)
}
}
3、css样式
.message-text{line-height:88rpx; font-size:26rpx; text-align:center;}
.buttons{
display: flex;
justify-content: center;
padding: 40rpx 20rpx;
button{
background:linear-gradient(0deg, #bed3e7 , #fff);
}
}
.remarks{
padding: 10rpx 20rpx;
overflow: hidden;
font-weight: bold;
background-color: rgba(255,255,255,.7);
.rank{
float: left;
}
.interval{
float: right;
}
}
.center-container{
width: 96%;
margin: 30rpx auto;
.square-row{
display: flex;
justify-content: center;
view{
width: 80rpx;
height: 70rpx;
text-align: center;
line-height: 70rpx;
margin: 5rpx;
background-color:#00ff00;
font-weight: bold;
&.bg-yelow{
background-color:#ffff00;
}
&.bg-white{
background-color:white;
}
box-shadow: 10rpx 10rpx 10rpx gray;
}
}
}
.bottom-num{
display: flex;
justify-content: center;
button{
background:linear-gradient(0deg, #bed3e7 , #fff);
}
}
.select-display{
text-align: right;
padding: 20rpx;
font-weight: bold;
background-color: rgba(255,255,255,.7);
}
.win-note{
color: white;
padding: 10px;
text-align: center;
font-weight: bold;
font-size: 50rpx;
}
.win-btn{
width: 90%;
margin: -100rpx auto;
button{
width: 200rpx;
font-size: 30rpx;
font-weight: bold;
&:nth-child(1){
float: left;
background:linear-gradient(45deg, #9de757 , #fff);
}
&:nth-child(2){
float: right;
background:linear-gradient(45deg, #fff, #55aaff );
}
}
}
三、呈现的结果
【这里建议字体引入,图片的静态资源用cdn,不然会造成页面卡顿,字体无效】
最后
贴上自己的作品二维码 --- 九宫格数独游戏,希望大家给点建议指导,谢谢查看!