js 使用bfs求解拼数游戏

使用广度优先算法,求解拼图游戏的最少步骤数

目前只能支持简单移动和较少的步骤

使用dfs会不会更好点。。。。。

辅助工具类

在求解时,需要判断新的状态是否已经存在于队列中,如果存在则放弃,因为表明有更短的步骤

游戏界面稍微修改下即可

获取到路径数组后,设置定时器,每隔一段时间就移动一次

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>

猜你喜欢

转载自my.oschina.net/ahaoboy/blog/1795546