整體流程以下所示css
command (keyCode) { // 總部
this.WhetherToRotate(keyCode) // 是否須要將上下操做轉換爲左右操做
this.Init() // 數據初始化 合併數字
this.IfInvalid() // 判斷是否無效
this.Rendering(keyCode) // 渲染到頁面
}
複製代碼
首先先將基本的 HTML 標籤跟 CSS 樣式寫出來html
因爲用的 vue ,因此渲染 html 部分的代碼不用咱們去手寫vue
<template>
<div id='app'>
<div class='total'>總分: {{this.total}} 分</div> // {{}} 這個中間表示 JavaScript 表達式
<div class='main'>
<div class='row' v-for='(items,index) of arr' :key='index'> // v-for表示循環渲染當前元素,具體渲染次數爲 arr.length
<div
:class='`c-${item} item`'
v-for='(item,index) of items'
:key='index'
>{{item>0?item:''}}</div> // :class= 表示將 JavaScript 變量做爲類名
</div>
</div>
<footer>
<h2>玩法說明:</h2>
<p>1.用鍵盤上下左右鍵控制數字走向</p>
<p>2.當點擊了一個方向時,格子中的數字會所有往那個方向移動,直到不能再移動,若是有相同的數字則會合並</p>
<p>3.當格子中再也不有可移動和可合併的數字時,遊戲結束</p>
</footer>
</div>
</template>
複製代碼
css因爲太長就不放了跟以前基本沒有太多區別git
接下來是數據的初始化github
data () {
return {
arr: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], // 與頁面綁定的數組
Copyarr: [[], [], [], []], // 用來數據操做的數組
initData: [], // 包含數字詳細座標的數組
haveGrouping: false, // 有能夠合併的數字
itIsLeft: false, // 是否爲向左合併,默認不是向左合併
endGap: true, // 判斷最邊上有沒有空隙 默認有空隙
middleGap: true, // 真 爲某行中間有空隙
haveZero: true, // 當前頁面有沒有 0
total: 0, // 總分數
itIs2048: false, // 是否成功
max: 2048 // 最高分數
}
}
複製代碼
在 mounted 添加事件監聽bash
爲何在 mounted 添加事件? 咱們先了解下vue的生命週期app
因此若是太早的話可能找不到 dom 節點,太晚的話,可能不能第一時間進行事件的響應框架
mounted () {
window.onkeydown = e => {
switch (e.keyCode) {
case 37:
// ←
console.log('←')
this.Command(e.keyCode)
break
case 38:
// ↑
console.log('↑')
this.Command(e.keyCode)
break
case 39:
// →
this.Command(e.keyCode)
console.log('→')
break
case 40:
// ↓
console.log('↓')
this.Command(e.keyCode)
break
}
}
}
複製代碼
這段代碼我是某天半夢半醒想到的,可能思惟很差轉過來,能夠看看代碼下面的圖dom
這樣一來就將向上的操做轉換成了向左的操做
向下的操做就轉換成了向右的操做
這樣折騰下能夠少寫一半的數字合併代碼
WhetherToRotate (keyCode) { // 是否須要將上下操做轉換爲左右操做
if (keyCode === 38 || keyCode === 40) { // 38 是上 40 是下
this.Copyarr = this.ToRotate(this.arr)
} else if (keyCode === 37 || keyCode === 39) { // 37 是左 39 是右
[...this.Copyarr] = this.arr
}
// 將當前操做作一個標識
if (keyCode === 37 || keyCode === 38) { // 數據轉換後只有左右操做
this.itIsLeft = true
} else if (keyCode === 39 || keyCode === 40) {
this.itIsLeft = false
}
}
複製代碼
轉換代碼
ToRotate (arr) { // 將數據從 x 到 y y 到 x 相互轉換
let afterCopyingArr = [[], [], [], []]
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr[i].length; j++) {
afterCopyingArr[i][j] = arr[j][i]
}
}
return afterCopyingArr
}
複製代碼
Init () { // 數據初始化
this.initData = this.DataDetails() // 非零數字詳情
this.Copyarr = this.NumberMerger() // 數字合併
}
複製代碼
IfInvalid () { // 判斷是否無效
// 判斷每行中間有沒有空隙
this.MiddleGap() // 真 爲某行中間有空隙
this.EndPointGap() // 在沒有中間空隙的條件下去判斷最邊上有沒有空隙
}
複製代碼
MiddleGap () { // 檢查每行中間有沒有空隙
// 當全部的數都是挨着的,那麼 x 下標兩兩相減併除以組數獲得的絕對數是 1 ,比他大說明中間有空隙
// 先將 x 下標兩兩相減 並添加到新的數組
let subarr = [[], [], [], []] // 兩兩相減的數據
let sumarr = [] // 處理後的最終數據
this.initData.forEach((items, index) => {
items.forEach((item, i) => {
if (typeof items[i + 1] !== 'undefined') {
subarr[index].push(item.col - items[i + 1].col)
}
})
})
// 將每一行的結果相加獲得總和 而後除以每一行結果的長度
subarr.forEach((items) => {
sumarr.push(items.reduceRight((a, b) => a + b, 0))
})
sumarr = sumarr.map((item, index) => Math.abs(item / subarr[index].length))
// 最後判斷有沒有比 1 大的值
sumarr.some(item => item > 1)
this.middleGap = sumarr.some(item => item > 1) // 真 爲 有中間空隙
}
複製代碼
EndPointGap () { // 檢查最邊上有沒有空隙
// 判斷是向左仍是向右 由於左右的判斷是不同的
this.endGap = true
let end
let initData = this.initData
if (this.itIsLeft) {
end = 0
this.endGap = initData.some(items => items.length !== 0 ? items[0].col !== end : false)
} else {
end = 3
this.endGap = initData.some(items => items.length !== 0 ? items[items.length - 1].col !== end : false)
}
// 取出每行的第一個數的 x 下標
// 判斷是否是最邊上
// 有不是的 說明邊上 至少有一個空隙
// 是的話說明邊上沒有空隙
}
複製代碼
這樣就將基本的判斷是否有效,是否失敗的條件都獲得了
至因而否有可合併數字已經在數據初始化時就獲得了
Rendering (keyCode) {
this.AddZero() // 先將佔位符加上
// 由於以前的數據都處理好了 因此只須要將上下的數據轉換回去就行了
if (keyCode === 38 || keyCode === 40) { // 38 是上 40 是下
this.Copyarr = this.ToRotate(this.Copyarr)
}
if (this.haveGrouping || this.endGap || this.middleGap) { // 知足任一條件就說明能夠新建隨機數字
this.RandomlyCreate(this.Copyarr)
} else if (this.haveZero) {
// 都不知足 可是有空位不作失敗判斷
} else {
// 以上都不知足視爲沒有空位,不可合併
if (this.itIs2048) { // 判斷是否達成2048
this.RandomlyCreate(this.Copyarr)
alert('恭喜達成2048!')
// 下面註釋掉的可以讓遊戲在點擊彈框按鈕後從新開始新遊戲
// this.arr = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
// this.RandomlyCreate(this.arr)
} else { //以上都不知足視爲失敗
this.RandomlyCreate(this.Copyarr)
alert('遊戲結束!')
// this.arr = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
// this.RandomlyCreate(this.arr)
}
}
if (this.itIs2048) { // 每次頁面渲染完,都判斷是否達成2048
this.RandomlyCreate(this.Copyarr)
alert('恭喜達成2048!')
// this.arr = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
// this.RandomlyCreate(this.arr)
}
}
複製代碼
這裏以前是用遞歸函數的形式去判斷,可是用遞歸函數的話會有不少問題,最大的問題就是可能會堆棧溢出,或者卡死(遞歸函數就是在函數的最後還會去調用本身,若是不給出 return 的條件,很容易堆棧溢出或卡死)
因此此次改爲抽獎的模式,將全部的空位的座標取到,放入一個數組,而後取這個數組的隨機下標,這樣咱們會獲得一個空位的座標,而後再對這個空位進行處理
RandomlyCreate (Copyarr) { // 隨機空白處建立新數字
// 判斷有沒有能夠新建的地方
let max = this.max
let copyarr = Copyarr
let zero = [] // 作一個抽獎的箱子
let subscript = 0 // 作一個拿到的獎品號
let number = 0 // 獎品號兌換的物品
// 找到全部的 0 將下標添加到新的數組
copyarr.forEach((items, index) => {
items.forEach((item, i) => {
if (item === 0) {
zero.push({ x: index, y: i })
}
})
})
// 取隨機數 而後在空白座標集合中找到它
subscript = Math.floor(Math.random() * zero.length)
if (Math.floor(Math.random() * 10) % 3 === 0) {
number = 4 // 三分之一的機會
} else {
number = 2 // 三分之二的機會
}
if (zero.length) {
Copyarr[zero[subscript].x][zero[subscript].y] = number
this.arr = Copyarr
}
this.total = 0
this.arr.forEach(items => {
items.forEach(item => {
if (item === max && !this.itIs2048) {
this.itIs2048 = true
}
this.total += item
})
})
}
複製代碼
以上就是本次 2048 的主要代碼 最後,由於隨機出現4的概率我改的比較大,因此相應的下降了一些難度,具體體如今當全部數字都在左邊(最邊上),且數字與數字間沒有空隙,再按左也會生成數字