js uses bfs to solve the puzzle game

Minimum number of steps to solve jigsaw puzzle using breadth-first algorithm

Currently only supports simple moves and fewer steps

 

Wouldn't it be better to use dfs. . . . .

 

 

 

Auxiliary tools

When solving, it is necessary to judge whether the new state already exists in the queue, and if so, give up because it indicates that there are shorter steps

 

The game interface can be slightly modified

After getting the path array, set the timer and move it every once in a while

 

Num.js

// 返回数组中16(空白块所在位置)
function zero(items) {
  return items.indexOf(16)
}

// 返回交换后的新数组
function swap(items, x, y) {
  let items2 = items.map(item => item)
  let t = items2[x]
  items2[x] = items2[y]
  items2[y] = t
  return items2
}


// 数组是否已经满足条件
function isPass(items) {
  return items.every(
    (item, index) => item == index + 1
  )
}

// 比较两个数组是否完全相等
function equal(item1, item2) {
  return item1.length == item2.length && item1.every((i, index) => i == item2[index])
}

function bfs(items) {
  let q = []

  // 队列中每个元素有两个数据,一个是数组,一个是该元素的前一个元素
  q.push({
    items: items,
    pre: -1
  })

  let path = []

  let front = 0
  let tail = 1

  while (front < tail) {
    let t = q[front]

    // 如果满足条件
    if (isPass(t.items)) {
      path.push(t)
      // console.log('success', t)
      while (t.pre != -1) {
        t = q[t.pre]
        // console.log(t)
        path.push(t)
      }
      return path.reverse()
    }

    // 继续遍历,上右下左,四个方向
    /*    for (let i = 0; i < 4; i++) {
          q[tail++] = {
            items: move(t.items, i),
            pre: front
          }
        }*/


    // 减少不必要的状态,只将有效的不重复的状态放入队列
    for (let dir = 0; dir < 4; dir++) {
      let nitems = false
      if (dir == 0 && zero(t.items) > 3) {
        // console.log('up', zero(t.items))
        nitems = swap(t.items, zero(t.items), zero(t.items) - 4)
      } else if (dir == 1 && zero(t.items) % 4 != 3) {
        // console.log('right', zero(t.items))
        nitems = swap(t.items, zero(t.items), zero(t.items) + 1)
      } else if (dir == 2 && zero(t.items) <= 11) {
        // console.log('down', zero(t.items))
        nitems = swap(t.items, zero(t.items), zero(t.items) + 4)
      } else if (dir == 3 && zero(t.items) % 4 != 0) {
        // console.log('left')
        nitems = swap(t.items, zero(t.items), zero(t.items) - 1)
      }


      // 如果队列中出现过的状态,则不用再次放入
      // true 表示可以放入,即q中的每一个元素都与新状态不相等
      let flag = !nitems ? false : q.every(
        (item) => {
          return !equal(Array.from(item.items), nitems)
        }
      )
      if (nitems && flag) {
        q[tail++] = {
          items: nitems,
          pre: front
        }
      }
    }
    front++
  }
  return path.reverse()
}

export default bfs

 

Game.js

<template>
  <div class="game">
    <transition-group name="cells" tag="div" class="grid">
      <div class="cell" v-for="(i,index) in items" :key="i" @click="click(i,index)">{{i==16?'':i}}</div>
    </transition-group>

    <div class="egg" v-show="show">
      {{egg}}
    </div>
    <div class="egg" v-show="isPass">
      <h3>恭喜拼图成功 </h3>
    </div>
    <button @click="init" class="reset">重置</button>
    <button @click="autoPlay" class="reset">自动</button>
  </div>
</template>

<script>
  import bfs from './Num'

  export default {
    name: "game-card",
    data() {
      return {
        egg: 'hwq,你知道吗,我喜欢你很久了',
        items: Array.from(Array(16)).map((_, index) => index + 1),
        // items: [5, 2, 1, 13, 6, 3, 14, 7, 8, 9, 10, 11, 12, 15, 16, 4],
        showEgg: false,
        show: false,
      }
    },
    watch: {
      items() {
        console.log('watch', this.items)
        if (this.isPass) {
          console.log('pass')
        }

        console.log(this.isEgg)
        if (this.isEgg && !this.showEgg) {
          this.showEgg = true
          this.show = true
          setTimeout(
            () => this.show = false,
            1000
          )

        }
      }
    },
    computed: {

      zero() {
        return this.items.indexOf(16)
      },
      isPass() {
        return this.items.every(
          (item, index) => item == index + 1
        )
      },
      isEgg() {
        console.log(this.items.join(''))
        return this.items.join('').indexOf('5211314') != -1
      }
    },
    methods: {
      getPath() {
        // console.log('mounted')
        let path = bfs(this.items)
        // console.log(path)
        return path
      },
      autoPlay() {
        console.log('正在计算路径....')
        let path = this.getPath()
        console.log(`计算完毕...开始移动...最少需要${path.length-1}步`)
        let i = 1
        console.log('path', path)
        this.items = path[i++].items
        let inv = setInterval(
          () => {
            this.items = path[i].items
            i++
            if (i == path.length)
              clearInterval(inv)
          },
          1000
        )
      },

      // 交换x,y 位置变量
      swap(x, y) {
        let t = this.items[x]
        this.items[x] = this.items[y]
        this.items[y] = t
        // this.$set(this.items, '' + y, t)
        let arr = Array.from(this.items)
        this.items = arr
      },

      // 0,1,2,3 表示上右下左,四个方向
      move(dir) {
        if (dir == 0 && this.zero > 3) {
          console.log('up', this.zero)
          this.swap(this.zero, this.zero - 4)
        } else if (dir == 1 && this.zero % 4 != 3) {
          console.log('right', this.zero)
          this.swap(this.zero, this.zero + 1)
        } else if (dir == 2 && this.zero <= 11) {
          console.log('down', this.zero)
          this.swap(this.zero, this.zero + 4)
        } else if (dir == 3 && this.zero % 4 != 0) {
          console.log('left')
          this.swap(this.zero, this.zero - 1)
        }
      },

      // 随机初始化
      init() {
        let num = parseInt(Math.random() * 1000)
        console.log('init', num)

        for (let i = 0; i < num; i++) {
          let d = parseInt((Math.random() * 100)) % 4
          this.move(d)
          console.log(d)
        }
      },
      click(item, index) {
        console.log('click', item, index)
        if (item == 16)
          return
        if (index + 1 < 16 && this.items[index + 1] == 16) {
          console.log('click move', 3)
          this.move(3)
        } else if (index - 1 >= 0 && this.items[index - 1] == 16) {
          this.move(1)
          console.log('click move', 1)

        } else if (index + 4 < 16 && this.items[index + 4] == 16) {
          this.move(0)
          console.log('click move', 0)

        } else if (index - 4 >= 0 && this.items[index - 4] == 16) {
          this.move(2)
          console.log('click move', 2)

        }
      }
    },

  }
</script>

<style scoped>
  .grid {
    position: relative;
    display: grid;
    grid-gap: 5px;
    background-color: rgb(233, 233, 233);
    width: 215px;
    border: 5px rgb(233, 233, 233) solid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
  }

  .cell {
    background-color: lightskyblue;
    width: 50px;
    height: 50px;
    display: flex;
    align-items: center;
    justify-content: center;
    /*border: 5px solid white;*/
    box-sizing: border-box;
  }

  /*过渡时间*/
  .cells-move {
    transition: transform 1s;
  }

  .cells-enter-active, .cells-leave-active {
    transition: all 1s;
  }

  /*效果*/
  .cells-enter, .cells-leave-to {
    opacity: 0;
    transform: translateY(30px);
  }

  .game {
    display: flex;
    justify-content: center;
    flex-direction: column;
    align-items: center;
  }

  .egg {
    position: absolute;
    bottom: 50px;
    background: rgba(0, 0, 0, 0.5);
    /*opacity: 0.8;*/
    border-radius: 20px;
    padding: 10px;
    color: white;
  }

  .reset {
    margin-top: 20px;
  }
</style>

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324530735&siteId=291194637