ホームページ  >  記事  >  ウェブフロントエンド  >  vueを使用して数独ゲームを開発する方法

vueを使用して数独ゲームを開発する方法

亚连
亚连オリジナル
2018-06-19 14:28:193700ブラウズ

数独は18世紀にスイスで生まれた数学ゲームで、紙とペンを使って計算を行います。以下の記事では、vue を使用したいわゆる数独の開発に関する関連情報を中心に、サンプルコードを通じて詳しく紹介しています。

1. はじめに

最近、仕事でバックエンド管理システムのページ機能に新たな需要がないので、何をホームページに載せるか考えていました。いわゆる Sudoku を入れることを考えましたが、なぜ Sudoku と呼ばれるのでしょうか? ルールが標準の Sudoku とは異なるのは、各行と列の数字が異なるだけだからです。この例も vue に基づいており、コードは全員と共有されます。コードを渡したからといって、そのままコピーして良いというわけではありませんが、練習プロジェクトとして使ったり、知識を学んだりしていただければ幸いです。もし私の書いた内容が間違っている、間違っていると思われる点がありましたら、ご指摘いただければ、みんなで意見を交換し、一緒に進めていくことができます。

コードは github にアップロードされています。必要に応じてスターを付けることができます。 vue-demos

2. 実行効果

実装手順は少し複雑に感じないように、記事を読むことをお勧めします。混乱した。または、ソース コード (数独) に直接アクセスして、ソース コードを理解してください。このプロジェクトも複雑ではありません!

3-1. データと組版を準備する

組版のための html+css コードについては詳しく説明しませんが、これは誰にとっても難しいことではないと思います。もう少し複雑なのは、データの相互作用です。 最初のステップは、まず数独データを準備することです。データとは誰でも知っています。それは以下のようなデータです。


レイアウト効果は以下の通りです。

HTMLコードは次のとおりです

<p class="num-table" @mouseleave="hoverCol=&#39;&#39;" :class="{&#39;shake&#39;:isShake}">
 <!--遍历每一行-->
 <p v-for="row,index in allNum" class="num-row chearfix">
 <!--遍历行里面的每一列-->
 <p v-for="num1,indexSub in row" class="num-col">
  {{allNumText[index][indexSub]}}
 </p>
 </p>
</p>
コードも次のように非常に単純です

mounted(){
 let arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 let row = [], rowCol = 0;
 for (let i = 0, len = arr1.length; i < len; i++) {
 row = Object.assign([], arr1);
 this.allNum.push(row);
 //删除第一个数字并记录下来
 rowCol = arr1.splice(0, 1)[0];
 //在最后面插入数字
 arr1.push(rowCol)
 }
}

このデータの各行と列の数値が異なることもわかります。

3-2. 行

をシャッフルした後、順序をランダムにシャッフルするための前提条件は、各行と列の数字が異なることを確認することです。今回は行単位や列単位で破壊するという単純かつ大雑把な方法を使いました。たとえば、1 行目と 3 行目は相互作用し、1 列目と 5 列目は位置を交換します。行動単位の秩序の乱れについて話しましょう。 行のシャッフルは非常に簡単で、配列をランダムにシャッフルするだけです。たった 1 行のコードで完了です。

this.allNum.sort((n1, n2) => Math.random() - 0.5);

3-3. 列をシャッフルします

次に、少し複雑ですが、列をシャッフルします。 たとえば、2 番目の列が 5 番目の列の値を交換する場合、つまり、各行の 2 番目のグリッドの値を 5 番目のグリッドの値と交換する場合、それぞれを走査する必要があります。行!先ほどの2列目と5列目の列数の入れ替えは関数で実現できます!

以下のコードをご覧ください。

//随机获取两列的索引
function randomText() {
 let rondomIndex = 0, rondomIndexAfter = 0;
 //获取第一列的索引
 rondomIndex = Math.floor(Math.random() * 9);
 function randomDo() {
 rondomIndexAfter = Math.floor(Math.random() * 9);
 //如果第一列和第二列索引一样,第二列的索引再次重新随机获取
 if (rondomIndexAfter === rondomIndex) {
  randomDo();
 }
 }
 randomDo();
 //返回两列的索引
 return [rondomIndex, rondomIndexAfter]
}
//打乱列
let randomArr = [], nowValue = 0;
//同样遍历9次
for (let i = 0; i < 9; i++) {
 randomArr = Object.assign([], randomText());
 //遍历每一行,给每一行的随机两列交换值
 for (let j = 0, len = this.allNum.length; j < len; j++) {
 //随机两列交换值
 nowValue = this.allNum[j][randomArr[0]];
 this.allNum[j][randomArr[0]] = this.allNum[j][randomArr[1]];
 this.allNum[j][randomArr[1]] = nowValue;
 }
}

3-3. ランダムにセルを空にする

セルのクリアとは、いくつかのセルをランダムに空にして、人々に数独をプレイさせることです。これらのセルに入力してください。 要件、私が今気づいていることは、各行に2つの空のグリッドがあるということです。ここでの私のアプローチは、最初に各グリッドの座標を記録し、次に記録された座標からランダムに座標を取得し、「の座標は空です!」を使用することです。

まず、すべての点の座標を取得します

//记录所有坐标
let rowText = &#39;&#39;, arrText = []
for (let i = 0; i < 9; i++) {
 rowText = &#39;&#39;
 for (let j = 0; j < 9; j++) {
  rowText += i + &#39;-&#39; + j + &#39;,&#39;;
 }
 arrText.push(rowText.substr(0, rowText.length - 1))
}
console.log(arrText);

この座標を見ると、配列の1つの要素が最初の行であり、「0-0」が最初の行の最初のグリッドであることが誰でも簡単にわかります。配列の最後の要素は最後の行、「8-8」は最後の行、最後のグリッドなどです。

以下はランダムなくり抜きです。コードも非常に簡単です。

//随机掏空
let nowItme = [], _option, nowOption = [];
for (let i = 0; i < 9; i++) {
 //抽取当前行的所有坐标
 nowItme = arrText[i].split(&#39;,&#39;);
 nowOption = [];
 //当前行的随机两个坐标掏空
 for (let j = 0; j < 2; j++) {
  //抽取当前行的随机一个坐标
  _option = Math.floor(Math.random() * nowItme.length);
  //分割坐标的x,y
  nowOption = nowItme.splice(_option,1)[0].split("-");
  this.allNum[nowOption[0]][nowOption[1]] = &#39;&#39;;
 }
}

誰もがこれを奇妙に感じると思います。では、空のグリッドのスタイルを変更するスタイルを書いてみましょう。 cssには.noクラスに相当するスタイルを記述していますので注意してください。

りー

3-4.显示数字键盘

首先,我简单的用一个流程图说下逻辑,如下

然后关于数字键盘的位置,看下图(数字键盘的样式我不多说了,就是一个是相对定位,一个绝对定位的设置而已)

如上图,我点击的是第一行第三个格子,首先,我期待被点击的格子的样式有所改变,方便我区分,这个不难,用一个class改变样式就可以了,这个可以看下面的代码,我用一个.cur的class控制样式。还有一个就是期待数字键盘在第二行,第四个格子那里出现。这样的话,大家就知道,数字键盘的位置是怎么定位的了!数字键盘的top就是,被点击格子所在的行的索引+160(60是格子的宽高),left就是,被点击格子所在的列的索引+160(60是格子的宽高)。比如上图,第一行第三个格子,top=(0+1)*60+'px',left=(2+1)*60+'px'。

代码如下

<!--遍历每一行-->
 <p v-for="row,index in allNum" class="num-row chearfix">
  <!--遍历行里面的每一列-->
  <!--
   no:被掏空数组的样式
   cur:格子被点击时触发,被点击的格子样式
  -->
  <p v-for="num1,indexSub in row"
    :class="{&#39;no&#39;:num1===&#39;&#39;,
    &#39;cur&#39;:curRow===index&&indexSub===curCol}"
    @click="showCheck(index,indexSub)" class="num-col">
   {{allNumText[index][indexSub]}}
  </p>
 </p>
<!--数字键盘-->
<p class="num-check chearfix" :style="{&#39;top&#39;:(curRow+1)*60+&#39;px&#39;,&#39;left&#39;:(curCol+1)*60+&#39;px&#39;}"
  v-show="checkShow">
 <ul>
  <li @click="inputText(1)">1</li>
  <li @click="inputText(2)">2</li>
  <li @click="inputText(3)">3</li>
  <li @click="inputText(4)">4</li>
  <li @click="inputText(5)">5</li>
  <li @click="inputText(6)">6</li>
  <li @click="inputText(7)">7</li>
  <li @click="inputText(8)">8</li>
  <li @click="inputText(9)">9</li>
 </ul>
</p>

js代码

/**
 * @description 显示数字键盘
 * @param i1
 * @param i2
 */
showCheck(i1, i2){
 //点击的格子是否是被掏空的格子
 if (this.allNum[i1][i2] !== &#39;&#39;) {
  return
 }
 //点击的格子如果是上一次点击的格子(当前格子)
 if (i1 === this.curRow && i2 === this.curCol) {
  //隐藏数字键盘,curRow和curCol设空
  this.checkShow = false;
  this.curRow = &#39;&#39;;
  this.curCol = &#39;&#39;;
 }
 else {
  //隐藏数字键盘,curRow和curCol分别设置成当前的点
  this.checkShow = true;
  this.curRow = i1;
  this.curCol = i2;
 }
},

运行效果

3-5.高亮显示同行同列

这一步很简单,首先,高亮显示行,大家都知道怎么做了,就是行对应的p,设置一个:hover,然后对应设置单元格的样式而已!这个不多说!

然后,高亮显示列,复杂一点,但是也很简单,原理我想大家也知道,就是当鼠标进如格子的时候,在data里面,用一个变量储存进入的格子的列的索引,然后加上判断,如果格子的列的索引等于进入的格子的列的索引。就加上一个class,这里我用.cur-col。

代码如下

<!--遍历每一行-->
<p v-for="row,index in allNum" class="num-row clear">
 <!--遍历行里面的每一列-->
 <!--
  no:被掏空数组的样式
  cur:格子被点击时触发,被点击的格子样式
  cur-col:鼠标进入的时候触发,和被点击格子同一列的格子的样式
 -->
 <p v-for="num1,indexSub in row"
   :class="{&#39;no&#39;:num1===&#39;&#39;,
   &#39;cur&#39;:curRow===index&&indexSub===curCol,
   &#39;cur-col&#39;:hoverCol===indexSub}"
   @click="showCheck(index,indexSub)" @mouseenter="hoverCol=indexSub;" class="num-col">
  {{allNumText[index][indexSub]}}
 </p>
</p>

运行效果

3-6.填写操作和错误提示

这一步的操作函数,我直接发代码吧,看代码比我说的会清晰些,毕竟说的有点绕

<!--遍历每一行-->
<p v-for="row,index in allNum" class="num-row clear">
 <!--遍历行里面的每一列-->
 <!--
  no:被掏空数组的样式
  cur:格子被点击时触发,被点击的格子样式
  cur-col:鼠标进入的时候触发,和被点击格子同一列的格子的样式
  err:填写错误的时候触发的样式
 -->
 <p v-for="num1,indexSub in row"
   :class="{&#39;no&#39;:num1===&#39;&#39;,
   &#39;cur&#39;:curRow===index&&indexSub===curCol,
   &#39;cur-col&#39;:hoverCol===indexSub,
   &#39;err&#39;:(optionNow.x===index&&optionNow.y===indexSub)||(optionNowInRow.x===index&&optionNowInRow.y===indexSub)||(optionNowInCol.x===index&&optionNowInCol.y===indexSub)}"
   @click="showCheck(index,indexSub)" @mouseenter="hoverCol=indexSub;" class="num-col">
  {{allNumText[index][indexSub]}}
 </p>
</p>

js代码

inputText(_text){
 //*****************************检查前的初始化
 let _row = this.curRow, _col = this.curCol;
 this.curRow = &#39;&#39;;
 this.curCol = &#39;&#39;;
 this.isErr = false;
 this.optionNow = {
  x: &#39;&#39;,
  y: &#39;&#39;,
 }
 this.optionNowInRow = {
  x: &#39;&#39;,
  y: &#39;&#39;,
 }
 this.optionNowInCol = {
  x: &#39;&#39;,
  y: &#39;&#39;,
 }
 //*****************************检查行
 //根据当前格子进行赋值
 this.allNumText[_row][_col] = _text;
 let rowCheck = Object.assign(this.allNumText[_row], []);
 this.checkShow = false;
 for (let i = 0, len = rowCheck.length; i < len; i++) {
  //如果值一样,但是坐标不一样,就是填写错误
  if (_text === rowCheck[i] && _col !== i) {
   this.isErr = true;
   this.isShake = true;
   //记录当前格子的信息
   this.optionNow = {
    x: _row,
    y: _col,
   }
   //记录和当前格子同一行,以及同一个值的格子的坐标
   this.optionNowInRow = {
    x: _row,
    y: i,
   }
  }
 }
 //*****************************检查列
 let colCheck = [];
 //首先把每一行的那一列的数值保存起来
 for (let i = 0, len = this.allNumText.length; i < len; i++) {
  colCheck.push(this.allNumText[i][_col]);
 }
 //遍历检查
 for (let i = 0, len = colCheck.length; i < len; i++) {
  //如果值一样,但是坐标不一样,就是填写错误
  if (_text === colCheck[i] && _row !== i) {
   this.isErr = true;
   this.isShake = true;
   //记录和当前格子同一列,以及同一个值的格子的坐标
   this.optionNowInCol = {
    x: i,
    y: _col,
   }
  }
 }
 //如果发现的同样的
 if (this.isErr) {
  setTimeout(() => {
   this.isShake = false;
  }, 1000)
  return;
 }
 //如果数组去重后,长度小于9,就是行没完成
 rowCheck = rowCheck.filter(item => item !== &#39;&#39;);
 if (rowCheck.length !== 9) {
  //console.log(&#39;行没完成&#39;)
  return;
 }
 let coloCheck = [];
 //如果数组去重后,长度小于9,就是列没完成
 for (let i = 0, len = this.allNumText.length; i < len; i++) {
  coloCheck = [...new Set(this.allNumText[i])];
  coloCheck = coloCheck.filter(item => item !== &#39;&#39;);
  if (coloCheck.length !== 9) {
   //console.log(&#39;没完成&#39;)
   return;
  }
 }
 alert(&#39;挑战成功,但是没奖品&#39;);
 this.numShow = false;
}

上面的代码逻辑,简单说下

1..err 这个class是设置红色字体所使用的,至于判断,就是在inputText这个函数里面,有optionNow和 optionNowInRow和optionNowInCol。只要格子的坐标等于三者其中之一,就会添加这个class,就会变红。

2..isShake这个class是控制,抖动的动画,添加上了之后,在一秒后,要去掉这个class,不然下次添加没有动画效果。

3.在inputText这个函数里面,我操作的数独列表,并不是之前,提到的allNum,而是利用allNum,深度拷贝生成出的allNumText(this.allNumText = JSON.parse(JSON.stringify(this.allNum));) 。主要就是为了避免下图的情况!

这样是为了往掏空的格子输入数字的时候,然后那个格子就不能再改了,即使是填错了,都不能改。样式控制也不正确!正确的格式应该是下面这样,即使填入了,格子的样式还是灰色的,这样可以方便的知道哪个格子是当时被掏空的,填写错了,也是可以改的。

4.完整代码




 
 vue-所谓的数独
 
 


所谓的数独:规则

1.每一行数字不重复

2.每一列数字不重复

{{allNumText[index][indexSub]}}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

<script> new Vue({ el:&#39;#num&#39;, data:{ name: &#39;welcome&#39;, testText: &#39;欢迎来到&#39;, nowIndex: 0, allNum: [],//数字排列 answer: [],//所有答案的坐标点 allNumText: [],//数字,包括输入后的数字 curRow: &#39;&#39;,//当前格子所在的行的索引 curCol: &#39;&#39;,//当前格子所在的列的索引 checkShow: false,//数字键盘的显示 hoverCol: &#39;&#39;,//鼠标进去的当前列 hoverRow: 0,//鼠标进入的当前行 numShow: true,//数独的显示 optionNow: {},//输入后的格子的坐标 optionNowInRow: {},//和输入后的格子在同一行,并且同样值的格子的坐标 optionNowInCol: {},//和输入后的格子在同一列,并且同样值的格子的坐标 isErr: false,//是否输入错误后 isShake: false//是否显示震动的样式 }, methods: { /** * @description 显示数字键盘 * @param i1 * @param i2 */ showCheck(i1, i2){ //点击的格子是否是被掏空的格子 if (this.allNum[i1][i2] !== &#39;&#39;) { return } //点击的格子如果是上一次点击的格子(当前格子) if (i1 === this.curRow && i2 === this.curCol) { //隐藏数字键盘,curRow和curCol设空 this.checkShow = false; this.curRow = &#39;&#39;; this.curCol = &#39;&#39;; } else { //隐藏数字键盘,curRow和curCol分别设置成当前的点 this.checkShow = true; this.curRow = i1; this.curCol = i2; } }, inputText(_text){ //*****************************检查前的初始化 let _row = this.curRow, _col = this.curCol; this.curRow = &#39;&#39;; this.curCol = &#39;&#39;; this.isErr = false; this.optionNow = { x: &#39;&#39;, y: &#39;&#39;, } this.optionNowInRow = { x: &#39;&#39;, y: &#39;&#39;, } this.optionNowInCol = { x: &#39;&#39;, y: &#39;&#39;, } //*****************************检查行 //保存当前格子的值 this.allNumText[_row][_col] = _text; let rowCheck = Object.assign(this.allNumText[_row], []); this.checkShow = false; for (let i = 0, len = rowCheck.length; i < len; i++) { //如果值一样,但是坐标不一样,就是填写错误 if (_text === rowCheck[i] && _col !== i) { this.isErr = true; this.isShake = true; //记录当前格子的信息 this.optionNow = { x: _row, y: _col } //记录和当前格子同一行,以及同一个值的格子的坐标 this.optionNowInRow = { x: _row, y: i } } } //*****************************检查列 let colCheck = []; //首先把每一行的那一列的数值保存起来 for (let i = 0, len = this.allNumText.length; i < len; i++) { colCheck.push(this.allNumText[i][_col]); } //遍历检查 for (let i = 0, len = colCheck.length; i < len; i++) { //如果值一样,但是坐标不一样,就是填写错误 if (_text === colCheck[i] && _row !== i) { this.isErr = true; this.isShake = true; //记录和当前格子同一列,以及同一个值的格子的坐标 this.optionNowInCol = { x: i, y: _col } } } //如果发现的同样的 if (this.isErr) { setTimeout(() => { this.isShake = false; }, 1000) return; } //如果数组去重后,长度小于9,就是行没完成 rowCheck = rowCheck.filter(item => item !== &#39;&#39;); if (rowCheck.length !== 9) { console.log(&#39;行没完成&#39;) return; } let coloCheck = []; //如果数组去重后,长度小于9,就是列没完成 for (let i = 0, len = this.allNumText.length; i < len; i++) { coloCheck = [...new Set(this.allNumText[i])]; coloCheck = coloCheck.filter(item => item !== &#39;&#39;); if (coloCheck.length !== 9) { console.log(&#39;没完成&#39;) return; } } alert(&#39;挑战成功,但是没奖品&#39;); this.numShow = false; } }, mounted(){ let arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let row = [], rowCol = 0; for (let i = 0, len = arr1.length; i < len; i++) { row = Object.assign([], arr1); this.allNum.push(row); rowCol = arr1.splice(0, 1)[0]; arr1.push(rowCol) } //打乱行 this.allNum.sort((n1, n2) =&gt; Math.random() - 0.5); //随机获取两列的索引 function randomText() { let rondomIndex = 0, rondomIndexAfter = 0; //获取第一列的索引 rondomIndex = Math.floor(Math.random() * 9); function randomDo() { rondomIndexAfter = Math.floor(Math.random() * 9); //如果第一列和第二列索引一样,第二列的索引再次重新获取 if (rondomIndexAfter === rondomIndex) { randomDo(); } } randomDo(); //返回两列的索引 return [rondomIndex, rondomIndexAfter] } //打乱列 let randomArr = [], nowValue = 0; //同样遍历9次 for (let i = 0; i < 9; i++) { randomArr = Object.assign([], randomText()); //遍历每一行,给每一行的随机两列交换值 for (let j = 0, len = this.allNum.length; j < len; j++) { //随机两列交换值 nowValue = this.allNum[j][randomArr[0]]; this.allNum[j][randomArr[0]] = this.allNum[j][randomArr[1]]; this.allNum[j][randomArr[1]] = nowValue; } } //记录所有坐标 let rowText = &#39;&#39;, arrText = [] for (let i = 0; i < 9; i++) { rowText = &#39;&#39; for (let j = 0; j < 9; j++) { rowText += i + &#39;-&#39; + j + &#39;,&#39;; } arrText.push(rowText.substr(0, rowText.length - 1)) } console.log(arrText); //随机掏空 let nowItme = [], _option, nowOption = []; for (let i = 0; i < 9; i++) { //抽取当前行的所有坐标 nowItme = arrText[i].split(&#39;,&#39;); nowOption = []; //当前行的随机两个坐标掏空 for (let j = 0; j < 2; j++) { //抽取当前行的随机一个坐标 _option = Math.floor(Math.random() * nowItme.length); //分割坐标的x,y nowOption = nowItme.splice(_option,1)[0].split("-"); this.allNum[nowOption[0]][nowOption[1]] = &#39;&#39;; } } //深度拷贝数独的数字 this.allNumText = JSON.parse(JSON.stringify(this.allNum)); } }) </script>

reset.css和vue.min.js大家自行到github下载!

5.小结

好了,用vue做的所谓的数独,就写到这里了,主要就是逻辑有点绕,其它的问题相信都难不倒大家。这个实例比之前快速入门的三个小实例要麻烦一点,但是也很好理解!大家只要稍微看下估计都不难理解!最后,如果大家觉得文章写得不好,哪里写错了,欢迎给建议或者指点下迷津。期待和大家交流意见,共同进步!

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

使用js如何实现从新赋值

在js中如何将canvas生成图片保存

jsで双方向バインディングを実装する方法

webpackのより実践的な機能を詳しく紹介

jQueryを使ってメニューの追加・削除機能を実装する方法

以上がvueを使用して数独ゲームを開発する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。