UNPKG

tsshogi

Version:

TypeScript library for Shogi (Japanese chess)

915 lines 72.9 kB
"use strict"; // KIF file format (.kif or .kifu) // See http://kakinoki.o.oo7.jp/kif_format.html Object.defineProperty(exports, "__esModule", { value: true }); exports.KakinokiFormatType = void 0; exports.kakinokiToMetadataKey = kakinokiToMetadataKey; exports.metadataKeyToKakinoki = metadataKeyToKakinoki; exports.importKIF = importKIF; exports.importKI2 = importKI2; exports.formatKIFMove = formatKIFMove; exports.exportKIF = exportKIF; exports.exportKI2 = exportKI2; exports.exportBOD = exportBOD; const string_1 = require("./helpers/string.cjs"); const time_1 = require("./helpers/time.cjs"); const color_1 = require("./color.cjs"); const errors_1 = require("./errors.cjs"); const move_1 = require("./move.cjs"); const piece_1 = require("./piece.cjs"); const position_1 = require("./position.cjs"); const record_1 = require("./record.cjs"); const square_1 = require("./square.cjs"); const text_1 = require("./text.cjs"); var KakinokiFormatType; (function (KakinokiFormatType) { KakinokiFormatType["KIF"] = "KIF"; KakinokiFormatType["KI2"] = "KI2"; })(KakinokiFormatType || (exports.KakinokiFormatType = KakinokiFormatType = {})); const metadataKeyMap = { 先手: record_1.RecordMetadataKey.BLACK_NAME, 後手: record_1.RecordMetadataKey.WHITE_NAME, 下手: record_1.RecordMetadataKey.SHITATE_NAME, 上手: record_1.RecordMetadataKey.UWATE_NAME, 開始日時: record_1.RecordMetadataKey.START_DATETIME, 終了日時: record_1.RecordMetadataKey.END_DATETIME, 対局日: record_1.RecordMetadataKey.DATE, 棋戦: record_1.RecordMetadataKey.TOURNAMENT, 戦型: record_1.RecordMetadataKey.STRATEGY, 表題: record_1.RecordMetadataKey.TITLE, 持ち時間: record_1.RecordMetadataKey.TIME_LIMIT, 秒読み: record_1.RecordMetadataKey.BYOYOMI, 消費時間: record_1.RecordMetadataKey.TIME_SPENT, 場所: record_1.RecordMetadataKey.PLACE, 掲載: record_1.RecordMetadataKey.POSTED_ON, 備考: record_1.RecordMetadataKey.NOTE, 先手省略名: record_1.RecordMetadataKey.BLACK_SHORT_NAME, 後手省略名: record_1.RecordMetadataKey.WHITE_SHORT_NAME, 記録係: record_1.RecordMetadataKey.SCOREKEEPER, 作品番号: record_1.RecordMetadataKey.OPUS_NO, 作品名: record_1.RecordMetadataKey.OPUS_NAME, 作者: record_1.RecordMetadataKey.AUTHOR, 発表誌: record_1.RecordMetadataKey.PUBLISHED_BY, 発表年月: record_1.RecordMetadataKey.PUBLISHED_AT, 出典: record_1.RecordMetadataKey.SOURCE, 手数: record_1.RecordMetadataKey.LENGTH, 完全性: record_1.RecordMetadataKey.INTEGRITY, 分類: record_1.RecordMetadataKey.CATEGORY, 受賞: record_1.RecordMetadataKey.AWARD, // CSA 形式で規定されている項目 先手持ち時間: record_1.RecordMetadataKey.BLACK_TIME_LIMIT, 後手持ち時間: record_1.RecordMetadataKey.WHITE_TIME_LIMIT, 最大手数: record_1.RecordMetadataKey.MAX_MOVES, 持将棋: record_1.RecordMetadataKey.JISHOGI, }; /** * 柿木形式のメタデータのキー名を RecordMetadataKey へ変換します。 * @param key */ function kakinokiToMetadataKey(key) { return metadataKeyMap[key]; } const metadataNameMap = { [record_1.RecordMetadataKey.BLACK_NAME]: "先手", [record_1.RecordMetadataKey.WHITE_NAME]: "後手", [record_1.RecordMetadataKey.SHITATE_NAME]: "下手", [record_1.RecordMetadataKey.UWATE_NAME]: "上手", [record_1.RecordMetadataKey.START_DATETIME]: "開始日時", [record_1.RecordMetadataKey.END_DATETIME]: "終了日時", [record_1.RecordMetadataKey.DATE]: "対局日", [record_1.RecordMetadataKey.TOURNAMENT]: "棋戦", [record_1.RecordMetadataKey.STRATEGY]: "戦型", [record_1.RecordMetadataKey.TITLE]: "表題", [record_1.RecordMetadataKey.TIME_LIMIT]: "持ち時間", [record_1.RecordMetadataKey.BYOYOMI]: "秒読み", [record_1.RecordMetadataKey.TIME_SPENT]: "消費時間", [record_1.RecordMetadataKey.PLACE]: "場所", [record_1.RecordMetadataKey.POSTED_ON]: "掲載", [record_1.RecordMetadataKey.NOTE]: "備考", [record_1.RecordMetadataKey.BLACK_SHORT_NAME]: "先手省略名", [record_1.RecordMetadataKey.WHITE_SHORT_NAME]: "後手省略名", [record_1.RecordMetadataKey.SCOREKEEPER]: "記録係", [record_1.RecordMetadataKey.OPUS_NO]: "作品番号", [record_1.RecordMetadataKey.OPUS_NAME]: "作品名", [record_1.RecordMetadataKey.AUTHOR]: "作者", [record_1.RecordMetadataKey.PUBLISHED_BY]: "発表誌", [record_1.RecordMetadataKey.PUBLISHED_AT]: "発表年月", [record_1.RecordMetadataKey.SOURCE]: "出典", [record_1.RecordMetadataKey.LENGTH]: "手数", [record_1.RecordMetadataKey.INTEGRITY]: "完全性", [record_1.RecordMetadataKey.CATEGORY]: "分類", [record_1.RecordMetadataKey.AWARD]: "受賞", // CSA 形式で規定されている項目 [record_1.RecordMetadataKey.BLACK_TIME_LIMIT]: "先手持ち時間", [record_1.RecordMetadataKey.WHITE_TIME_LIMIT]: "後手持ち時間", [record_1.RecordMetadataKey.MAX_MOVES]: "最大手数", [record_1.RecordMetadataKey.JISHOGI]: "持将棋", }; /** * RecordMetadataKey を柿木形式のメタデータのキー名へ変換します。 * @param key */ function metadataKeyToKakinoki(key) { return metadataNameMap[key]; } var LineType; (function (LineType) { LineType[LineType["PROGRAM_COMMENT"] = 0] = "PROGRAM_COMMENT"; LineType[LineType["METADATA"] = 1] = "METADATA"; LineType[LineType["HANDICAP"] = 2] = "HANDICAP"; LineType[LineType["BLACK_HAND"] = 3] = "BLACK_HAND"; LineType[LineType["WHITE_HAND"] = 4] = "WHITE_HAND"; LineType[LineType["BOARD"] = 5] = "BOARD"; LineType[LineType["BLACK_TURN"] = 6] = "BLACK_TURN"; LineType[LineType["WHITE_TURN"] = 7] = "WHITE_TURN"; LineType[LineType["MOVE"] = 8] = "MOVE"; LineType[LineType["MOVE2"] = 9] = "MOVE2"; LineType[LineType["BRANCH"] = 10] = "BRANCH"; LineType[LineType["COMMENT"] = 11] = "COMMENT"; LineType[LineType["BOOKMARK"] = 12] = "BOOKMARK"; LineType[LineType["END_OF_GAME"] = 13] = "END_OF_GAME"; LineType[LineType["UNKNOWN"] = 14] = "UNKNOWN"; })(LineType || (LineType = {})); const linePatterns = [ { prefix: /^#/, type: LineType.PROGRAM_COMMENT, removePrefix: false, isPosition: false, }, { prefix: /^手合割[::]/, type: LineType.HANDICAP, removePrefix: true, isPosition: true, }, { prefix: /^(先|下)手の持駒[::]/, type: LineType.BLACK_HAND, removePrefix: true, isPosition: true, }, { prefix: /^(後|上)手の持駒[::]/, type: LineType.WHITE_HAND, removePrefix: true, isPosition: true, }, { prefix: /^\|/, type: LineType.BOARD, removePrefix: false, isPosition: true, }, { prefix: /^(先|下)手番/, type: LineType.BLACK_TURN, removePrefix: false, isPosition: true, }, { prefix: /^(後|上)手番/, type: LineType.WHITE_TURN, removePrefix: false, isPosition: true, }, { prefix: /^ *[0-9]+ +/, type: LineType.MOVE, removePrefix: false, isPosition: false, }, { prefix: /^[ \u3000]*[▲△▼▽☗☖]/, type: LineType.MOVE2, removePrefix: false, isPosition: false, }, { prefix: /^[ \u3000]*変化[::][ \u3000]*/, type: LineType.BRANCH, removePrefix: true, isPosition: false, }, { prefix: /^\*/, type: LineType.COMMENT, removePrefix: true, isPosition: false, }, { prefix: /^&/, type: LineType.BOOKMARK, removePrefix: true, isPosition: false, }, { prefix: /^まで、?([0-9]+手で)?/, type: LineType.END_OF_GAME, removePrefix: true, isPosition: false, }, ]; function parseLine(line) { for (let i = 0; i < linePatterns.length; i++) { const pattern = linePatterns[i]; const matched = line.match(pattern.prefix); if (matched) { const begin = pattern.removePrefix ? matched[0].length : 0; return { type: pattern.type, data: line.substring(begin), isPosition: pattern.isPosition, metadataKey: "", }; } } const metadataPrefix = line.match(/^[^ ::]+[::]/); if (metadataPrefix) { const prefix = metadataPrefix[0]; return { type: LineType.METADATA, data: line.substring(prefix.length), isPosition: false, metadataKey: prefix.substring(0, prefix.length - 1), }; } return { type: LineType.UNKNOWN, data: line, isPosition: false, metadataKey: "", }; } function readHandicap(position, data) { switch (data.trim()) { case "平手": position.resetBySFEN(position_1.InitialPositionSFEN.STANDARD); return; case "香落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_LANCE); return; case "右香落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_RIGHT_LANCE); return; case "角落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_BISHOP); return; case "飛車落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_ROOK); return; case "飛香落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_ROOK_LANCE); return; case "二枚落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_2PIECES); return; case "四枚落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_4PIECES); return; case "六枚落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_6PIECES); return; case "八枚落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_8PIECES); return; case "十枚落ち": position.resetBySFEN(position_1.InitialPositionSFEN.HANDICAP_10PIECES); return; case "その他": position.resetBySFEN(position_1.InitialPositionSFEN.EMPTY); return; } // マイナビ系のソフトウェアは「手合割:詰将棋」を使用する場合がある。 // それを含め柿木将棋で規定していない値が使われるケースがしばしばあり、 // それらに対して適切な処理を判断しようがなく、 // エラーを返すわけにも行かないためここでは何もしない。 } const stringToSpecialMoveType = { 中断: move_1.SpecialMoveType.INTERRUPT, 投了: move_1.SpecialMoveType.RESIGN, 持将棋: move_1.SpecialMoveType.IMPASS, 千日手: move_1.SpecialMoveType.REPETITION_DRAW, 詰み: move_1.SpecialMoveType.MATE, 詰: move_1.SpecialMoveType.MATE, 不詰: move_1.SpecialMoveType.NO_MATE, 切れ負け: move_1.SpecialMoveType.TIMEOUT, 反則勝ち: move_1.SpecialMoveType.FOUL_WIN, 反則負け: move_1.SpecialMoveType.FOUL_LOSE, 入玉勝ち: move_1.SpecialMoveType.ENTERING_OF_KING, 不戦勝: move_1.SpecialMoveType.WIN_BY_DEFAULT, 不戦敗: move_1.SpecialMoveType.LOSE_BY_DEFAULT, }; const moveRegExp = /^ *([0-9]+) +[▲△▼▽]?([123456789][一二三四五六七八九]|同\u3000*)(王|玉|飛|龍|竜|角|馬|金|銀|成銀|全|桂|成桂|圭|香|成香|杏|歩|と)\u3000*(成?)(打|\([1-9][1-9]\)) *([^ ].*|$)/; const timeRegExp = /\( *([0-9]+):([0-9]+)\/[0-9: ]*\)/; const specialMoveRegExp = /^ *([0-9]+) +([^ \u3000]+) *([^ ].*|$)/; const branchRegExp = /^ *([0-9]+)/; function readBoard(board, data) { if (data.length < 21) { return new errors_1.InvalidBoardError(data); } const rankStr = data[20]; const rank = (0, text_1.stringToNumber)(rankStr); if (!rank) { return new errors_1.InvalidBoardError(data); } for (let x = 0; x < 9; x += 1) { const file = 9 - x; const square = new square_1.Square(file, rank); const index = x * 2 + 1; const pieceStr = data[index + 1]; const pieceType = (0, text_1.stringToPieceType)(pieceStr); if (!pieceType) { board.remove(square); continue; } const color = data[index] !== "v" ? color_1.Color.BLACK : color_1.Color.WHITE; board.set(square, new piece_1.Piece(color, pieceType)); } } function readHand(hand, data) { // NOTE: // スペースで区切られていないものでも Kifu for Windows や ShogiGUI は読み込める。 // See: https://github.com/sunfish-shogi/shogihome/issues/572 const sections = data.split(/[  ]/); for (const section of sections) { if (!section || section === "なし") { continue; } const pieceStr = section[0]; const numberStr = section.substring(1); const pieceType = (0, text_1.stringToPieceType)(pieceStr); const n = (0, text_1.stringToNumber)(numberStr) || 1; if (!pieceType) { return new errors_1.InvalidHandPieceError(section); } hand.add(pieceType, n); } return; } function readMoveTime(record, data) { const timeResult = timeRegExp.exec(data); if (timeResult) { const minutes = timeResult[1]; const seconds = timeResult[2]; const s = Number.parseInt(minutes) * 60 + Number.parseInt(seconds); record.current.setElapsedMs(s * 1e3); } } function readMove(record, data) { const result = readRegularMove(record, data); if (result instanceof Error) { return result; } else if (result) { return; } if (readSpecialMove(record, data)) { return; } return new errors_1.InvalidMoveError(data); } function readRegularMove(record, data) { const result = moveRegExp.exec(data); if (!result) { return false; } const num = Number(result[1]); const toStr = result[2]; const pieceTypeStr = result[3]; const promStr = result[4]; const fromStr = result[5]; const time = result[6]; if (num === 0) { return new errors_1.InvalidMoveNumberError(data); } record.goto(num - 1); let to; let from; if (toStr.startsWith("同")) { if (!(record.current.move instanceof move_1.Move)) { return new errors_1.InvalidDestinationError(data); } to = record.current.move.to; } else { const file = (0, text_1.stringToNumber)(toStr[0]); const rank = (0, text_1.stringToNumber)(toStr[1]); to = new square_1.Square(file, rank); } if (fromStr === "打") { from = (0, text_1.stringToPieceType)(pieceTypeStr); } else { const file = (0, text_1.stringToNumber)(fromStr[1]); const rank = (0, text_1.stringToNumber)(fromStr[2]); from = new square_1.Square(file, rank); } let move = record.position.createMove(from, to); if (!move) { return new errors_1.InvalidMoveError(data); } if (promStr === "成") { move = move.withPromote(); } record.append(move, { ignoreValidation: true, }); readMoveTime(record, time); return true; } function readSpecialMove(record, data) { const result = specialMoveRegExp.exec(data); if (!result) { return false; } const num = Number(result[1]); const type = stringToSpecialMoveType[result[2]]; const time = result[3]; record.goto(num - 1); let move; if (type) { move = (0, move_1.specialMove)(type); } else { move = (0, move_1.anySpecialMove)(result[2]); } record.append(move, { ignoreValidation: true, }); readMoveTime(record, time); return true; } function readMove2(record, data) { const lastMove = record.current.move instanceof move_1.Move ? record.current.move : undefined; const [moves, e] = (0, text_1.parseMoves)(record.position, data, lastMove); if (e) { return e; } for (const move of moves) { record.append(move, { ignoreValidation: true }); } } function readBranch(record, data) { const result = branchRegExp.exec(data); if (!result) { return new errors_1.InvalidMoveNumberError(data); } const num = Number(result[1]); if (num === 0 || num > record.current.ply + 1) { return new errors_1.InvalidMoveNumberError(data); } record.goto(num - 1); } function readEndOfGame(record, data) { const clean = data.replaceAll(/[\s\u3000]/g, ""); // NOTE: 末尾 "勝ち" は "時間切れにより...の勝ち" "反則勝ち" ともマッチするので最後に判定する必要がある。 if (clean.startsWith("時間切れ")) { record.append((0, move_1.specialMove)(move_1.SpecialMoveType.TIMEOUT)); } else if (clean.endsWith("反則勝ち")) { record.append((0, move_1.specialMove)(move_1.SpecialMoveType.FOUL_WIN)); } else if (clean.endsWith("反則負け")) { record.append((0, move_1.specialMove)(move_1.SpecialMoveType.FOUL_LOSE)); } else if (clean.endsWith("入玉勝ち")) { record.append((0, move_1.specialMove)(move_1.SpecialMoveType.ENTERING_OF_KING)); } else if (clean.endsWith("勝ち")) { record.append((0, move_1.specialMove)(move_1.SpecialMoveType.RESIGN)); } else { const type = stringToSpecialMoveType[clean]; if (type) { record.append((0, move_1.specialMove)(type)); } else { record.append((0, move_1.anySpecialMove)(clean)); } } } /** * KIF 形式の文字列を読み込みます。 * @param data */ function importKIF(data) { return importKakinoki(data, KakinokiFormatType.KIF); } /** * KI2 形式の文字列を読み込みます。 * @param data */ function importKI2(data) { return importKakinoki(data, KakinokiFormatType.KI2); } function importKakinoki(data, formatType) { const metadata = new record_1.RecordMetadata(); const record = new record_1.Record(); const lines = data.split(/\r?\n/); const position = new position_1.Position(); let preMoveComment = ""; let preMoveBookmark = ""; let isMoveSection = false; const startMoveSectionIfNot = () => { if (isMoveSection) { return; } record.clear(position); record.first.comment = preMoveComment; record.first.bookmark = preMoveBookmark; isMoveSection = true; }; for (const line of lines) { if (line === "") { continue; } const parsed = parseLine(line); if (isMoveSection && parsed.isPosition) { return new errors_1.InvalidLineError(line); } let e; switch (parsed.type) { case LineType.METADATA: { const standardKey = metadataKeyMap[parsed.metadataKey]; if (standardKey) { metadata.setStandardMetadata(standardKey, parsed.data); } else { metadata.setCustomMetadata(parsed.metadataKey, parsed.data); } break; } case LineType.HANDICAP: readHandicap(position, parsed.data); break; case LineType.BLACK_HAND: e = readHand(position.blackHand, parsed.data); break; case LineType.WHITE_HAND: e = readHand(position.whiteHand, parsed.data); break; case LineType.BOARD: e = readBoard(position.board, parsed.data); break; case LineType.BLACK_TURN: position.setColor(color_1.Color.BLACK); break; case LineType.WHITE_TURN: position.setColor(color_1.Color.WHITE); break; case LineType.MOVE: if (formatType !== KakinokiFormatType.KIF) { return new errors_1.InvalidLineError(line); } startMoveSectionIfNot(); e = readMove(record, parsed.data); break; case LineType.MOVE2: if (formatType !== KakinokiFormatType.KI2) { return new errors_1.InvalidLineError(line); } startMoveSectionIfNot(); e = readMove2(record, parsed.data); break; case LineType.BRANCH: // NOTE: // KIF では指し手の先頭に手数が付与されるので必要ない。 // https://github.com/sunfish-shogi/shogihome/issues/570 // の不具合により、ヘッダー部に "変化:" で始まる行が存在する場合がある。 // 指し手が始まるより前に "変化:" が出現しても無視しなければならない。 if (isMoveSection && formatType === KakinokiFormatType.KI2) { e = readBranch(record, parsed.data); } break; case LineType.COMMENT: if (isMoveSection) { record.current.comment = (0, string_1.appendLine)(record.current.comment, parsed.data); } else { preMoveComment = (0, string_1.appendLine)(preMoveComment, parsed.data); } break; case LineType.BOOKMARK: if (isMoveSection) { record.current.bookmark = parsed.data; } else { preMoveBookmark = parsed.data; } break; case LineType.END_OF_GAME: // NOTE: // KI2 では "までn手で" で始まる行から終局理由を読み取らなければいけない。 // KIF では指し手の一つとしても記載されるので必要ない。 if (formatType === KakinokiFormatType.KI2) { startMoveSectionIfNot(); readEndOfGame(record, parsed.data); } break; case LineType.PROGRAM_COMMENT: break; case LineType.UNKNOWN: break; } if (e) { return e; } } startMoveSectionIfNot(); record.goto(0); record.resetAllBranchSelection(); record.metadata = metadata; return record; } const specialMoveToString = { [move_1.SpecialMoveType.START]: "", [move_1.SpecialMoveType.RESIGN]: "投了", [move_1.SpecialMoveType.INTERRUPT]: "中断", [move_1.SpecialMoveType.MAX_MOVES]: "持将棋", [move_1.SpecialMoveType.IMPASS]: "持将棋", [move_1.SpecialMoveType.DRAW]: "持将棋", [move_1.SpecialMoveType.REPETITION_DRAW]: "千日手", [move_1.SpecialMoveType.MATE]: "詰み", [move_1.SpecialMoveType.NO_MATE]: "不詰", [move_1.SpecialMoveType.TIMEOUT]: "切れ負け", [move_1.SpecialMoveType.FOUL_WIN]: "反則勝ち", [move_1.SpecialMoveType.FOUL_LOSE]: "反則負け", [move_1.SpecialMoveType.ENTERING_OF_KING]: "入玉勝ち", [move_1.SpecialMoveType.WIN_BY_DEFAULT]: "不戦勝", [move_1.SpecialMoveType.LOSE_BY_DEFAULT]: "不戦敗", [move_1.SpecialMoveType.TRY]: "トライ", }; function formatMetadata(metadata, options) { let ret = ""; const returnCode = options?.returnCode || "\n"; for (const key of metadata.standardMetadataKeys) { ret += metadataNameMap[key] + ":" + metadata.getStandardMetadata(key) + returnCode; } for (const key of metadata.customMetadataKeys) { ret += key + ":" + metadata.getCustomMetadata(key) + returnCode; } return ret; } function formatPosition(position, options) { const returnCode = options?.returnCode || "\n"; switch (position.sfen) { case position_1.InitialPositionSFEN.STANDARD: return "手合割:平手" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_LANCE: return "手合割:香落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_RIGHT_LANCE: return "手合割:右香落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_BISHOP: return "手合割:角落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_ROOK: return "手合割:飛車落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_ROOK_LANCE: return "手合割:飛香落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_2PIECES: return "手合割:二枚落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_4PIECES: return "手合割:四枚落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_6PIECES: return "手合割:六枚落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_8PIECES: return "手合割:八枚落ち" + returnCode; case position_1.InitialPositionSFEN.HANDICAP_10PIECES: return "手合割:十枚落ち" + returnCode; } return formatBOD(position, options); } function formatBOD(position, options) { const returnCode = options?.returnCode || "\n"; let ret = ""; ret += "後手の持駒:" + formatHand(position.whiteHand) + returnCode; ret += " 9 8 7 6 5 4 3 2 1" + returnCode; ret += "+---------------------------+" + returnCode; for (let y = 0; y < 9; y++) { ret += "|"; for (let x = 0; x < 9; x++) { const square = square_1.Square.newByXY(x, y); const piece = position.board.at(square); if (!piece) { ret += " ・"; } else if (piece.color === color_1.Color.BLACK) { ret += " " + (0, text_1.pieceTypeToStringForBoard)(piece.type); } else { ret += "v" + (0, text_1.pieceTypeToStringForBoard)(piece.type); } } ret += "|" + (0, text_1.rankToKanji)(y + 1) + returnCode; } ret += "+---------------------------+" + returnCode; ret += "先手の持駒:" + formatHand(position.blackHand) + returnCode; if (position.color === color_1.Color.BLACK) { ret += "先手番" + returnCode; } else { ret += "後手番" + returnCode; } return ret; } /** * KIF 形式の指し手を出力します。 * @param move 対象の指し手 * @param prev 直前の指し手 ("同"の判定に使用) */ function formatKIFMove(move, options) { let ret = ""; if (options?.prev && move.to.equals(options.prev.to)) { ret += "同\u3000"; } else { ret += (0, text_1.fileToMultiByteChar)(move.to.file); ret += (0, text_1.rankToKanji)(move.to.rank); } ret += (0, text_1.pieceTypeToStringForMove)(move.pieceType); if (move.promote) { ret += "成"; } if (move.from instanceof square_1.Square) { ret += "(" + move.from.file + move.from.rank + ")"; ret += ret.length === 7 && options?.padding ? " " : ""; } else { ret += "打"; ret += options?.padding ? " " : ""; } return ret; } function formatHand(hand) { let ret = ""; hand.forEach((pieceType, n) => { if (n >= 1) { ret += (0, text_1.pieceTypeToStringForBoard)(pieceType); if (n >= 2) { ret += (0, text_1.numberToKanji)(n); } ret += " "; } }); if (ret === "") { ret = "なし"; } return ret; } /** * KIF形式の文字列を出力します。 * @param record * @param options */ function exportKIF(record, options) { let ret = ""; const returnCode = options?.returnCode || "\n"; if (options?.comment) { for (const line of options.comment.split("\n")) { ret += "#" + line + returnCode; } } ret += formatMetadata(record.metadata, options); ret += formatPosition(record.initialPosition, options); ret += "手数----指手---------消費時間--" + returnCode; record.forEach((node) => { if (node.ply !== 0) { if (!node.isFirstBranch) { ret += returnCode; ret += "変化:" + node.ply + "手" + returnCode; } ret += String(node.ply).padStart(4, " ") + " "; if (node.move instanceof move_1.Move) { const prev = node.prev?.move instanceof move_1.Move ? node.prev.move : undefined; ret += formatKIFMove(node.move, { prev, padding: true }); } else if ((0, move_1.isKnownSpecialMove)(node.move)) { const s = specialMoveToString[node.move.type]; ret += s + " ".repeat(Math.max(12 - s.length * 2, 0)); } else { ret += node.move.name + " ".repeat(Math.max(12 - node.move.name.length * 2, 0)); } const elapsed = (0, time_1.millisecondsToMSS)(node.elapsedMs); const totalElapsed = (0, time_1.millisecondsToHHMMSS)(node.totalElapsedMs); ret += ` (${elapsed}/${totalElapsed})`; if (node.branch) { // Kifu for Windows にならい、後続の分岐が存在する場合は行末に "+" を付ける。 ret += "+"; } ret += returnCode; } if (node.comment.length !== 0) { const comment = node.comment.endsWith("\n") ? node.comment.slice(0, -1) : node.comment; ret += "*" + comment.replaceAll("\n", returnCode + "*") + returnCode; } if (node.bookmark.length !== 0) { ret += "&" + node.bookmark + returnCode; } }); return ret; } /** * KI2形式の文字列を出力します。 * @param record * @param options */ function exportKI2(record, options) { let ret = ""; let moveCountInLine = 0; let lastMoveLength = 0; const returnCode = options?.returnCode ? options.returnCode : "\n"; ret += formatMetadata(record.metadata, options); ret += formatPosition(record.initialPosition, options); record.forEach((node) => { if (node.prev) { if (!node.isFirstBranch) { if (!ret.endsWith(returnCode)) { ret += returnCode; } ret += returnCode; ret += "変化:" + node.ply + "手" + returnCode; } if (node.move instanceof move_1.Move) { const pos = position_1.Position.newBySFEN(node.prev.sfen); if (!pos) { throw new Error("Invalid SFEN"); } const str = (0, text_1.formatMove)(pos, node.move, { lastMove: node.prev?.move instanceof move_1.Move ? node.prev.move : undefined, compatible: true, }); if (ret.endsWith(returnCode)) { moveCountInLine = 0; } else { ret += " ".repeat(Math.max(12 - lastMoveLength * 2, 0)); } ret += str; lastMoveLength = str.length; moveCountInLine++; if (moveCountInLine >= 6) { ret += returnCode; } } else { if (!ret.endsWith(returnCode)) { ret += returnCode; } ret += `まで${node.ply - 1}手で`; if ((0, move_1.isKnownSpecialMove)(node.move)) { const [next, last] = node.nextColor === color_1.Color.BLACK ? ["先手", "後手"] : ["後手", "先手"]; switch (node.move.type) { case move_1.SpecialMoveType.RESIGN: ret += `${last}の勝ち`; break; case move_1.SpecialMoveType.TIMEOUT: ret += `時間切れにより${last}の勝ち`; break; case move_1.SpecialMoveType.ENTERING_OF_KING: ret += `${next}の入玉勝ち`; break; case move_1.SpecialMoveType.FOUL_WIN: ret += `${next}の反則勝ち`; break; case move_1.SpecialMoveType.FOUL_LOSE: ret += `${next}の反則負け`; break; default: ret += specialMoveToString[node.move.type]; break; } } else { ret += node.move.name; } ret += returnCode; } } if (node.comment.length !== 0) { if (!ret.endsWith(returnCode)) { ret += returnCode; } const comment = node.comment.endsWith("\n") ? node.comment.slice(0, -1) : node.comment; ret += "*" + comment.replaceAll("\n", returnCode + "*") + returnCode; } if (node.bookmark.length !== 0) { if (!ret.endsWith(returnCode)) { ret += returnCode; } ret += "&" + node.bookmark + returnCode; } }); return ret; } function exportBOD(record, options) { let ret = ""; const returnCode = options?.returnCode || "\n"; ret += formatBOD(record.position, options); const ply = record.current.ply; const lastMove = record.current.move instanceof move_1.Move ? record.current.move : undefined; const lastMoveStr = lastMove ? formatKIFMove(lastMove) : ""; ret += `手数=${ply} ${lastMoveStr} まで` + returnCode; return ret; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2FraW5va2kuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMva2FraW5va2kudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLGtDQUFrQztBQUNsQywrQ0FBK0M7OztBQTRGL0Msc0RBRUM7QUE4Q0Qsc0RBRUM7QUFzWkQsOEJBRUM7QUFNRCw4QkFFQztBQStORCxzQ0FvQkM7QUF3QkQsOEJBNkNDO0FBV0QsOEJBb0ZDO0FBRUQsOEJBU0M7QUE5OEJELDZDQUE4QztBQUM5Qyx5Q0FBeUU7QUFFekUsbUNBQWdDO0FBQ2hDLHFDQU9rQjtBQUVsQixpQ0FPZ0I7QUFDaEIsbUNBQTJDO0FBQzNDLHlDQUE4RTtBQUM5RSxxQ0FNa0I7QUFDbEIscUNBQWtDO0FBQ2xDLGlDQVVnQjtBQUVoQixJQUFZLGtCQUdYO0FBSEQsV0FBWSxrQkFBa0I7SUFDNUIsaUNBQVcsQ0FBQTtJQUNYLGlDQUFXLENBQUE7QUFDYixDQUFDLEVBSFcsa0JBQWtCLGtDQUFsQixrQkFBa0IsUUFHN0I7QUFFRCxNQUFNLGNBQWMsR0FBcUQ7SUFDdkUsRUFBRSxFQUFFLDBCQUFpQixDQUFDLFVBQVU7SUFDaEMsRUFBRSxFQUFFLDBCQUFpQixDQUFDLFVBQVU7SUFDaEMsRUFBRSxFQUFFLDBCQUFpQixDQUFDLFlBQVk7SUFDbEMsRUFBRSxFQUFFLDBCQUFpQixDQUFDLFVBQVU7SUFDaEMsSUFBSSxFQUFFLDBCQUFpQixDQUFDLGNBQWM7SUFDdEMsSUFBSSxFQUFFLDBCQUFpQixDQUFDLFlBQVk7SUFDcEMsR0FBRyxFQUFFLDBCQUFpQixDQUFDLElBQUk7SUFDM0IsRUFBRSxFQUFFLDBCQUFpQixDQUFDLFVBQVU7SUFDaEMsRUFBRSxFQUFFLDBCQUFpQixDQUFDLFFBQVE7SUFDOUIsRUFBRSxFQUFFLDBCQUFpQixDQUFDLEtBQUs7SUFDM0IsSUFBSSxFQUFFLDBCQUFpQixDQUFDLFVBQVU7SUFDbEMsR0FBRyxFQUFFLDBCQUFpQixDQUFDLE9BQU87SUFDOUIsSUFB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