preface

  • This time, VUE was used to write 2048. The main purpose is to brush up on VUE.
  • But it doesn’t seem to use much vUE stuff, ==! Probably used to not using frames
  • Due to time constraints, the implementation process was not explained in detail before, but this time we will explain the more convoluted functions in detail
  • I won’t do the simple functions because of space problems
  • Code address: github.com/yhtx1997/Sm…

Realize the function

  1. Digital combination
  2. Current total score calculation
  3. No operation is performed when there are no movable digits
  4. There are no movable, conflable numbers, and no new game can fail
  5. Reach 2048 end game

Knowledge used

  1. ES6
  2. Vue partial template syntax
  3. Vue life cycle
  4. Array methods
    1. reverse()
    2. push()
    3. unshift()
    4. some()
    5. forEach()
    6. reduceRight()
  5. Mathematical methods
    1. Math.abs()
    2. Math.floor()

The specific implementation

  • Whether the up and down operation needs to be converted to the left and right operation
  • Data initialization
  • Incorporate digital
  • Determine whether the operation is invalid
  • Render to page
  • Create random numbers
  • Calculate the total score
  • Judge success
  • Judgment of failure

The overall process is as follows

command(keyCode) {// headquarters this.whethertorotate (keyCode) // whether up and down operations need to be converted to left and right operations this.init () // data initialization merge number this.ifinvalid () // determine IfInvalid This.Rendering(keyCode) // Render to page}Copy the code

Initialize the

Start by writing out the basic HTML tags and CSS styles

Because vUE is used, the rendering HTML part of the code does not need to be handwritten

<template>
  <div id='app'>
    <div class='total'<div > // {{}} this middle represents the JavaScript expression <div class='main'>
      <div class='row' v-for='(items,index) of arr' :key='index'Arr. Length <div :class='`c-${item} item`'
          v-for='(item,index) of items'
          :key='index'>{{item>0? item:' '</div> </div> <footer> <h2> </h2> <p>1 2. When clicking a direction, the numbers in the grid will all move in that direction until they can't move any more. If there are the same numbers, they will merge </p> <p>3. </p> </footer> </div> </template>Copy the code

I’m not going to put CSS in there because it’s too long and it’s not much different than it was before

Next comes initialization of the data

data () {
    return{arr: [[0, 0, 0, 0] to [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], / / and binding array Copyarr page: [[], [], [], []], / / used for data manipulation array initData: [], / / contains detailed coordinates of digital array haveGrouping:false// There are numbers that can be merged:false, // Whether to merge left, default is not merge left endGap:true// There is a middleGap:true// If there is a gap in the middle of a linetrueTotal: 0, // itIs2048:false, // Successful Max: 2048 // maximum score}}Copy the code

Doing a good initialization should look something like this

Adding Event Listeners

Add the event listener to Mounted

Why add events to Mounted? Let’s take a look at the vUE lifecycle

  • The code we wrote at this stage has not been run before the beforeCreate instance was created
  • At this stage, the code we wrote is already running but has not yet rendered the HTML to the page
  • Mounted At this stage the HTML is rendered to the page and the DOM node can be retrieved
  • BeforeUpdate Calls like warp.innerHTML = HTML before we need to rerender the HTML; before
  • Updated Invoked after the data is updated after the HTML is rerendered
  • Called after the destroyed instance discards the code we wrote
  • ErrorCaptured is called when an error is caught from a descendant component. New in 2.5.0+
  • Note: I only refer to the code we wrote for ease of understanding, not the actual code we wrote

Therefore, if it is too early, the DOM node may not be found, and if it is too late, the event may not be the first time to respond

  mounted () {
    window.onkeydown = e => {
      switch (e.keyCode) {
        case37: / / please console. The log ('please')
          this.Command(e.keyCode)
          break
        case38: / / write to the console. The log ('write')
          this.Command(e.keyCode)
          break
        case39: // → this.mand (e.keycode) console.log('- >')
          break
        case40: / / left the console. The log ('left')
          this.Command(e.keyCode)
          break}}}Copy the code

Simplify the operation to only left and right

I came up with this code half asleep one day. Maybe my thinking is not getting better. You can see the figure below the code

And that’s going to convert the up operation to the left operation and the down operation to the right operation and you’re going to have to do half of the number merging

WhetherToRotate (keyCode) {// Whether the up and down operations need to be converted to left and right operationsif38 (keyCode = = = | | keyCode = = = 40) {/ / 38 is under 40 is enclosing Copyarr = this. ToRotate (enclosing arr)}else if37 (keyCode = = = | | keyCode = = = 39) {/ / 37 is left 39 is right [... this. Copyarr] = this. Arr} / / the current operation to do a logoif(keyCode = = = 37 | | keyCode = = = 38) {/ / the data transformation after only about operating this. ItIsLeft =true
      } else if (keyCode === 39 || keyCode === 40) {
        this.itIsLeft = false}}Copy the code

The transformation code

ToRotate (arr) {// Convert data from x to y and y to xlet 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
    }
Copy the code

Data initialization

  • The zeros in the array are only used as placeholders in this small work and are considered junk data, so they need to be disposed of before starting and added to at the end
  • There are two data formats, one that contains detailed information and is used to make some judgments; One is a two-dimensional array of pure numbers that are then used to rerender the page
 InitDataDetails() {this.initData = this.dataDetails () // non-zero number.copyarr = this.numberMerger () // NumberMerger}Copy the code

Determine whether it is invalid

 IfInvalidThis.middlegap () this.endpointGap () {this.middlegap () {this.endpointGap ()}Copy the code
  • Determine if there is a gap between the two numbers
    MiddleGap() {// If all the numbers are next to each other, then the absolute number of x subtracting and dividing by the number of groups is 1, which means there is a gapletSubarr = [[], [], [], []] //letForEach ((items, index) => {items. ForEach ((item, I) => {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, Math.abs(item/subarr[index].length)) // Find if there is a value greater than 1 sumarr.some(item => item > 1) this.middlegap = Sumarr. some(item => item > 1)Copy the code
  • Determine if the number is on the edge
   EndPointGap() {this.endGap = this.endGap = this.endGap = this.endGap = this.endGaptrue
     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// If there is at least one space on the edge // if there is no space on the edge}Copy the code

And so the basic criteria for whether it’s valid or not, whether it’s failed or not is given at the time the data is initialized

Now all the data should look like this

To render the page

Render.addzero () {this.addzero (); render (keyCode) {this.addzero (); render (keyCode) {this.addzero ()if38 (keyCode = = = | | keyCode = = = 40) {/ / 38 is under 40 is enclosing Copyarr = this. ToRotate (enclosing Copyarr)}if(enclosing haveGrouping | | this. EndGap | | this. MiddleGap) {/ / meet any conditions that can create random number enclosing RandomlyCreate (enclosing Copyarr)}else if(this.havezero) {}else{// None of the above is considered empty and cannot be mergedif(this.itis2048) {// Check whether 2048 this.randomlyCreate (this.copyarr) alert(this.itis2048) {// Check whether 2048 this.randomlyCreate (this.copyarr) alert('Congratulations on 2048! ') / / comment out below allows the game to start a new game after click play box button / / this. Arr = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] // this.RandomlyCreate(this.arr) }elseThis.randomlycreate (this.copyarr) alert(this.randomlyCreate (this.copyarr) alert('Game over! ')
          // 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) {// Every time the page is rendered, check whether 2048 this.randomlyCreate (this.copyarr) alert('Congratulations on 2048! ')
        // this.arr = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
        // this.RandomlyCreate(this.arr)
      }
    }
Copy the code
  • Create numbers in random white space

Before here is in the form of a recursive function to judge, but the use of recursive function words will have a lot of problems, one of the biggest problems is likely to stack overflow, or stuck (recursive functions is at the end of the function will also go to call themselves, if they don’t give a return conditions, it is easy to stack overflow or jammed) so this time to draw the pattern, Take the coordinates of all the vacancies, put them in an array, and then take the random subscripts of that array, and we’ll get the coordinates of a vacancy, and then we’ll do something about that vacancy

RandomlyCreate (Copyarr) {// Create a new number in a random space // Determine if there is a new place to createlet max = this.max
      let copyarr = Copyarr
      letZero = [] // Make a raffle boxletSubscript = 0 // Make a prize numberletCopyarr.foreach ((items, index) => {items. ForEach ((item, I) => {items.if(item === 0) { zero.push({ x: index, y: Subscript = math.floor (math.random () * zero.length) subscript = math.floor (math.random () * zero.length)ifMath.floor(math.random () * 10) % 3 === 0) {number = 4 // a third chance}else{number = 2 // two out of three chances}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
        })
      })
    }
Copy the code

The above is the main code of 2048 finally, because the probability of random occurrence of 4 I changed relatively large, so the corresponding reduction of some difficulty, specifically reflected in when all the numbers are on the left (the most edge), and there is no gap between the numbers and numbers, then press left will also generate numbers