UNPKG

shogi-player

Version:

Shogi board web components has functions for replaying, manipulating, and editing

252 lines (223 loc) 7.93 kB
import XRegExp from "xregexp" XRegExp.uninstall("namespacing") import Vue from "vue" import _ from "lodash" import { Board } from "./board.js" import { ParserBase } from "./parser_base.js" import { Piece } from "./piece.js" import { Place } from "./place.js" import { Soldier } from "./soldier.js" import { Location } from "./location.js" import { MoveHash } from "./move_hash.js" import { Xcontainer } from "./xcontainer.js" export class SfenParser extends ParserBase { static sfen_flop(sfen) { const source = this.parse(sfen) // 方法1. Xcontainer を仲介する方法 // 一応動くけど Xcontainer まで出動する必要はない // もっと下位層のライブラリで行うべき if (false) { const xcontainer = new Xcontainer() xcontainer.data_source = source xcontainer.current_turn = 0 xcontainer.run() xcontainer.board = xcontainer.board.flop const parts = [] parts.push(xcontainer.to_short_sfen) const v = source.move_infos if (v.length >= 1) { parts.push("moves") parts.push(v.map(e => e.to_flop_sfen).join(" ")) } return parts.join(" ") } // 方法2. SFENパーサーで読み取ってそのままSFEN出力する間で属性を変更する方法 if (true) { source.attributes["board"] = source.board.flop.to_sfen source.attributes["moves"] = source.move_infos.map(e => e.to_flop_sfen).join(" ") return source.to_sfen } } reset() { super.reset() this.attributes = {} } parse() { this.raw_body = this.raw_body.replace(/startpos/, "sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1") const regex = XRegExp("sfen\\s+(?<board>\\S+)\\s+(?<b_or_w>\\S+)\\s+(?<hold_pieces>\\S+)\\s+(?<turn_counter_next>\\d+)(\\s+moves\\s+(?<moves>.*))?") this.attributes = XRegExp.exec(this.raw_body, regex) if (process.env.NODE_ENV === "deveopment") { console.log(this.attributes) } } get board() { const board = new Board() this.attributes["board"].split("/").forEach((e, y) => { let x = 0 XRegExp.forEach(e, XRegExp("(?<promoted>\\+?)(?<piece>\\S)"), (m, i) => { if (/\d+/.test(m.piece)) { x += Number(m.piece) } else { const soldier = new Soldier({ place: new Place([x, y]), piece: Piece.fetch(m.piece), promoted: (m.promoted === "+"), location: this.__location_by_upper_or_lower_case(m.piece), }) board.place_on(soldier) x++ } }) }) return board } get base_location() { let key = null if (this.attributes["b_or_w"] === "b") { key = "black" } else { key = "white" } return Location.fetch(key) } get hold_pieces() { const hash = this.hold_pieces_empty_hash() if (this.attributes["hold_pieces"] !== "-") { XRegExp.forEach(this.attributes["hold_pieces"], XRegExp("(?<count>\\d+)?(?<piece_char>\\S)"), (md, i) => { const piece = Piece.fetch(md.piece_char) let count = Number(md.count || 1) const location = this.__location_by_upper_or_lower_case(md.piece_char) count += hash[location.key][piece.key] || 0 Vue.set(hash[location.key], piece.key, count) }) } return hash } // sfen_serializer 用 get turn_base() { return Number(this.attributes["turn_counter_next"]) - 1 } // "b - 1" なら 0 // "w - 2" なら 1 // "b - 3" なら 2 // get turn_offset_min() { // // return Number(this.attributes["turn_counter_next"]) - 1 // // return Number(this.attributes["turn_counter_next"]) - 1 // } // // "b - 1" -> turn_offset_min:0 % 2 -> 0 && w // // "w - 2" -> turn_offset_min:1 % 2 -> 1 && w // // "b - 3" -> turn_offset_min:2 % 2 -> 0 && w // // "w - 1" -> turn_offset_min:0 % 2 -> 0 && w -> true // // "b - 2" -> turn_offset_min:1 % 2 -> 1 && w // // "w - 3" -> turn_offset_min:2 % 2 -> 0 && w -> true // get komaochi_p() { // return (this.turn_offset_min % 2) === 0 && this.base_location.key === "white" // } // FIXME: move_hashes が正しい get move_infos() { // this.moves.map((e, i) => { としたかったが break できないため lodash の forEach に変更。lodash のは false で break できる const ary = [] _.forEach(this.moves, (e, i) => { const move_hash = MoveHash.parse(e) if (!move_hash) { return false // break } // if (true) { // move_hash["scene_index"] = this.turn_offset_min + i // move_hash["scene_offset"] = i // } move_hash["location"] = this.location_by_offset(i) // これいるのか? → いる ary.push(move_hash) }) return ary } get moves() { if (!this.moves_exist_p) { return [] } return this.attributes["moves"].split(/\s+/) } get moves_exist_p() { return (this.attributes["moves"] || "") !== "" } // 最初の局面(1から始まる) // // 以下のようにしてもよい // const sfen_parser = new SfenParser() // sfen_parser.raw_body = "position sfen 7nl/7k1/9/7pp/6N2/9/9/9/9 b GS2r2b3g3s2n3l16p 2" // sfen_parser.parse() // sfen_parser.attributes.turn_counter_next = 1 // expect(sfen_parser.init_sfen).toEqual('position sfen 7nl/7k1/9/7pp/6N2/9/9/9/9 b GS2r2b3g3s2n3l16p 1') // get init_sfen_from_one() { const parts = [] parts.push("position") parts.push("sfen") parts.push(this.attributes["board"]) parts.push(this.attributes["b_or_w"]) parts.push(this.attributes["hold_pieces"]) parts.push(1) return parts.join(" ") } // 最初の局面 get init_sfen() { const parts = [] parts.push("position") parts.push("sfen") parts.push(this.init_sfen_strip) return parts.join(" ") } // 最初の局面(position sfenなし) get init_sfen_strip() { const parts = [] parts.push(this.attributes["board"]) parts.push(this.attributes["b_or_w"]) parts.push(this.attributes["hold_pieces"]) parts.push(this.attributes["turn_counter_next"]) return parts.join(" ") } // 元の状態で返す // このとき attributes を更新していれば違う形で返せる get to_sfen() { const parts = [] parts.push("position") parts.push("sfen") parts.push(this.attributes["board"]) parts.push(this.attributes["b_or_w"]) parts.push(this.attributes["hold_pieces"]) parts.push(this.attributes["turn_counter_next"]) if (this.moves_exist_p) { parts.push("moves") parts.push(this.attributes["moves"]) } return parts.join(" ") } __location_by_upper_or_lower_case(v) { let key = null if (/[A-Z]/.test(v)) { key = "black" } else { key = "white" } return Location.fetch(key) } } if (typeof process !== "undefined" && process.argv[1] === __filename) { let sfen_parser sfen_parser = SfenParser.parse("position sfen +lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b S2s 1 moves 7i6h S*2d") console.log(sfen_parser) sfen_parser = SfenParser.parse("position sfen +lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b S2s 1 moves 7i6h S*2d") console.log(sfen_parser) // sfen_parser = new SfenParser("position sfen +lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b S2s 1 moves 7i6h S*2d") // sfen_parser.parse() // console.log(sfen_parser.board) // console.log(sfen_parser.base_location.key) // console.log(sfen_parser.hold_pieces) // console.log(sfen_parser.move_infos) // console.log(sfen_parser.moves) // console.log(sfen_parser.init_sfen) // // sfen_parser = new SfenParser("position sfen lr4knl/3g2gs1/4ppP2/p4bNpp/2pSsN3/PPPP1P2P/2N1P1G2/2G6/L1K4RL w BPs3p 72 moves 2b3c") // sfen_parser.parse() // console.log(sfen_parser.location_by_offset(0)) }