useShogiProblem
useShogiProblem は、「次の一手問題」UIを構築するためのフックです。課題局面を受け取り、ユーザーが指した手と正解手を比較して正誤判定を行います。
主な使い方: extractProblemsFromKifu との組み合わせ
問題データを手動で作成する必要はありません。エンジン解析済みの棋譜(KIF)があれば、extractProblemsFromKifu が自動的に悪手局面を抽出して useShogiProblem に渡せる形式で返してくれます。
解析済みKIF → extractProblemsFromKifu → ShogiProblem[] → useShogiProblem
import React from 'react';
import {
extractProblemsFromKifu,
useShogiProblem,
ShogiBoard,
CandidateList,
} from 'react-kifu-player';
const kifuString = `...(エンジン解析コメント付きのKIF)...`;
function ProblemApp() {
const [index, setIndex] = React.useState(0);
// 解析済み棋譜から問題を自動生成(後手側の悪手を最大5問抽出)
const problems = React.useMemo(() =>
extractProblemsFromKifu(kifuString, {
playerColor: 'white',
evalDropThreshold: 300,
correctMoveThreshold: 150,
maxProblems: 5,
}), []
);
const current = problems[index];
// useShogiProblem は ShogiProblem をそのまま受け取れる
// ※ フックのルールにより、常に呼び出す必要がある
const problem = useShogiProblem(current ?? {
sfen: 'lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1',
playerColor: 'black',
correctMoves: [],
});
if (!current) return <p>問題がありません</p>;
return (
<div>
<p>{index + 1}/{problems.length} 問目 (評価値 -{Math.abs(current.evalDrop)} の悪手局面)</p>
<ShogiBoard
position={problem.position}
lastMove={problem.lastMove}
interactive={problem.status === 'playing'}
onSquareClick={problem.handleSquareClick}
selectedSquare={problem.selectedSquare}
highlightSquares={problem.legalMoveSquares}
reversed={current.playerColor === 'white'} // 後手番なら盤面を反転
/>
{problem.status === 'correct' && (
<p>🎉 正解! ({problem.result?.correctMovesText?.[0]})</p>
)}
{problem.status === 'incorrect' && (
<div>
<p>❌ 不正解。正解: {problem.result?.correctMovesText?.join(' / ')}</p>
{/* 正解手の読み筋を候補手リストで表示することも可能 */}
{current.correctMoveCandidates && (
<CandidateList
candidates={current.correctMoveCandidates}
onCandidateClick={(cand) => problem.startPlayback(cand)}
/>
)}
</div>
)}
<button onClick={problem.reset}>やり直す</button>
<button onClick={() => setIndex((i) => (i + 1) % problems.length)}>
次の問題
</button>
</div>
);
}
SFENを直接指定する使い方
問題データを自分で用意している場合は、SFEN・手番・正解手を直接指定することもできます。
import { useShogiProblem, ShogiBoard } from 'react-kifu-player';
function ManualProblem() {
const problem = useShogiProblem({
sfen: 'lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1',
playerColor: 'black',
correctMoves: ['7g7f'], // USI形式で指定
});
return (
<ShogiBoard
position={problem.position}
lastMove={problem.lastMove}
interactive={true}
onSquareClick={problem.handleSquareClick}
selectedSquare={problem.selectedSquare}
highlightSquares={problem.legalMoveSquares}
/>
);
}
💡 正解手なし(
correctMoves: [])にすると、自由に駒を動かせるインタラクティブな盤面として使えます。
入力オプション
| パラメータ | 型 | 必須 | 説明 |
|---|---|---|---|
sfen | string | ✅ | 課題局面のSFEN文字列 |
playerColor | 'black' | 'white' | ✅ | 自分の手番 |
correctMoves | string[] | ✅ | 正解手のリスト(USI形式)。extractProblemsFromKifu から取得した値をそのまま渡せます |
correctMoveCandidates | Candidate[] | 正解手の読み筋(extractProblemsFromKifu の返り値から取得できます) | |
badMove | string | 当時の悪手(USI形式)。extractProblemsFromKifu の返り値から自動で入ります | |
badMoveEval | number | 悪手時の評価値 | |
bestMoveEval | number | 最善手の評価値 |
返り値
| プロパティ/メソッド | 型 | 説明 |
|---|---|---|
position | Position | 現在の局面(ShogiBoard の position にそのまま渡せます) |
lastMove | {from, to} | undefined | 最終手の座標 |
status | 'playing' | 'correct' | 'incorrect' | 現在の解答状況 |
selectedSquare | BoardSquareCoord | null | 選択中のマス(ShogiBoard の selectedSquare にそのまま渡せます) |
legalMoveSquares | BoardSquareCoord[] | 合法手のマスリスト(ShogiBoard の highlightSquares にそのまま渡せます) |
handleSquareClick | (sq: SquareClick) => void | マスクリックハンドラ(ShogiBoard の onSquareClick にそのまま渡せます) |
result | ProblemResult | null | 手を指した後の正誤結果情報 |
reset() | () => void | 問題を最初からやり直す |
startPlayback(candidate) | (candidate: Candidate) => void | 正解手の読み筋を自動再生する |
playbackState | {currentPly, totalPlies} | null | 再生中の状態 |
実際の動作デモ
以下は extractProblemsFromKifu で demo.kifu から実際の次の一手問題を抽出し、useShogiProblem で出題するデモです。駒をクリック→移動先をクリックで解答できます。
Loading Demo...