将棋

先手の持ち駒

後手の持ち駒

駒の定義と初期配置

view source

JavaScript

// 駒の定義と初期配置


const initialBoard = [
    ["l", "n", "s", "g", "k", "g", "s", "n", "l"],
    [null, "r", null, null, null, null, null, "b", null],
    ["p", "p", "p", "p", "p", "p", "p", "p", "p"],
    [null, null, null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null, null, null],
    [null, null, null, null, null, null, null, null, null],
    ["P", "P", "P", "P", "P", "P", "P", "P", "P"],
    [null, "B", null, null, null, null, null, "R", null],
    ["L", "N", "S", "G", "K", "G", "S", "N", "L"]
];

const pieceNames = {
    P: "歩", L: "香", N: "桂", S: "銀", G: "金", K: "王", B: "角", R: "飛",
    p: "歩", l: "香", n: "桂", s: "銀", g: "金", k: "玉", b: "角", r: "飛"
};

const directions = {
    P: [[-1, 0]], p: [[1, 0]],
    L: Array.from({length: 8}, (_, i) => [-i - 1, 0]),
    l: Array.from({length: 8}, (_, i) => [i + 1, 0]),
    N: [[-2, -1], [-2, 1]], n: [[2, -1], [2, 1]],
    S: [[-1, -1], [-1, 0], [-1, 1], [1, -1], [1, 1]], s: [[1, -1], [1, 0], [1, 1], [-1, -1], [-1, 1]],
    G: [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, 0]],
    g: [[1, -1], [1, 0], [1, 1], [0, -1], [0, 1], [-1, 0]],
    K: [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]],
    k: [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]],
    B: "diagonal", b: "diagonal",
    R: "orthogonal", r: "orthogonal"
};

let board = JSON.parse(JSON.stringify(initialBoard));
let selected = null;
let currentTurn = "upper";
let hands = { upper: {}, lower: {} };
let lastMove = null; // {from: [i, j], to: [i, j]}

function createBoard() {
    const container = document.getElementById("board-container");
    container.innerHTML = "";
    const table = document.createElement("table");

    for (let i = 0; i < 9; i++) {
        const tr = document.createElement("tr");
        for (let j = 0; j < 9; j++) {
            const td = document.createElement("td");
            const piece = board[i][j];
            if (piece) {
                td.textContent = pieceNames[piece];
                if (piece === piece.toLowerCase()) {
                    td.style.transform = "rotate(180deg)";
                }
            }

            td.addEventListener("click", () => handleClick(i, j));
            if (selected && Array.isArray(selected) && isLegalMove(selected, [i, j])) {
                td.classList.add("highlight");
            }
            if (selected && Array.isArray(selected) && selected[0] === i && selected[1] === j) {
                td.classList.add("selected");
            }
            if (
                lastMove &&
                ((lastMove.from[0] === i && lastMove.from[1] === j) ||
                 (lastMove.to[0] === i && lastMove.to[1] === j))
            ) {
                td.style.backgroundColor = "#ffdddd"; // 最後に動いた駒のマス
            }
            tr.appendChild(td);
        }
        table.appendChild(tr);
    }


    container.appendChild(table);
    updateHands();

    const status = document.getElementById("status");
    status.textContent = currentTurn === "upper" ? "先手の番です" : "後手(AI)の番です";

    // ✅ AIのターンだったら自動で1手指す(少し待機)
    if (currentTurn === "lower") {
        setTimeout(() => {
            aiMove();
        }, 500);
    }
}


function isInside(i, j) {
    return i >= 0 && i < 9 && j >= 0 && j < 9;
}

function isOwnPiece(piece) {
    return currentTurn === "upper" ? piece && piece === piece.toUpperCase() : piece && piece === piece.toLowerCase();
}

function handleClick(i, j) {
    const cell = board[i][j];

    if (selected && selected.drop) {
        // ✅ 打ち処理(持ち駒から打つ)
        const piece = selected.drop;

        if (cell === null) {
            // 最低限の合法判定(例:歩は1段目に打てない)
            if (piece === "P" && i === 0) return;

            board[i][j] = piece;
            lastMove = { from: null, to: [i, j] };

            hands.upper[piece]--;
            if (hands.upper[piece] === 0) delete hands.upper[piece];

            selected = null;
            currentTurn = "lower";
            createBoard();
            return;
        }
    }

    if (selected && Array.isArray(selected)) {
        const [si, sj] = selected;
        if (isLegalMove([si, sj], [i, j])) {
            const movingPiece = board[si][sj];
            const destPiece = board[i][j];

            if (destPiece && !isOwnPiece(destPiece)) {
                const captured = destPiece.toUpperCase();
                hands.upper[captured] = (hands.upper[captured] || 0) + 1;
            }

            board[i][j] = movingPiece;
            lastMove = { from: [si, sj], to: [i, j] };  // 通常移動の時
            board[si][sj] = null;

            if (movingPiece === "P" && i === 0) {
                board[i][j] = "+P";
            }

            selected = null;
            checkVictory();
            currentTurn = "lower";
            createBoard();
        } else {
            selected = null;
            createBoard();
        }
    } else if (cell && isOwnPiece(cell)) {
        selected = [i, j];
        createBoard();
    }
}


function isLegalMove(from, to) {
    const [fi, fj] = from;
    const [ti, tj] = to;
    const piece = board[fi][fj];
    if (!piece) return false;

    const dir = directions[piece];
    const di = ti - fi;
    const dj = tj - fj;

    if (dir === "diagonal") {
        if (Math.abs(di) !== Math.abs(dj)) return false;
        const stepI = di / Math.abs(di);
        const stepJ = dj / Math.abs(dj);
        for (let k = 1; k < Math.abs(di); k++) {
            if (board[fi + stepI * k][fj + stepJ * k]) return false;
        }
        return true;
    }

    if (dir === "orthogonal") {
        if (di !== 0 && dj !== 0) return false;
        const stepI = di === 0 ? 0 : di / Math.abs(di);
        const stepJ = dj === 0 ? 0 : dj / Math.abs(dj);
        for (let k = 1; k < Math.max(Math.abs(di), Math.abs(dj)); k++) {
            if (board[fi + stepI * k][fj + stepJ * k]) return false;
        }
        return true;
    }

    for (const [dx, dy] of dir || []) {
        if (dx === di && dy === dj) return true;
    }

    return false;
}

function updateHands() {
    const sente = document.getElementById("sente-hand");
    const gote = document.getElementById("gote-hand");
    sente.innerHTML = "";
    gote.innerHTML = "";

    for (const piece in hands.upper) {
        const span = document.createElement("span");
        span.textContent = pieceNames[piece] + "×" + hands.upper[piece];

        // ✅ プレイヤーの持ち駒はクリック可能
        span.style.cursor = "pointer";
        span.addEventListener("click", () => {
            selected = { drop: piece };
            createBoard();
        });

        sente.appendChild(span);
    }

    for (const piece in hands.lower) {
        const span = document.createElement("span");
        span.textContent = pieceNames[piece] + "×" + hands.lower[piece];
        gote.appendChild(span);
    }
}


function checkVictory() {
    let kings = { K: false, k: false };
    for (const row of board) {
        for (const cell of row) {
            if (cell === "K") kings.K = true;
            if (cell === "k") kings.k = true;
        }
    }
    if (!kings.K) alert("後手の勝ち(王取り)!");
    if (!kings.k) alert("先手の勝ち(玉取り)!");
}

window.onload = createBoard;

CSS

HTML

ページのソースを表示 : Ctrl+U , DevTools : F12

view-source:https://hi0a.com/game/game-shogi/

ABOUT

hi0a.com 「ひまあそび」は無料で遊べるミニゲームや便利ツールを公開しています。

プログラミング言語の動作デモやWEBデザイン、ソースコード、フロントエンド等の開発者のための技術を公開しています。

必要な機能の関数をコピペ利用したり勉強に活用できます。

プログラムの動作サンプル結果は画面上部に出力表示されています。

環境:最新のブラウザ GoogleChrome / Windows / Android / iPhone 等の端末で動作確認しています。

画像素材や音素材は半分自作でフリー素材配布サイトも利用しています。LINK参照。

仕様変更、API廃止等、実験途中で動かないページもあります。