tetris

 <!DOCTYPE html>

<html lang="id">

<head>

  <meta charset="UTF-8">

  <title>Tetris Klasik</title>

  <style>

    body {

      background: #202028;

      color: #fff;

      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

      text-align: center;

      margin-top: 40px;

    }

    canvas {

      border: solid 4px #444;

      background: #000;

      border-radius: 5px;

      box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);

    }

    h1 {

      margin-bottom: 10px;

      color: #0DC2FF;

      text-transform: uppercase;

      letter-spacing: 2px;

    }

    #score-container {

      font-size: 24px;

      margin-bottom: 20px;

    }

    #score-value {

      font-weight: bold;

      color: #F538FF;

    }

    .instructions {

      margin-top: 20px;

      color: #aaa;

      font-size: 14px;

    }

  </style>

</head>

<body>

  <h1>Tetris</h1>

  <div id="score-container">Skor: <span id="score-value">0</span></div>

  

  <canvas id="tetris" width="300" height="600"></canvas>

  

  <div class="instructions">

    Gunakan tombol <strong>Panah Atas/Bawah/Kiri/Kanan</strong> untuk bermain.

  </div>


  <script>

    const canvas = document.getElementById('tetris');

    const context = canvas.getContext('2d');

    

    // Memperbesar skala agar 1 blok = 30 piksel

    context.scale(30, 30); 


    // Daftar warna untuk setiap bentuk balok (Tetromino)

    const colors = [

      null,

      '#FF0D72', // T (Pink)

      '#0DC2FF', // O (Biru Muda)

      '#0DFF72', // J (Hijau)

      '#F538FF', // L (Ungu)

      '#FF8E0D', // I (Oranye)

      '#FFE138', // S (Kuning)

      '#3877FF', // Z (Biru Tua)

    ];


    // Matriks pembentuk balok

    function createPiece(type) {

      if (type === 'T') {

        return [

          [0, 0, 0],

          [1, 1, 1],

          [0, 1, 0],

        ];

      } else if (type === 'O') {

        return [

          [2, 2],

          [2, 2],

        ];

      } else if (type === 'L') {

        return [

          [0, 3, 0],

          [0, 3, 0],

          [0, 3, 3],

        ];

      } else if (type === 'J') {

        return [

          [0, 4, 0],

          [0, 4, 0],

          [4, 4, 0],

        ];

      } else if (type === 'I') {

        return [

          [0, 5, 0, 0],

          [0, 5, 0, 0],

          [0, 5, 0, 0],

          [0, 5, 0, 0],

        ];

      } else if (type === 'S') {

        return [

          [0, 6, 6],

          [6, 6, 0],

          [0, 0, 0],

        ];

      } else if (type === 'Z') {

        return [

          [7, 7, 0],

          [0, 7, 7],

          [0, 0, 0],

        ];

      }

    }


    // Membuat papan permainan (Arena)

    function createMatrix(w, h) {

      const matrix = [];

      while (h--) {

        matrix.push(new Array(w).fill(0));

      }

      return matrix;

    }


    const arena = createMatrix(10, 20);


    const player = {

      pos: {x: 0, y: 0},

      matrix: null,

      score: 0,

    };


    // Fungsi menggambar matriks (Papan & Balok)

    function drawMatrix(matrix, offset) {

      matrix.forEach((row, y) => {

        row.forEach((value, x) => {

          if (value !== 0) {

            context.fillStyle = colors[value];

            context.fillRect(x + offset.x, y + offset.y, 1, 1);

            

            // Efek garis pada balok

            context.fillStyle = "rgba(0,0,0,0.3)";

            context.strokeRect(x + offset.x, y + offset.y, 1, 1);

            context.lineWidth = 0.05;

          }

        });

      });

    }


    function draw() {

      // Bersihkan layar

      context.fillStyle = '#000';

      context.fillRect(0, 0, canvas.width, canvas.height);

      

      // Gambar arena & pemain

      drawMatrix(arena, {x: 0, y: 0});

      drawMatrix(player.matrix, player.pos);

    }


    // Menyatukan balok yang sudah jatuh ke arena

    function merge(arena, player) {

      player.matrix.forEach((row, y) => {

        row.forEach((value, x) => {

          if (value !== 0) {

            arena[y + player.pos.y][x + player.pos.x] = value;

          }

        });

      });

    }


    // Deteksi tabrakan (dengan dinding atau balok lain)

    function collide(arena, player) {

      const m = player.matrix;

      const o = player.pos;

      for (let y = 0; y < m.length; ++y) {

        for (let x = 0; x < m[y].length; ++x) {

          if (m[y][x] !== 0 &&

             (arena[y + o.y] && arena[y + o.y][x + o.x]) !== 0) {

            return true;

          }

        }

      }

      return false;

    }


    // Menurunkan balok

    function playerDrop() {

      player.pos.y++;

      if (collide(arena, player)) {

        player.pos.y--;

        merge(arena, player);

        playerReset();

        arenaSweep();

        updateScore();

      }

      dropCounter = 0;

    }


    // Menggeser balok

    function playerMove(offset) {

      player.pos.x += offset;

      if (collide(arena, player)) {

        player.pos.x -= offset; // Batalkan gerakan jika menabrak

      }

    }


    // Memunculkan balok baru

    function playerReset() {

      const pieces = 'TJLOSZI';

      player.matrix = createPiece(pieces[pieces.length * Math.random() | 0]);

      player.pos.y = 0;

      player.pos.x = (Math.floor(arena[0].length / 2)) -

                     (Math.floor(player.matrix[0].length / 2));

      

      // Jika langsung menabrak saat muncul, Game Over (reset arena)

      if (collide(arena, player)) {

        arena.forEach(row => row.fill(0));

        player.score = 0;

        updateScore();

      }

    }


    // Memutar balok

    function playerRotate(dir) {

      const pos = player.pos.x;

      let offset = 1;

      rotate(player.matrix, dir);

      

      // Menangani benturan dengan dinding saat berputar

      while (collide(arena, player)) {

        player.pos.x += offset;

        offset = -(offset + (offset > 0 ? 1 : -1));

        if (offset > player.matrix[0].length) {

          rotate(player.matrix, -dir); // Batalkan putaran

          player.pos.x = pos;

          return;

        }

      }

    }


    function rotate(matrix, dir) {

      for (let y = 0; y < matrix.length; ++y) {

        for (let x = 0; x < y; ++x) {

          [

            matrix[x][y],

            matrix[y][x],

          ] = [

            matrix[y][x],

            matrix[x][y],

          ];

        }

      }

      if (dir > 0) {

        matrix.forEach(row => row.reverse());

      } else {

        matrix.reverse();

      }

    }


    // Menghapus baris yang penuh dan menambah skor

    function arenaSweep() {

      let rowCount = 1;

      outer: for (let y = arena.length - 1; y >= 0; --y) {

        for (let x = 0; x < arena[y].length; ++x) {

          if (arena[y][x] === 0) {

            continue outer;

          }

        }

        

        // Hapus baris yang penuh dan tambahkan baris kosong di atas

        const row = arena.splice(y, 1)[0].fill(0);

        arena.unshift(row);

        ++y;

        

        // Sistem skor kelipatan ganda

        player.score += rowCount * 10;

        rowCount *= 2;

      }

    }


    function updateScore() {

      document.getElementById('score-value').innerText = player.score;

    }


    // Loop Permainan (Waktu berjalan)

    let dropCounter = 0;

    let dropInterval = 1000; // Balok turun otomatis setiap 1 detik

    let lastTime = 0;


    function update(time = 0) {

      const deltaTime = time - lastTime;

      lastTime = time;

      

      dropCounter += deltaTime;

      if (dropCounter > dropInterval) {

        playerDrop();

      }

      

      draw();

      requestAnimationFrame(update);

    }


    // Deteksi input dari keyboard

    document.addEventListener('keydown', event => {

      // Mencegah layar scroll ke bawah saat menekan panah

      if([37, 38, 39, 40].indexOf(event.keyCode) > -1) {

          event.preventDefault();

      }

      

      if (event.keyCode === 37) {

        playerMove(-1);       // Kiri

      } else if (event.keyCode === 39) {

        playerMove(1);        // Kanan

      } else if (event.keyCode === 40) {

        playerDrop();         // Bawah

      } else if (event.keyCode === 38) {

        playerRotate(1);      // Atas

      }

    });


    // Memulai permainan

    playerReset();

    updateScore();

    update();

  </script>

</body>

</html>

Komentar

Postingan populer dari blog ini

01 Mit Click Image (3 Level)