JavaScript:シンプルな8パズル

JavaScript

ゲームクリアまでの時間計測つきの8パズルゲームです。
JavaScriptのソースコードも掲載しています。

参考

以前C#で作った8パズルの記事を参考に作ったので基本的な処理は同じです。
表示フォントにGoogle Fontsを利用しています。

C#:8パズルをコントロール配列を使って作ってみる
昔からある8パズル(エイトパズル)をC#で再現してみます。この記事では、8パズルを作る上での考え方とコントロール配列の使い方についても一緒に学びます。一応、この手のパズルは4×4の正方形を使った15パズルや、3×3の正方形を使った8パズルが...
ソースコード

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="robots" content="INDEX,FOLLOW">
	<meta name="description" content="シンプルな8パズルゲームです。クリアまでの時間計測つきです。">
	<link rel="apple-touch-icon" href="icon.png" >	<!--ホームアイコンの設定-->
	<link rel="SHORTCUT ICON" href="favicon.ico">	<!-- PC用ホームアイコンの設定 -->    <link rel="stylesheet" href="style.css">
    <!-- Google Fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Righteous&display=swap" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap" rel="stylesheet">
    <!-- /Google Fonts -->
    <script src="main.js"></script>
    <title>8puzzle</title>
</head>
<body>
    <div class="timer">00:00</div>
    <div class="board">
        <div class="cell" data-number="0">1</div>
        <div class="cell" data-number="1">2</div>
        <div class="cell" data-number="2">3</div>
        <div class="cell" data-number="3">4</div>
        <div class="cell" data-number="4">5</div>
        <div class="cell" data-number="5">6</div>
        <div class="cell" data-number="6">7</div>
        <div class="cell" data-number="7">8</div>
        <div class="cell" data-number="8"></div>
    </div>
    <div class="btn"><button id="btnStart">START</button></div>
    <div class="message"></div>
</body>
</html>

style.css

/* style.css */
@charset "utf-8";

*{
    margin: 0;
    padding: 0;
}

body{
    margin: 1em;
    display: flex;
    flex-direction: column;
    font-size: 5vh;
    font-family: 'Righteous', cursive;
}

.timer{
    font-family: 'Courier Prime', monospace;
    display: flex;
    align-items: center;
    justify-content: center;
}

.board{
    display: grid;
    grid-template-rows: 12vh 12vh 12vh;
    grid-template-columns: 12vh 12vh 12vh;
    background: orange;
    justify-self: center;   /* 横方向中央揃え */
    align-self: center;     /* 縦方向中央揃え */
}

.cell{
    background: #be3455;
    border: solid 1px white;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
}

.btn{
    display: flex;
    align-items: center;
    justify-content: center;
}

#btnStart{
    font-family: 'Courier Prime', monospace;
    width: 36vh;
    padding: 1em;
    font-size: 3vh;
}

.message{
    display: flex;
    align-items: center;
    justify-content: center;
}

main.js

// -----------------------------
// 8パズル(数値版) main.js
// -----------------------------

let cells = null;       // セル配列
let timer, startTime;   // 経過時間計測用
let mode;

const WAIT = Symbol();  // 待機
const GAME = Symbol();  // ゲーム中

// セルを入れ替える
function swapCell(x, y){
    console.log("入れ替え.");
    [ cells[x].textContent, cells[y].textContent ] = [ cells[y].textContent, cells[x].textContent ];
    // クリアしたか?
    checkClear();
}

// セルが入れ替え可能かチェックする
function checkCell(number){
    // 上下左右が空いていれば入れ替え
    if(number >= 3 && cells[number-3].textContent === ""){
        swapCell(number, number-3);
    }
    if(number <= 5 && cells[number+3].textContent === ""){
        swapCell(number, number+3);
    }
    if(number % 3 != 2 && cells[number+1].textContent === ""){
        swapCell(number, number+1);
    }
    if(number % 3 != 0 && cells[number-1].textContent === ""){
        swapCell(number, number-1);
    }
}

// クリアしたか?
function checkClear(){
    if(mode === GAME){
        // 1~8に並んでいるか?
        let i=0;
        for(; i<cells.length; i++){
            let value = Number(cells[i].textContent);
            console.log("textContent: " + value);
            if(value !== (i+1)) break;
        }
        // 並んでいればクリア!
        if(i === 8){
            mode = WAIT;
            document.querySelector(".message").textContent = "CLEAR!!";
            clearTimeout(timer);
        }
    }
}

// 経過時間表示
function showPassedTime(){
    // 現在時刻
    const currentTime = new Date().getTime();

    // スタートからの差分
    const diff = new Date(currentTime - startTime);
    const [minute, second] = [diff.getMinutes(), diff.getSeconds()];

    // 分と秒を0埋め2桁に整形
    const [txtMinute, txtSecond] = [
        String(minute).padStart(2, "0"), String(second).padStart(2, "0")
    ]

    // 表示
    document.querySelector(".timer").textContent = `${txtMinute}:${txtSecond}`;

    timer = setTimeout(showPassedTime, 250);
}

// 起動時の処理
window.addEventListener("load", ()=>{
    // ゲームの状態
    mode = WAIT;

    // 9つのセルを取得
    cells = document.querySelectorAll(".cell");

    // セルのイベントを設定
    for(let cell of cells){

        cell.addEventListener("click", (e)=>{
            // セルの番号を取得(data-numberプロパティ)
            const number = Number(e.currentTarget.dataset.number);
            console.log("cells[" + number + "] Click.");

            // セルが入れ替え可能か?
            checkCell(number);
        }, false);
    }

    // スタートボタン
    const btnStart = document.getElementById("btnStart");

    btnStart.addEventListener("click", ()=>{
        document.querySelector(".message").textContent = "";

        // シャッフル
        for(let i=0; i<1000; i++){
            const r = Math.floor(Math.random() * 9);
            checkCell(r);
        }
        // ゲーム開始
        mode = GAME;

        // タイマースタート
        clearTimeout(timer);
        startTime = new Date().getTime();
        showPassedTime();
    }, false);
});

コメント

タイトルとURLをコピーしました