20
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CoconeAdvent Calendar 2022

Day 25

Reactで作る将棋ゲーム

Last updated at Posted at 2022-12-24

はじめに

こんにちは。ココネでビリングシステムエンジニアをしていますnk60です。

自分の日々の業務は、主に会社の決済のサーバサイドの開発に携わっているのですが、ある日業務内で少しReactに触れることがありました。

ココネはゲーム会社なのですが、自分はあまりゲームの具体的な制作には携わっていません。

Reactを触った経験もほとんどなかったので、少し勉強しておこうと思いReactのチュートリアルを見てみると、内容が三目並べでした!

Reactの考え方やプログラムの書き方、三目並べを作るまでの過程などをみて面白いなと思いました。

ゲームを作ったことはほとんどないのですが、チュートリアルを参考に、自分なりに考えて将棋のゲームを作ってみました。

プロジェクトの作成

公式チュートリアルと同様にプロジェクトを作成します。

npx create-react-app my-app
cd my-app
cd src

# If you're using a Mac or Linux:
rm -f *

# Or, if you're on Windows:
del *

# Then, switch back to the project folder
cd ..

ソースコードの配置

作成した将棋ゲームのソースコードは以下になります。

src/index.jsがメインの画面を作る部分です。

プロジェクトディレクトリ/src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import imgKing from "./img/玉.png";
import imgRook from "./img/飛.png";
import imgBishop from "./img/角.png";
import imgGoldGeneral from "./img/金.png";
import imgSilverGeneral from "./img/銀.png";
import imgKnight from "./img/桂.png";
import imgLance from "./img/香.png";
import imgPawn from "./img/歩.png";
import imgPromotedRook from "./img/竜.png";
import imgPromotedBishop from "./img/馬.png";
import imgPromotedSilverGeneral from "./img/成銀.png";
import imgPromotedKnight from "./img/成桂.png";
import imgPromotedLance from "./img/成香.png";
import imgPromotedPawn from "./img/と.png";
import { BoardInfo, Selection } from './components/BoardInfo';

const imgByName = {
  "": imgKing,
  "": imgRook,
  "": imgBishop,
  "": imgGoldGeneral,
  "": imgSilverGeneral,
  "": imgKnight,
  "": imgLance,
  "": imgPawn,
  "": imgPromotedRook,
  "": imgPromotedBishop,
  "成銀": imgPromotedSilverGeneral,
  "成桂": imgPromotedKnight,
  "成香": imgPromotedLance,
  "": imgPromotedPawn
};

function Square(props) {
  return (
    <button id={props.selectInfo} className="square" onClick={props.onClick} >
      <img id={props.piece.owner} src={imgByName[props.piece.name]} alt="" />
      <p>{(props.num >= 2) && props.num}</p>
    </button>
  );
}

class Board extends React.Component {
  renderSquare(i, j) {
    return (
      <Square
        key={j}
        piece={this.props.board[i][j]}
        selectInfo={this.props.boardSelectInfo[i][j]}
        onClick={() => this.props.onClick(i, j)}
      />
    );
  }

  render() {

    return (
      <div>
        {
          Array(9).fill(0).map((_, i) => {
            return (
              <div className="board-row" key={i}>
                {
                  Array(9).fill(0).map((_, j) => {
                    return (
                      this.renderSquare(i, j)
                    )
                  })
                }
              </div>
            )
          })
        }
      </div>
    );
  }
}

class PieceStand extends React.Component {
  renderSquare(i) {
    return (
      <Square
        key={i}
        piece={this.props.pieceStand[i]}
        num={this.props.pieceStandNum[this.props.pieceStand[i].name]}
        selectInfo={this.props.pieceStandSelectInfo[i]}
        onClick={() => this.props.onClick(i)}
      />
    );
  }

  render() {

    return (
      <div className="board-row">
        {
          Array(9).fill(0).map((_, i) => {
            return (
              this.renderSquare(i)
            )
          })
        }
      </div>
    );
  }
}

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      boardInfo: new BoardInfo()
    };
  }

  canselSelection() {
    const nextBoardInfo = this.state.boardInfo;
    if (nextBoardInfo.selection.isNow) {
      nextBoardInfo.selection.isNow = false;
    } else {
      nextBoardInfo.selection = new Selection();
    }
    this.setState({
      boardInfo: nextBoardInfo
    });
  }

  boardClick(i, j) {
    this.state.boardInfo.boardClick(i, j);
  }

  pieceStandClick(piece) {
    this.state.boardInfo.pieceStandClick(piece);
  }

  render() {

    return (
      <div className="game" onClick={() => this.canselSelection()}>
        <div className="game-board">
          <PieceStand
            pieceStand={this.state.boardInfo.pieceStand["後手"]}
            pieceStandNum={this.state.boardInfo.pieceStandNum["後手"]}
            pieceStandSelectInfo={this.state.boardInfo.selection.pieceStandSelectInfo["後手"]}
            onClick={(i) => this.pieceStandClick(this.state.boardInfo.pieceStand["後手"][i])}
          />
          <br />
          <Board
            board={this.state.boardInfo.board}
            boardSelectInfo={this.state.boardInfo.selection.boardSelectInfo}
            onClick={(i, j) => this.boardClick(i, j)}
          />
          <br />
          <PieceStand
            pieceStand={this.state.boardInfo.pieceStand["先手"]}
            pieceStandNum={this.state.boardInfo.pieceStandNum["先手"]}
            pieceStandSelectInfo={this.state.boardInfo.selection.pieceStandSelectInfo["先手"]}
            onClick={(i) => this.pieceStandClick(this.state.boardInfo.pieceStand["先手"][i])}
          />
        </div>
      </div>
    );
  }
}

// ========================================

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Game />);

盤面の情報はcomponents/BoardInfo.jsにまとめています。

constructorで初期画面を生成しています。

プロジェクトディレクトリ/src/components/BoardInfo.js
import { Piece, Blank, King, Rook, Bishop, GoldGeneral, SilverGeneral, Knight, Lance, Pawn } from './Pieces';

class BoardInfo {

    constructor() {
        this.turn = "先手";
        this.board = [[new Lance("後手"), new Knight("後手"), new SilverGeneral("後手"), new GoldGeneral("後手"), new King("後手"), new GoldGeneral("後手"), new SilverGeneral("後手"), new Knight("後手"), new Lance("後手")],
        [new Blank(), new Rook("後手"), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Bishop("後手"), new Blank()],
        [new Pawn("後手"), new Pawn("後手"), new Pawn("後手"), new Pawn("後手"), new Pawn("後手"), new Pawn("後手"), new Pawn("後手"), new Pawn("後手"), new Pawn("後手")],
        [new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank()],
        [new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank()],
        [new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank()],
        [new Pawn("先手"), new Pawn("先手"), new Pawn("先手"), new Pawn("先手"), new Pawn("先手"), new Pawn("先手"), new Pawn("先手"), new Pawn("先手"), new Pawn("先手")],
        [new Blank(), new Bishop("先手"), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Rook("先手"), new Blank()],
        [new Lance("先手"), new Knight("先手"), new SilverGeneral("先手"), new GoldGeneral("先手"), new King("先手"), new GoldGeneral("先手"), new SilverGeneral("先手"), new Knight("先手"), new Lance("先手")]
        ];
        this.selection = new Selection();
        this.pieceStandNum = {
            "先手": { "": 0, "": 0, "": 0, "": 0, "": 0, "": 0, "": 0 },
            "後手": { "": 0, "": 0, "": 0, "": 0, "": 0, "": 0, "": 0 }
        };
        this.pieceStand = {
            "先手": [new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank()],
            "後手": [new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank(), new Blank()]
        };
    }

    boardClick(i, j) {
        if (this.selection.state) {
            if (this.selection.boardSelectInfo[i][j] !== "配置可能") {
                return;
            }
            let myPiece;
            if (this.selection.pieceStandPiece.name) {
                myPiece = this.selection.pieceStandPiece;
                this.pieceStandNum[this.turn][myPiece.name] -= 1;
                this.makePieceStand();
            } else {
                myPiece = this.board[this.selection.before_i][this.selection.before_j];
                this.board[this.selection.before_i][this.selection.before_j] = new Blank();
                let yourPiece = this.board[i][j];
                if (yourPiece.name) {
                    if (yourPiece.getPiece()) {
                        yourPiece = yourPiece.getPiece();
                    }
                    this.pieceStandNum[myPiece.owner][yourPiece.name] += 1;
                    this.makePieceStand();
                }
                if (this.existCanMove(i, j, myPiece)) {
                    myPiece = this.checkPromote(myPiece, i, this.selection.before_i);
                } else {
                    myPiece = myPiece.getPromotedPiece();
                }
            }
            this.board[i][j] = myPiece;
            this.turn = this.turn === "先手" ? "後手" : "先手";
        } else {
            if (this.turn !== this.board[i][j].owner) {
                return;
            }
            this.selection.isNow = true;
            this.selection.state = true;
            this.selection.before_i = i;
            this.selection.before_j = j;
            this.selection.boardSelectInfo = JSON.parse(JSON.stringify((new Array(9)).fill((new Array(9)).fill("未選択"))));
            this.selection.pieceStandSelectInfo = {
                "先手": Array(9).fill("未選択"),
                "後手": Array(9).fill("未選択")
            };
            this.selection.boardSelectInfo[i][j] = "選択状態";
            this.checkCanPutBoard(i, j);
        }
    }

    existCanMove(i, j, piece) {
        for (let l = 0; l < piece.dx.length; l++) {
            let y = i;
            let x = j;
            y += this.turn === "先手" ? piece.dy[l] : -piece.dy[l];
            x += this.turn === "先手" ? piece.dx[l] : -piece.dx[l];
            if (0 <= y && y <= 8 && 0 <= x && x <= 8) {
                return true;
            }
        }
        return false;
    }

    checkPromote(piece, i, before_i) {
        if (!piece.getPromotedPiece()) {
            return piece;
        }
        const promoteAreaMinY = piece.owner === "先手" ? 0 : 6;
        const promoteAreaMaxY = piece.owner === "先手" ? 2 : 8;
        if ((promoteAreaMinY <= i && i <= promoteAreaMaxY) || (promoteAreaMinY <= before_i && before_i <= promoteAreaMaxY)) {
            if (window.confirm('成りますか?')) {
                return piece.getPromotedPiece()
            }
        }
        return piece;
    }

    checkCanPutBoard(i, j) {
        const piece = this.board[i][j];
        for (let l = 0; l < piece.dx.length; l++) {
            let y = i;
            let x = j;
            for (let _ = 0; _ < piece.dk[l]; _++) {
                y += this.turn === "先手" ? piece.dy[l] : -piece.dy[l];
                x += this.turn === "先手" ? piece.dx[l] : -piece.dx[l];
                if (y < 0 || y > 8 || x < 0 || x > 8 || this.board[y][x].owner === piece.owner) {
                    break;
                }
                this.selection.boardSelectInfo[y][x] = "配置可能";
                if (!this.board[y][x].owner) {
                    continue;
                }
                break;
            }
        }
    }

    pieceStandClick(piece) {
        if (this.selection.state || this.turn !== piece.owner) {
            return;
        }
        this.selection.isNow = true;
        this.selection.state = true;
        this.selection.boardSelectInfo = JSON.parse(JSON.stringify((new Array(9)).fill((new Array(9)).fill("未選択"))));
        this.selection.pieceStandPiece = piece;
        this.selection.pieceStandSelectInfo = {
            "先手": Array(9).fill("未選択"),
            "後手": Array(9).fill("未選択")
        };
        const i = this.pieceStand[piece.owner].findIndex(p => p.name === piece.name);
        this.selection.pieceStandSelectInfo[this.turn][i] = "選択状態";
        this.checkCanPutPieceStand(piece);
    }

    makePieceStand() {
        let myPieceStand = [];
        const myPieceStandNum = this.pieceStandNum[this.turn];
        for (let name in myPieceStandNum) {
            if (myPieceStandNum[name] > 0) {
                myPieceStand.push(Piece.getPieceByName(name, this.turn));
            }
        }
        while (myPieceStand.length < 9) {
            myPieceStand.push(new Blank());
        }
        this.pieceStand[this.turn] = myPieceStand;
    }

    checkCanPutPieceStand(piece) {
        let pawnColMemo = Array(9).fill(true);
        if (piece.name === "") {
            for (let i = 0; i < 9; i++) {
                for (let j = 0; j < 9; j++) {
                    if (this.board[i][j].name === "" && this.board[i][j].owner === piece.owner) {
                        pawnColMemo[j] = false;
                    }
                }
            }
        }
        for (let i = 0; i < 9; i++) {
            for (let j = 0; j < 9; j++) {
                if (!this.board[i][j].owner && this.existCanMove(i, j, piece) && pawnColMemo[j]) {
                    this.selection.boardSelectInfo[i][j] = "配置可能";
                }
            }
        }
    }

}

class Selection {
    boardSelectInfo = JSON.parse(JSON.stringify((new Array(9)).fill((new Array(9)).fill(""))));
    isNow = false;
    state = false;
    before_i = null;
    before_j = null;
    pieceStandSelectInfo = {
        "先手": Array(9).fill("持駒"),
        "後手": Array(9).fill("持駒")
    };
    pieceStandPiece = new Blank();
}

export { BoardInfo, Selection };

各駒の動き方や名前、成った後の駒、成る前の駒などをcomponents/Pieces.jsでまとめています。

各駒がPieceクラスを継承しており、constructorの引数でowner(先手 or 後手)を渡します。

プロジェクトディレクトリ/src/components/Pieces.js
class Piece {
    constructor(owner) {
        this.owner = owner;
    }
    getPiece() {
        return null;
    }
    getPromotedPiece() {
        return null;
    }
    static getPieceByName(name, owner) {
        switch (name) {
            case "":
                return new Rook(owner);
            case "":
                return new Bishop(owner);
            case "":
                return new GoldGeneral(owner);
            case "":
                return new SilverGeneral(owner);
            case "":
                return new Knight(owner);
            case "":
                return new Lance(owner);
            case "":
                return new Pawn(owner);
            default:
                return null;
        }
    }
}

class Blank extends Piece {
}

class King extends Piece {
    name = "";
    dx = [-1, -1, -1, 0, 1, 1, 1, 0];
    dy = [-1, 0, 1, 1, 1, 0, -1, -1];
    dk = [1, 1, 1, 1, 1, 1, 1, 1];
}

class Rook extends Piece {
    name = "";
    dx = [-1, 0, 1, 0];
    dy = [0, 1, 0, -1];
    dk = [10, 10, 10, 10];
    getPromotedPiece() {
        return new PromotedRook(this.owner);
    }
}

class Bishop extends Piece {
    name = "";
    dx = [-1, -1, 1, 1];
    dy = [-1, 1, 1, -1];
    dk = [10, 10, 10, 10];
    getPromotedPiece() {
        return new PromotedBishop(this.owner);
    }
}

class GoldGeneral extends Piece {
    name = "";
    dx = [-1, -1, 0, 1, 1, 0];
    dy = [-1, 0, 1, 0, -1, -1];
    dk = [1, 1, 1, 1, 1, 1];
}

class SilverGeneral extends Piece {
    name = "";
    dx = [-1, -1, 1, 1, 0];
    dy = [-1, 1, 1, -1, -1];
    dk = [1, 1, 1, 1, 1];
    getPromotedPiece() {
        return new PromotedSilverGeneral(this.owner);
    }
}

class Knight extends Piece {
    name = "";
    dx = [-1, 1];
    dy = [-2, -2];
    dk = [1, 1];
    getPromotedPiece() {
        return new PromotedKnight(this.owner);
    }
}

class Lance extends Piece {
    name = "";
    dx = [0];
    dy = [-1];
    dk = [10];
    getPromotedPiece() {
        return new PromotedLance(this.owner);
    }
}

class Pawn extends Piece {
    name = "";
    dx = [0];
    dy = [-1];
    dk = [1];
    getPromotedPiece() {
        return new PromotedPawn(this.owner);
    }
}

class PromotedRook extends Piece {
    name = "";
    dx = [-1, 0, 1, 0, -1, -1, 1, 1];
    dy = [0, 1, 0, -1, -1, 1, 1, -1];
    dk = [10, 10, 10, 10, 1, 1, 1, 1];
    getPiece() {
        return new Rook(this.owner);
    }
}

class PromotedBishop extends Piece {
    name = "";
    dx = [-1, -1, 1, 1, -1, 0, 1, 0];
    dy = [-1, 1, 1, -1, 0, 1, 0, -1];
    dk = [10, 10, 10, 10, 1, 1, 1, 1];
    getPiece() {
        return new Bishop(this.owner);
    }
}

class PromotedSilverGeneral extends Piece {
    name = "成銀";
    dx = [-1, -1, 0, 1, 1, 0];
    dy = [-1, 0, 1, 0, -1, -1];
    dk = [1, 1, 1, 1, 1, 1];
    getPiece() {
        return new SilverGeneral(this.owner);
    }
}

class PromotedKnight extends Piece {
    name = "成桂";
    dx = [-1, -1, 0, 1, 1, 0];
    dy = [-1, 0, 1, 0, -1, -1];
    dk = [1, 1, 1, 1, 1, 1];
    getPiece() {
        return new Knight(this.owner);
    }
}

class PromotedLance extends Piece {
    name = "成香";
    dx = [-1, -1, 0, 1, 1, 0];
    dy = [-1, 0, 1, 0, -1, -1];
    dk = [1, 1, 1, 1, 1, 1];
    getPiece() {
        return new Lance(this.owner);
    }
}

class PromotedPawn extends Piece {
    name = "";
    dx = [-1, -1, 0, 1, 1, 0];
    dy = [-1, 0, 1, 0, -1, -1];
    dk = [1, 1, 1, 1, 1, 1];
    getPiece() {
        return new Pawn(this.owner);
    }
}

export { Piece, Blank, King, Rook, Bishop, GoldGeneral, SilverGeneral, Knight, Lance, Pawn};

index.cssで見た目を将棋盤に整えます。

後手の駒か、持ち駒か、選択状態にあるか、配置可能なマス目かなどで色を変えています。

プロジェクトディレクトリ/src/index.css
body {
  font: 14px "Century Gothic", Futura, sans-serif;
  margin: 20px;
}

.board-row:after {
  clear: both;
  content: "";
  display: table;
}

.square {
  position: relative;
  background: #FFCC00;
  border: 1px solid #999;
  float: left;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  width: 34px;
}

.square p {
  margin-top: 1px;
  color: yellow;
  position: absolute;
  top: 0;
  left: 0;
}

.square:focus {
  outline: none;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

#未選択.square {
  background: rgba(0, 0, 0, .5);
}

#選択状態.square {
  background: red;
}

#配置可能.square {
  background: yellow;
}

#持駒.square {
  background: darkgoldenrod;
}

img {
  width: 28px;
}

img#後手 {
  transform: scale(-1, -1);
}

ソース配置

srcフォルダ内にcomponentsディレクトリとimgディレクトリが含まれ、imgディレクトリに各駒の画像を入れています。

この画像はフリー素材の駒の画像をサイズが同じに成るように揃え、背景を透明にしたものです。

スクリーンショット 2022-12-24 17.46.25.png

玉.png飛.png角.png金.png銀.png桂.png香.png
歩.png竜.png馬.png成銀.png成桂.png成香.pngと.png

完成後の動作

上記ソースコードを配置すると以下のように動作します。

ダウンロード.gif

移動可能なマス目の表示

将棋のゲームを作ろうと思った際に最初にぶつかる壁は、駒の種類と移動のパターンの多さだと思います。

歩は前に一回進めますが、角は斜めにずっと進めます。

プロジェクトディレクトリ/src/components/Pieces.js - class Pawn
class Pawn extends Piece {
    name = "";
    dx = [0];
    dy = [-1];
    dk = [1];
    getPromotedPiece() {
        return new PromotedPawn(this.owner);
    }
}
プロジェクトディレクトリ/src/components/Pieces.js - class Bishop
class Bishop extends Piece {
    name = "";
    dx = [-1, -1, 1, 1];
    dy = [-1, 1, 1, -1];
    dk = [10, 10, 10, 10];
    getPromotedPiece() {
        return new PromotedBishop(this.owner);
    }
}

dxの長さ分for文を回し、現在の座標からdx, dyぶん進みそれをdk回繰り返します。

(二次元配列と同じなので、yは下向きが正)

駒がそれ以上進めない条件は以下です。

1.盤の外に出たらそれ以上進めない。
2.自分の駒があるときそのマスには進めず、それ以上進めない。
3.相手の駒がある時、相手の駒をとりそのマスに進める。それ以降は進めない。

これらを用いて、移動可能なマスは以下のように表せます。

プロジェクトディレクトリ/src/components/BoardInfo.js - checkCanPutBoard(i, j)
checkCanPutBoard(i, j) {
    const piece = this.board[i][j];
    for (let l = 0; l < piece.dx.length; l++) {
        let y = i;
        let x = j;
        for (let _ = 0; _ < piece.dk[l]; _++) {
            y += this.turn === "先手" ? piece.dy[l] : -piece.dy[l];
            x += this.turn === "先手" ? piece.dx[l] : -piece.dx[l];
            if (y < 0 || y > 8 || x < 0 || x > 8 || this.board[y][x].owner === piece.owner) {
                break;
            }
            this.selection.boardSelectInfo[y][x] = "配置可能";
            if (!this.board[y][x].owner) {
                continue;
            }
            break;
        }
    }
}

全体的な処理の流れ

このゲームには3つのイベント処理が入ってます。

1.canselSelection
2.pieceStandClick
3.boardClick

プロジェクトディレクトリ/src/index.js - class Game - render()
render() {
  return (
    <div className="game" onClick={() => this.canselSelection()}>
      <div className="game-board">
        <PieceStand
          pieceStand={this.state.boardInfo.pieceStand["後手"]}
          pieceStandNum={this.state.boardInfo.pieceStandNum["後手"]}
          pieceStandSelectInfo={this.state.boardInfo.selection.pieceStandSelectInfo["後手"]}
          onClick={(i) => this.pieceStandClick(this.state.boardInfo.pieceStand["後手"][i])}
        />
        <br />
        <Board
          board={this.state.boardInfo.board}
          boardSelectInfo={this.state.boardInfo.selection.boardSelectInfo}
          onClick={(i, j) => this.boardClick(i, j)}
        />
        <br />
        <PieceStand
          pieceStand={this.state.boardInfo.pieceStand["先手"]}
          pieceStandNum={this.state.boardInfo.pieceStandNum["先手"]}
          pieceStandSelectInfo={this.state.boardInfo.selection.pieceStandSelectInfo["先手"]}
          onClick={(i) => this.pieceStandClick(this.state.boardInfo.pieceStand["先手"][i])}
        />
      </div>
    </div>
  );
}

ReactのonClickイベントは、小要素から親要素に伝播していきます。

一番親要素のclassName="game"のcanselSelectionは、盤面をクリックした時も、持ち駒をクリックした時も、またそれ以外の場所をクリックした時も呼ばれることになります。

プロジェクトディレクトリ/src/index.js - class Game
canselSelection() {
  const nextBoardInfo = this.state.boardInfo;
  if (nextBoardInfo.selection.isNow) {
    nextBoardInfo.selection.isNow = false;
  } else {
    nextBoardInfo.selection = new Selection();
  }
  this.setState({
    boardInfo: nextBoardInfo
  });
}
boardClick(i, j) {
  this.state.boardInfo.boardClick(i, j);
}
pieceStandClick(piece) {
  this.state.boardInfo.pieceStandClick(piece);
}

これは、盤面の駒、または持ち駒の駒を選択したときに盤面をグレーにしていますが、次どこかをクリックした時、必ず選択した状態を初期化し、色を元の状態に戻すためです。(nextBoardInfo.selection = new Selection();)

boardClick(盤面をクリック)、pieceStandClick(持ち駒をクリック)した時は、this.state.boardInfoの中身が次の状態に書き換えられますが、これはまだstateにsetされていません。直後に呼ばれるcanselSelectionのsetStateによってstate内のboardInfoの情報がnextBoardInfoの情報に書き換えられます。

最後に

ここまで読んでいただきありがとうございました。

Reactで作る将棋ゲームについて書かせていただきましたが、細かいルールなどに関しては実装できていない部分も多いと思います。

Reactチュートリアルと同じように棋譜を残すようにしたり、詰みの判定を入れるなど、まだできることは多くあると思います。

自分もReactの知識はまだまだ浅い部分があるので、改善できるところも多くあるかもしれないです。

少しでも興味を持っていただいた部分や、参考になった部分があったと思っていただけなら幸いです。

今日はクリスマスですがいかがお過ごしでしょうか:santa_tone1:

来年も良いお年を!

20
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?