vue.jsで作るぷよぷよ

### 簡易版　デモ
https://jsfiddle.net/gjf3vkpm/2/

![index.jpeg](https://qiita-image-store.s3.amazonaws.com/0/54926/336dd561-1b2b-15af-b861-4924f4bd0e03.jpeg)

```
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>vue.js 落ち物パズルゲーム  ver.2</title>
<style>
   table {background-color:gray;}
   td {width: 30px;height: 30px;border-radius: 15px;}
   .box{display: flex;}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.7/vue.min.js"></script>
</head>
<body>
<div id="app">
   <h3>vue.js 落ち物パズルゲーム ver.2</h3>
   <p>startを押してからキーボードの矢印キーで操作します</p>
   <input type="button" @click="startFn" value="START" v-if="!gameStart">
   <input type="button" @click="restartFn" value="RESTART"  v-if="gameOver">
   <div class="box" v-if="gameStart">
      <table>
           <tr v-for="i in H">
               <td v-for="j in W" :style="{ backgroundColor:colors[yx[i-1][j-1]] }"></td>
           </tr>
      </table>
      <table>
           <tr v-for="i in 2">
               <td :style="{ backgroundColor: colors[next[i - 1]] }"></td>
           </tr>
      </table>
   </div>
</div>
<script>
var vm = new Vue({
   el: '#app',
   data: {
      H: 13,
      W: 6,
      yx: [],
      block: [],
      blockX: 0,
      blockY: 0,
      blockMemo: [],
      blockColor: [],
      next : [Math.floor(Math.random() * 5) + 1, Math.floor(Math.random() * 5) + 1],
      timerID: null,
      otitaFlag:false,
      gameStart: false,
      gameOver: false,
      colors: ['gray', 'red', 'blue', 'green', 'yellow', 'purple'],
      saveData : null,
   },
   methods: {
      startFn: function() {
         this.saveData = JSON.stringify(this.$data);
         for (var i = 0; i < this.H; i++) {
            this.yx[i] = [];
            for (var j = 0; j < this.W; j++) this.yx[i][j] = 0;
         }
         this.gameStart = true;
         this.createFn();
         this.timerID = setInterval(this.fallFn, 500);
      },
      createFn: function() {
         this.blockMemo = [];
         this.blockX = 2;
         this.blockY = 1;
         this.block = [[0, 0], [0, -1]];
         this.blockColor = [this.next[1], this.next[0]];
         this.next = [Math.floor(Math.random() * 5) + 1, Math.floor(Math.random() * 5) + 1];
         this.drawFn(false, 0, 0);
      },
      fallFn: function() {
         if (this.isMoveFn(false, 0, 1)) this.drawFn(false, 0, 1);
         else {
            this.otitaFlag = true;
            clearInterval(this.timerID);
            this.timerID = setInterval(this.rensaFn, 200);
         }
      },
      isMoveFn: function(rotate, dx, dy) {
         var yx = this.yx.map(v => v.slice());
         var block = this.block.map(v => v.slice());
         this.blockMemo.forEach(v => {
            yx[v[1]][v[0]] = 0;
         });
         if (rotate) block = block.map(v => [v[1] * -1, v[0]]);
         var flag = block.every(v => {
            var x = v[0] + this.blockX + dx;
            var y = v[1] + this.blockY + dy;
            return (y >= 0 && y < this.H && x >= 0 && x < this.W && yx[y][x] == 0);
         });
         return flag;
      },
      drawFn: function(rotate, dx, dy) {
         this.blockMemo.forEach(v => {
            this.yx[v[1]][v[0]] = 0;
         });
         this.blockMemo = [];
         if (rotate) this.block = this.block.map(v => [v[1] * -1, v[0]]);
         this.blockX += dx;
         this.blockY += dy;
         this.block.forEach((v, i) => {
            var x = v[0] + this.blockX;
            var y = v[1] + this.blockY;
            if (this.yx[y][x] != 0) this.gameOver = true;
            this.yx[y][x] = this.blockColor[i];
            this.blockMemo.push([x, y]);
         });
         if (this.gameOver) clearInterval(this.timerID);
         this.$forceUpdate();
      },
      rensaFn:function(){
         if (this.kuzureruFn() || this.removeFn()) this.$forceUpdate();
         else {
            clearInterval(this.timerID);
             this.timerID = setInterval(this.fallFn, 500);
             this.createFn();
             this.otitaFlag = false;
         }
      },
      kuzureruFn: function() {
         var flag= false;
            for (var i = this.H - 2; i >= 0; i--) for (var j = 0; j < this.W; j++) {
               if (this.yx[i + 1][j] == 0 && this.yx[i][j] != 0) {
                  this.yx[i + 1][j] = this.yx[i][j];
                  this.yx[i][j] = 0;
                  flag = true;
               }
            }
         return flag;
      },
      removeFn: function() {
         var flag = false;
         var dydx = [[-1, 0], [0, 1], [1, 0], [0, -1]];
         for (var i = 0; i < this.H; i++) for (var j = 0; j < this.W; j++) {
            var iro = this.yx[i][j];
            if (iro == 0) continue;
            var yx = this.yx.map(v => v.slice());
            yx[i][j] = 0;
            var onazi = 1;
            var memo = [[i, j]];
            while (memo.length != 0) {
               var m = memo.shift();
               dydx.forEach(v => {
                  var y = m[0] + v[0];
                  var x = m[1] + v[1];
                  if (y >= 0 && y < this.H && x >= 0 && x < this.W && yx[y][x] == iro) {
                     onazi++;
                     memo.push([y, x]);
                     yx[y][x] = 0;
                  }
               });
            }
            if (onazi >= 4) {
               flag = true;
               this.yx = yx.map(v => v.slice());
            }
         }
         return  flag;
      },
      keydownFn: function(rotate, dx, dy) {
         if (!this.gameOver && !this.otitaFlag && this.isMoveFn(rotate, dx, dy)) this.drawFn(rotate, dx, dy);
      },
      restartFn: function() {
         var obj = JSON.parse(this.saveData);
         for (var k in obj) this[k] = obj[k];
         this.startFn();
      },
   }
});

document.onkeydown = function(e) {
   if (e.keyCode == 38) vm.keydownFn(true, 0, 0); //up
   else if (e.keyCode == 37) vm.keydownFn(false, -1, 0); //left
   else if (e.keyCode == 40) vm.keydownFn(false, 0, 1); //down
   else if (e.keyCode == 39) vm.keydownFn(false, 1, 0); //right
};
</script>
</body>
</html>
```

### CSS3　ぷよぷよ
https://jsfiddle.net/ujveasnb/2/

```
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>CSS3　ぷよぷよ</title>
<style>
.box {
   width: 50px;
   height: 50px;
   animation: bounce 1.0s infinite ease-in-out;
   box-shadow: 5px 5px 15px 5px rgba(0,0,0,0.6) inset;
   font-size: 15px;
   color:white;
   font-weight: bold; 
   text-align: center; 
}
@keyframes bounce {
  0%, 100% { transform: scale(0.9) }
  50% { transform: scale(1.0) }
}
.red {background-color:red;border-radius: 20px;}
.blue {background-color:blue;border-radius: 10px 20px 10px 25px / 10px 30px 10px 40px;}
.green {background-color:green;border-radius: 20px;}
.yellow {background-color:yellow;border-radius: 25px 25px 10px 10px / 50px 50px 10px 10px;}
.purple {background-color:purple;border-radius: 20px;}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.7/vue.min.js"></script>
</head>
<body>
   <div id="app">
      <div v-for="i in 5" :class="['box', arr[i-1]]">0_0</div>
   </div>
<script>
   var vm = new Vue({
      el: '#app',
      data: {
         arr : ["red","blue","green","yellow","purple"]
      }
   });
</script>
</body>
</html>
```