├── .eslintrc.js
├── .github
├── release.yml
└── workflows
│ ├── bump.yml
│ ├── gh-pages.yml
│ ├── main.yml
│ └── publish.yml
├── .gitignore
├── .npmignore
├── .nvmrc
├── .prettierignore
├── .prettierrc.json
├── .travis.yml
├── LICENSE.txt
├── README.md
├── example
├── csa-player.html
├── jkf-player.html
├── ki2-player.html
└── kif-player.html
├── jest.config.js
├── package-lock.json
├── package.json
├── specification
├── files
│ ├── ryuou201409020101.jkf.json
│ └── same_move_minimal.jkf.json
└── json-kifu-format.schema.json
├── src
├── Formats.ts
├── __tests__
│ ├── __snapshots__
│ │ ├── fileTest.ts.snap
│ │ ├── jkfplayerTest.ts.snap
│ │ └── normalizerTest.ts.snap
│ ├── fileTest.ts
│ ├── jkfplayerTest.ts
│ ├── normalizerTest-relative.ts
│ └── normalizerTest.ts
├── jkfplayer.ts
├── main.ts
├── normalizer.ts
└── peg
│ ├── __tests__
│ ├── __snapshots__
│ │ ├── csaParserTest.ts.snap
│ │ └── kifParserTest.ts.snap
│ ├── csaParserTest.ts
│ ├── ki2ParserTest.ts
│ └── kifParserTest.ts
│ ├── ambient.ts
│ ├── csa-parser.pegjs
│ ├── ki2-parser.pegjs
│ ├── kif-parser.pegjs
│ └── parsers.ts
├── test
├── board-serializer.js
├── debug-comment-content.html
├── files
│ ├── csa
│ │ ├── 2005_GPS_K-SHOGI.csa
│ │ ├── 2005_YAMADA_GPS.csa
│ │ ├── 8mai.csa
│ │ ├── 8mai_hirate.csa
│ │ ├── 9fu.csa
│ │ ├── 9fu_komabetsu.csa
│ │ ├── chudan.csa
│ │ ├── dr1test1test0+tu-1_aobazero_viper-300-5F+aobazero+viper+20200719201040.csa
│ │ ├── example.csa
│ │ ├── formal.csa
│ │ ├── illegal_lose.csa
│ │ ├── illegal_win.csa
│ │ ├── noeol.csa
│ │ └── v2.csa
│ ├── jkf
│ │ ├── ryuou201409020101.jkf
│ │ └── same_move_minimal.jkf
│ ├── ki2
│ │ ├── 20091203.ki2
│ │ ├── 7mai.ki2u
│ │ ├── 8mai.ki2
│ │ ├── 8mai.ki2u
│ │ ├── 9fu.ki2
│ │ ├── chudan.ki2
│ │ ├── denou3-1.ki2
│ │ ├── fork.ki2
│ │ ├── illegal.ki2
│ │ ├── kobayashi_kinsho196702.ki2
│ │ └── noeol.ki2
│ └── kif
│ │ ├── 20081220kyoochi.kif
│ │ ├── 8mai.kif
│ │ ├── 9fu.kif
│ │ ├── chudan.kif
│ │ ├── doh_branch.kifu
│ │ ├── fork-notime.kif
│ │ ├── fork-test.kif
│ │ ├── fork.kif
│ │ ├── henka.kif
│ │ ├── illegal.kif
│ │ ├── jt201409130101.kif
│ │ ├── kifu_for_iphone.kif
│ │ ├── kiou201403160101.kif
│ │ ├── last-fork.kifu
│ │ ├── last_comment.kif
│ │ ├── meijinsen_20180508_M7_10034.kif
│ │ ├── no_henka.kif
│ │ ├── no_henka.kifu
│ │ ├── noeol.kif
│ │ ├── oui201407080101.kif
│ │ ├── ouza201410070101.kif
│ │ ├── ryuou201409020101.kif
│ │ ├── ryuou4.kif
│ │ ├── shogidb2.kifu
│ │ ├── shogidokoro.kifu
│ │ └── taichitsume.kif
└── pegjs-jest.js
├── tsconfig.json
├── tsconfig.typecheck.json
└── webpack.config.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | },
6 | extends: [
7 | "eslint:recommended",
8 | "plugin:@typescript-eslint/recommended",
9 | "plugin:jest/recommended",
10 | "prettier",
11 | ],
12 | parser: "@typescript-eslint/parser",
13 | parserOptions: {
14 | ecmaVersion: 13,
15 | sourceType: "module",
16 | },
17 | plugins: ["@typescript-eslint", "jest"],
18 | ignorePatterns: [
19 | ".eslintrc.js",
20 | "jest.config.js",
21 | "pegjs-jest.js",
22 | "webpack.config.js",
23 | "docs/",
24 | "bundle/",
25 | "dist/",
26 | ],
27 | rules: {},
28 | };
29 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | changelog:
2 | categories:
3 | - title: Documentation Changes
4 | labels:
5 | - documentation
6 | - title: New Features
7 | labels:
8 | - enhancement
9 | - title: Bug Fixes
10 | labels:
11 | - bug
12 | - title: Other Changes
13 | labels:
14 | - "*"
--------------------------------------------------------------------------------
/.github/workflows/bump.yml:
--------------------------------------------------------------------------------
1 | name: Bump version
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | version:
7 | description: 'Type of version (major / minor / patch)'
8 | required: true
9 |
10 | jobs:
11 | bump-version:
12 | name: Bump version
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Check out source
16 | uses: actions/checkout@v3
17 | with:
18 | ssh-key: ${{ secrets.DEPLOY_KEY }}
19 | - name: Setup Node.js
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: '16'
23 | cache: 'npm'
24 | - name: Install dependencies
25 | uses: bahmutov/npm-install@v1
26 | - name: Setup Git
27 | run: |
28 | git config user.name '${{ secrets.GIT_USER_NAME }}'
29 | git config user.email '${{ secrets.GIT_USER_EMAIL }}'
30 | - name: bump version
31 | run: npm version ${{ github.event.inputs.version }}
32 |
33 | - name: Push latest version
34 | run: git push origin master --follow-tags
35 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-20.04
11 | steps:
12 | - uses: actions/checkout@v3
13 |
14 | - name: Setup Node
15 | uses: actions/setup-node@v3
16 | with:
17 | node-version: '16.x'
18 |
19 | - name: Cache dependencies
20 | uses: actions/cache@v2
21 | with:
22 | path: ~/.npm
23 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
24 | restore-keys: |
25 | ${{ runner.os }}-node-
26 | - run: npm ci
27 | - run: npm run deploy:ghpages
28 |
29 | - name: Deploy
30 | uses: peaceiris/actions-gh-pages@v3
31 | with:
32 | github_token: ${{ secrets.GITHUB_TOKEN }}
33 | publish_dir: ./public
34 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | name: Build
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout repo
11 | uses: actions/checkout@v3
12 |
13 | - name: Use Node 16
14 | uses: actions/setup-node@v3
15 | with:
16 | node-version: 16
17 |
18 | - name: Install dependencies
19 | uses: bahmutov/npm-install@v1
20 |
21 | - name: Build
22 | run: npm run build
23 |
24 | typecheck:
25 | name: Typechecker
26 | runs-on: ubuntu-latest
27 | steps:
28 | - name: Checkout repo
29 | uses: actions/checkout@v3
30 |
31 | - name: Use Node 16
32 | uses: actions/setup-node@v3
33 | with:
34 | node-version: 16
35 |
36 | - name: Install dependencies
37 | uses: bahmutov/npm-install@v1
38 |
39 | - name: Typecheck
40 | run: npm run typecheck
41 |
42 | test:
43 | name: Unit and Integration Tests
44 | runs-on: ubuntu-latest
45 | steps:
46 | - name: Checkout repo
47 | uses: actions/checkout@v3
48 |
49 | - name: Use Node 16
50 | uses: actions/setup-node@v3
51 | with:
52 | node-version: 16
53 |
54 | - name: Install dependencies
55 | uses: bahmutov/npm-install@v1
56 |
57 | - name: Test
58 | run: npm run test -- --ci --coverage --maxWorkers=2
59 |
60 | lint:
61 | name: Linter
62 | runs-on: ubuntu-latest
63 | steps:
64 | - name: Checkout repo
65 | uses: actions/checkout@v3
66 |
67 | - name: Use Node 16
68 | uses: actions/setup-node@v3
69 | with:
70 | node-version: 16
71 |
72 | - name: Install dependencies
73 | uses: bahmutov/npm-install@v1
74 |
75 | - name: Build
76 | run: npm run build
77 |
78 | - name: Lint
79 | run: npm run lint
80 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v1
12 | - uses: actions/setup-node@v3
13 | with:
14 | node-version: 16
15 | registry-url: https://registry.npmjs.org/
16 | - run: npm install
17 | - run: npm run build
18 | - run: npm publish --access public
19 | env:
20 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bundle
2 | /coverage
3 | /dist
4 | /docs
5 | /node_modules
6 | /.idea
7 | .DS_Store
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /**/__tests__/
2 | /.github/
3 | /.idea/
4 | /coverage/
5 | /test/
6 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | lts/gallium
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /.github/
2 | /bundle/
3 | /coverage/
4 | /dist/
5 | /docs/
6 | /node_modules/
7 | /public/
8 | /src/__tests__/jkfplayerTest.ts
9 | README.md
10 | package-lock.json
11 | package.json
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "tabWidth": 4,
4 | "bracketSpacing": false
5 | }
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 16
4 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 na2hiro (https://github.com/na2hiro)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # (Moved) `json-kifu-format`はmonorepoへ移行しました
2 |
3 | https://github.com/na2hiro/Kifu-for-JS/tree/master/packages/json-kifu-format
4 |
5 | # JSON棋譜フォーマット(JKF) 
6 | JSONで将棋の棋譜を取り扱う標準形式JKFを定義しています.またJKFを既存のKIF, KI2, CSA等から変換するライブラリ及びJKFを用いて盤面再生を行うライブラリを提供します.
7 |
8 | ## 概要
9 |
10 | * 現状の問題として,既存のKIF, KI2, CSA形式が持つ情報はバラバラであり,アプリケーション側の対応に手間がかかる.
11 | * 上記形式の情報に加え,棋譜再生や表示に必要な情報を持った形式をJSONで表し,これを流通用の **JSON棋譜フォーマット(JKF)** とする.
12 | * JKFが一般的になれば,棋譜再生を行うアプリケーションの作成が容易になる.
13 | * 一例として,JKFを元に棋譜再生を行うJKFPlayerクラス(JavaScript)を提供する.
14 | * KIF, KI2, CSA各形式のパーサ(JavaScript)を用意し,JKFへ変換できるようにする.
15 | * これはパーサジェネレータによるパーサである.つまり, **KIF, KI2, CSA形式のEBNF表現** を提供している.これは将棋界において画期的である.
16 |
17 | ### 棋譜形式の比較
18 |
19 | | フォーマット | KIF | KI2 | CSA | JKF |
20 | | --- | --- | --- | --- | --- |
21 | | 0) 1) 元座標(from) | ○ | △(要相対逆算) | ○ | ○ |
22 | | 1) 取った駒(capture) | × | × | × | ○ |
23 | | 1) 2) 成り(promote) | ○(不成なし) | ○ | △ | ○ |
24 | | 2) 相対情報(relative) | △ | ○ | △ | ○ |
25 | | 2) 同〜(same) | ○ | ○ | × | ○ |
26 | | 手番(color) | × | ○ | ○ | ○ |
27 | | 消費時間(time) | ○ | × | ○ | ○ |
28 |
29 | ○=棋譜だけで可能 △=現在の局面を見れば可能 ×=不可能か,以前の局面を見れば可能
30 |
31 | * 0: 局面を1手進めるために必要な情報
32 | * 1: 局面を1手戻すために必要な情報
33 | * 2: 可読棋譜にするために必要な情報
34 | * 相対情報: 上下左右など,同じ座標に複数の駒が移動できる場合に駒を区別するための情報.
35 | * 相対逆算: 局面と座標と相対情報から,その位置に移動する駒を特定すること.
36 |
37 | ## 形式の定義
38 | 以下で定義される形式をVersion 1.0とします.1.x台では後方互換性を保つ変更のみを採用します. ([TypeDoc](https://apps.81.la/json-kifu-format/docs/modules.html) も参照)
39 |
40 | ### JSONの形式 (Version 1.0)
41 | [./src/Formats.ts](./src/Formats.ts)にある内容です."?"はない場合があるという意味です.小文字で始まる`型名`は組み込み型です.
42 |
43 | * JKFが持つフィールドの定義
44 | * header `string=>string` ヘッダ情報.キーはKI2,KIF等の日本語のものに準ずる.(例: "場所", "先手")
45 | * initial? 初期局面(なければ平手)
46 | * preset `InitialPresetString` 手合名
47 | * data? 初期局面データ(手合名がOTHERの時に使用)
48 | * color: `Color` 初手の手番
49 | * board: `以下[][]` board[x-1][y-1]に(x,y)の駒情報.駒がない場合は空オブジェクト`{}`
50 | * color?: `Color` 先手/後手
51 | * kind?: `string` 駒の種類
52 | * hands: `(string=>number)[]` 駒種がkey, 枚数がvalueの連想配列.0番目が先手,1番目が後手の持駒
53 | * moves `MoveFormat[]` n番目はn手目の棋譜(0番目は初期局面のコメント用)
54 |
55 | 上の定義で使うオブジェクトの補助定義は次の通り.
56 |
57 | * `MoveFormat` 指し手を表す
58 | * comments? `string[]` コメント
59 | * move? 駒の動き
60 | * color `Color` 先手/後手
61 | * from? `PlaceFormat` 移動元 打った場合はなし
62 | * to `PlaceFormat` 移動先
63 | * piece `string` 駒の種類(`FU` `KY`等のCSA形式)
64 | * same? `boolean` 直前と同じ場合
65 | * promote? `Bool` 成るかどうか true:成, false:不成, 無いかnull:どちらでもない
66 | * capture? `string` 取った駒の種類
67 | * relative? `RelativeString` 相対情報
68 | * time? 消費時間
69 | * now `TimeFormat` 1手
70 | * total `TimeFormat` 合計
71 | * special? `string` 特殊棋譜(CSAのTORYO, CHUDAN等)
72 | * forks? `MoveFormat[][]` 任意の長さの分岐を任意個格納する.分岐の初手はこのforksを持つ棋譜の代替の手とする(次の手ではなく)
73 | * `TimeFormat` 時間を表す
74 | * h? `Integer` 時
75 | * m `Integer` 分
76 | * s `Integer` 秒
77 | * `PlaceFormat` 座標を表す
78 | * x `Integer` 1から9
79 | * y `Integer` 一から九
80 |
81 | エイリアスは次の通り.
82 |
83 | * `Color = number` 陣営を表す. 先手: 0, 後手: 1
84 | * `RelativeString = string` 以下の文字列を連結したもの
85 | * 左, 直, 右: それぞれL, C, R(Left, Center/Choku, Right)
86 | * 上, 寄, 引: それぞれU, M, D(Up, Middle, Down)
87 | * 打: H(Hit (本来はDrop))
88 | * `InitialPresetString = string` 平手,香落ち等KIFでサポートされている手合情報
89 | * HIRATE: 平手
90 | * KY: 香落ち
91 | * KY_R: 右香落ち
92 | * KA: 角落ち
93 | * HI: 飛車落ち
94 | * HIKY: 飛香落ち
95 | * 2: 二枚落ち
96 | * 3: 三枚落ち
97 | * 4: 四枚落ち
98 | * 5: 五枚落ち
99 | * 5_L: 左五枚落ち
100 | * 6: 六枚落ち
101 | * 7_L: 左七枚落ち
102 | * 7_R: 右七枚落ち
103 | * 8: 八枚落ち
104 | * 10: 十枚落ち
105 | * OTHER: その他
106 |
107 | ### 文字コード
108 | JSONで一般的なUTF-8を使用するものとします.
109 |
110 | ### JSON Schemaによる定義
111 | [JSON Schema](https://json-schema.org/) のバージョン 2020-12 による定義が [./specification/json-kifu-format.schema.json](./specification/json-kifu-format.schema.json) にあります。
112 | また、 `npm run schema:compile` によりこのJSON Schemaファイル自体の検証を、 `npm run schema:validate` により [specification/files/](specification/files/) 以下にあるJKFファイルのJSON Schemaファイルに対する検証を行います。
113 |
114 | ## JKFの例
115 | [.test/](./test/) 以下にも例が載っています.
116 |
117 | ### 通常
118 | ```json
119 | {
120 | "header": {
121 | "先手": "na2hiro",
122 | "後手": "うひょ"
123 | },
124 | "moves": [
125 | {},
126 | {"move":{"from":{"x":7,"y":7},"to":{"x":7,"y":6},"color":0,"piece":"FU"}},
127 | {"move":{"from":{"x":3,"y":3},"to":{"x":3,"y":4},"color":1,"piece":"FU"}},
128 | {"move":{"from":{"x":8,"y":8},"to":{"x":2,"y":2},"color":0,"piece":"KA","capture":"KA","promote":false}},
129 | {"move":{"from":{"x":3,"y":1},"to":{"x":2,"y":2},"color":1,"piece":"GI","capture":"KA","same":true}},
130 | {"move":{"to":{"x":4,"y":5},"color":0,"piece":"KA"}},
131 |
132 | {"special": "CHUDAN"}
133 | ]
134 | }
135 | ```
136 |
137 | ### 分岐
138 | ```json
139 | {
140 | "header": {},
141 | "moves": [
142 | {"comments":["分岐の例"]},
143 | {"move":{"from":{"x":7,"y":7},"to":{"x":7,"y":6},"color":0,"piece":"FU"}},
144 | {"move":{"from":{"x":3,"y":3},"to":{"x":3,"y":4},"color":1,"piece":"FU"}, "comments":["次の手で二種類が考えられる:7七桂か2二角成である.","2二角成を選ぶと筋違い角となる."]},
145 | {"move":{"from":{"x":8,"y":9},"to":{"x":7,"y":7},"color":0,"piece":"KE"}, "forks":[
146 | [
147 | {"move":{"from":{"x":8,"y":8},"to":{"x":2,"y":2},"color":0,"piece":"KA","capture":"KA","promote":false}},
148 | {"move":{"from":{"x":3,"y":1},"to":{"x":2,"y":2},"color":1,"piece":"GI","capture":"KA","same":true}},
149 | {"move":{"to":{"x":4,"y":5},"color":0,"piece":"KA"}}
150 | ]
151 | ]},
152 | {"move":{"from":{"x":2,"y":2},"to":{"x":7,"y":7},"color":1,"piece":"KA","capture":"KE","promote":true,"same":true}},
153 | {"move":{"from":{"x":8,"y":8},"to":{"x":7,"y":7},"color":0,"piece":"KA","capture":"UM","same":true}},
154 | {"move":{"to":{"x":3,"y":3},"color":1,"piece":"KE","relative":"H"}}
155 | ]
156 | }
157 | ```
158 |
159 | ### 駒落ち
160 | ```json
161 | {
162 | "header": {},
163 | "initial": {"preset": "6"},
164 | "moves": [
165 | {},
166 | {"move":{"from":{"x":5,"y":1},"to":{"x":4,"y":2},"color":1,"piece":"OU"}},
167 | {"move":{"from":{"x":7,"y":7},"to":{"x":7,"y":6},"color":0,"piece":"FU"}},
168 | {"move":{"from":{"x":6,"y":1},"to":{"x":7,"y":2},"color":1,"piece":"KI"}}
169 | ]
170 | }
171 | ```
172 | ### 初形変則
173 | ```json
174 | {
175 | "header": {},
176 | "initial": {
177 | "preset": "OTHER",
178 | "data": {
179 | "board": [
180 | [{"color":1, "kind":"KY"}, { },{"color":1, "kind":"FU"}, {}, {}, {}, {"color":0, "kind":"FU"}, { }, {"color":0, "kind":"KY"}],
181 | [{"color":1, "kind":"KE"}, {"color":1, "kind":"KA"},{"color":1, "kind":"FU"}, {}, {}, {}, { }, {"color":0, "kind":"HI"}, {"color":0, "kind":"KE"}],
182 | [{"color":1, "kind":"GI"}, { },{"color":1, "kind":"FU"}, {}, {}, {}, {"color":0, "kind":"FU"}, { }, {"color":0, "kind":"GI"}],
183 | [{"color":1, "kind":"KI"}, { },{"color":1, "kind":"FU"}, {}, {}, {}, {"color":0, "kind":"FU"}, { }, {"color":0, "kind":"KI"}],
184 | [{"color":1, "kind":"OU"}, { },{"color":1, "kind":"FU"}, {}, {}, {}, {"color":0, "kind":"FU"}, { }, {"color":0, "kind":"OU"}],
185 | [{"color":1, "kind":"KI"}, { },{"color":1, "kind":"FU"}, {}, {}, {}, {"color":0, "kind":"FU"}, { }, {"color":0, "kind":"KI"}],
186 | [{"color":1, "kind":"GI"}, { },{"color":1, "kind":"FU"}, {}, {}, {}, { }, { }, {"color":0, "kind":"GI"}],
187 | [{"color":1, "kind":"KE"}, {"color":1, "kind":"HI"},{"color":1, "kind":"FU"}, {}, {}, {}, {"color":0, "kind":"FU"}, {"color":0, "kind":"KA"}, {"color":0, "kind":"KE"}],
188 | [{"color":1, "kind":"KY"}, { },{"color":1, "kind":"FU"}, {}, {}, {}, {"color":0, "kind":"FU"}, { }, {"color":0, "kind":"KY"}]
189 | ],
190 | "color": 0,
191 | "hands":[
192 | {"FU":0,"KY":0,"KE":0,"GI":0,"KI":0,"KA":0,"HI":0},
193 | {"FU":0,"KY":0,"KE":0,"GI":0,"KI":0,"KA":0,"HI":0}
194 | ]
195 | }
196 | },
197 | "moves": [
198 | {"comments": ["飛車角先落ち."]},
199 | {"move":{"from":{"x":2,"y":8},"to":{"x":2,"y":3},"color":0,"piece":"HI","promote":true,"capture":"FU"}}
200 | ]
201 | }
202 | ```
203 |
204 | ## プログラム
205 | ### ブラウザ向け
206 |
207 | [Releases](https://github.com/na2hiro/json-kifu-format/releases) からどうぞ.`json-kifu-format-*.*.*.min.js`を読み込むと,`JSONKifuFormat` が使えるようになります.
208 |
209 | ### node.js用
210 | ```shell
211 | $ npm install json-kifu-format
212 | ```
213 |
214 | exportされているクラス群は次の通りです
215 |
216 | * `Parsers`
217 | * `parseKIF`: KIFをJSON形式に一対一変換するパーサ
218 | * `parseKI2`: KI2をJSON形式に一対一変換するパーサ
219 | * `parseCSA`: CSA(V1, V2, V2.1, V2.2)をJSON形式に一対一変換するパーサ
220 | * `Normalizer`: {KIF/KI2/CSA}と同等の情報しか持たないJKFを完全なJKFに変換するプログラム
221 | * `JKFPlayer`: JKFを扱う棋譜再生盤の例
222 |
223 | ## 開発環境
224 | ```shell
225 | $ nvm use && nvm i && npm i
226 | ```
227 |
228 | 上記コマンドを実行することで開発に必要なパッケージをインストールできます.
229 |
230 | * [na2hiro/Shogi.js](https://github.com/na2hiro/Shogi.js): 将棋の盤駒を扱うライブラリ
231 | * [PEG.js](http://pegjs.majda.cz/): パーサジェネレータ
232 | * [TypeScript](https://www.typescriptlang.org/)
233 | * [Webpack](https://webpack.js.org/)
234 | * [Jest](https://jestjs.io/)
235 | * [ESLint](https://eslint.org/)
236 |
237 | ### コマンド
238 |
239 | ```shell
240 | $ npm run build
241 | $ npm run build:watch
242 | $ npm run build:analyze
243 | ```
244 |
245 | ビルドが走ります.`build:watch`の場合,変更されるたびにビルドが走ります.`build:analyze`の場合,バンドルの大きさの可視化ができます.
246 |
247 | ```shell
248 | $ npm run test:watch
249 | ```
250 |
251 | コンソールでテスト結果が表示されます.コードの変更が保存されるたびに必要なテストが再実行されるため,実装が既存の有効なテストを壊してないか簡単に確認できます.
252 |
253 | ```shell
254 | $ npm run test
255 | ```
256 |
257 | 全てのテストが走るとともにカバレッジレポートが表示されます.`coverage/lcov-report/index.html`では,行ごとのカバレッジを確認できます.追加されたコードのブランチカバレッジが100%になるようにしてください.push時にチェックされ満たしていなければ却下されるはずです.
258 |
259 | ```shell
260 | $ npm run lint
261 | ```
262 |
263 | コードの品質が検査されます.エラーがあればそれに従い直してください.push前にもチェックされます.
264 |
265 | ```shell
266 | $ npm run lint:fix
267 | ```
268 |
269 | 自動的に修正可能な問題(インデント等)を直してくれます.
270 |
271 | ## バグ報告、機能要望等
272 |
273 | [issues](https://github.com/na2hiro/json-kifu-format/issues) 参照.
274 | 読み込みエラーとなる棋譜があったら教えて下さい.
275 |
276 | ## 参考文献
277 |
278 | * [CSA標準棋譜ファイル形式](http://www.computer-shogi.org/protocol/record_v22.html)
279 | * [shogi-format](https://code.google.com/p/shogi-format/): こちらは昔自ら提案したもの.大風呂敷だったため挫折しました.今回はより小さく洗練された形式を目指しており,また実装を用意し実用第一で進めていきます.
280 | * [棋譜の表記方法](http://www.shogi.or.jp/faq/kihuhyouki.html): 相対情報の書き方
281 | * [棋譜の形式について - 将棋の棋譜でーたべーす](http://wiki.optus.nu/shogi/index.php?post=%B4%FD%C9%E8%A4%CE%B7%C1%BC%B0%A4%CB%A4%C4%A4%A4%A4%C6): 出回っている棋譜形式のまとめ
282 |
283 | ## ライセンス
284 |
285 | MIT License (see [./LICENSE.txt](./LICENSE.txt))
286 |
287 | ## Workflows (it's for me)
288 |
289 | ### Publish
290 | 1. Bump version using `bump` action
291 | 2. Create a new release with the new version
292 | 3. Check an email about publish result
293 |
--------------------------------------------------------------------------------
/example/csa-player.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
46 |
--------------------------------------------------------------------------------
/example/jkf-player.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
46 |
--------------------------------------------------------------------------------
/example/ki2-player.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
47 |
--------------------------------------------------------------------------------
/example/kif-player.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
46 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transform: {
3 | "^.+\\.tsx?$": "ts-jest",
4 | "^.+\\.pegjs$": "/test/pegjs-jest.js",
5 | },
6 | testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
7 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
8 | coverageThreshold: {
9 | global: {
10 | branches: -6,
11 | functions: -1,
12 | lines: -3,
13 | statements: -5,
14 | },
15 | },
16 | collectCoverage: true,
17 | collectCoverageFrom: [
18 | "src/**",
19 | "!**/__tests__/**",
20 | "!**/*.d.ts",
21 | "!src/main.ts",
22 | "!src/peg/**",
23 | ],
24 | snapshotSerializers: ["./test/board-serializer.js"],
25 | };
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "json-kifu-format",
3 | "version": "1.3.1",
4 | "description": "JSON棋譜フォーマット(JKF)の定義とその関連ライブラリ",
5 | "main": "dist/json-kifu-format.js",
6 | "types": "dist/src/main.d.ts",
7 | "directories": {
8 | "test": "test"
9 | },
10 | "scripts": {
11 | "build": "webpack --mode=production",
12 | "build:analyze": "npm run build -- --env analyze",
13 | "build:watch": "webpack --watch",
14 | "lint": "eslint ./ && prettier --check .",
15 | "lint:fix": "eslint ./ --fix && prettier --write .",
16 | "test": "jest",
17 | "test:watch": "jest --watch",
18 | "typecheck": "tsc --project tsconfig.typecheck.json --noEmit",
19 | "prepublishOnly": "npm run build",
20 | "docs": "typedoc --exclude '**/__tests__/**/*' src/main.ts",
21 | "deploy:ghpages": "rm -rf ./public && mkdir -p ./public && npm run docs && mv ./docs ./public/",
22 | "schema:compile": "ajv compile --spec=draft2020 --strict-tuples=false -s specification/json-kifu-format.schema.json",
23 | "schema:validate": "ajv validate --spec=draft2020 --strict-tuples=false -s specification/json-kifu-format.schema.json -d 'specification/files/*.json'"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/na2hiro/json-kifu-format.git"
28 | },
29 | "keywords": [
30 | "shogi",
31 | "json",
32 | "kifu",
33 | "format",
34 | "jkf"
35 | ],
36 | "author": "na2hiro",
37 | "license": "MIT",
38 | "bugs": {
39 | "url": "https://github.com/na2hiro/json-kifu-format/issues"
40 | },
41 | "homepage": "https://github.com/na2hiro/json-kifu-format#readme",
42 | "devDependencies": {
43 | "@types/jest": "^28.1.4",
44 | "@types/node": "^13.1.1",
45 | "@typescript-eslint/eslint-plugin": "^5.28.0",
46 | "@typescript-eslint/parser": "^5.28.0",
47 | "ajv-cli": "^5.0.0",
48 | "clean-webpack-plugin": "^4.0.0",
49 | "eslint": "^8.17.0",
50 | "eslint-config-prettier": "^8.5.0",
51 | "eslint-plugin-jest": "^26.5.3",
52 | "iconv-lite": "^0.6.3",
53 | "jest": "^28.1.2",
54 | "jschardet": "^1.3.0",
55 | "pegjs": "^0.10.0",
56 | "pegjs-loader": "^0.5.6",
57 | "pre-push": "^0.1.1",
58 | "prettier": "^2.7.1",
59 | "ts-jest": "^28.0.5",
60 | "ts-loader": "^4.5.0",
61 | "typedoc": "^0.22.11",
62 | "typescript": "^4.0.0",
63 | "webpack": "^5.65.0",
64 | "webpack-bundle-analyzer": "^4.5.0",
65 | "webpack-cli": "^4.9.1",
66 | "webpack-merge": "^5.8.0"
67 | },
68 | "dependencies": {
69 | "shogi.js": "^2.0.5"
70 | },
71 | "pre-push": [
72 | "lint",
73 | "test"
74 | ]
75 | }
76 |
--------------------------------------------------------------------------------
/specification/files/ryuou201409020101.jkf.json:
--------------------------------------------------------------------------------
1 | ../../test/files/jkf/ryuou201409020101.jkf
--------------------------------------------------------------------------------
/specification/files/same_move_minimal.jkf.json:
--------------------------------------------------------------------------------
1 | ../../test/files/jkf/same_move_minimal.jkf
--------------------------------------------------------------------------------
/specification/json-kifu-format.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "https://github.com/na2hiro/json-kifu-format",
4 | "$ref": "#/$defs/record",
5 | "$defs": {
6 | "record": {
7 | "title": "json-kifu-format",
8 | "type": "object",
9 | "properties": {
10 | "header": {
11 | "description": "ヘッダ情報。キーはKI2,KIF等の日本語のものに準ずる.(例: \"場所\", \"先手\")",
12 | "$ref": "#/$defs/header"
13 | },
14 | "initial": {
15 | "description": "初期局面。nullの場合は平手を表す",
16 | "$ref": "#/$defs/initial"
17 | },
18 | "moves": {
19 | "description": "n番目はn手目の棋譜(0番目は初期局面のコメント用)。",
20 | "$ref": "#/$defs/moves"
21 | }
22 | },
23 | "required": ["header", "moves"],
24 | "additionalProperties": false
25 | },
26 | "initial": {
27 | "title": "初期状態",
28 | "type": ["object", "null"],
29 | "properties": {
30 | "preset": {
31 | "description": "手合名",
32 | "$ref": "#/$defs/preset"
33 | },
34 | "data": {
35 | "description": "初期局面データ。手合名がOTHERの時に使用する",
36 | "$ref": "#/$defs/initialData"
37 | }
38 | },
39 | "required": ["preset"],
40 | "additionalProperties": false
41 | },
42 | "header": {
43 | "title": "ヘッダ情報",
44 | "type": "object",
45 | "additionalProperties": {
46 | "type": "string"
47 | }
48 | },
49 | "preset": {
50 | "title": "手合情報",
51 | "description": "KIFでサポートされている手合情報。順に、平手、香落ち、右香落ち、角落ち、飛車落ち、飛香落ち、二枚落ち、三枚落ち、四枚落ち、五枚落ち、左五枚落ち、六枚落ち、左七枚落ち、右七枚落ち、八枚落ち、十枚落ち、その他、を表す。",
52 | "enum": [
53 | "HIRATE",
54 | "KY",
55 | "KY_R",
56 | "KA",
57 | "HI",
58 | "HIKY",
59 | "2",
60 | "3",
61 | "4",
62 | "5",
63 | "5_L",
64 | "6",
65 | "7_L",
66 | "7_R",
67 | "8",
68 | "10",
69 | "OTHER"
70 | ]
71 | },
72 | "initialData": {
73 | "title": "初期局面のデータ",
74 | "type": ["object", "null"],
75 | "properties": {
76 | "color": {
77 | "description": "初手の手番",
78 | "$ref": "#/$defs/color"
79 | },
80 | "board": {
81 | "description": "盤上の駒の配置",
82 | "$ref": "#/$defs/initialBoard"
83 | },
84 | "hands": {
85 | "description": "0番目が先手,1番目が後手の持駒",
86 | "$ref": "#/$defs/hands"
87 | }
88 | },
89 | "required": ["color", "board", "hands"]
90 | },
91 | "initialBoard": {
92 | "title": "初期状態の盤面",
93 | "oneOf": [
94 | {
95 | "description": "駒がない場合",
96 | "type": "object",
97 | "additionalProperties": false
98 | },
99 | {
100 | "description": "盤上の駒。board[x-1][y-1]に(x,y)の駒情報",
101 | "$ref": "#/$defs/board"
102 | }
103 | ]
104 | },
105 | "board": {
106 | "title": "盤",
107 | "type": "array",
108 | "items": {
109 | "$ref": "#/$defs/column"
110 | }
111 | },
112 | "column": {
113 | "title": "盤の列",
114 | "type": "array",
115 | "items": {
116 | "$ref": "#/$defs/square"
117 | }
118 | },
119 | "square": {
120 | "title": "盤の升",
121 | "type": "object",
122 | "properties": {
123 | "color": {
124 | "description": "先手/後手",
125 | "oneOf": [
126 | {
127 | "$ref": "#/$defs/color"
128 | },
129 | {
130 | "type": "null"
131 | }
132 | ]
133 | },
134 | "kind": {
135 | "description": "駒の種類",
136 | "oneOf": [
137 | {
138 | "$ref": "#/$defs/kind"
139 | },
140 | {
141 | "type": "null"
142 | }
143 | ]
144 | }
145 | },
146 | "additionalProperties": false
147 | },
148 | "moves": {
149 | "title": "指し手のリスト",
150 | "type": "array",
151 | "prefixItems": [
152 | {
153 | "$ref": "#/$defs/initialComment"
154 | }
155 | ],
156 | "items": {
157 | "$ref": "#/$defs/moveWithForksAndInfo"
158 | }
159 | },
160 | "initialComment": {
161 | "title": "初期局面のコメント",
162 | "type": "object",
163 | "properties": {
164 | "comment": {
165 | "type": "string"
166 | }
167 | }
168 | },
169 | "hands": {
170 | "title": "先後双方の持駒",
171 | "type": "array",
172 | "minItems": 2,
173 | "maxItems": 2,
174 | "items": {
175 | "description": "駒種がkey, 枚数がvalueの連想配列",
176 | "$ref": "#/$defs/hand"
177 | }
178 | },
179 | "hand": {
180 | "title": "持駒",
181 | "type": "object",
182 | "additionalProperties": false,
183 | "properties": {
184 | "FU": {
185 | "type": "integer"
186 | },
187 | "KY": {
188 | "type": "integer"
189 | },
190 | "KE": {
191 | "type": "integer"
192 | },
193 | "GI": {
194 | "type": "integer"
195 | },
196 | "KI": {
197 | "type": "integer"
198 | },
199 | "KA": {
200 | "type": "integer"
201 | },
202 | "HI": {
203 | "type": "integer"
204 | },
205 | "OU": {
206 | "type": "integer"
207 | },
208 | "TO": {
209 | "type": "integer"
210 | },
211 | "NY": {
212 | "type": "integer"
213 | },
214 | "NK": {
215 | "type": "integer"
216 | },
217 | "NG": {
218 | "type": "integer"
219 | },
220 | "UM": {
221 | "type": "integer"
222 | },
223 | "RY": {
224 | "type": "integer"
225 | }
226 | }
227 | },
228 | "color": {
229 | "title": "陣営",
230 | "description": "先手:0、後手:1",
231 | "enum": [0, 1]
232 | },
233 | "moveWithForksAndInfo": {
234 | "title": "指し手",
235 | "type": "object",
236 | "properties": {
237 | "comments": {
238 | "description": "コメント",
239 | "$ref": "#/$defs/comments"
240 | },
241 | "move": {
242 | "description": "駒の動き",
243 | "$ref": "#/$defs/move"
244 | },
245 | "time": {
246 | "description": "消費時間",
247 | "$ref": "#/$defs/consumption"
248 | },
249 | "special": {
250 | "description": "特殊棋譜。それぞれの意味はCSA標準棋譜ファイル形式 (V2.2) に準拠する。",
251 | "$ref": "#/$defs/special"
252 | },
253 | "forks": {
254 | "description": "分岐。任意の長さの分岐を任意個格納する.分岐の初手はこのforksを持つ棋譜の代替の手とする(次の手ではなく)",
255 | "$ref": "#/$defs/forks"
256 | }
257 | },
258 | "additionalProperties": false
259 | },
260 | "special": {
261 | "title": "特殊棋譜",
262 | "enum": [
263 | "TORYO",
264 | "CHUDAN",
265 | "SENNICHITE",
266 | "TIME_UP",
267 | "ILLEGAL_MOVE",
268 | "+ILLEGAL_ACTION",
269 | "-ILLEGAL_ACTION",
270 | "JISHOGI",
271 | "KACHI",
272 | "HIKIWAKE",
273 | "MATTA",
274 | "TSUMI",
275 | "FUZUMI",
276 | "ERROR",
277 | null
278 | ]
279 | },
280 | "forks": {
281 | "title": "分岐群",
282 | "type": ["array", "null"],
283 | "items": {
284 | "$ref": "#/$defs/fork"
285 | }
286 | },
287 | "fork": {
288 | "title": "分岐",
289 | "type": "array",
290 | "items": {
291 | "$ref": "#/$defs/moveWithForksAndInfo"
292 | }
293 | },
294 | "consumption": {
295 | "title": "消費時間",
296 | "type": ["object", "null"],
297 | "properties": {
298 | "now": {
299 | "description": "1手",
300 | "$ref": "#/$defs/time"
301 | },
302 | "total": {
303 | "description": "合計",
304 | "$ref": "#/$defs/time"
305 | }
306 | },
307 | "required": ["now", "total"],
308 | "additionalProperties": false
309 | },
310 | "move": {
311 | "title": "指し手",
312 | "type": ["object", "null"],
313 | "properties": {
314 | "color": {
315 | "description": "先手/後手",
316 | "$ref": "#/$defs/color"
317 | },
318 | "from": {
319 | "description": "移動元。打った場合はなし",
320 | "oneOf": [
321 | {
322 | "$ref": "#/$defs/placeFormat"
323 | },
324 | {
325 | "type": "null"
326 | }
327 | ]
328 | },
329 | "to": {
330 | "description": "移動先",
331 | "$ref": "#/$defs/placeFormat"
332 | },
333 | "piece": {
334 | "description": "駒の種類",
335 | "$ref": "#/$defs/kind"
336 | },
337 | "same": {
338 | "description": "直前と同じ場合",
339 | "type": ["boolean", "null"]
340 | },
341 | "promote": {
342 | "description": "成るかどうか。true:成, false:不成, 無いかnull:どちらでもない",
343 | "type": ["boolean", "null"]
344 | },
345 | "capture": {
346 | "description": "取った駒の種類",
347 | "oneOf": [
348 | {
349 | "$ref": "#/$defs/capturablekind"
350 | },
351 | {
352 | "type": "null"
353 | }
354 | ]
355 | },
356 | "relative": {
357 | "description": "相対情報",
358 | "oneOf": [
359 | {
360 | "$ref": "#/$defs/relative"
361 | },
362 | {
363 | "type": "null"
364 | }
365 | ]
366 | }
367 | },
368 | "required": ["color", "to", "piece"],
369 | "additionalProperties": false
370 | },
371 | "comments": {
372 | "title": "コメント",
373 | "oneOf": [
374 | {
375 | "type": "null"
376 | },
377 | {
378 | "type": "array",
379 | "items": {
380 | "type": "string"
381 | }
382 | }
383 | ]
384 | },
385 | "relative": {
386 | "title": "相対情報",
387 | "type": "string",
388 | "pattern": "[LCR]|[UMD]|[LCR][UD]|[LR]M|H"
389 | },
390 | "time": {
391 | "title": "時間",
392 | "type": "object",
393 | "properties": {
394 | "h": {
395 | "description": "時",
396 | "type": ["integer", "null"]
397 | },
398 | "m": {
399 | "description": "分",
400 | "type": "integer"
401 | },
402 | "s": {
403 | "description": "秒",
404 | "type": "integer"
405 | }
406 | },
407 | "required": ["m", "s"],
408 | "additionalProperties": false
409 | },
410 | "placeFormat": {
411 | "title": "座標",
412 | "type": "object",
413 | "properties": {
414 | "x": {
415 | "description": "1から9",
416 | "type": "integer"
417 | },
418 | "y": {
419 | "description": "一から九",
420 | "type": "integer"
421 | }
422 | },
423 | "required": ["x", "y"],
424 | "additionalProperties": false
425 | },
426 | "kind": {
427 | "title": "駒の種類",
428 | "description": "CSA標準棋譜ファイル形式 (V2.2) の表記を使用している。",
429 | "enum": [
430 | "FU",
431 | "KY",
432 | "KE",
433 | "GI",
434 | "KI",
435 | "KA",
436 | "HI",
437 | "OU",
438 | "TO",
439 | "NY",
440 | "NK",
441 | "NG",
442 | "UM",
443 | "RY"
444 | ]
445 | },
446 | "capturablekind": {
447 | "title": "持駒に加えることができる駒の種類",
448 | "description": "kindの部分集合である。",
449 | "enum": ["FU", "KY", "KE", "GI", "KI", "KA", "HI", "TO", "NY", "NK", "NG", "UM", "RY"]
450 | }
451 | }
452 | }
453 |
--------------------------------------------------------------------------------
/src/Formats.ts:
--------------------------------------------------------------------------------
1 | /** @license
2 | * JSON Kifu Format
3 | * Copyright (c) 2014 na2hiro (https://github.com/na2hiro)
4 | * This software is released under the MIT License.
5 | * http://opensource.org/licenses/mit-license.php
6 | */
7 | import {Color} from "shogi.js";
8 | import {Kind} from "shogi.js/dist/src/Kind";
9 |
10 | /**
11 | * Kifu format
12 | */
13 | export interface IJSONKifuFormat {
14 | header: {[index: string]: string};
15 | initial?: {
16 | preset: string;
17 | data?: IStateFormat;
18 | };
19 | moves: IMoveFormat[];
20 | }
21 |
22 | /**
23 | * Game state
24 | */
25 | export interface IStateFormat {
26 | color: Color;
27 | board: IPiece[][];
28 | hands: Array<{
29 | [index in Extract]: number;
30 | }>;
31 | }
32 |
33 | /**
34 | * Piece
35 | * TODO: Make color and kind nonnull
36 | */
37 | export interface IPiece {
38 | color?: Color;
39 | kind?: Kind;
40 | }
41 |
42 | /**
43 | * Abstract Move
44 | */
45 | export interface IMoveMoveFormat {
46 | color: Color;
47 | from?: IPlaceFormat;
48 | to?: IPlaceFormat;
49 | piece: Kind;
50 | same?: boolean;
51 | promote?: boolean;
52 | capture?: Kind;
53 | relative?: string;
54 | }
55 |
56 | /**
57 | * Move
58 | */
59 | export interface IMoveFormat {
60 | comments?: string[];
61 | move?: IMoveMoveFormat;
62 | time?: {
63 | now: ITimeFormat;
64 | total: ITimeFormat;
65 | };
66 | special?: string;
67 | forks?: IMoveFormat[][];
68 | }
69 |
70 | /**
71 | * Elapsed Time
72 | */
73 | export interface ITimeFormat {
74 | h?: number;
75 | m: number;
76 | s: number;
77 | }
78 |
79 | /**
80 | * Position on boards
81 | */
82 | export interface IPlaceFormat {
83 | x: number;
84 | y: number;
85 | }
86 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/normalizerTest.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`module Normalizer normalizeKIF same at first fork 1`] = `{"header":{},"moves":[{},{"move":{"from":{"x":7,"y":7},"piece":"FU","to":{"x":7,"y":6},"color":0}},{"move":{"from":{"x":3,"y":3},"piece":"FU","to":{"x":3,"y":4},"color":1}},{"move":{"from":{"x":2,"y":7},"piece":"FU","to":{"x":2,"y":6},"color":0},"forks":[[{"move":{"from":{"x":8,"y":8},"piece":"KA","promote":true,"to":{"x":2,"y":2},"color":0,"capture":"KA"}},{"move":{"from":{"x":3,"y":1},"piece":"GI","same":true,"color":1,"to":{"x":2,"y":2},"capture":"UM"},"forks":[[{"move":{"from":{"x":8,"y":2},"piece":"HI","same":true,"color":1,"to":{"x":2,"y":2},"capture":"UM"}},{"special":"JISHOGI"}]]},{"special":"CHUDAN"}]]},{"special":"TORYO"}]}`;
4 |
--------------------------------------------------------------------------------
/src/__tests__/fileTest.ts:
--------------------------------------------------------------------------------
1 | import {readdirSync, readFile} from "fs";
2 | import {decode} from "iconv-lite";
3 | import {detect} from "jschardet";
4 | import JKFPlayer from "../jkfplayer";
5 |
6 | const SJIS = "cp932";
7 | const FILES_DIR = __dirname + "/../../test/files";
8 |
9 | makeTest("kif", (filename) => (filename.match(/u$/) ? loadUTF : loadSJIS));
10 | makeTest("ki2", (filename) => (filename.match(/u$/) ? loadUTF : loadSJIS));
11 | makeTest("csa", () => loadAuto);
12 | makeTest("jkf", () => loadUTF);
13 |
14 | function makeTest(ext, fileNameToLoadFunc) {
15 | describe(ext + " file", () => {
16 | const files = readdirSync(FILES_DIR + "/" + ext);
17 | for (const file of files) {
18 | ((filename) => {
19 | if (!filename.match(new RegExp("\\." + ext + "u?$"))) {
20 | return;
21 | }
22 | // eslint-disable-next-line jest/valid-title
23 | it(filename, () => {
24 | return new Promise((done) => {
25 | try {
26 | fileNameToLoadFunc(filename)(
27 | FILES_DIR + "/" + ext + "/" + filename,
28 | (err, data) => {
29 | if (err) {
30 | done(err);
31 | return;
32 | }
33 | data = data.replace(/^\ufeff/, ""); // delete BOM
34 | try {
35 | const player: JKFPlayer =
36 | JKFPlayer["parse" + ext.toUpperCase()](data);
37 | player.goto(Infinity);
38 | player.goto(0);
39 | expect(player.kifu).toMatchSnapshot();
40 | done();
41 | } catch (e) {
42 | done(e);
43 | }
44 | }
45 | );
46 | } catch (e) {
47 | done(e);
48 | }
49 | });
50 | });
51 | })(file);
52 | }
53 | });
54 | }
55 | function loadUTF(filename, cb) {
56 | readFile(filename, {encoding: "utf-8"}, cb);
57 | }
58 | function loadSJIS(filename, cb) {
59 | readFile(filename, (err, data) => {
60 | if (err) {
61 | cb(err);
62 | return;
63 | }
64 | cb(null, decode(data, SJIS));
65 | });
66 | }
67 | function loadAuto(filename, cb) {
68 | readFile(filename, (err, data) => {
69 | if (err) {
70 | cb(err);
71 | return;
72 | }
73 | const {encoding} = detect(data);
74 | cb(null, decode(data, encoding));
75 | });
76 | }
77 |
--------------------------------------------------------------------------------
/src/__tests__/normalizerTest-relative.ts:
--------------------------------------------------------------------------------
1 | import {Color, Piece, Shogi} from "shogi.js";
2 | import {IMoveMoveFormat} from "../Formats";
3 | import {JKFPlayer} from "../main";
4 | import {addRelativeInformation, filterMovesByRelatives} from "../normalizer";
5 |
6 | const emptyLine = [{}, {}, {}, {}, {}, {}, {}, {}, {}];
7 | describe("module Normalizer (relative information test from https://www.shogi.or.jp/faq/kihuhyouki.html)", () => {
8 | describe("上寄引", () => {
9 | run("KI", [93, 72], 82, ["上", "寄"]);
10 | run("KI", [43, 31], 32, ["上", "引"]);
11 | run("KI", [56, 45], 55, ["上", "寄"]);
12 | run("GI", [89, 77], 88, ["上", "引"]);
13 | run("GI", [49, 27], 38, ["上", "引"]);
14 | });
15 | describe("左右", () => {
16 | run("KI", [92, 72], 81, ["左", "右"]);
17 | run("KI", [32, 12], 22, ["左", "右"]);
18 | run("GI", [65, 45], 56, ["左", "右"]);
19 | run("KI", [89, 79], 78, ["左", "直"]);
20 | run("GI", [39, 29], 38, ["直", "右"]);
21 | });
22 | describe("3枚", () => {
23 | run("KI", [63, 53, 43], 52, ["左", "直", "右"]);
24 | run("TO", [79, 89, 99, 98, 87], 88, ["右", "直", "左上", "寄", "引"]);
25 | run("GI", [29, 17, 39, 37], 28, ["直", "右", "左上", "左引"]);
26 | });
27 | describe("竜", () => {
28 | run("RY", [91, 84], 82, ["引", "上"]);
29 | run("RY", [23, 52], 43, ["寄", "引"]);
30 | run("RY", [55, 15], 35, ["左", "右"]);
31 | run("RY", [99, 89], 88, ["左", "右"]);
32 | run("RY", [28, 19], 17, ["左", "右"]);
33 | });
34 | describe("馬", () => {
35 | run("UM", [91, 81], 82, ["左", "右"]);
36 | run("UM", [95, 63], 85, ["寄", "引"]);
37 | run("UM", [11, 34], 12, ["引", "上"]);
38 | run("UM", [99, 59], 77, ["左", "右"]);
39 | run("UM", [47, 18], 29, ["左", "右"]);
40 | });
41 | // eslint-disable-next-line jest/no-identical-title
42 | describe("馬", () => {
43 | run("UM", [93, 74], 92, ["左", "右"]);
44 | });
45 | });
46 |
47 | function run(piece, coords, toCoord, expected) {
48 | describe(`${coords.join(", ")} ${piece} to ${toCoord}`, () => {
49 | const shogi = new Shogi({
50 | preset: "OTHER",
51 | data: {
52 | board: [
53 | emptyLine,
54 | emptyLine,
55 | emptyLine,
56 | emptyLine,
57 | emptyLine,
58 | emptyLine,
59 | emptyLine,
60 | emptyLine,
61 | emptyLine,
62 | ],
63 | color: 0,
64 | hands: [
65 | {FU: 18, KY: 4, KE: 4, GI: 4, KI: 4, KA: 2, HI: 2},
66 | {FU: 0, KY: 0, KE: 0, GI: 0, KI: 0, KA: 0, HI: 0},
67 | ],
68 | },
69 | });
70 | shogi.editMode(true);
71 | coords.forEach((coord) => {
72 | const {x, y} = coordToXY(coord);
73 | if (Piece.isPromoted(piece)) {
74 | shogi.drop(x, y, Piece.unpromote(piece), Color.Black);
75 | shogi.flip(x, y);
76 | } else {
77 | shogi.drop(x, y, piece, Color.Black);
78 | }
79 | });
80 | shogi.editMode(false);
81 | const to = coordToXY(toCoord);
82 | const movesTo = shogi.getMovesTo(to.x, to.y, piece, Color.Black);
83 | coords.forEach((coord, i) => {
84 | it(`${coord}`, () => {
85 | const from = coordToXY(coord);
86 | const move: IMoveMoveFormat = {
87 | from,
88 | to,
89 | color: Color.Black,
90 | piece,
91 | };
92 | addRelativeInformation(shogi, move);
93 | expect((move.relative || "").split("").map(JKFPlayer.relativeToKan).join("")).toBe(
94 | expected[i]
95 | );
96 |
97 | const found = filterMovesByRelatives(move.relative, Color.Black, movesTo);
98 | expect(found).toHaveLength(1);
99 | expect(found[0].from).toEqual(from);
100 | });
101 | });
102 | });
103 | }
104 | function coordToXY(coord: number) {
105 | return {
106 | x: Math.floor(coord / 10),
107 | y: coord % 10,
108 | };
109 | }
110 |
--------------------------------------------------------------------------------
/src/jkfplayer.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-empty */
2 | /** @license
3 | * JSON Kifu Format
4 | * Copyright (c) 2014 na2hiro (https://github.com/na2hiro)
5 | * This software is released under the MIT License.
6 | * http://opensource.org/licenses/mit-license.php
7 | */
8 |
9 | import {Color, Piece, Shogi} from "shogi.js";
10 | import {IJSONKifuFormat, IMoveFormat, IMoveMoveFormat, IStateFormat} from "./Formats";
11 | import {canPromote, normalizeCSA, normalizeKI2, normalizeKIF, normalizeMinimal} from "./normalizer";
12 | import {parseCSA, parseKI2, parseKIF} from "./peg/parsers";
13 |
14 | export default class JKFPlayer {
15 | public static debug = false;
16 | public static logs = [];
17 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
18 | public static log(...lg: any[]) {
19 | if (JKFPlayer.debug) {
20 | console.log(lg);
21 | } else {
22 | JKFPlayer.logs.push(lg);
23 | }
24 | }
25 | public static parse(kifu: string, filename?: string) {
26 | if (filename) {
27 | const tmp = filename.split(".");
28 | const ext = tmp[tmp.length - 1].toLowerCase();
29 | switch (ext) {
30 | case "jkf":
31 | return JKFPlayer.parseJKF(kifu);
32 | case "kif":
33 | case "kifu":
34 | return JKFPlayer.parseKIF(kifu);
35 | case "ki2":
36 | case "ki2u":
37 | return JKFPlayer.parseKI2(kifu);
38 | case "csa":
39 | return JKFPlayer.parseCSA(kifu);
40 | }
41 | }
42 | // 不明
43 | try {
44 | return JKFPlayer.parseJKF(kifu);
45 | } catch (e) {
46 | JKFPlayer.log("failed to parse as jkf", e);
47 | }
48 | try {
49 | return JKFPlayer.parseKIF(kifu);
50 | } catch (e) {
51 | JKFPlayer.log("failed to parse as kif", e);
52 | }
53 | try {
54 | return JKFPlayer.parseKI2(kifu);
55 | } catch (e) {
56 | JKFPlayer.log("failed to parse as ki2", e);
57 | }
58 | try {
59 | return JKFPlayer.parseCSA(kifu);
60 | } catch (e) {
61 | JKFPlayer.log("failed to parse as csa", e);
62 | }
63 | throw new Error("JKF, KIF, KI2, CSAいずれの形式でも失敗しました");
64 | }
65 | public static parseJKF(kifu: string) {
66 | JKFPlayer.log("parseJKF", kifu);
67 | return new JKFPlayer(JSON.parse(kifu));
68 | }
69 | public static parseKIF(kifu: string) {
70 | JKFPlayer.log("parseKIF", kifu);
71 | return new JKFPlayer(normalizeKIF(parseKIF(JKFPlayer.addLastNewLine(kifu))));
72 | }
73 | public static parseKI2(kifu: string) {
74 | JKFPlayer.log("parseKI2", kifu);
75 | return new JKFPlayer(normalizeKI2(parseKI2(JKFPlayer.addLastNewLine(kifu))));
76 | }
77 | public static parseCSA(kifu: string) {
78 | JKFPlayer.log("parseCSA", kifu);
79 | return new JKFPlayer(normalizeCSA(parseCSA(JKFPlayer.addLastNewLine(kifu))));
80 | }
81 | public static addLastNewLine(kifu: string) {
82 | if (kifu.substr(kifu.length - 1) === "\n") {
83 | return kifu;
84 | }
85 | return kifu + "\n";
86 | }
87 | public static numToZen(n: number) {
88 | return "0123456789"[n];
89 | }
90 | public static numToKan(n: number) {
91 | return "〇一二三四五六七八九"[n];
92 | }
93 | public static kindToKan(kind: string): string {
94 | return {
95 | FU: "歩",
96 | KY: "香",
97 | KE: "桂",
98 | GI: "銀",
99 | KI: "金",
100 | KA: "角",
101 | HI: "飛",
102 | OU: "玉",
103 | TO: "と",
104 | NY: "成香",
105 | NK: "成桂",
106 | NG: "成銀",
107 | UM: "馬",
108 | RY: "龍",
109 | }[kind];
110 | }
111 | public static relativeToKan(relative: string) {
112 | return {
113 | L: "左",
114 | C: "直",
115 | R: "右",
116 | U: "上",
117 | M: "寄",
118 | D: "引",
119 | H: "打",
120 | }[relative];
121 | }
122 | public static specialToKan(special: string) {
123 | return (
124 | {
125 | TORYO: "投了",
126 | CHUDAN: "中断",
127 | SENNICHITE: "千日手",
128 | TIME_UP: "時間切れ",
129 | ILLEGAL_MOVE: "反則負け",
130 | "+ILLEGAL_ACTION": "先手反則負け",
131 | "-ILLEGAL_ACTION": "後手反則負け",
132 | JISHOGI: "持将棋",
133 | KACHI: "勝ち宣言",
134 | HIKIWAKE: "引き分け宣言",
135 | MATTA: "待った",
136 | TSUMI: "詰",
137 | FUZUMI: "不詰",
138 | ERROR: "エラー",
139 | }[special] || special
140 | );
141 | }
142 | public static moveToReadableKifu(mv: IMoveFormat): string {
143 | if (mv.special) {
144 | return JKFPlayer.specialToKan(mv.special);
145 | }
146 | const move = mv.move;
147 | let ret = move.color === Color.Black ? "☗" : "☖";
148 | if (move.same) {
149 | ret += "同 ";
150 | } else {
151 | ret += JKFPlayer.numToZen(move.to.x) + JKFPlayer.numToKan(move.to.y);
152 | }
153 | ret += JKFPlayer.kindToKan(move.piece);
154 | if (move.relative) {
155 | ret += move.relative.split("").map(JKFPlayer.relativeToKan).join("");
156 | }
157 | if (move.promote != null) {
158 | ret += move.promote ? "成" : "不成";
159 | }
160 | return ret;
161 | }
162 | public static doMove(shogi: Shogi, move: IMoveMoveFormat) {
163 | if (!move) {
164 | return;
165 | }
166 | if (move.from) {
167 | shogi.move(move.from.x, move.from.y, move.to.x, move.to.y, move.promote);
168 | } else {
169 | shogi.drop(
170 | move.to.x,
171 | move.to.y,
172 | move.piece,
173 | typeof move.color !== "undefined" ? move.color : void 0
174 | );
175 | }
176 | }
177 | public static undoMove(shogi: Shogi, move: IMoveMoveFormat) {
178 | if (!move) {
179 | return;
180 | }
181 | if (move.from) {
182 | shogi.unmove(
183 | move.from.x,
184 | move.from.y,
185 | move.to.x,
186 | move.to.y,
187 | move.promote,
188 | move.capture
189 | );
190 | } else {
191 | shogi.undrop(move.to.x, move.to.y);
192 | }
193 | }
194 | public static getState(shogi: Shogi): IStateFormat {
195 | return {
196 | board: JKFPlayer.getBoardState(shogi),
197 | color: shogi.turn,
198 | hands: JKFPlayer.getHandsState(shogi),
199 | };
200 | }
201 |
202 | private static sameMoveMinimal(move1: IMoveMoveFormat, move2: IMoveMoveFormat) {
203 | return (
204 | move1.to.x === move2.to.x &&
205 | move1.to.y === move2.to.y &&
206 | (move1.from && move2.from
207 | ? move1.from.x === move2.from.x &&
208 | move1.from.y === move2.from.y &&
209 | move1.promote === move2.promote
210 | : move1.piece === move2.piece)
211 | );
212 | }
213 | private static getBoardState(shogi: Shogi) {
214 | const ret = [];
215 | for (let i = 1; i <= 9; i++) {
216 | const arr = [];
217 | for (let j = 1; j <= 9; j++) {
218 | const piece = shogi.get(i, j);
219 | arr.push(piece ? {color: piece.color, kind: piece.kind} : {});
220 | }
221 | ret.push(arr);
222 | }
223 | return ret;
224 | }
225 | private static getHandsState(shogi: Shogi) {
226 | return [shogi.getHandsSummary(Color.Black), shogi.getHandsSummary(Color.White)];
227 | }
228 |
229 | public shogi: Shogi;
230 | public kifu: IJSONKifuFormat;
231 | public tesuu: number;
232 | public forkPointers: Array<{te: number; forkIndex: number}> = [];
233 | private forks_ = null;
234 | private currentStream_: IMoveFormat[] = null;
235 | get forks(): Array<{te: number; moves: IMoveFormat[]}> {
236 | if (this.forks_ === null) {
237 | this.updateForksAndCurrentStream();
238 | }
239 | return this.forks_;
240 | }
241 | get currentStream(): IMoveFormat[] {
242 | if (this.currentStream_ === null) {
243 | this.updateForksAndCurrentStream();
244 | }
245 | return this.currentStream_;
246 | }
247 |
248 | constructor(kifu: IJSONKifuFormat) {
249 | this.shogi = new Shogi(kifu.initial || undefined);
250 | this.initialize(kifu);
251 | }
252 | public initialize(kifu: IJSONKifuFormat) {
253 | this.kifu = kifu;
254 | this.tesuu = 0;
255 | this.forkPointers = [];
256 | }
257 | // 1手進める
258 | public forward() {
259 | const nextMove = this.getMoveFormat(this.tesuu + 1);
260 | if (!nextMove) {
261 | return false;
262 | }
263 | this.tesuu++;
264 | const move = nextMove.move;
265 | if (!move) {
266 | return true;
267 | }
268 | JKFPlayer.log("forward", this.tesuu, move);
269 | this.doMove(move);
270 | return true;
271 | }
272 | // 1手戻す
273 | public backward() {
274 | if (this.tesuu <= 0) {
275 | return false;
276 | }
277 | const move = this.getMoveFormat(this.tesuu).move;
278 | JKFPlayer.log("backward", this.tesuu - 1, move);
279 | this.undoMove(move);
280 | this.tesuu--;
281 | this.forkPointers = this.forkPointers.filter((fork) => fork.te <= this.tesuu);
282 | this.updateForksAndCurrentStream();
283 | return true;
284 | }
285 | // tesuu手目へ行く
286 | public goto(tesuu: number | string) {
287 | if (typeof tesuu === "string") {
288 | const commaPos = tesuu.indexOf(",");
289 | if (commaPos > 0) {
290 | // Specify tesuu pointer
291 | const te = Number(tesuu.slice(0, commaPos));
292 | const destPointers = JSON.parse(tesuu.slice(commaPos + 1));
293 | let matchingSoFar = true;
294 | for (let i = 0; i < destPointers.length; i++) {
295 | const dest = destPointers[i];
296 | const current = this.forkPointers[i];
297 | if (
298 | matchingSoFar &&
299 | current &&
300 | current.te === dest.te &&
301 | current.forkIndex === dest.forkIndex
302 | ) {
303 | // We don't need to repeat the same forking as currently while it's matching
304 | continue;
305 | }
306 | matchingSoFar = false;
307 | this.goto(dest.te - 1);
308 | this.forkAndForward(dest.forkIndex);
309 | }
310 | // Rewind if current fork is deeper than the destination
311 | if (matchingSoFar && destPointers.length < this.forkPointers.length) {
312 | this.goto(this.forkPointers[destPointers.length].te - 1);
313 | }
314 | this.goto(te);
315 | return;
316 | }
317 | tesuu = Number(tesuu);
318 | }
319 | if (isNaN(tesuu)) {
320 | return;
321 | }
322 | let limit = 10000; // for safe
323 | if (this.tesuu < tesuu) {
324 | while (this.tesuu !== tesuu && this.forward() && limit-- > 0) {}
325 | } else {
326 | while (this.tesuu !== tesuu && this.backward() && limit-- > 0) {}
327 | }
328 | if (limit === 0) {
329 | throw new Error("tesuu overflows");
330 | }
331 | }
332 | // tesuu手前後に移動する
333 | public go(tesuu: number | string) {
334 | if (typeof tesuu === "string") {
335 | tesuu = Number(tesuu);
336 | }
337 | if (isNaN(tesuu)) {
338 | return;
339 | }
340 | this.goto(this.tesuu + tesuu);
341 | }
342 | // 現在の局面から別れた分岐のうちnum番目の変化へ1つ進む
343 | public forkAndForward(num: number | string): boolean {
344 | if (typeof num === "string") {
345 | num = parseInt(num, 10);
346 | }
347 | const forks = this.getMoveFormat(this.tesuu + 1).forks;
348 | if (!forks || forks.length <= num) {
349 | return false;
350 | }
351 | this.forkPointers.push({te: this.tesuu + 1, forkIndex: num});
352 | this.updateForksAndCurrentStream();
353 | return this.forward();
354 | }
355 |
356 | /**
357 | * Return a tesuu pointer string which can be used to call goto()
358 | */
359 | public getTesuuPointer(tesuu?: number): string {
360 | if (isNaN(tesuu)) {
361 | tesuu = this.tesuu;
362 | }
363 | return `${tesuu},${JSON.stringify(this.forkPointers.filter((p) => p.te <= tesuu))}`;
364 | }
365 | // TODO: Distinguish minimal move and full move
366 | // 現在の局面から新しいかもしれない手を1手動かす.
367 | // 必要フィールドは,指し: from, to, promote(成れる場合のみ).打ち: to, piece
368 | // 新しい手の場合,最終手であれば手を追加,そうでなければ分岐を追加
369 | // もしpromoteの可能性があればfalseを返して何もしない
370 | // 成功すればその局面に移動してtrueを返す.
371 | public inputMove(move: IMoveMoveFormat) {
372 | if (this.getMoveFormat().special) {
373 | throw new Error("終了局面へ棋譜を追加することは出来ません");
374 | }
375 | if (move.from != null && move.promote == null) {
376 | const piece = this.shogi.get(move.from.x, move.from.y);
377 | if (
378 | !Piece.isPromoted(piece.kind) &&
379 | Piece.canPromote(piece.kind) &&
380 | (canPromote(move.from, piece.color) || canPromote(move.to, piece.color))
381 | ) {
382 | return false;
383 | }
384 | }
385 | const nextMove = this.getMoveFormat(this.tesuu + 1);
386 | if (nextMove) {
387 | if (nextMove.move && JKFPlayer.sameMoveMinimal(nextMove.move, move)) {
388 | // 次の一手と一致
389 | this.forward();
390 | return true;
391 | }
392 | if (nextMove.forks) {
393 | for (let i = 0; i < nextMove.forks.length; i++) {
394 | const forkCand = nextMove.forks[i][0];
395 | if (
396 | forkCand &&
397 | forkCand.move &&
398 | JKFPlayer.sameMoveMinimal(forkCand.move, move)
399 | ) {
400 | // 分岐と一致
401 | this.forkAndForward(i);
402 | return true;
403 | }
404 | }
405 | }
406 | }
407 | this.doMove(move); // 動かしてみる(throwされうる)
408 | const newMove = {move};
409 | const addToFork = this.tesuu < this.getMaxTesuu();
410 | let next;
411 | if (addToFork) {
412 | // 最終手でなければ分岐に追加
413 | next = this.getMoveFormat(this.tesuu + 1);
414 | if (!next.forks) {
415 | next.forks = [];
416 | }
417 | next.forks.push([newMove]);
418 | } else {
419 | // 最終手に追加
420 | this.forks[this.forks.length - 1].moves.push(newMove);
421 | }
422 | this.updateForksAndCurrentStream();
423 | normalizeMinimal(this.kifu); // 復元
424 | this.undoMove(move);
425 | // 考え改めて再生
426 | if (addToFork) {
427 | this.forkAndForward(next.forks.length - 1);
428 | } else {
429 | this.forward();
430 | }
431 | return true;
432 | }
433 | // wrapper
434 | public getBoard(x: number, y: number): Piece {
435 | return this.shogi.get(x, y);
436 | }
437 | public getComments(tesuu: number = this.tesuu) {
438 | return this.getMoveFormat(tesuu).comments || [];
439 | }
440 | public getMove(tesuu: number = this.tesuu) {
441 | return this.getMoveFormat(tesuu).move;
442 | }
443 | public getReadableKifu(tesuu: number = this.tesuu): string {
444 | if (tesuu === 0) {
445 | return "開始局面";
446 | }
447 | return JKFPlayer.moveToReadableKifu(this.getMoveFormat(tesuu));
448 | }
449 | public getReadableForkKifu(tesuu: number = this.tesuu): string[] {
450 | return this.getNextFork(tesuu).map((fork) => JKFPlayer.moveToReadableKifu(fork[0]));
451 | }
452 | public getMaxTesuu() {
453 | return this.currentStream.length - 1;
454 | }
455 | public toJKF() {
456 | return JSON.stringify(this.kifu);
457 | }
458 | // jkf.initial.dataの形式を得る
459 | public getState() {
460 | return JKFPlayer.getState(this.shogi);
461 | }
462 | public getReadableKifuState(): Array<{kifu: string; forks: string[]; comments: string[]}> {
463 | const ret = [];
464 | for (let i = 0; i <= this.getMaxTesuu(); i++) {
465 | ret.push({
466 | comments: this.getComments(i),
467 | forks: this.getReadableForkKifu(i - 1),
468 | kifu: this.getReadableKifu(i),
469 | });
470 | }
471 | return ret;
472 | }
473 |
474 | private updateForksAndCurrentStream() {
475 | const forks: Array<{te: number; moves: IMoveFormat[]}> = [];
476 | let currentStream: IMoveFormat[] = [];
477 | let currentFork: IMoveFormat[] = this.kifu.moves;
478 | let tesuuOffset = 0;
479 | for (const pointer of this.forkPointers) {
480 | forks.push({te: tesuuOffset, moves: currentFork});
481 | currentStream = currentStream.concat(currentFork.slice(0, pointer.te - tesuuOffset));
482 | currentFork = currentFork[pointer.te - tesuuOffset].forks[pointer.forkIndex];
483 | tesuuOffset = pointer.te;
484 | }
485 | forks.push({te: tesuuOffset, moves: currentFork});
486 | this.forks_ = forks;
487 | this.currentStream_ = currentStream.concat(currentFork.slice());
488 | }
489 |
490 | // 現在の局面から分岐を遡った初手から,現在の局面からの本譜の中から棋譜を得る
491 | private getMoveFormat(tesuu: number = this.tesuu): IMoveFormat {
492 | return this.currentStream[tesuu];
493 | }
494 | private getNextFork(tesuu: number = this.tesuu) {
495 | const next = this.getMoveFormat(tesuu + 1);
496 | return next && next.forks ? next.forks : [];
497 | }
498 | private doMove(move: IMoveMoveFormat) {
499 | JKFPlayer.doMove(this.shogi, move);
500 | }
501 | private undoMove(move: IMoveMoveFormat) {
502 | JKFPlayer.undoMove(this.shogi, move);
503 | }
504 | }
505 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import * as Shogi from "shogi.js";
2 | import * as Formats from "./Formats";
3 | import JKFPlayer from "./jkfplayer";
4 | import * as Normalizer from "./normalizer";
5 | import * as Parsers from "./peg/parsers";
6 |
7 | export {Normalizer, JKFPlayer, Parsers, Formats, Shogi};
8 |
--------------------------------------------------------------------------------
/src/normalizer.ts:
--------------------------------------------------------------------------------
1 | /** @license
2 | * JSON Kifu Format
3 | * Copyright (c) 2014 na2hiro (https://github.com/na2hiro)
4 | * This software is released under the MIT License.
5 | * http://opensource.org/licenses/mit-license.php
6 | */
7 | import {Color, IMove, Piece, Shogi} from "shogi.js";
8 | import {IJSONKifuFormat, IMoveFormat, IMoveMoveFormat, IPlaceFormat, ITimeFormat} from "./Formats";
9 |
10 | export function canPromote(place: IPlaceFormat, color: Color) {
11 | return color === Color.Black ? place.y <= 3 : place.y >= 7;
12 | }
13 |
14 | // 最小形式の棋譜をnormalizeする
15 | // 最小形式: (指し) from, to, promote; (打ち) piece, to
16 | export function normalizeMinimal(obj: IJSONKifuFormat): IJSONKifuFormat {
17 | const shogi = new Shogi(obj.initial);
18 | normalizeMinimalMoves(shogi, obj.moves);
19 | return obj;
20 | }
21 | function normalizeMinimalMoves(shogi: Shogi, moves: IMoveFormat[], lastMove?: IMoveFormat) {
22 | for (let i = 0; i < moves.length; i++) {
23 | const last = i === 0 ? lastMove : moves[i - 1];
24 | const move = moves[i].move;
25 | if (!move) {
26 | continue;
27 | }
28 | // 手番
29 | move.color = shogi.turn;
30 | if (move.from) {
31 | // move
32 |
33 | // toからsame復元
34 | if (last && last.move && move.to.x === last.move.to.x && move.to.y === last.move.to.y) {
35 | move.same = true;
36 | }
37 |
38 | // capture復元
39 | addCaptureInformation(shogi, move);
40 |
41 | // piece復元
42 | if (!move.piece) {
43 | move.piece = shogi.get(move.from.x, move.from.y).kind;
44 | }
45 |
46 | // 不成復元
47 | if (!move.promote && !Piece.isPromoted(move.piece) && Piece.canPromote(move.piece)) {
48 | // 成ってない
49 | if (canPromote(move.to, shogi.turn) || canPromote(move.from, shogi.turn)) {
50 | move.promote = false;
51 | }
52 | }
53 | // relative復元
54 | addRelativeInformation(shogi, move);
55 |
56 | try {
57 | shogi.move(move.from.x, move.from.y, move.to.x, move.to.y, move.promote);
58 | } catch (e) {
59 | throw new Error(i + "手目で失敗しました: " + e);
60 | }
61 | } else {
62 | // drop
63 | if (shogi.getMovesTo(move.to.x, move.to.y, move.piece).length > 0) {
64 | move.relative = "H";
65 | }
66 | shogi.drop(move.to.x, move.to.y, move.piece);
67 | }
68 | }
69 | restoreColorOfIllegalAction(moves, shogi);
70 | for (let i = moves.length - 1; i >= 0; i--) {
71 | const move = moves[i].move;
72 | if (move) {
73 | if (move.from) {
74 | shogi.unmove(
75 | move.from.x,
76 | move.from.y,
77 | move.to.x,
78 | move.to.y,
79 | move.promote,
80 | move.capture
81 | );
82 | } else {
83 | shogi.undrop(move.to.x, move.to.y);
84 | }
85 | }
86 | const last = i <= 1 ? lastMove : moves[i - 1];
87 | if (moves[i].forks) {
88 | for (const fork of moves[i].forks) {
89 | normalizeMinimalMoves(shogi, fork, last);
90 | }
91 | }
92 | }
93 | }
94 | /**
95 | * Normalize JKF
96 | * @param obj JKF. TODO: Introduce a type which is not a normalized KIF
97 | */
98 | export function normalizeKIF(obj: IJSONKifuFormat): IJSONKifuFormat {
99 | // Kifu for iPhone bug
100 | if (obj.initial && obj.initial.preset === "HIRATE" && obj.initial.data) {
101 | obj.initial.preset = "OTHER";
102 | }
103 | const shogi = new Shogi(obj.initial || undefined);
104 | normalizeKIFMoves(shogi, obj.moves);
105 | return obj;
106 | }
107 | function normalizeKIFMoves(shogi: Shogi, moves: IMoveFormat[], lastMove?: IMoveFormat) {
108 | for (let i = 0; i < moves.length; i++) {
109 | const last = i === 0 ? lastMove : moves[i - 1];
110 | const move = moves[i].move;
111 | if (!move) {
112 | continue;
113 | }
114 | // 手番
115 | move.color = shogi.turn;
116 | if (move.from) {
117 | // move
118 |
119 | // sameからto復元
120 | if (move.same) {
121 | move.to = last.move.to;
122 | }
123 |
124 | // capture復元
125 | addCaptureInformation(shogi, move);
126 |
127 | // 不成復元
128 | if (!move.promote && !Piece.isPromoted(move.piece) && Piece.canPromote(move.piece)) {
129 | // 成ってない
130 | if (canPromote(move.to, shogi.turn) || canPromote(move.from, shogi.turn)) {
131 | move.promote = false;
132 | }
133 | }
134 | // relative復元
135 | addRelativeInformation(shogi, move);
136 |
137 | try {
138 | shogi.move(move.from.x, move.from.y, move.to.x, move.to.y, move.promote);
139 | } catch (e) {
140 | throw new Error(i + "手目で失敗しました: " + e);
141 | }
142 | } else {
143 | // drop
144 | if (shogi.getMovesTo(move.to.x, move.to.y, move.piece).length > 0) {
145 | move.relative = "H";
146 | }
147 | shogi.drop(move.to.x, move.to.y, move.piece);
148 | }
149 | }
150 | restoreColorOfIllegalAction(moves, shogi);
151 | for (let i = moves.length - 1; i >= 0; i--) {
152 | const move = moves[i].move;
153 | if (move) {
154 | if (move.from) {
155 | shogi.unmove(
156 | move.from.x,
157 | move.from.y,
158 | move.to.x,
159 | move.to.y,
160 | move.promote,
161 | move.capture
162 | );
163 | } else {
164 | shogi.undrop(move.to.x, move.to.y);
165 | }
166 | }
167 | const last = i === 0 ? lastMove : moves[i - 1]; // When first fork has fork, use lastMove of this fork
168 | if (moves[i].forks) {
169 | for (const fork of moves[i].forks) {
170 | normalizeKIFMoves(shogi, fork, last);
171 | }
172 | }
173 | }
174 | }
175 | export function normalizeKI2(obj: IJSONKifuFormat): IJSONKifuFormat {
176 | const shogi = new Shogi(obj.initial || undefined);
177 | normalizeKI2Moves(shogi, obj.moves);
178 | return obj;
179 | }
180 | function normalizeKI2Moves(shogi: Shogi, moves: IMoveFormat[], lastMove?: IMoveFormat) {
181 | for (let i = 0; i < moves.length; i++) {
182 | const last = i === 0 ? lastMove : moves[i - 1];
183 | const move = moves[i].move;
184 | if (!move) {
185 | continue;
186 | }
187 |
188 | // 手番
189 | move.color = shogi.turn;
190 | // 同からto復元
191 | if (move.same) {
192 | move.to = last.move.to;
193 | }
194 |
195 | // from復元
196 | const candMoves = shogi.getMovesTo(move.to.x, move.to.y, move.piece);
197 | if (move.relative === "H" || candMoves.length === 0) {
198 | // ok
199 | } else if (candMoves.length === 1) {
200 | move.from = candMoves[0].from;
201 | } else {
202 | // 相対逆算
203 | const moveAns = filterMovesByRelatives(move.relative, shogi.turn, candMoves);
204 | if (moveAns.length !== 1) {
205 | throw new Error("相対情報が不完全で複数の候補があります");
206 | }
207 | move.from = moveAns[0].from;
208 | }
209 |
210 | if (move.from) {
211 | // move
212 |
213 | // capture復元
214 | addCaptureInformation(shogi, move);
215 |
216 | try {
217 | shogi.move(move.from.x, move.from.y, move.to.x, move.to.y, move.promote);
218 | } catch (e) {
219 | throw new Error(i + "手目で失敗しました: " + e);
220 | }
221 | } else {
222 | // drop
223 | shogi.drop(move.to.x, move.to.y, move.piece);
224 | }
225 | }
226 | restoreColorOfIllegalAction(moves, shogi);
227 | for (let i = moves.length - 1; i >= 0; i--) {
228 | const move = moves[i].move;
229 | if (!move) {
230 | continue;
231 | }
232 | if (move.from) {
233 | shogi.unmove(
234 | move.from.x,
235 | move.from.y,
236 | move.to.x,
237 | move.to.y,
238 | move.promote,
239 | move.capture
240 | );
241 | } else {
242 | shogi.undrop(move.to.x, move.to.y);
243 | }
244 | const last = i <= 1 ? lastMove : moves[i - 1];
245 | if (moves[i].forks) {
246 | for (const fork of moves[i].forks) {
247 | normalizeKI2Moves(shogi, fork, last);
248 | }
249 | }
250 | }
251 | }
252 | export function normalizeCSA(obj: IJSONKifuFormat): IJSONKifuFormat {
253 | restorePreset(obj);
254 | const shogi = new Shogi(obj.initial || undefined);
255 | for (let i = 0; i < obj.moves.length; i++) {
256 | restoreTotalTime(obj.moves[i].time, i >= 2 ? obj.moves[i - 2].time : void 0);
257 | const move = obj.moves[i].move;
258 | if (!move) {
259 | continue;
260 | }
261 | // 手番
262 | move.color = shogi.turn;
263 | if (move.from) {
264 | // move
265 |
266 | // same復元
267 | if (
268 | i > 0 &&
269 | obj.moves[i - 1].move &&
270 | obj.moves[i - 1].move.to.x === move.to.x &&
271 | obj.moves[i - 1].move.to.y === move.to.y
272 | ) {
273 | move.same = true;
274 | }
275 |
276 | // capture復元
277 | addCaptureInformation(shogi, move);
278 | if (Piece.isPromoted(move.piece)) {
279 | // 成かも
280 | const from = shogi.get(move.from.x, move.from.y);
281 | if (from.kind !== move.piece) {
282 | move.piece = from.kind;
283 | move.promote = true;
284 | }
285 | } else if (Piece.canPromote(move.piece)) {
286 | // 不成かも
287 | if (canPromote(move.to, shogi.turn) || canPromote(move.from, shogi.turn)) {
288 | move.promote = false;
289 | }
290 | }
291 | // relative復元
292 | addRelativeInformation(shogi, move);
293 |
294 | try {
295 | shogi.move(move.from.x, move.from.y, move.to.x, move.to.y, move.promote);
296 | } catch (e) {
297 | throw new Error(i + "手目で失敗しました: " + e);
298 | }
299 | } else {
300 | // drop
301 | if (shogi.getMovesTo(move.to.x, move.to.y, move.piece).length > 0) {
302 | move.relative = "H";
303 | }
304 | shogi.drop(move.to.x, move.to.y, move.piece);
305 | }
306 | }
307 | return obj;
308 | }
309 | // export for testing
310 | export function addRelativeInformation(shogi: Shogi, move: IMoveMoveFormat) {
311 | const moveVectors = shogi
312 | .getMovesTo(move.to.x, move.to.y, move.piece)
313 | .map((mv) => flipVector(shogi.turn, spaceshipVector(mv.to, mv.from)));
314 | if (moveVectors.length >= 2) {
315 | const realVector = flipVector(shogi.turn, spaceshipVector(move.to, move.from));
316 | move.relative = (() => {
317 | // 上下方向唯一
318 | if (moveVectors.filter((mv) => mv.y === realVector.y).length === 1) {
319 | return YToUMD(realVector.y);
320 | }
321 | // 左右方向唯一
322 | if (moveVectors.filter((mv) => mv.x === realVector.x).length === 1) {
323 | if ((move.piece === "UM" || move.piece === "RY") && realVector.x === 0) {
324 | // 直はだめ
325 | return XToLCR(moveVectors.filter((mv) => mv.x < 0).length === 0 ? -1 : 1);
326 | } else {
327 | return XToLCR(realVector.x);
328 | }
329 | }
330 | // 上下も左右も他の駒がいる
331 | if (realVector.x === 0) {
332 | // 直は上下とは使わない
333 | return XToLCR(realVector.x);
334 | }
335 | return XToLCR(realVector.x) + YToUMD(realVector.y);
336 | })();
337 | }
338 | }
339 | function addCaptureInformation(shogi: Shogi, move: IMoveMoveFormat) {
340 | const to = shogi.get(move.to.x, move.to.y);
341 | if (to) {
342 | move.capture = to.kind;
343 | }
344 | }
345 |
346 | function flipVector(color: Color, vector: {x: number; y: number}) {
347 | return color === Color.Black ? vector : {x: -vector.x, y: -vector.y};
348 | }
349 | function spaceship(a: number, b: number) {
350 | return a === b ? 0 : a > b ? 1 : -1;
351 | }
352 | function spaceshipVector(a: {x: number; y: number}, b: {x: number; y: number}) {
353 | return {x: spaceship(a.x, b.x), y: spaceship(a.y, b.y)};
354 | }
355 | // yの段から移動した場合の相対情報
356 | function YToUMD(y: number) {
357 | return y === 0 ? "M" : y > 0 ? "D" : "U";
358 | }
359 | // xの行から移動した場合の相対情報
360 | function XToLCR(x: number) {
361 | return x === 0 ? "C" : x > 0 ? "R" : "L";
362 | }
363 | export function filterMovesByRelatives(relative: string, color: Color, moves: IMove[]): IMove[] {
364 | let candidates = moves
365 | .map((move) => ({
366 | move,
367 | vec: flipVector(color, {x: move.to.x - move.from.x, y: move.to.y - move.from.y}),
368 | }))
369 | .filter(({vec}) =>
370 | relative.split("").every((rel) => moveSatisfiesRelative(rel, color, vec))
371 | );
372 | if (relative.indexOf("C") >= 0) {
373 | // 直は上がる時だけ
374 | candidates = candidates.filter(({vec}) => vec.y < 0);
375 | }
376 | if (relative.indexOf("L") >= 0) {
377 | let min = Infinity;
378 | candidates.forEach(({vec}) => (min = Math.min(min, vec.x)));
379 | return candidates.filter(({vec}) => vec.x === min).map(({move}) => move);
380 | }
381 | if (relative.indexOf("R") >= 0) {
382 | let max = -Infinity;
383 | candidates.forEach(({vec}) => (max = Math.max(max, vec.x)));
384 | return candidates.filter(({vec}) => vec.x === max).map(({move}) => move);
385 | }
386 | return candidates.map(({move}) => move);
387 | }
388 | function moveSatisfiesRelative(
389 | relative: string,
390 | color: Color,
391 | vec: {x: number; y: number}
392 | ): boolean {
393 | switch (relative) {
394 | case "U":
395 | return vec.y < 0;
396 | case "M":
397 | return vec.y === 0;
398 | case "D":
399 | return vec.y > 0;
400 | case "C":
401 | return vec.x === 0;
402 | case "L":
403 | case "R":
404 | // 移動先ではなく他の駒との相対的な位置関係のため後で判定
405 | return true;
406 | }
407 | }
408 | // CSA等で盤面みたままで表現されているものをpresetに戻せれば戻す
409 | function restorePreset(obj: IJSONKifuFormat) {
410 | if (!obj.initial || obj.initial.preset !== "OTHER") {
411 | return;
412 | }
413 | const kinds = ["FU", "KY", "KE", "GI", "KI", "KA", "HI"];
414 | for (let i = 0; i < 2; i++) {
415 | for (const kind of kinds) {
416 | if (obj.initial.data.hands[i][kind] !== 0) {
417 | return;
418 | }
419 | }
420 | }
421 | const hirate = [
422 | [
423 | {color: Color.White, kind: "KY"},
424 | {},
425 | {color: Color.White, kind: "FU"},
426 | {},
427 | {},
428 | {},
429 | {color: Color.Black, kind: "FU"},
430 | {},
431 | {color: Color.Black, kind: "KY"},
432 | ],
433 | [
434 | {color: Color.White, kind: "KE"},
435 | {color: Color.White, kind: "KA"},
436 | {color: Color.White, kind: "FU"},
437 | {},
438 | {},
439 | {},
440 | {color: Color.Black, kind: "FU"},
441 | {color: Color.Black, kind: "HI"},
442 | {color: Color.Black, kind: "KE"},
443 | ],
444 | [
445 | {color: Color.White, kind: "GI"},
446 | {},
447 | {color: Color.White, kind: "FU"},
448 | {},
449 | {},
450 | {},
451 | {color: Color.Black, kind: "FU"},
452 | {},
453 | {color: Color.Black, kind: "GI"},
454 | ],
455 | [
456 | {color: Color.White, kind: "KI"},
457 | {},
458 | {color: Color.White, kind: "FU"},
459 | {},
460 | {},
461 | {},
462 | {color: Color.Black, kind: "FU"},
463 | {},
464 | {color: Color.Black, kind: "KI"},
465 | ],
466 | [
467 | {color: Color.White, kind: "OU"},
468 | {},
469 | {color: Color.White, kind: "FU"},
470 | {},
471 | {},
472 | {},
473 | {color: Color.Black, kind: "FU"},
474 | {},
475 | {color: Color.Black, kind: "OU"},
476 | ],
477 | [
478 | {color: Color.White, kind: "KI"},
479 | {},
480 | {color: Color.White, kind: "FU"},
481 | {},
482 | {},
483 | {},
484 | {color: Color.Black, kind: "FU"},
485 | {},
486 | {color: Color.Black, kind: "KI"},
487 | ],
488 | [
489 | {color: Color.White, kind: "GI"},
490 | {},
491 | {color: Color.White, kind: "FU"},
492 | {},
493 | {},
494 | {},
495 | {color: Color.Black, kind: "FU"},
496 | {},
497 | {color: Color.Black, kind: "GI"},
498 | ],
499 | [
500 | {color: Color.White, kind: "KE"},
501 | {color: Color.White, kind: "HI"},
502 | {color: Color.White, kind: "FU"},
503 | {},
504 | {},
505 | {},
506 | {color: Color.Black, kind: "FU"},
507 | {color: Color.Black, kind: "KA"},
508 | {color: Color.Black, kind: "KE"},
509 | ],
510 | [
511 | {color: Color.White, kind: "KY"},
512 | {},
513 | {color: Color.White, kind: "FU"},
514 | {},
515 | {},
516 | {},
517 | {color: Color.Black, kind: "FU"},
518 | {},
519 | {color: Color.Black, kind: "KY"},
520 | ],
521 | ];
522 | const diff = [];
523 | for (let i = 0; i < 9; i++) {
524 | for (let j = 0; j < 9; j++) {
525 | if (!samePiece(obj.initial.data.board[i][j], hirate[i][j])) {
526 | diff.push("" + (i + 1) + (j + 1));
527 | }
528 | }
529 | }
530 |
531 | const presets = {};
532 | presets[""] = "HIRATE";
533 | presets["11"] = "KY";
534 | presets["91"] = "KY_R";
535 | presets["22"] = "KA";
536 | presets["82"] = "HI";
537 | presets["1182"] = "HIKY";
538 | presets["2282"] = "2";
539 | presets["228291"] = "3";
540 | presets["11228291"] = "4";
541 | presets["1122818291"] = "5";
542 | presets["1121228291"] = "5_L";
543 | presets["112122818291"] = "6";
544 | presets["11212231818291"] = "7_L";
545 | presets["11212271818291"] = "7_R";
546 | presets["1121223171818291"] = "8";
547 | presets["11212231416171818291"] = "10";
548 |
549 | const preset = presets[diff.sort().join("")];
550 | if (preset === "HIRATE") {
551 | if (obj.initial.data.color === Color.Black) {
552 | obj.initial.preset = "HIRATE";
553 | delete obj.initial.data;
554 | }
555 | } else if (preset && obj.initial.data.color === Color.White) {
556 | obj.initial.preset = preset;
557 | delete obj.initial.data;
558 | }
559 | }
560 | function samePiece(p1, p2) {
561 | return (
562 | (typeof p1.color === "undefined" && typeof p2.color === "undefined") ||
563 | (typeof p1.color !== "undefined" &&
564 | typeof p2.color !== "undefined" &&
565 | p1.color === p2.color &&
566 | p1.kind === p2.kind)
567 | );
568 | }
569 | function restoreColorOfIllegalAction(moves: IMoveFormat[], shogi: Shogi) {
570 | if (moves.length >= 1 && moves[moves.length - 1].special === "ILLEGAL_ACTION") {
571 | moves[moves.length - 1].special = (shogi.turn ? "+" : "-") + "ILLEGAL_ACTION";
572 | }
573 | }
574 | function restoreTotalTime(
575 | time: {now: ITimeFormat; total: ITimeFormat},
576 | lastTime: {now: ITimeFormat; total: ITimeFormat} = {
577 | now: {m: 0, s: 0},
578 | total: {h: 0, m: 0, s: 0},
579 | }
580 | ) {
581 | if (!time) {
582 | return;
583 | }
584 | time.total = {
585 | h: (time.now.h || 0) + lastTime.total.h,
586 | m: time.now.m + lastTime.total.m,
587 | s: time.now.s + lastTime.total.s,
588 | };
589 | time.total.m += Math.floor(time.total.s / 60);
590 | time.total.s = time.total.s % 60;
591 | time.total.h += Math.floor(time.total.m / 60);
592 | time.total.m = time.total.m % 60;
593 | }
594 |
--------------------------------------------------------------------------------
/src/peg/__tests__/__snapshots__/kifParserTest.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`kif-parser same at first fork 1`] = `{"header":{},"moves":[{},{"move":{"piece":"FU","to":{"x":7,"y":6},"from":{"x":7,"y":7}},"time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}}},{"move":{"piece":"FU","to":{"x":3,"y":4},"from":{"x":3,"y":3}},"time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}}},{"move":{"piece":"FU","to":{"x":2,"y":6},"from":{"x":2,"y":7}},"time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}},"forks":[[{"move":{"piece":"KA","to":{"x":2,"y":2},"promote":true,"from":{"x":8,"y":8}},"time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}}},{"move":{"piece":"GI","same":true,"from":{"x":3,"y":1}},"time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}},"forks":[[{"move":{"piece":"HI","same":true,"from":{"x":8,"y":2}},"time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}}},{"special":"CHUDAN","time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}}}]]},{"special":"CHUDAN","time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}}}]]},{"special":"CHUDAN","time":{"now":{"m":0,"s":0},"total":{"h":0,"m":0,"s":0}}}]}`;
4 |
--------------------------------------------------------------------------------
/src/peg/__tests__/csaParserTest.ts:
--------------------------------------------------------------------------------
1 | import {parseCSA as parse} from "../parsers";
2 |
3 | describe("V2 parser for V2 formats", () => {
4 | it("supports header with $ markups", () => {
5 | expect(
6 | parse(
7 | `V2.2
8 | N+sente
9 | N-gote
10 | $SITE:将棋会館
11 | $START_TIME:2015/08/04 13:00:00
12 | PI
13 | +
14 | +7776FU
15 | -3334FU
16 | +7978GI
17 | -2288UM
18 | %TORYO
19 | `
20 | ).header
21 | ).toMatchInlineSnapshot(`
22 | Object {
23 | "先手": "sente",
24 | "場所": "将棋会館",
25 | "後手": "gote",
26 | "開始日時": "2015/08/04 13:00:00",
27 | }
28 | `);
29 | });
30 | it.todo("separator -- csa file can contain multiple kifus");
31 | });
32 |
33 | describe.each([
34 | {v: "V1", prefix: ""},
35 | {v: "V2", prefix: "V2.2\n"},
36 | ])("$v parser for V1 formats", ({prefix}) => {
37 | it("matches snapshot for simple case", () => {
38 | expect(
39 | parse(`${prefix}PI
40 | +
41 | +7776FU
42 | -3334FU
43 | +8822UM
44 | -3122GI
45 | +0045KA
46 | `)
47 | ).toMatchSnapshot();
48 | });
49 | it("supports comments", () => {
50 | expect(
51 | parse(
52 | `${prefix}PI
53 | +
54 | '開始時コメント
55 | +7776FU
56 | '初手コメント
57 | '初手コメント2
58 | -3334FU
59 | +8822UM
60 | `
61 | ).moves
62 | ).toMatchSnapshot();
63 | });
64 | it("supports special moves", () => {
65 | expect(
66 | parse(`${prefix}PI
67 | +
68 | +7776FU
69 | -3334FU
70 | +7978GI
71 | -2288UM
72 | %TORYO
73 | `).moves
74 | ).toMatchSnapshot();
75 | });
76 | it("supports multiple statements with commas", () => {
77 | expect(
78 | parse(
79 | `${prefix}PI
80 | +
81 | +7776FU,T12,-3334FU,T2
82 | +8822UM,T100
83 | -3122GI,T1
84 | +0045KA,T0
85 | `
86 | ).moves
87 | ).toMatchSnapshot();
88 | });
89 | it("supports time", () => {
90 | expect(
91 | parse(
92 | `${prefix}PI
93 | +
94 | +7776FU
95 | T12
96 | -3334FU
97 | T2
98 | +8822UM
99 | T100
100 | -3122GI
101 | T1
102 | +0045KA
103 | T0
104 | `
105 | ).moves
106 | ).toMatchSnapshot();
107 | });
108 | it("supports header", () => {
109 | expect(
110 | parse(`${prefix}N+sente
111 | N-gote
112 | PI
113 | +
114 | +7776FU
115 | -3334FU
116 | +7978GI
117 | -2288UM
118 | %TORYO
119 | `).header
120 | ).toMatchSnapshot();
121 | });
122 | describe("initial field", () => {
123 | it("supports 平手初期局面 (PI)", () => {
124 | expect(
125 | parse(
126 | `${prefix}PI82HI22KA91KY81KE21KE11KY
127 | -
128 | -5142OU
129 | +7776FU
130 | -3122GI
131 | +8866KA
132 | -7182GI
133 | `
134 | ).initial
135 | ).toMatchSnapshot();
136 | });
137 | it("supports 一括表現", () => {
138 | expect(
139 | parse(
140 | `${prefix}P1 * * -GI-KI-OU-KI-GI * *
141 | P2 * * * * * * * * *
142 | P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
143 | P4 * * * * * * * * *
144 | P5 * * * * * * * * *
145 | P6 * * * * * * * * *
146 | P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
147 | P8 * +KA * * * * * +HI *
148 | P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
149 | -
150 | -5142OU
151 | +7776FU
152 | -3122GI
153 | +8866KA
154 | -7182GI
155 | `
156 | ).initial
157 | ).toMatchSnapshot();
158 | });
159 | it("supports 駒別単独表現", () => {
160 | expect(
161 | parse(
162 | `${prefix}P-11OU21FU22FU23FU24FU25FU26FU27FU28FU29FU
163 | P+00HI00HI00KY00KY00KY00KY
164 | P-00GI00GI00GI00GI00KE00KE00KE00KE
165 | +
166 | +0013KY
167 | -0012KE
168 | +1312NY
169 | `
170 | ).initial
171 | ).toMatchSnapshot();
172 | });
173 | describe("00AL stands for the rest", () => {
174 | it("works with 駒別単独", () => {
175 | expect(
176 | parse(`${prefix}P+23TO
177 | P-11OU21KE
178 | P+00KI
179 | P-00AL
180 | +
181 | +0022KI
182 | %TSUMI
183 | `).initial
184 | ).toMatchSnapshot();
185 | });
186 | it("works with 一括表現", () => {
187 | expect(
188 | parse(`${prefix}P1 * * * * * * * * *
189 | P2 * * * * * * * * *
190 | P3 * * * -FU-FU * * * *
191 | P4-RY * * -OU * -FU * * *
192 | P5 * * +KY * -TO * * * *
193 | P6 * * +RY * * * * * *
194 | P7+KA * * * * * * * *
195 | P8+KA * * * * * * * *
196 | P9 * * * * * * * * *
197 | P-00AL
198 | +
199 | `).initial
200 | ).toMatchSnapshot();
201 | });
202 | });
203 | });
204 | });
205 |
--------------------------------------------------------------------------------
/src/peg/__tests__/ki2ParserTest.ts:
--------------------------------------------------------------------------------
1 | import {parseKI2 as parse} from "../parsers";
2 |
3 | function p(x, y) {
4 | return {x, y};
5 | }
6 | describe("ki2-parser", () => {
7 | it("simple", () => {
8 | expect(parse("▲7六歩 △3四歩 ▲2二角成 △同 銀 ▲4五角")).toEqual({
9 | header: {},
10 | moves: [
11 | {},
12 | {move: {to: p(7, 6), piece: "FU"}},
13 | {move: {to: p(3, 4), piece: "FU"}},
14 | {move: {to: p(2, 2), piece: "KA", promote: true}},
15 | {move: {same: true, piece: "GI"}},
16 | {move: {to: p(4, 5), piece: "KA"}},
17 | ],
18 | });
19 | });
20 | it("comment", () => {
21 | expect(
22 | parse("*開始時コメント\n▲7六歩\n*初手コメント\n*初手コメント2\n△3四歩 ▲2二角成")
23 | ).toEqual({
24 | header: {},
25 | moves: [
26 | {comments: ["開始時コメント"]},
27 | {move: {to: p(7, 6), piece: "FU"}, comments: ["初手コメント", "初手コメント2"]},
28 | {move: {to: p(3, 4), piece: "FU"}},
29 | {move: {to: p(2, 2), piece: "KA", promote: true}},
30 | ],
31 | });
32 | });
33 | it("special", () => {
34 | expect(parse("▲7六歩 △3四歩 ▲7八銀 △8八角成\nまで4手で後手の勝ち\n")).toEqual({
35 | header: {},
36 | moves: [
37 | {},
38 | {move: {to: p(7, 6), piece: "FU"}},
39 | {move: {to: p(3, 4), piece: "FU"}},
40 | {move: {to: p(7, 8), piece: "GI"}},
41 | {move: {to: p(8, 8), piece: "KA", promote: true}},
42 | {special: "TORYO"},
43 | ],
44 | });
45 | });
46 | describe("header", () => {
47 | it("手合割", () => {
48 | expect(parse("手合割:平手\n▲7六歩 △3四歩 ▲2二角成 △同 銀 ▲4五角")).toEqual({
49 | header: {
50 | 手合割: "平手",
51 | },
52 | initial: {preset: "HIRATE"},
53 | moves: [
54 | {},
55 | {move: {to: p(7, 6), piece: "FU"}},
56 | {move: {to: p(3, 4), piece: "FU"}},
57 | {move: {to: p(2, 2), piece: "KA", promote: true}},
58 | {move: {same: true, piece: "GI"}},
59 | {move: {to: p(4, 5), piece: "KA"}},
60 | ],
61 | });
62 | expect(parse("手合割:六枚落ち\n△4二玉 ▲7六歩 △2二銀 ▲6六角 △8二銀")).toEqual({
63 | header: {
64 | 手合割: "六枚落ち",
65 | },
66 | initial: {preset: "6"},
67 | moves: [
68 | {},
69 | {move: {to: p(4, 2), piece: "OU"}},
70 | {move: {to: p(7, 6), piece: "FU"}},
71 | {move: {to: p(2, 2), piece: "GI"}},
72 | {move: {to: p(6, 6), piece: "KA"}},
73 | {move: {to: p(8, 2), piece: "GI"}},
74 | ],
75 | });
76 | });
77 | });
78 | describe("initial", () => {
79 | it("simple", () => {
80 | expect(
81 | parse(
82 | "\
83 | 手合割:その他 \n\
84 | 上手の持駒:銀四 桂四 \n\
85 | 9 8 7 6 5 4 3 2 1\n\
86 | +---------------------------+\n\
87 | | ・ ・ ・ ・ ・ ・ ・v歩v玉|一\n\
88 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|二\n\
89 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|三\n\
90 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|四\n\
91 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|五\n\
92 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|六\n\
93 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|七\n\
94 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|八\n\
95 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|九\n\
96 | +---------------------------+\n\
97 | 下手の持駒:飛二 香四 \n\
98 | 下手番\n\
99 | 下手:shitate\n\
100 | 上手:uwate\n\
101 | ▲1三香 △1二桂 ▲同香成"
102 | )
103 | ).toEqual({
104 | header: {
105 | 手合割: "その他 ",
106 | 上手: "uwate",
107 | 下手: "shitate",
108 | },
109 | initial: {
110 | preset: "OTHER",
111 | data: {
112 | board: [
113 | [{color: 1, kind: "OU"}, {}, {}, {}, {}, {}, {}, {}, {}],
114 | [
115 | {color: 1, kind: "FU"},
116 | {color: 1, kind: "FU"},
117 | {color: 1, kind: "FU"},
118 | {color: 1, kind: "FU"},
119 | {color: 1, kind: "FU"},
120 | {color: 1, kind: "FU"},
121 | {color: 1, kind: "FU"},
122 | {color: 1, kind: "FU"},
123 | {color: 1, kind: "FU"},
124 | ],
125 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
126 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
127 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
128 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
129 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
130 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
131 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
132 | ],
133 | color: 0,
134 | hands: [
135 | {FU: 0, KY: 4, KE: 0, GI: 0, KI: 0, KA: 0, HI: 2},
136 | {FU: 0, KY: 0, KE: 4, GI: 4, KI: 0, KA: 0, HI: 0},
137 | ],
138 | },
139 | },
140 | moves: [
141 | {},
142 | {move: {to: p(1, 3), piece: "KY"}},
143 | {move: {to: p(1, 2), piece: "KE"}},
144 | {move: {same: true, piece: "KY", promote: true}},
145 | ],
146 | });
147 | });
148 | });
149 | describe("fork", () => {
150 | it("normal", () => {
151 | expect(
152 | parse(
153 | "\
154 | 手合割:平手\n\
155 | ▲7六歩 △3四歩 ▲2二角成 △同 銀 ▲4五角\n\
156 | まで5手で中断\n\
157 | \n\
158 | 変化:3手\n\
159 | ▲6六歩 △8四歩\
160 | "
161 | )
162 | ).toEqual({
163 | header: {
164 | 手合割: "平手",
165 | },
166 | initial: {preset: "HIRATE"},
167 | moves: [
168 | {},
169 | {move: {to: p(7, 6), piece: "FU"}},
170 | {move: {to: p(3, 4), piece: "FU"}},
171 | {
172 | move: {to: p(2, 2), piece: "KA", promote: true},
173 | forks: [
174 | [
175 | // {},
176 | {move: {to: p(6, 6), piece: "FU"}},
177 | {move: {to: p(8, 4), piece: "FU"}},
178 | ],
179 | ],
180 | },
181 | {move: {same: true, piece: "GI"}},
182 | {move: {to: p(4, 5), piece: "KA"}},
183 | {special: "CHUDAN"},
184 | ],
185 | });
186 | });
187 |
188 | it("Multiple forks", () => {
189 | expect(
190 | parse(
191 | "▲7六歩 △3四歩\n" +
192 | "\n" +
193 | "変化:2手\n" +
194 | "△8四歩\n" +
195 | "\n" +
196 | "変化:2手\n" +
197 | "△4四歩\n" +
198 | "\n" +
199 | "変化:1手\n" +
200 | "▲2六歩\n" +
201 | "\n" +
202 | "変化:1手\n" +
203 | "▲8六歩\n" +
204 | "\n" +
205 | "変化:1手\n" +
206 | "▲7八金"
207 | )
208 | ).toEqual({
209 | header: {},
210 | moves: [
211 | {},
212 | {
213 | move: {to: p(7, 6), piece: "FU"},
214 | forks: [
215 | [{move: {to: p(2, 6), piece: "FU"}}],
216 | [{move: {to: p(8, 6), piece: "FU"}}],
217 | [{move: {to: p(7, 8), piece: "KI"}}],
218 | ],
219 | },
220 | {
221 | move: {to: p(3, 4), piece: "FU"},
222 | forks: [
223 | [{move: {to: p(8, 4), piece: "FU"}}],
224 | [{move: {to: p(4, 4), piece: "FU"}}],
225 | ],
226 | },
227 | ],
228 | });
229 | });
230 | });
231 | });
232 |
--------------------------------------------------------------------------------
/src/peg/__tests__/kifParserTest.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-irregular-whitespace */
2 | import {parseKIF as parse} from "../parsers";
3 |
4 | function p(x, y) {
5 | return {x, y};
6 | }
7 | describe("kif-parser", () => {
8 | it("simple", () => {
9 | expect(
10 | parse("1 7六歩(77)\n2 3四歩(33)\n3 2二角成(88)\n 4 同 銀(31)\n5 4五角打\n")
11 | ).toEqual({
12 | header: {},
13 | moves: [
14 | {},
15 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}},
16 | {move: {from: p(3, 3), to: p(3, 4), piece: "FU"}},
17 | {move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true}},
18 | {move: {from: p(3, 1), same: true, piece: "GI"}},
19 | {move: {to: p(4, 5), piece: "KA"}},
20 | ],
21 | });
22 | });
23 | it("comment", () => {
24 | expect(
25 | parse(
26 | "*開始時コメント\n1 7六歩(77)\n*初手コメント\n*初手コメント2\n2 3四歩(33)\n3 2二角成(88)\n"
27 | )
28 | ).toEqual({
29 | header: {},
30 | moves: [
31 | {comments: ["開始時コメント"]},
32 | {
33 | move: {from: p(7, 7), to: p(7, 6), piece: "FU"},
34 | comments: ["初手コメント", "初手コメント2"],
35 | },
36 | {move: {from: p(3, 3), to: p(3, 4), piece: "FU"}},
37 | {move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true}},
38 | ],
39 | });
40 | });
41 | it("time", () => {
42 | expect(
43 | parse(
44 | "1 7六歩(77) (0:01/00:00:01)\n2 3四歩(33) (0:02/00:00:02)\n3 2二角成(88) (0:20/00:00:21)\n 4 同 銀(31) (0:03/00:00:05)\n5 4五角打 (0:39/00:01:00)\n"
45 | )
46 | ).toEqual({
47 | header: {},
48 | moves: [
49 | {},
50 | {
51 | move: {from: p(7, 7), to: p(7, 6), piece: "FU"},
52 | time: {now: {m: 0, s: 1}, total: {h: 0, m: 0, s: 1}},
53 | },
54 | {
55 | move: {from: p(3, 3), to: p(3, 4), piece: "FU"},
56 | time: {now: {m: 0, s: 2}, total: {h: 0, m: 0, s: 2}},
57 | },
58 | {
59 | move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true},
60 | time: {now: {m: 0, s: 20}, total: {h: 0, m: 0, s: 21}},
61 | },
62 | {
63 | move: {from: p(3, 1), same: true, piece: "GI"},
64 | time: {now: {m: 0, s: 3}, total: {h: 0, m: 0, s: 5}},
65 | },
66 | {
67 | move: {to: p(4, 5), piece: "KA"},
68 | time: {now: {m: 0, s: 39}, total: {h: 0, m: 1, s: 0}},
69 | },
70 | ],
71 | });
72 | });
73 | it("special", () => {
74 | expect(
75 | parse(
76 | "1 7六歩(77)\n2 3四歩(33)\n3 7八銀(79)\n 4 8八角成(22)\n5 投了\nまで4手で後手の勝ち\n"
77 | )
78 | ).toEqual({
79 | header: {},
80 | moves: [
81 | {},
82 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}},
83 | {move: {from: p(3, 3), to: p(3, 4), piece: "FU"}},
84 | {move: {from: p(7, 9), to: p(7, 8), piece: "GI"}},
85 | {move: {from: p(2, 2), to: p(8, 8), piece: "KA", promote: true}},
86 | {special: "TORYO"},
87 | ],
88 | });
89 | });
90 | describe("header", () => {
91 | it("手合割", () => {
92 | expect(
93 | parse(
94 | "手合割:平手\n1 7六歩(77)\n2 3四歩(33)\n3 2二角成(88)\n 4 同 銀(31)\n5 4五角打\n"
95 | )
96 | ).toEqual({
97 | header: {
98 | 手合割: "平手",
99 | },
100 | initial: {preset: "HIRATE"},
101 | moves: [
102 | {},
103 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}},
104 | {move: {from: p(3, 3), to: p(3, 4), piece: "FU"}},
105 | {move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true}},
106 | {move: {from: p(3, 1), same: true, piece: "GI"}},
107 | {move: {to: p(4, 5), piece: "KA"}},
108 | ],
109 | });
110 | expect(
111 | parse(
112 | "手合割:六枚落ち\n1 4二玉(51)\n2 7六歩(77)\n3 2二銀(31)\n 4 6六角(88)\n5 8二銀(71)\n"
113 | )
114 | ).toEqual({
115 | header: {
116 | 手合割: "六枚落ち",
117 | },
118 | initial: {preset: "6"},
119 | moves: [
120 | {},
121 | {move: {from: p(5, 1), to: p(4, 2), piece: "OU"}},
122 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}},
123 | {move: {from: p(3, 1), to: p(2, 2), piece: "GI"}},
124 | {move: {from: p(8, 8), to: p(6, 6), piece: "KA"}},
125 | {move: {from: p(7, 1), to: p(8, 2), piece: "GI"}},
126 | ],
127 | });
128 | });
129 | });
130 | describe("initial", () => {
131 | it("simple", () => {
132 | expect(
133 | parse(
134 | "\
135 | 手合割:その他 \n\
136 | 上手の持駒:銀四 桂四 \n\
137 | 9 8 7 6 5 4 3 2 1\n\
138 | +---------------------------+\n\
139 | | ・ ・ ・ ・ ・ ・ ・v歩v玉|一\n\
140 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|二\n\
141 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|三\n\
142 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|四\n\
143 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|五\n\
144 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|六\n\
145 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|七\n\
146 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|八\n\
147 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|九\n\
148 | +---------------------------+\n\
149 | 下手の持駒:飛二 香四 \n\
150 | 下手番\n\
151 | 下手:shitate\n\
152 | 上手:uwate\n\
153 | 1 1三香打\n2 1二桂打\n3 同 香成(13)\n"
154 | )
155 | ).toEqual({
156 | header: {
157 | 手合割: "その他 ",
158 | 上手: "uwate",
159 | 下手: "shitate",
160 | },
161 | initial: {
162 | preset: "OTHER",
163 | data: {
164 | board: [
165 | [{color: 1, kind: "OU"}, {}, {}, {}, {}, {}, {}, {}, {}],
166 | [
167 | {color: 1, kind: "FU"},
168 | {color: 1, kind: "FU"},
169 | {color: 1, kind: "FU"},
170 | {color: 1, kind: "FU"},
171 | {color: 1, kind: "FU"},
172 | {color: 1, kind: "FU"},
173 | {color: 1, kind: "FU"},
174 | {color: 1, kind: "FU"},
175 | {color: 1, kind: "FU"},
176 | ],
177 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
178 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
179 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
180 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
181 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
182 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
183 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
184 | ],
185 | color: 0,
186 | hands: [
187 | {FU: 0, KY: 4, KE: 0, GI: 0, KI: 0, KA: 0, HI: 2},
188 | {FU: 0, KY: 0, KE: 4, GI: 4, KI: 0, KA: 0, HI: 0},
189 | ],
190 | },
191 | },
192 | moves: [
193 | {},
194 | {move: {to: p(1, 3), piece: "KY"}},
195 | {move: {to: p(1, 2), piece: "KE"}},
196 | {move: {from: p(1, 3), same: true, piece: "KY", promote: true}},
197 | ],
198 | });
199 | });
200 | it("Kifu for iPhone dialect", () => {
201 | // 持ち駒が半角スペース区切り
202 | // 手合割が平手
203 | expect(
204 | parse(
205 | "\
206 | 手合割:平手\n\
207 | 上手の持駒:銀四 桂四 \n\
208 | 9 8 7 6 5 4 3 2 1\n\
209 | +---------------------------+\n\
210 | | ・ ・ ・ ・ ・ ・ ・v歩v玉|一\n\
211 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|二\n\
212 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|三\n\
213 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|四\n\
214 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|五\n\
215 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|六\n\
216 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|七\n\
217 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|八\n\
218 | | ・ ・ ・ ・ ・ ・ ・v歩 ・|九\n\
219 | +---------------------------+\n\
220 | 下手の持駒:飛二 香四 \n\
221 | 下手番\n\
222 | 下手:shitate\n\
223 | 上手:uwate\n\
224 | 1 1三香打\n2 1二桂打\n3 同 香成(13)\n"
225 | )
226 | ).toEqual({
227 | header: {
228 | 手合割: "平手",
229 | 上手: "uwate",
230 | 下手: "shitate",
231 | },
232 | initial: {
233 | preset: "OTHER", // 上書き
234 | data: {
235 | board: [
236 | [{color: 1, kind: "OU"}, {}, {}, {}, {}, {}, {}, {}, {}],
237 | [
238 | {color: 1, kind: "FU"},
239 | {color: 1, kind: "FU"},
240 | {color: 1, kind: "FU"},
241 | {color: 1, kind: "FU"},
242 | {color: 1, kind: "FU"},
243 | {color: 1, kind: "FU"},
244 | {color: 1, kind: "FU"},
245 | {color: 1, kind: "FU"},
246 | {color: 1, kind: "FU"},
247 | ],
248 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
249 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
250 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
251 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
252 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
253 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
254 | [{}, {}, {}, {}, {}, {}, {}, {}, {}],
255 | ],
256 | color: 0,
257 | hands: [
258 | {FU: 0, KY: 4, KE: 0, GI: 0, KI: 0, KA: 0, HI: 2},
259 | {FU: 0, KY: 0, KE: 4, GI: 4, KI: 0, KA: 0, HI: 0},
260 | ],
261 | },
262 | },
263 | moves: [
264 | {},
265 | {move: {to: p(1, 3), piece: "KY"}},
266 | {move: {to: p(1, 2), piece: "KE"}},
267 | {move: {from: p(1, 3), same: true, piece: "KY", promote: true}},
268 | ],
269 | });
270 | });
271 | });
272 | describe("fork", () => {
273 | it("normal", () => {
274 | expect(
275 | parse(
276 | "\
277 | 手合割:平手\n\
278 | 1 7六歩(77)\n\
279 | 2 3四歩(33)\n\
280 | 3 2二角成(88)+\n\
281 | 4 同 銀(31)\n\
282 | 5 4五角打\n\
283 | 6 中断\n\
284 | \n\
285 | 変化:3手\n\
286 | 3 6六歩(67)\n\
287 | 4 8四歩(83)\n\
288 | "
289 | )
290 | ).toEqual({
291 | header: {
292 | 手合割: "平手",
293 | },
294 | initial: {preset: "HIRATE"},
295 | moves: [
296 | {},
297 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}},
298 | {move: {from: p(3, 3), to: p(3, 4), piece: "FU"}},
299 | {
300 | move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true},
301 | forks: [
302 | [
303 | // {},
304 | {move: {from: p(6, 7), to: p(6, 6), piece: "FU"}},
305 | {move: {from: p(8, 3), to: p(8, 4), piece: "FU"}},
306 | ],
307 | ],
308 | },
309 | {move: {from: p(3, 1), same: true, piece: "GI"}},
310 | {move: {to: p(4, 5), piece: "KA"}},
311 | {special: "CHUDAN"},
312 | ],
313 | });
314 | });
315 |
316 | it("Multiple forks", () => {
317 | expect(
318 | parse(
319 | "1 7六歩(77)+\n" +
320 | "2 3四歩(33)+\n" +
321 | "\n" +
322 | "変化:2手\n" +
323 | "2 8四歩(83)+\n" +
324 | "\n" +
325 | "変化:2手\n" +
326 | "2 4四歩(43)\n" +
327 | "\n" +
328 | "変化:1手\n" +
329 | "1 2六歩(27)+\n" +
330 | "\n" +
331 | "変化:1手\n" +
332 | "1 8六歩(87)+\n" +
333 | "\n" +
334 | "変化:1手\n" +
335 | "1 7八金(69)\n"
336 | )
337 | ).toEqual({
338 | header: {},
339 | moves: [
340 | {},
341 | {
342 | move: {from: p(7, 7), to: p(7, 6), piece: "FU"},
343 | forks: [
344 | [{move: {from: p(2, 7), to: p(2, 6), piece: "FU"}}],
345 | [{move: {from: p(8, 7), to: p(8, 6), piece: "FU"}}],
346 | [{move: {from: p(6, 9), to: p(7, 8), piece: "KI"}}],
347 | ],
348 | },
349 | {
350 | move: {from: p(3, 3), to: p(3, 4), piece: "FU"},
351 | forks: [
352 | [{move: {from: p(8, 3), to: p(8, 4), piece: "FU"}}],
353 | [{move: {from: p(4, 3), to: p(4, 4), piece: "FU"}}],
354 | ],
355 | },
356 | ],
357 | });
358 | });
359 | });
360 | describe("split", () => {
361 | it("normal", () => {
362 | expect(
363 | parse(
364 | "\
365 | 手合割:平手\n\
366 | 手数----指手--\n\
367 | *開始コメント\n\
368 | 1 7六歩(77)\n\
369 | *初手コメント\n\
370 | 2 3四歩(33)\n\
371 | 3 2二角成(88)+\n\
372 | 4 中断\n\
373 | "
374 | )
375 | ).toEqual({
376 | header: {
377 | 手合割: "平手",
378 | },
379 | initial: {preset: "HIRATE"},
380 | moves: [
381 | {comments: ["開始コメント"]},
382 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}, comments: ["初手コメント"]},
383 | {move: {from: p(3, 3), to: p(3, 4), piece: "FU"}},
384 | {move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true}},
385 | {special: "CHUDAN"},
386 | ],
387 | });
388 | });
389 | it("after initial comment", () => {
390 | expect(
391 | parse(
392 | "\
393 | 手合割:平手\n\
394 | *開始コメント\n\
395 | 手数----指手--\n\
396 | 1 7六歩(77)\n\
397 | *初手コメント\n\
398 | 2 3四歩(33)\n\
399 | 3 2二角成(88)+\n\
400 | 4 中断\n\
401 | "
402 | )
403 | ).toEqual({
404 | header: {
405 | 手合割: "平手",
406 | },
407 | initial: {preset: "HIRATE"},
408 | moves: [
409 | {comments: ["開始コメント"]},
410 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}, comments: ["初手コメント"]},
411 | {move: {from: p(3, 3), to: p(3, 4), piece: "FU"}},
412 | {move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true}},
413 | {special: "CHUDAN"},
414 | ],
415 | });
416 | });
417 | });
418 | describe("unsupported annotations", () => {
419 | it("盤面回転", () => {
420 | expect(
421 | parse(
422 | "盤面回転\n1 7六歩(77)\n2 3四歩(33)\n3 2二角成(88)\n 4 同 銀(31)\n5 4五角打\n"
423 | )
424 | ).toEqual({
425 | header: {},
426 | moves: [
427 | {},
428 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}},
429 | {move: {from: p(3, 3), to: p(3, 4), piece: "FU"}},
430 | {move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true}},
431 | {move: {from: p(3, 1), same: true, piece: "GI"}},
432 | {move: {to: p(4, 5), piece: "KA"}},
433 | ],
434 | });
435 | });
436 | it("&読み込み時表示", () => {
437 | expect(
438 | parse(
439 | "1 7六歩(77)\n2 3四歩(33)\n&読み込み時表示\n3 2二角成(88)\n 4 同 銀(31)\n5 4五角打\n"
440 | )
441 | ).toEqual({
442 | header: {},
443 | moves: [
444 | {},
445 | {move: {from: p(7, 7), to: p(7, 6), piece: "FU"}},
446 | {
447 | move: {from: p(3, 3), to: p(3, 4), piece: "FU"},
448 | comments: ["&読み込み時表示"],
449 | },
450 | {move: {from: p(8, 8), to: p(2, 2), piece: "KA", promote: true}},
451 | {move: {from: p(3, 1), same: true, piece: "GI"}},
452 | {move: {to: p(4, 5), piece: "KA"}},
453 | ],
454 | });
455 | });
456 | });
457 | it("same at first fork", () => {
458 | expect(
459 | parse(` 1 7六歩(77) ( 0:00/00:00:00)
460 | 2 3四歩(33) ( 0:00/00:00:00)
461 | 3 2六歩(27) ( 0:00/00:00:00)
462 | 4 中断 ( 0:00/00:00:00)
463 | まで3手で中断
464 |
465 | 変化:3手
466 | 3 2二角成(88) ( 0:00/00:00:00)
467 | 4 同 銀(31) ( 0:00/00:00:00)+
468 | 5 中断 ( 0:00/00:00:00)
469 | まで4手で中断
470 |
471 | 変化:4手
472 | 4 同 飛(82) ( 0:00/00:00:00)
473 | 5 中断 ( 0:00/00:00:00)
474 | まで4手で中断
475 | `)
476 | ).toMatchSnapshot();
477 | });
478 | });
479 |
--------------------------------------------------------------------------------
/src/peg/ambient.ts:
--------------------------------------------------------------------------------
1 | declare module "*.pegjs" {
2 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
3 | export function parse(kifuString: string): any;
4 | }
5 |
--------------------------------------------------------------------------------
/src/peg/csa-parser.pegjs:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | * CSA V1 http://www2.computer-shogi.org/wcsc12/record.html
4 | * CSA V2.2 http://www2.computer-shogi.org/protocol/record_v22.html
5 | */
6 | function secToTime(sec){
7 | var remain, h, m, s = sec%60;
8 | m = (sec-s)/60;
9 | return {m:m, s:s};
10 | }
11 | function getHirate(){
12 | return [
13 | [{color:1,kind:"KY"},{ },{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{ },{color:0,kind:"KY"},],
14 | [{color:1,kind:"KE"},{color:1,kind:"KA"},{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{color:0,kind:"HI"},{color:0,kind:"KE"},],
15 | [{color:1,kind:"GI"},{ },{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{ },{color:0,kind:"GI"},],
16 | [{color:1,kind:"KI"},{ },{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{ },{color:0,kind:"KI"},],
17 | [{color:1,kind:"OU"},{ },{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{ },{color:0,kind:"OU"},],
18 | [{color:1,kind:"KI"},{ },{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{ },{color:0,kind:"KI"},],
19 | [{color:1,kind:"GI"},{ },{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{ },{color:0,kind:"GI"},],
20 | [{color:1,kind:"KE"},{color:1,kind:"HI"},{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{color:0,kind:"KA"},{color:0,kind:"KE"},],
21 | [{color:1,kind:"KY"},{ },{color:1,kind:"FU"},{},{},{},{color:0,kind:"FU"},{ },{color:0,kind:"KY"},],
22 | ];
23 | }
24 | function normalizeHeaderKey(key){
25 | return {
26 | "EVENT": "棋戦",
27 | "SITE": "場所",
28 | "START_TIME": "開始日時",
29 | "END_TIME": "終了日時",
30 | "TIME_LIMIT": "持ち時間",
31 | }[key] || key;
32 | }
33 | function unpromote(kind) {
34 | return {
35 | TO: "FU",
36 | NY: "KY",
37 | NK: "KE",
38 | NG: "GI",
39 | UM: "KA",
40 | RY: "HI",
41 | }[kind] || kind;
42 | }
43 | }
44 | kifu = csa2 / csa1
45 |
46 | csa2 = version20to22 i:information? ini:initialboard ms:moves? {
47 | var ret = {header:i.header, initial:ini, moves:ms}
48 | if(i && i.players){
49 | if(i.players[0]) ret.header["先手"]=i.players[0];
50 | if(i.players[1]) ret.header["後手"]=i.players[1];
51 | }
52 | return ret;
53 | }
54 |
55 | version20to22 = comment* "V2" (".1" / ".2")? nl
56 |
57 | information = players:players? header:headers {return {players:players, header:header}}
58 |
59 | headers = header:header* {
60 | var ret = {};
61 | for(var i=0; i=0) {
87 | var rest = {FU:18,KY:4,KE:4,GI:4,KI:4,KA:2,HI:2};
88 | for(var piece in data.data.hands[1-alColor]) {
89 | rest[piece] -= data.data.hands[1-alColor][piece];
90 | }
91 | for(var x=0; x0?{comments:c}:{}}
162 |
163 | move = move:(normalmove / specialmove) comments1:comment* time:time? comments2:comment* {
164 | var ret = {};
165 | const comments = comments1.concat(comments2);
166 | if(comments.length>0){ret.comments=comments;}
167 | if(time){ret.time=time;}
168 | if(move.special){ret.special=move.special}else{ret.move=move};
169 | return ret;
170 | }
171 | normalmove = teban from:xy to:xy piece:piece nl {
172 | var ret = {to:to, piece:piece}
173 | if(from.x!=0) ret.from = from;
174 | return ret;
175 | }
176 | specialmove = "%" m:[-+_A-Z]+ nl { return {special: m.join("")}; }
177 |
178 | teban = "+"{return 0}/"-"{return 1}
179 |
180 | comment = "'" c:nonl* nl { return c.join(""); }
181 | time = "T" t:[0-9]* nl { return {now: secToTime(parseInt(t.join("")))}; }
182 |
183 | xy = x:[0-9] y:[0-9] { return {x:parseInt(x), y:parseInt(y)}; }
184 | piece = a:[A-Z] b:[A-Z] { return a+b; }
185 | xypiece = xy:xy piece:piece {return {xy:xy, piece:piece}}
186 |
187 | nl = ("\r"? "\n") / " "* ","
188 | nonl = [^\r\n]
189 |
--------------------------------------------------------------------------------
/src/peg/ki2-parser.pegjs:
--------------------------------------------------------------------------------
1 | {
2 | function toN(ss){
3 | return parseInt(ss.join(""), 10);
4 | }
5 | function zenToN(s){
6 | return "0123456789".indexOf(s);
7 | }
8 | function kanToN(s){
9 | return "〇一二三四五六七八九".indexOf(s);
10 | }
11 | function kanToN2(s){
12 | switch(s.length){
13 | case 1:
14 | return "〇一二三四五六七八九十".indexOf(s);
15 | case 2:
16 | return "〇一二三四五六七八九十".indexOf(s[1])+10;
17 | default:
18 | throw "21以上の数値に対応していません";
19 | }
20 | }
21 | function kindToCSA(kind){
22 | if(kind[0]=="成"){
23 | return {
24 | "香": "NY",
25 | "桂": "NK",
26 | "銀": "NG"
27 | }[kind[1]];
28 | }
29 | return {
30 | "歩": "FU",
31 | "香": "KY",
32 | "桂": "KE",
33 | "銀": "GI",
34 | "金": "KI",
35 | "角": "KA",
36 | "飛": "HI",
37 | "玉": "OU",
38 | "王": "OU",
39 | "と": "TO",
40 | "杏": "NY",
41 | "圭": "NK",
42 | "全": "NG",
43 | "馬": "UM",
44 | "竜": "RY",
45 | "龍": "RY"
46 | }[kind];
47 | }
48 | function soutaiToRelative(str){
49 | return {
50 | "左": "L",
51 | "直": "C",
52 | "右": "R",
53 | }[str] || "";
54 | }
55 | function dousaToRelative(str){
56 | return {
57 | "上": "U",
58 | "寄": "M",
59 | "引": "D",
60 | }[str] || "";
61 | }
62 | function presetToString(preset){
63 | return {
64 | "平手": "HIRATE",
65 | "香落ち": "KY",
66 | "右香落ち": "KY_R",
67 | "角落ち": "KA",
68 | "飛車落ち": "HI",
69 | "飛香落ち": "HIKY",
70 | "二枚落ち": "2",
71 | "三枚落ち": "3",
72 | "四枚落ち": "4",
73 | "五枚落ち": "5",
74 | "左五枚落ち": "5_L",
75 | "六枚落ち": "6",
76 | "左七枚落ち": "7_L",
77 | "右七枚落ち": "7_R",
78 | "八枚落ち": "8",
79 | "十枚落ち": "10",
80 | "その他": "OTHER",
81 | }[preset.replace(/\s/g, "")];
82 | }
83 | function makeHand(str){
84 | var kinds = str.replace(/ $/, "").split(" ");
85 | var ret = {FU:0,KY:0,KE:0,GI:0,KI:0,KA:0,HI:0};
86 | if(str=="") return ret;
87 | for(var i=0; i=0 ? 0 : 1;
112 | delete ret.header["手番"];
113 | }else{
114 | ret.initial.data.color = 0;
115 | }
116 | ret.initial.data.hands = [
117 | makeHand(ret.header["先手の持駒"] || ret.header["下手の持駒"]),
118 | makeHand(ret.header["後手の持駒"] || ret.header["上手の持駒"])
119 | ];
120 | delete ret.header["先手の持駒"];
121 | delete ret.header["下手の持駒"];
122 | delete ret.header["後手の持駒"];
123 | delete ret.header["上手の持駒"];
124 | }
125 | var forkStack = [{te:0, moves:moves}];
126 | for(var i=0; i=nowFork.te){fork = forkStack.pop();}
130 | var move = fork.moves[nowFork.te-fork.te];
131 | move.forks = move.forks || [];
132 | move.forks.push(nowFork.moves);
133 | forkStack.push(fork);
134 | forkStack.push(nowFork);
135 | }
136 | return ret;
137 | }
138 |
139 | header
140 | = key:[^:\r\n]+ ":" value:nonl* nl+ {return {k:key.join(""), v:value.join("")}} / te:[先後上下] "手番" nl {return {k:"手番",v:te}}
141 | initialboard = (" " nonl* nl)? ("+" nonl* nl)? lines:ikkatsuline+ ("+" nonl* nl)? {
142 | var ret = [];
143 | for(var i=0; i<9; i++){
144 | var line = [];
145 | for(var j=0; j<9; j++){
146 | line.push(lines[j][8-i]);
147 | }
148 | ret.push(line);
149 | }
150 | return {preset: "OTHER", data: {board:ret}};
151 | }
152 | ikkatsuline = "|" masu:masu+ "|" nonl+ nl { return masu; }
153 | masu = c:teban k:piece {return {color:c, kind:k}} / " ・" { return {} }
154 | teban = (" "/"+"/"^"){return 0} / ("v"/"V"){return 1}
155 |
156 | moves = hd:firstboard tl:move* res:result? {
157 | tl.unshift(hd);
158 | if(res && !tl[tl.length-1].special){
159 | tl.push({special: res});
160 | }
161 | return tl;
162 | }
163 |
164 | firstboard = c:comment* pointer? {return c.length==0 ? {} : {comments:c}}
165 | move = line:line c:comment* pointer? (nl / " ")* {
166 | var ret = {move: line};
167 | if(c.length>0) ret.comments=c;
168 | return ret;
169 | }
170 |
171 | pointer = "&" nonl* nl
172 |
173 | line = [▲△] f:fugou (nl / " ")* {return f}
174 | fugou = pl:place pi:piece sou:soutai? dou:dousa? pro:("成"/"不成")? da:"打"? {
175 | var ret = {piece: pi};
176 | if(pl.same){
177 | ret.same = true;
178 | }else{
179 | ret.to = pl;
180 | }
181 | if(pro)ret.promote=pro=="成";
182 | if(da){
183 | ret.relative = "H";
184 | }else{
185 | var rel = soutaiToRelative(sou)+dousaToRelative(dou);
186 | if(rel!="") ret.relative=rel;
187 | }
188 | return ret;
189 | }
190 | place = x:num y:numkan {return {x:x,y:y}} / "同" " "? {return {same:true}}
191 | piece = pro:"成"? p:[歩香桂銀金角飛王玉と杏圭全馬竜龍] {return kindToCSA((pro||"")+p)}
192 | soutai = [左直右]
193 | dousa = [上寄引]
194 |
195 | num = n:[123456789] {return zenToN(n);}
196 | numkan = n:[一二三四五六七八九] {return kanToN(n);}
197 |
198 | comment = "*" comm:nonl* nl {return comm.join("")}
199 |
200 | result = "まで" [0-9]+ "手" res:(
201 | "で" win:turn "手の" res:("勝ち" {return "TORYO"}
202 | / "反則" res:("勝ち" {return "ILLEGAL_ACTION"}
203 | / "負け" {return "ILLEGAL_MOVE"}) {return res}) {return res}
204 | / "で時間切れにより" win:turn "手の勝ち" {return "TIME_UP"}
205 | / "で中断" {return "CHUDAN"}
206 | / "で持将棋" {return "JISHOGI"}
207 | / "で千日手" {return "SENNICHITE"}
208 | / "で"? "詰" "み"? {return "TSUMI"}
209 | / "で不詰" {return "FUZUMI"}
210 | ) nl {return res}
211 |
212 | fork = "変化:" " "* te:[0-9]+ "手" nl moves:moves {return {te:parseInt(te.join("")), moves:moves.slice(1)}}
213 |
214 | turn = [先後上下]
215 | nl = newline+ skipline*
216 | skipline = ("#" nonl* newline)
217 | whitespace = " " / "\t"
218 | newline = whitespace* ("\n" / "\r" "\n"?)
219 | nonl = [^\r\n]
220 |
--------------------------------------------------------------------------------
/src/peg/kif-parser.pegjs:
--------------------------------------------------------------------------------
1 | {
2 | function toN(ss){
3 | return parseInt(ss.join(""), 10);
4 | }
5 | function zenToN(s){
6 | return "0123456789".indexOf(s);
7 | }
8 | function kanToN(s){
9 | return "〇一二三四五六七八九".indexOf(s);
10 | }
11 | function kanToN2(s){
12 | switch(s.length){
13 | case 1:
14 | return "〇一二三四五六七八九十".indexOf(s);
15 | case 2:
16 | return "〇一二三四五六七八九十".indexOf(s[1])+10;
17 | default:
18 | throw "21以上の数値に対応していません";
19 | }
20 | }
21 | function kindToCSA(kind){
22 | if(kind[0]=="成"){
23 | return {
24 | "香": "NY",
25 | "桂": "NK",
26 | "銀": "NG"
27 | }[kind[1]];
28 | }
29 | return {
30 | "歩": "FU",
31 | "香": "KY",
32 | "桂": "KE",
33 | "銀": "GI",
34 | "金": "KI",
35 | "角": "KA",
36 | "飛": "HI",
37 | "玉": "OU",
38 | "王": "OU",
39 | "と": "TO",
40 | "杏": "NY",
41 | "圭": "NK",
42 | "全": "NG",
43 | "馬": "UM",
44 | "竜": "RY",
45 | "龍": "RY"
46 | }[kind];
47 | }
48 | function specialToCSA(str){
49 | return {
50 | "中断": "CHUDAN",
51 | "封じ手": "CHUDAN", // とりあえず
52 | "投了": "TORYO",
53 | "持将棋": "JISHOGI",
54 | "千日手": "SENNICHITE",
55 | "詰み": "TSUMI",
56 | "不詰": "FUZUMI",
57 | "切れ負け": "TIME_UP",
58 | "反則勝ち": "ILLEGAL_ACTION", // 直前の手が反則(先頭に+か-で反則した側の情報を含める必要が有る)
59 | "反則負け": "ILLEGAL_MOVE" // ここで手番側が反則,反則の内容はコメントで表現
60 | }[str];
61 | }
62 | function presetToString(preset){
63 | return {
64 | "平手": "HIRATE",
65 | "香落ち": "KY",
66 | "右香落ち": "KY_R",
67 | "角落ち": "KA",
68 | "飛車落ち": "HI",
69 | "飛香落ち": "HIKY",
70 | "二枚落ち": "2",
71 | "三枚落ち": "3",
72 | "四枚落ち": "4",
73 | "五枚落ち": "5",
74 | "左五枚落ち": "5_L",
75 | "六枚落ち": "6",
76 | "左七枚落ち": "7_L",
77 | "右七枚落ち": "7_R",
78 | "八枚落ち": "8",
79 | "十枚落ち": "10",
80 | "その他": "OTHER",
81 | }[preset.replace(/\s/g, "")];
82 | }
83 | function makeHand(str){
84 | // Kifu for iPhoneは半角スペース区切り
85 | var kinds = str.split(/[ ]/);
86 | var ret = {FU:0,KY:0,KE:0,GI:0,KI:0,KA:0,HI:0};
87 | if(str=="") return ret;
88 | for(var i=0; i=0 ? 0 : 1 ;
114 | delete ret.header["手番"];
115 | }else{
116 | ret.initial.data.color = 0;
117 | }
118 | ret.initial.data.hands = [
119 | makeHand(ret.header["先手の持駒"] || ret.header["下手の持駒"]),
120 | makeHand(ret.header["後手の持駒"] || ret.header["上手の持駒"])
121 | ];
122 | delete ret.header["先手の持駒"];
123 | delete ret.header["下手の持駒"];
124 | delete ret.header["後手の持駒"];
125 | delete ret.header["上手の持駒"];
126 | }
127 | var forkStack = [{te:0, moves:moves}];
128 | for(var i=0; i=nowFork.te){fork = forkStack.pop();}
132 | var move = fork.moves[nowFork.te-fork.te];
133 | move.forks = move.forks || [];
134 | move.forks.push(nowFork.moves);
135 | forkStack.push(fork);
136 | forkStack.push(nowFork);
137 | }
138 | return ret;
139 | }
140 |
141 | // ヘッダ
142 | header = key:[^:\r\n]+ ":" value:nonl* nl {return {k:key.join(""), v:value.join("")}}
143 | / te:turn "手番" nl {return {k:"手番",v:te}}
144 | / "盤面回転" nl {return null}
145 |
146 | turn = [先後上下]
147 |
148 | initialboard = (" " nonl* nl)? ("+" nonl* nl)? lines:ikkatsuline+ ("+" nonl* nl)? {
149 | var ret = [];
150 | for(var i=0; i<9; i++){
151 | var line = [];
152 | for(var j=0; j<9; j++){
153 | line.push(lines[j][8-i]);
154 | }
155 | ret.push(line);
156 | }
157 | return {preset: "OTHER", data: {board:ret}};
158 | }
159 | ikkatsuline = "|" masu:masu+ "|" nonl+ nl { return masu; }
160 | masu = c:teban k:piece {return {color:c, kind:k}} / " ・" { return {} }
161 | teban = (" "/"+"/"^"){return 0} / ("v"/"V"){return 1}
162 |
163 | split = "手数----指手--" "-------消費時間--"? nl
164 |
165 | // 棋譜部分
166 | moves = hd:firstboard split? tl:move* result? {tl.unshift(hd); return tl;}
167 |
168 | firstboard = c:comment* pointer? {return c.length==0 ? {} : {comments:c}}
169 | move = line:line c:comment* pointer? {
170 | var ret = {};
171 | if(c.length>0) ret.comments = c;
172 | if(typeof line.move=="object"){
173 | ret.move=line.move;
174 | }else{
175 | ret.special=specialToCSA(line.move)
176 | }
177 | if(line.time) ret.time=line.time;
178 | return ret;
179 | }
180 |
181 | pointer = "&" nonl* nl
182 |
183 | line = " "* te " "* move:(fugou:fugou from:from {
184 | var ret = {piece: fugou.piece};
185 | if(fugou.to){ret.to=fugou.to}else{ret.same=true};
186 | if(fugou.promote) ret.promote=true;
187 | if(from) ret.from=from;
188 | return ret;
189 | } / spe:[^\r\n ]* {return spe.join("")}) " "* time:time? "+"? nl {return {move: move, time: time}}
190 |
191 | te = [0-9]+
192 | fugou = pl:place pi:piece pro:"成"? {return {to:pl, piece: pi,promote:!!pro};}
193 | place = x:num y:numkan {return {x:x,y:y}} / "同" " "? {return null}
194 |
195 | num = n:[123456789] {return zenToN(n);}
196 | numkan = n:[一二三四五六七八九] {return kanToN(n);}
197 |
198 | piece = pro:"成"? p:[歩香桂銀金角飛王玉と杏圭全馬竜龍] {return kindToCSA((pro||"")+p);}
199 |
200 | from = "打" {return null} / "(" x:[1-9] y:[1-9] ")" {return {x:parseInt(x),y:parseInt(y)}}
201 |
202 | time = "(" " "* now:ms " "* "/" " "* total:hms ")" {return {now: now, total: total}}
203 |
204 | hms = h:[0-9]+ ":" m:[0-9]+ ":" s:[0-9]+ {return {h:toN(h),m:toN(m),s:toN(s)}}
205 | ms = m:[0-9]+ ":" s:[0-9]+ {return {m:toN(m),s:toN(s)}}
206 |
207 | comment = "*" comm:nonl* nl {return comm.join("")}
208 | / "&" annotation:nonl* nl {return "&"+annotation.join("")}
209 |
210 | result = "まで" [0-9]+ "手" res:(
211 | "で" win:turn "手の" res:("勝ち" {return "TORYO"}
212 | / "反則" res:("勝ち" {return "ILLEGAL_ACTION"}
213 | / "負け" {return "ILLEGAL_MOVE"}) {return res}) {return res}
214 | / "で時間切れにより" win:turn "手の勝ち" {return "TIME_UP"}
215 | / "で中断" {return "CHUDAN"}
216 | / "で封じ手" {return "CHUDAN"} // とりあえず
217 | / "で持将棋" {return "JISHOGI"}
218 | / "で千日手" {return "SENNICHITE"}
219 | / "で"? "詰" "み"? {return "TSUMI"}
220 | / "で不詰" {return "FUZUMI"}
221 | ) nl {return res}
222 |
223 | fork = "変化:" " "* te:[0-9]+ "手" nl moves:moves {return {te:parseInt(te.join("")), moves:moves.slice(1)}}
224 |
225 |
226 | nl = newline+ skipline*
227 | skipline = ("#" nonl* newline)
228 | whitespace = " " / "\t"
229 | newline = whitespace* ("\n" / "\r" "\n"?)
230 | nonl = [^\r\n]
231 |
--------------------------------------------------------------------------------
/src/peg/parsers.ts:
--------------------------------------------------------------------------------
1 | import {IJSONKifuFormat} from "../Formats";
2 | import "./ambient";
3 | import * as CSAParser from "./csa-parser.pegjs";
4 | import * as KI2Parser from "./ki2-parser.pegjs";
5 | import * as KIFParser from "./kif-parser.pegjs";
6 |
7 | export function parseCSA(csaString: string): IJSONKifuFormat {
8 | return CSAParser.parse(csaString);
9 | }
10 |
11 | export function parseKIF(kifString: string): IJSONKifuFormat {
12 | return KIFParser.parse(kifString);
13 | }
14 |
15 | export function parseKI2(ki2String: string): IJSONKifuFormat {
16 | return KI2Parser.parse(ki2String);
17 | }
18 |
--------------------------------------------------------------------------------
/test/board-serializer.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-undef
2 | module.exports = {
3 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
4 | serialize(val, config, indentation, depth, refs, printer) {
5 | return JSON.stringify(val);
6 | },
7 | test(val) {
8 | return val && Object.prototype.hasOwnProperty.call(val, "moves");
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/test/debug-comment-content.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
31 |
--------------------------------------------------------------------------------
/test/files/csa/2005_GPS_K-SHOGI.csa:
--------------------------------------------------------------------------------
1 | N+sente
2 | N-gote
3 | P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
4 | P2 * -HI * * * * * -KA *
5 | P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
6 | P4 * * * * * * * * *
7 | P5 * * * * * * * * *
8 | P6 * * * * * * * * *
9 | P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
10 | P8 * +KA * * * * * +HI *
11 | P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
12 | +
13 | 'Tue May 3 13:50:01 2005
14 | +7776FU
15 | T12
16 | 'time left 1488 1500
17 | -3334FU
18 | T1
19 | 'time left 1488 1499
20 | +2726FU
21 | T1
22 | 'time left 1487 1499
23 | -4344FU
24 | T1
25 | 'time left 1487 1498
26 | +3948GI
27 | T1
28 | 'time left 1486 1498
29 | -8242HI
30 | T1
31 | 'time left 1486 1497
32 | +5968OU
33 | T1
34 | 'time left 1485 1497
35 | -5162OU
36 | T1
37 | 'time left 1485 1496
38 | +6878OU
39 | T1
40 | 'time left 1484 1496
41 | -6272OU
42 | T1
43 | 'time left 1484 1495
44 | +5756FU
45 | T1
46 | 'time left 1483 1495
47 | -7282OU
48 | T1
49 | 'time left 1483 1494
50 | +4958KI
51 | T1
52 | 'time left 1482 1494
53 | -7172GI
54 | T1
55 | 'time left 1482 1493
56 | +9796FU
57 | T1
58 | 'time left 1481 1493
59 | -9394FU
60 | T1
61 | 'time left 1481 1492
62 | +7968GI
63 | T1
64 | 'time left 1480 1492
65 | -3435FU
66 | T1
67 | 'time left 1480 1491
68 | +6766FU
69 | T12
70 | 'time left 1468 1491
71 | -4232HI
72 | T18
73 | 'time left 1468 1473
74 | +6857GI
75 | T15
76 | 'time left 1453 1473
77 | -3142GI
78 | T33
79 | 'time left 1453 1440
80 | +2625FU
81 | T9
82 | 'time left 1444 1440
83 | -3234HI
84 | T8
85 | 'time left 1444 1432
86 | +6665FU
87 | T7
88 | 'time left 1437 1432
89 | -1314FU
90 | T5
91 | 'time left 1437 1427
92 | +8866KA
93 | T12
94 | 'time left 1425 1427
95 | -2133KE
96 | T5
97 | 'time left 1425 1422
98 | +4746FU
99 | T14
100 | 'time left 1411 1422
101 | -7374FU
102 | T8
103 | 'time left 1411 1414
104 | +6655KA
105 | T10
106 | 'time left 1401 1414
107 | -7273GI
108 | T17
109 | 'time left 1401 1397
110 | +2524FU
111 | T8
112 | 'time left 1393 1397
113 | -3424HI
114 | T2
115 | 'time left 1393 1395
116 | +2824HI
117 | T9
118 | 'time left 1384 1395
119 | -2324FU
120 | T8
121 | 'time left 1384 1387
122 | +0023HI
123 | T15
124 | 'time left 1369 1387
125 | -4132KI
126 | T16
127 | 'time left 1369 1371
128 | +2324RY
129 | T40
130 | 'time left 1329 1371
131 | -4243GI
132 | T1
133 | 'time left 1329 1370
134 | +2427RY
135 | T14
136 | 'time left 1315 1370
137 | -0025FU
138 | T46
139 | 'time left 1315 1324
140 | +3736FU
141 | T16
142 | 'time left 1299 1324
143 | -9495FU
144 | T47
145 | 'time left 1299 1277
146 | +9695FU
147 | T10
148 | 'time left 1289 1277
149 | -9195KY
150 | T12
151 | 'time left 1289 1265
152 | +0096FU
153 | T1
154 | 'time left 1288 1265
155 | -9596KY
156 | T21
157 | 'time left 1288 1244
158 | +0097FU
159 | T1
160 | 'time left 1287 1244
161 | -0024HI
162 | T12
163 | 'time left 1287 1232
164 | +4837GI
165 | T24
166 | 'time left 1263 1232
167 | -3536FU
168 | T12
169 | 'time left 1263 1220
170 | +2736RY
171 | T22
172 | 'time left 1241 1220
173 | -2526FU
174 | T28
175 | 'time left 1241 1192
176 | +3726GI
177 | T5
178 | 'time left 1236 1192
179 | -5354FU
180 | T28
181 | 'time left 1236 1164
182 | +5566KA
183 | T21
184 | 'time left 1215 1164
185 | -0028FU
186 | T21
187 | 'time left 1215 1143
188 | +2635GI
189 | T1
190 | 'time left 1214 1143
191 | -2423HI
192 | T11
193 | 'time left 1214 1132
194 | +9796FU
195 | T9
196 | 'time left 1205 1132
197 | -2231KA
198 | T13
199 | 'time left 1205 1119
200 | +2937KE
201 | T4
202 | 'time left 1201 1119
203 | -0098FU
204 | T16
205 | 'time left 1201 1103
206 | +0024FU
207 | T24
208 | 'time left 1177 1103
209 | -2321HI
210 | T42
211 | 'time left 1177 1061
212 | +9998KY
213 | T22
214 | 'time left 1155 1061
215 | -2829TO
216 | T3
217 | 'time left 1155 1058
218 | +0034FU
219 | T18
220 | 'time left 1137 1058
221 | -2919TO
222 | T26
223 | 'time left 1137 1032
224 | +3433TO
225 | T1
226 | 'time left 1136 1032
227 | -3233KI
228 | T45
229 | 'time left 1136 987
230 | +3745KE
231 | T14
232 | 'time left 1122 987
233 | -3332KI
234 | T41
235 | 'time left 1122 946
236 | +0023KY
237 | T15
238 | 'time left 1107 946
239 | -0022FU
240 | T46
241 | 'time left 1107 900
242 | +0033KE
243 | T17
244 | 'time left 1090 900
245 | -0034FU
246 | T45
247 | 'time left 1090 855
248 | +3534GI
249 | T19
250 | 'time left 1071 855
251 | -3197UM
252 | T19
253 | 'time left 1071 836
254 | +8997KE
255 | T30
256 | 'time left 1041 836
257 | -2151HI
258 | T30
259 | 'time left 1041 806
260 | +2322NY
261 | T2
262 | 'time left 1039 806
263 | -3242KI
264 | T32
265 | 'time left 1039 774
266 | +0086KA
267 | T1
268 | 'time left 1038 774
269 | -6152KI
270 | T20
271 | 'time left 1038 754
272 | +9785KE
273 | T34
274 | 'time left 1004 754
275 | -4445FU
276 | T30
277 | 'time left 1004 724
278 | +8573NK
279 | T40
280 | 'time left 964 724
281 | -8273OU
282 | T14
283 | 'time left 964 710
284 | +0031GI
285 | T16
286 | 'time left 948 710
287 | -0053KE
288 | T25
289 | 'time left 948 685
290 | +8695KA
291 | T1
292 | 'time left 947 685
293 | -0084KY
294 | T21
295 | 'time left 947 664
296 | +3142GI
297 | T20
298 | 'time left 927 664
299 | -5242KI
300 | T25
301 | 'time left 927 639
302 | +3443NG
303 | T1
304 | 'time left 926 639
305 | -4243KI
306 | T26
307 | 'time left 926 613
308 | +0085GI
309 | T1
310 | 'time left 925 613
311 | -7372OU
312 | T21
313 | 'time left 925 592
314 | +8584GI
315 | T23
316 | 'time left 902 592
317 | -8384FU
318 | T21
319 | 'time left 902 571
320 | +0062KI
321 | T22
322 | 'time left 880 571
323 | -7262OU
324 | T20
325 | 'time left 880 551
326 | +6684KA
327 | T21
328 | 'time left 859 551
329 | -0073GI
330 | T25
331 | 'time left 859 526
332 | +3341NK
333 | T21
334 | 'time left 838 526
335 | -5141HI
336 | T25
337 | 'time left 838 501
338 | +3632RY
339 | T18
340 | 'time left 820 501
341 | -4342KI
342 | T11
343 | 'time left 820 490
344 | +3241RY
345 | T7
346 | 'time left 813 490
347 | -4241KI
348 | T21
349 | 'time left 813 469
350 | +8473UM
351 | T1
352 | 'time left 812 469
353 | -8173KE
354 | T15
355 | 'time left 812 454
356 | +0092HI
357 | T1
358 | 'time left 811 454
359 | -0072HI
360 | T15
361 | 'time left 811 439
362 | +9573UM
363 | T22
364 | 'time left 789 439
365 | -6273OU
366 | T9
367 | 'time left 789 430
368 | +0085KE
369 | T11
370 | 'time left 778 430
371 | -7362OU
372 | T20
373 | 'time left 778 410
374 | +0073GI
375 | T1
376 | 'time left 777 410
377 | -6252OU
378 | T16
379 | 'time left 777 394
380 | +9272RY
381 | T26
382 | 'time left 751 394
383 | -5243OU
384 | T20
385 | 'time left 751 374
386 | +0023HI
387 | T2
388 | 'time left 749 374
389 | -0033GI
390 | T16
391 | 'time left 749 358
392 | +2333RY
393 | T25
394 | 'time left 724 358
395 | -4333OU
396 | T1
397 | 'time left 724 357
398 | +2423TO
399 | T25
400 | 'time left 699 357
401 | -3334OU
402 | T15
403 | 'time left 699 342
404 | +0038KY
405 | T16
406 | 'time left 683 342
407 | -0036KE
408 | T11
409 | 'time left 683 331
410 | +3836KY
411 | T18
412 | 'time left 665 331
413 | -3425OU
414 | T15
415 | 'time left 665 316
416 | +0027GI
417 | T2
418 | 'time left 663 316
419 | -0026KI
420 | T11
421 | 'time left 663 305
422 | +0037KE
423 | T19
424 | 'time left 644 305
425 | -2515OU
426 | T10
427 | 'time left 644 295
428 | +0028FU
429 | T1
430 | 'time left 643 295
431 | -2637KI
432 | T15
433 | 'time left 643 280
434 | +2211NY
435 | T1
436 | 'time left 642 280
437 | -0077GI
438 | T15
439 | 'time left 642 265
440 | +7877OU
441 | T21
442 | 'time left 621 265
443 | -0044KA
444 | T8
445 | 'time left 621 257
446 | +7767OU
447 | T5
448 | 'time left 616 257
449 | -0089KA
450 | T15
451 | 'time left 616 242
452 | +0078GI
453 | T33
454 | 'time left 583 242
455 | -5365KE
456 | T15
457 | 'time left 583 227
458 | +1716FU
459 | T9
460 | 'time left 574 227
461 | -1525OU
462 | T1
463 | 'time left 574 226
464 | +0026KY
465 | T27
466 | 'time left 547 226
467 | -4426KA
468 | T1
469 | 'time left 547 225
470 | +2324TO
471 | T20
472 | 'time left 527 225
473 | -2524OU
474 | T1
475 | 'time left 527 224
476 | +7222RY
477 | T20
478 | 'time left 507 224
479 | -0023FU
480 | T4
481 | 'time left 507 220
482 | +2233RY
483 | T14
484 | 'time left 493 220
485 | -2413OU
486 | T3
487 | 'time left 493 217
488 | +2726GI
489 | T15
490 | 'time left 478 217
491 | -6557NK
492 | T1
493 | 'time left 478 216
494 | +5857KI
495 | T60
496 | 'time left 418 216
497 | -0075KE
498 | T7
499 | 'time left 418 209
500 | +6777OU
501 | T9
502 | 'time left 409 209
503 | -7587NK
504 | T4
505 | 'time left 409 205
506 | +7787OU
507 | T19
508 | 'time left 390 205
509 | -0086FU
510 | T1
511 | 'time left 390 204
512 | +8786OU
513 | T6
514 | 'time left 384 204
515 | -0088HI
516 | T1
517 | 'time left 384 203
518 | +0087FU
519 | T1
520 | 'time left 383 203
521 | -0094KE
522 | T4
523 | 'time left 383 199
524 | +8695OU
525 | T1
526 | 'time left 382 199
527 | -0084GI
528 | T2
529 | 'time left 382 197
530 | +7384NG
531 | T1
532 | 'time left 381 197
533 | %TORYO
534 | '+ win
535 | 'Tue May 3 14:34:13 2005
536 | 'game end
537 |
--------------------------------------------------------------------------------
/test/files/csa/2005_YAMADA_GPS.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/2005_YAMADA_GPS.csa
--------------------------------------------------------------------------------
/test/files/csa/8mai.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/8mai.csa
--------------------------------------------------------------------------------
/test/files/csa/8mai_hirate.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/8mai_hirate.csa
--------------------------------------------------------------------------------
/test/files/csa/9fu.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/9fu.csa
--------------------------------------------------------------------------------
/test/files/csa/9fu_komabetsu.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/9fu_komabetsu.csa
--------------------------------------------------------------------------------
/test/files/csa/chudan.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/chudan.csa
--------------------------------------------------------------------------------
/test/files/csa/dr1test1test0+tu-1_aobazero_viper-300-5F+aobazero+viper+20200719201040.csa:
--------------------------------------------------------------------------------
1 | V2
2 | N+aobazero
3 | N-viper
4 | 'Max_Moves:512
5 | 'Least_Time_Per_Move:0
6 | 'Increment:5
7 | $EVENT:dr1test1test0+tu-1_aobazero_viper-300-5F+aobazero+viper+20200719201040
8 | $START_TIME:2020/07/19 20:10:37
9 | P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
10 | P2 * -HI * * * * * -KA *
11 | P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
12 | P4 * * * * * * * * *
13 | P5 * * * * * * * * *
14 | P6 * * * * * * * * *
15 | P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
16 | P8 * +KA * * * * * +HI *
17 | P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
18 | +
19 | +7776FU
20 | T10
21 | '** 130 7g7f 8c8d 7i6h 3c3d
22 | -4132KI
23 | T0
24 | '** 50 +2726FU -8384FU +7968GI -3334FU +6877GI -3142GI +2625FU
25 | +2726FU
26 | T10
27 | '** 106 2g2f 8c8d 7i6h 3c3d
28 | -8384FU
29 | T0
30 | '** 50 +7978GI -3334FU +7877GI -3142GI +2625FU -4233GI +5756FU
31 | +7968GI
32 | T10
33 | '** 104 7i6h 3c3d 6h7g 7c7d
34 | -3334FU
35 | T0
36 | '** 46 +6877GI -3142GI +4958KI -8485FU +6978KI -5354FU +2625FU
37 | +6877GI
38 | T9
39 | '** 98 6h7g 7c7d 2f2e 7a6b
40 | -3142GI
41 | T0
42 | '** 46 +4958KI -8485FU +6978KI -5354FU +2625FU -4233GI +3948GI
43 | +2625FU
44 | T10
45 | '** 144 2f2e 4b3c 3i4h 8d8e
46 | -4233GI
47 | T0
48 | '** 46 +5756FU -8485FU +3948GI -5354FU +3736FU -7374FU +6978KI
49 | +3948GI
50 | T20
51 | '** 144 3i4h 8d8e 6i7h 7a6b
52 | -1314FU
53 | T0
54 | '** 44 +8879KA -5141OU +5756FU -7162GI +3736FU -5354FU +6978KI
55 | +3736FU
56 | T20
57 | '** 161 3g3f 8d8e 6i7h 7a6b
58 | -7162GI
59 | T0
60 | '** 44 +6978KI -8485FU +5969OU -5141OU +4958KI -7374FU +5756FU
61 | +6978KI
62 | T20
63 | '** 163 6i7h 8d8e 5g5f 7c7d
64 | -8485FU
65 | T0
66 | '** 44 +5969OU -5141OU +4958KI -7374FU +5756FU -5354FU +8879KA
67 | +5756FU
68 | T20
69 | '** 161 5g5f 7c7d 5i6i 5a4a
70 | -2231KA
71 | T0
72 | '** 16 +4958KI -5354FU
73 | +5969OU
74 | T20
75 | '** 177 5i6i 7c7d 8h7i 5c5d
76 | -6152KI
77 | T0
78 | '** 6 +4837GI
79 | +8879KA
80 | T20
81 | '** 196 8h7i 5c5d 6g6f 7c7d
82 | -5354FU
83 | T5
84 | '** 20 +7946KA -3164KA +4958KI -7374FU +9796FU -4344FU +6979OU -5141OU +9695FU -8173KE +6766FU -4131OU +7988OU -3122OU +1716FU -6446KA +4746FU -6364FU +2937KE -6465FU +4645FU -6566FU +4544FU -3344GI +2524FU -2324FU +0067FU -8586FU +7786GI -0069KA
85 | +6766FU
86 | T15
87 | '** 193 6g6f 7c7d 4i5h 5a4a
88 | -3164KA
89 | T16
90 | '** 54 +7946KA -5141OU +4958KI -4344FU +6979OU -9394FU +9796FU -4131OU +1716FU -6253GI +4837GI -7374FU +3726GI -8173KE +3635FU -3435FU +2635GI -0034FU +2524FU -2324FU +3524GI -3324GI +2824HI -6446KA +4746FU -0023FU +2428HI
91 | +2937KE
92 | T20
93 | '** 206 2i3g 5a4a 4i5h 7c7d
94 | -5141OU
95 | T9
96 | '** 43 +7946KA
97 | +4958KI
98 | T10
99 | '** 174 4i5h 7c7d 7h6g 9c9d
100 | -7374FU
101 | T0
102 | '** 88 +7867KI -4131OU +6978OU -4344FU +4746FU -5243KI +1716FU
103 | +7867KI
104 | T20
105 | '** 165 7h6g 9c9d 9g9f 4c4d
106 | -4131OU
107 | T0
108 | '** 88 +6978OU -4344FU +4746FU -9394FU +9796FU -5243KI +1716FU
109 | +6978OU
110 | T20
111 | '** 174 6i7h 9c9d 9g9f 6b7c
112 | -4344FU
113 | T0
114 | '** 88 +4746FU -9394FU +9796FU -5243KI +1716FU -6473KA +4847GI
115 | +4746FU
116 | T20
117 | '** 184 4g4f 5b4c 4h4g 6b7c
118 | -9394FU
119 | T0
120 | '** 88 +9796FU -5243KI +1716FU -6473KA +4847GI
121 | +9796FU
122 | T20
123 | '** 222 9g9f 8a7c 4h4g 3b4c
124 | -5243KI
125 | T0
126 | '** 88 +1716FU -6473KA +4847GI
127 | +4847GI
128 | T20
129 | '** 291 4h4g 6b7c 2h2i 7c8d
130 | -1415FU
131 | T0
132 | '** 53 +1918KY -3122OU
133 | +2829HI
134 | T20
135 | '** 282 2h2i 6b7c 6f6e 6d4b
136 | -3122OU
137 | T0
138 | '** -11 +1918KY
139 | +1918KY
140 | T20
141 | '** 425 1i1h 8a7c 2i1i 8b8a
142 | -6273GI
143 | T3
144 | '** 56
145 | +1716FU
146 | T20
147 | '** 430 1g1f 1e1f 2i1i 7c8d
148 | -1516FU
149 | T3
150 | '** 137
151 | +2919HI
152 | T11
153 | '** 418 2i1i 7c8d 1h1f P*1d
154 | -7475FU
155 | T11
156 | '** 60 +7675FU -7384GI +1816KY -1116KY +1916HI -8475GI +6665FU -6442KA +7957KA -0014FU +0076FU -8586FU +8786FU -7586GI +7786GI -8286HI +0087KY -0069GI +7888OU -0084KY +8786KY -4286KA +0087FU -6958GI +4758GI -8659UM +5784KA -5958UM
157 | +7675FU
158 | T10
159 | '** 503 7f7e 7c8d 6f6e 6d4b
160 | -7384GI
161 | T3
162 | '** 60 +6665FU -6442KA +1816KY -0014FU +4645FU -4445FU +3745KE -3344GI +2524FU -2324FU +0025FU -8173KE +0013FU -1113KY +7924KA -7365KE +7766GI -4445GI +2442UM -4342KI +0046FU -0037KA +1949HI -3726UM +4645FU -2616UM +4919HI
163 | +6665FU
164 | T6
165 | '** 521 6f6e 6d4b 4f4e 4d4e
166 | -6442KA
167 | T6
168 | '** 61 +4645FU -4445FU +1816KY -1116KY +1916HI -0015FU +1615HI -0013FU +1518HI -8173KE +3745KE -7365KE +0016KY -8475GI +1613NY -2231OU +0076FU -8586FU +7786GI -7586GI +8786FU -4286KA +0083FU -8283HI +0084FU -8384HI
169 | +4645FU
170 | T3
171 | '** 506 4f4e 4d4e 1h1f P*1d
172 | -4445FU
173 | T3
174 | '** 74 +1816KY -0014FU +3745KE -3344GI +2524FU -2324FU +0025FU -8173KE +7924KA -7365KE +2442UM -3242KI +0012FU -6577NK +6777KI -1112KY +2524FU -4445GI +1614KY -1214KY +1914HI -0013KY +0011KA -2231OU +0044FU -1314KY +4443TO -4243KI +2423TO -0065KE
175 | +1816KY
176 | T6
177 | '** 480 1h1f P*1d 3g4e 3c4d
178 | -0014FU
179 | T9
180 | '** 196 +3745KE
181 | +3745KE
182 | T0
183 | '** 460 3g4e 3c4d 1f1d 1a1d
184 | -3344GI
185 | T5
186 | '** 192 +1614KY
187 | +1614KY
188 | T4
189 | '** 444 1f1d 1a1d 1i1d L*1a
190 | -0012FU
191 | T19
192 | '** 214 +2524FU
193 | +2524FU
194 | T10
195 | '** 697 2e2d 2c2d P*2e 2d2e
196 | -4224KA
197 | T3
198 | '** 173
199 | +7924KA
200 | T10
201 | '** 608 7i2d 2c2d 1i2i B*3g
202 | -2324FU
203 | T3
204 | '** 264
205 | +1929HI
206 | T6
207 | '** 591 1i2i B*3g B*5a 1b1c
208 | -0037KA
209 | T3
210 | '** 261 +0042FU
211 | +0051KA
212 | T0
213 | '** 575 B*5a 1b1c 5a2d+ P*2c
214 | -3242KI
215 | T14
216 | '** 72 +3635FU -4445GI +0046FU -4546GI +0044FU -4344KI +4746GI -3746UM +0083GI -8252HI +5184UM -4636UM +5868KI -3614UM +6564FU -5253HI +8462UM -0065KY +0025FU -2425FU +0036GI -5343HI +3625GI -6567NY +7867OU -1425UM +2925HI -0024FU +2524HI -0023FU
217 | +0016FU
218 | T10
219 | '** 540 P*1f 1b1c 6g5g 4d4e
220 | -1213FU
221 | T5
222 | '** 211 +3635FU
223 | +6757KI
224 | T0
225 | '** 550 6g5g 4d4e P*4f 8d7e
226 | -4445GI
227 | T20
228 | '** 237 +0046FU -8475GI +0076FU -8586FU +8786FU -7586GI +7786GI -8286HI +0077GI -8682HI +4645FU -0028GI +4544FU -4344KI +2949HI -3726UM +0071GI -8252HI +7162NG -5251HI +6251NG -0045KE +5746KI -4232KI +0082HI -2223OU +0025FU -2616UM +4979HI -1625UM +8281RY -2314OU
229 | +0046FU
230 | T0
231 | '** 512 P*4f 8d7e P*7f 8e8f
232 | -8475GI
233 | T5
234 | '** 206 +0076FU -8586FU +8786FU -7586GI +7786GI -8286HI +0087FU -8676HI +0077FU -0066KE +5766KI -7666HI +4645FU -0086FU +0067GI -8687TO +7887OU -6667RY +5867KI -1314FU +0072HI -0086FU +8786OU -0085FU +8685OU -0041FU +4544FU -0061KI +4443TO -6172KI +4342TO
235 | +0076FU
236 | T4
237 | '** 484 P*7f 8e8f 8g8f 7e8f
238 | -8586FU
239 | T3
240 | '** 240 +8786FU
241 | +8786FU
242 | T6
243 | '** 470 8g8f 7e8f 7g8f 8b8f
244 | -7586GI
245 | T3
246 | '** 305 +7786GI
247 | +7786GI
248 | T6
249 | '** 464 7g8f 8b8f S*7g 8f8b
250 | -8286HI
251 | T3
252 | '** 355 +0077GI -8682HI +4645FU -0028GI +4544FU -2829NG +4443TO -4243KI +0044FU -4344KI +5124UM -0023FU +2451UM -0086KE +7867OU -0069HI +5868KI -6989RY +0083FU -8272HI +0024FU -0085KE +2423TO -2223OU +7786GI -8986RY +0015KE -2332OU +0041GI -3243OU +5161UM -4333OU +0023KI -3342OU +6172UM
253 | +0077GI
254 | T6
255 | '** 484 S*7g 8f8b 4f4e S*2h
256 | -8682HI
257 | T3
258 | '** 245 +4645FU
259 | +4645FU
260 | T6
261 | '** 481 4f4e S*2h 2i4i P*8f
262 | -0028GI
263 | T25
264 | '** 392 +2949HI -3726UM +0083FU -8272HI +5184UM -0088FU +0082GI -2133KE +4746GI -8889TO +4989HI -1314FU +8291GI -7252HI +8382TO -2837GI +8281TO -3746NG +5746KI -0083FU +8466UM -0074KE +6675UM -0084KY +0085FU -8485KY +8985HI
265 | +2949HI
266 | T0
267 | '** 465 2i4i P*8f P*8c 8b5b
268 | -0086FU
269 | T17
270 | '** 296 +0083FU
271 | +0083FU
272 | T0
273 | '** 463 P*8c 8b5b 5a8d+ 1c1d
274 | -8272HI
275 | T3
276 | '** 264 +4544FU
277 | +4544FU
278 | T15
279 | '** 564 4e4d 4c3c 3f3e 3g2f+
280 | -4333KI
281 | T3
282 | '** 362 +3635FU
283 | +3635FU
284 | T6
285 | '** 538 3f3e 1c1d 4g4f 3g2f+
286 | -1314FU
287 | T3
288 | '** 337
289 | +7786GI
290 | T6
291 | '** 523 7g8f 3g2f+ 4g4f 2f1f
292 | -3726UM
293 | T7
294 | '** 220 +4746GI -0041KY +5184UM -3435FU +0034FU -3332KI +8451UM -2616UM +4947HI -1638UM +4443TO -3243KI +8677GI -0086FU +5161UM -7273HI +4635GI -3847UM +5747KI
295 | +4746GI
296 | T2
297 | '** 487 4g4f 2f1f 4i6i 3d3e
298 | -0041KY
299 | T4
300 | '** 202 +5184UM
301 | +5184UM
302 | T10
303 | '** 658 5a8d+ 3d3e 8d5a 2f1f
304 | -3435FU
305 | T3
306 | '** 257
307 | +8451UM
308 | T6
309 | '** 701 8d5a 2f1f 4i6i 1f2e
310 | -2616UM
311 | T3
312 | '** 288 +4969HI
313 | +4969HI
314 | T6
315 | '** 741 4i6i 1f2e 6e6d 7b7f
316 | -5455FU
317 | T11
318 | '** 243 +4655GI -1625UM +4443TO -3343KI +8677GI -0045KE +0026FU -4557KE +2625FU -5769NK +2524FU -0038HI +7867OU -0071FU +0061KA -0057FU +6172UM -7172FU +0023HI -2232OU +0022GI -3858RY +6766OU -0067KI +6675OU
319 | +4655GI
320 | T10
321 | '** 605 4f5e 1f3d 6e6d N*4e
322 | -1625UM
323 | T3
324 | '** 213 +4443TO
325 | +6968HI
326 | T4
327 | '** 651 6i6h 2h3g P*3h P*5d
328 | -2837NG
329 | T28
330 | '** 62 +5867KI -8193KE +0045GI -7252HI +0026FU -5251HI +2625FU -0088FU +7888OU -0059KA +0034FU -3332KI +5758KI -5968UM +5868KI -0087FU +8887OU -0049HI +0073KA -4989RY +0088KA -0085FU
331 | +6564FU
332 | T4
333 | '** 447 6e6d N*4e P*2f 4e5g+
334 | -0047FU
335 | T27
336 | '** 45 +5867KI -7271HI +5162UM -7161HI +0026FU -2534UM +6272UM -6151HI +0043GI -4243KI +4443TO -3443UM +7262UM -5155HI +5655FU -0085FU +8677GI -0059GI +0082HI -8586FU +7786GI -5968NG +6768KI -0059GI +6251UM -0052FU +5141UM -5968NG +7868OU
337 | +6463TO
338 | T4
339 | '** 698 6d6c+ 7b7f S*7g 7f7a
340 | -7271HI
341 | T3
342 | '** -43
343 | +5162UM
344 | T4
345 | '** 523 5a6b 7a7f 8f7g 7f7g+
346 | -7176HI
347 | T7
348 | '** -64 +8677GI
349 | +8677GI
350 | T0
351 | '** 479 8f7g 7f7g+ 7h7g 4g4h+
352 | -7677RY
353 | T13
354 | '** 130 +7877OU -4748TO +5848KI -3748NG +6284UM -4847NG +0034FU -3332KI +5767KI -2534UM +0071HI -0058GI +0065GI -0054FU +7181RY -0092KE +0026KE -3445UM +8466UM -5455FU +8191RY -4563UM +9192RY -2213OU +0016KY -3223KI +4443TO -4243KI +6776KI -0085GI +8382TO
355 | +7877OU
356 | T0
357 | '** 534 7h7g 4g4h+ 5h4h 3g4h
358 | -4748TO
359 | T11
360 | '** 10 +5848KI -3748NG +0071HI -0045KE +5767KI -4858NG +6888HI -0087FU +7787OU -4557NK +6776KI -5756NK +5564GI -0084FU +8382TO -0067GI +8786OU -0061FU +6251UM -6776NG +8676OU
361 | +5867KI
362 | T0
363 | '** 581 5h6g N*6e 7g8f 6e5g+
364 | -0065KE
365 | T5
366 | '** -92 +7766OU
367 | +7786OU
368 | T5
369 | '** 511 7g8f 6e5g+ 6g5g S*7d
370 | -6557NK
371 | T5
372 | '** -284 +6757KI -0074KI +0034FU -0085FU +8697OU -2534UM +0026KE -7475KI +2634KE -3334KI +0054KA -7586KI +9798OU -0075KE +0076GI -8676KI +5476KA -0086GI +0088KI -3747NG +0023FU -2223OU +7621UM -4757NG +0022HI -2313OU
373 | +6757KI
374 | T4
375 | '** 443 6g5g S*7d P*3d 2e3d
376 | -0074GI
377 | T37
378 | '** -48 +0034FU -0085FU +8677OU -0086KI +7788OU -2534UM +0026KE -3423UM +0034FU -3332KI +6353TO -3747NG +5747KI -4847TO +5342TO -4142KY +0041HI -0087KI +8879OU -0077FU +4443TO -4243KY +3433TO -2133KE +0034KI -7778TO +6878HI -8778KI +7978OU -0028HI +0048FU -2826RY +3423KI -2223OU +4121RY -2334OU +2132RY
379 | +0034FU
380 | T0
381 | '** 463 P*3d 2e3d N*2f P*8e
382 | -2534UM
383 | T6
384 | '** -138 +0026KE
385 | +0026KE
386 | T9
387 | '** 422 N*2f P*8e 8f7g P*7f
388 | -0085FU
389 | T12
390 | '** 75 +8677OU -0076FU +7788OU -3423UM +0034FU -3332KI +6352TO -7677TO +8877OU -3747NG +5747KI -4847TO +5242TO -4142KY +0041HI -0086KI +7788OU -0077FU +4443TO -4243KY +3433TO -2333UM +0034GI -0087KI +8879OU -8778KI +6878HI -7778TO +7978OU -3334UM +2634KE -2213OU +6235UM
391 | +8677OU
392 | T2
393 | '** 440 8f7g P*7f 7g8h 3d2c
394 | -0086KI
395 | T33
396 | '** 71 +7788OU -3423UM +6352TO -3747NG +5747KI -4847TO +5242TO -4142KY +0071HI -2356UM +0031GI -2212OU +7151RY -0067FU +6867HI -0032KI +0034FU -3323KI +3142NG -5667UM +5121RY -1213OU +2111RY -0012HI +1112RY -1312OU +0013FU -1213OU +0011HI -0012HI
397 | +7788OU
398 | T15
399 | '** 556 7g8h 3d2c 6c5c 3g4g
400 | -3423UM
401 | T4
402 | '** -118
403 | +6353TO
404 | T6
405 | '** 490 6c5c 3g4g 5g4g 4h4g
406 | -3747NG
407 | T5
408 | '** -107
409 | +5747KI
410 | T4
411 | '** 497 5g4g 4h4g 5c4b 4a4b
412 | -4847TO
413 | T7
414 | '** 90 +5342TO -4142KY +0071HI -2356UM +0031GI -2212OU +0013FU -1223OU +3142GI -5655UM +0077FU -0087GI +8879OU -0078KI +6878HI -8778NG +7978OU -0028HI +0068KY -2826RY +6252UM -0043FU +4443TO -2334OU +7174RY -3425OU +4333TO -2536OU +5263UM -3637OU +7475RY -0066KE +6866KY -5566UM +7566RY -2666RY +0059KA -0048HI +5948KA -3748OU
415 | +5342TO
416 | T2
417 | '** 398 5c4b 4a4b R*5b P*4a
418 | -4142KY
419 | T3
420 | '** -7 +0071HI
421 | +0052HI
422 | T6
423 | '** 369 R*5b P*4a P*3d P*6g
424 | -0041FU
425 | T34
426 | '** 186 +0034FU
427 | +0034FU
428 | T0
429 | '** 346 P*3d P*6g 6h1h G*8g
430 | -3332KI
431 | T4
432 | '** 137 +4443TO -3243KI +6244UM -4344KI +5544GI -0032KI +0043FU -0076KA +0098GI -3243KI +4435GI -0067FU +6818HI -2231OU +0044FU -4344KI +3544GI -4244KY +0022KI -2322UM +5222RY -3122OU +0033KI -2133KE +3433TO -2233OU
433 | +4443TO
434 | T15
435 | '** 657 4d4c+ 3b4c 5e4d P*6g
436 | -4243KY
437 | T3
438 | '** 463 +6251UM
439 | +0044FU
440 | T10
441 | '** 1016 P*4d 2b1c P*1e P*7g
442 | -0078FU
443 | T28
444 | '** 1073 +6867HI -7463GI +6263UM -0087KI +6787HI -8687KI +8887OU -0067HI +0077KI -6763RY +5282RY -0073KA +0054GI -7382KA +8382TO -6373RY +4443TO -3243KI +5443GI -7343RY +0076KI -0086GI +7786KI -8586FU +7686KI -0085FU +8685KI -0084FU +3433TO -4333RY
445 | +6867HI
446 | T10
447 | '** 1494 6h6g 2b1c 4d4c+ 7h7i+
448 | -7463GI
449 | T6
450 | '** 1277 +6263UM -0087KI +6787HI -8687KI +8887OU -0067HI +0077KI -6763RY +5282RY -7879TO +0064KI -6364RY +5564GI -7989TO +4443TO -0065KA +0076GI -6543KA +0044GI -0088KI +8797OU -4376KA +7776KI -0078GI +0077KI -0074KE +0055KA -2213OU +0028HI -0066FU +7778KI -8878KI +2878HI -0086KI +7686KI -8586FU
451 | +6263UM
452 | T4
453 | '** 2271 6b6c 7h7i+ 8h7i P*7g
454 | -0087KI
455 | T3
456 | '** 1286 +6787HI
457 | +6787HI
458 | T4
459 | '** 2058 6g8g 8f8g 8h8g R*6g
460 | -8687KI
461 | T4
462 | '** 1341 +8887OU -0067HI +0077KI -6763RY +5282RY -2213OU +4443TO -6343RY +0015FU -2425FU +0044GI -4373RY +4435GI -0079KA +3433TO -7935UM +3323TO -3223KI +1514FU -2314KI +0022GI -1324OU +0051KA -0033FU +5173UM -8173KE +8273RY
463 | +8887OU
464 | T0
465 | '** 1984 8h8g R*6g G*7g 6g6c+
466 | -0067HI
467 | T6
468 | '** 1278 +0077KI
469 | +0077KI
470 | T4
471 | '** 1959 G*7g 6g6c+ 5b8b+ B*7c
472 | -6763RY
473 | T3
474 | '** 1295 +5282RY
475 | +5282RY
476 | T6
477 | '** 2028 5b8b+ B*7c P*6d 7c8b
478 | -0073KA
479 | T28
480 | '** 1338 +0054GI -7382KA +8382TO -6374RY +4443TO -0086HI +8778OU -3243KI +0076KY -8676HI +5443NG -2213OU +0044KA -7444RY +4344NG -7656HI +0022GI -2322UM +0053HI -0023GI +3433TO -2133KE +4433NG -5658RY +0068KE
481 | +0064FU
482 | T0
483 | '** 1912 P*6d 7c8b 6d6c+ 8b5e
484 | -7382KA
485 | T6
486 | '** 1343 +6463TO
487 | +6463TO
488 | T4
489 | '** 1929 6d6c+ 8b5e 5f5e R*6i
490 | -8255KA
491 | T3
492 | '** 1326 +5655FU
493 | +5655FU
494 | T6
495 | '** 1938 5f5e R*6i 4d4c+ S*8f
496 | -0046HI
497 | T30
498 | '** 1536 +0066GI
499 | +0066GI
500 | T10
501 | '** 2160 S*6f S*8f 8g7f 8f7g
502 | -0057GI
503 | T7
504 | '** 1563 +8776OU
505 | +8776OU
506 | T10
507 | '** 2374 8g7f 5g6f 7g6f S*5g
508 | -5766GI
509 | T3
510 | '** 1603 +7766KI
511 | +7766KI
512 | T0
513 | '** 2304 7g6f S*5g G*5f 5g6f+
514 | -4344KY
515 | T6
516 | '** 1595 +0072HI -0057GI +0077GI -4626HI +0053KA -2646HI +7675OU -5766NG +7766GI -0052FU +5364UM -2334UM +0065KI -3456UM +0076KI -0053KE +7252RY -5365KE +0043GI -0084KI +7584OU
517 | +0072HI
518 | T10
519 | '** 2068 R*7b 4f2f S*4c S*6a
520 | -0057GI
521 | T10
522 | '** 1600 +0077GI -0052FU +7675OU -5766GI +7766GI -4626HI +0065KI -2334UM +0082GI -4757TO +8281NG -5767TO +6677GI -0053KE +8191NG -5365KE +7584OU -6777TO +8977KE -3456UM +8382TO -7879TO +0074KI
523 | +0056KI
524 | T4
525 | '** 1988 G*5f 4f2f S*4c 5g6f+
526 | -5766NG
527 | T13
528 | '** 1652 +5666KI -0052FU +7252RY -4626HI +0053KA -2212OU +5364UM -4757TO +0077KI -5756TO +6675KI -5667TO +7665OU -6777TO +8977KE -2627RY +0066GI -3242KI +5272RY -0062FU +7262RY -0061FU +6272RY -7879TO +3433TO -2133KE +8382TO
529 | +5666KI
530 | T4
531 | '** 1834 5f6f 4f2f S*4c P*6b
532 | -0052FU
533 | T3
534 | '** 1647 +7252RY
535 | +0075GI
536 | T10
537 | '** 2456 S*7e 4f2f 7f8e 2c3d
538 | -4626HI
539 | T14
540 | '** 1646 +7252RY -0067KI +0053KA -2213OU +5272RY -6766KI +7566GI -7879TO +0065KI -0073FU +0074FU -0052FU +5364UM -0062FU +7262RY -4757TO +7473TO -5756TO +0075GI -5666TO +6566KI -0057GI
541 | +7685OU
542 | T0
543 | '** 2451 7f8e 2c3d 8e7d G*6a
544 | -0051KE
545 | T12
546 | '** 1537 +0064GI -5163KE +6463NG -7879TO +8382TO -2334UM +0065KI -0067KI +6667KI -3467UM +8574OU -0061KI +8281TO -0062FU +6362NG -6172KI +6272NG -6789UM
547 | +0064FU
548 | T10
549 | '** 2574 P*6d 2c3d S*8b G*6a
550 | -2334UM
551 | T17
552 | '** 1646 +0077KI
553 | +0082GI
554 | T0
555 | '** 2919 S*8b P*6e 6f6e G*6a
556 | -2425FU
557 | T27
558 | '** 1937 +8281NG
559 | +8281NG
560 | T10
561 | '** 3474 8b8a+ G*6a 7b7c+ P*6e
562 | -2223OU
563 | T10
564 | '** 1983 +6352TO -3536FU +5251TO -0033KI +0022FU -2113KE +2221TO -2324OU +2122TO -3243KI +2232TO -3323KI +8382TO -2628RY +6463TO -3637TO +8191NG -7879TO +3222TO -2333KI +8594OU -3489UM +2211TO -2526FU +9483OU -2627TO +7222RY -2435OU
565 | +8191NG
566 | T4
567 | '** 3142 8a9a 2c2d 6c5b 2f2i+
568 | -3536FU
569 | T11
570 | '** 1998 +6352TO
571 | +6352TO
572 | T4
573 | '** 3202 6c5b 3f3g+ 5b5a 2f2i+
574 | -3233KI
575 | T18
576 | '** 2037 +5251TO -3637TO +8594OU -2324OU +9493OU -2629RY +0063KA -0036FU +8977KE -4757TO +9392OU -2435OU +0022GI -2999RY +2211NG -5756TO +6665KI -9997RY +5554FU -9777RY +5453TO -4447NY +1121NG -7776RY +0054GI -3526OU +8382TO -7879TO +6341UM -2627OU +0039KE -2718OU +3947KE
577 | +5251TO
578 | T4
579 | '** 2659 5b5a 3f3g+ 5a4a 2c2d
580 | -3637TO
581 | T3
582 | '** 2057 +8594OU
583 | +5141TO
584 | T0
585 | '** 2551 5a4a 2c2d 7b2b+ 2d3e
586 | -2324OU
587 | T3
588 | '** 1814
589 | +7222RY
590 | T6
591 | '** 2609 7b2b+ 2d3e 2b2a 2f2i+
592 | -2435OU
593 | T3
594 | '** 1763 +8382TO
595 | +2221RY
596 | T6
597 | '** 2461 2b2a 2f2i+ 4a4b 2i8i
598 | -3412UM
599 | T12
600 | '** 1585 +2131RY -0032KI +0013FU -1222UM +3122RY -3222KI +8594OU -0087HI +9493OU -8789RY +0081KA -0045KE +6463TO -2628RY +9695FU -8999RY +9594FU -2526FU +0061KA -2627TO +7584GI -1113KY
601 | +2131RY
602 | T10
603 | '** 2381 2a3a G*3b P*1c 1b2b
604 | -0032KI
605 | T3
606 | '** 1694
607 | +0013FU
608 | T0
609 | '** 2536 P*1c 1b2b 3a2b 3b2b
610 | -1222UM
611 | T3
612 | '** 1692 +3122RY -3222KI +8594OU -2629RY +0063KA -0036FU +8977KE -1113KY +8382TO -3526OU +0071KA -1415FU +9483OU -1516FU +0045KY -0043FU +5554FU -2999RY +8392OU -1617TO +5453TO -4445KY +5343TO -2627OU +4333TO -2233KI
613 | +3122RY
614 | T6
615 | '** 2227 3a2b 3b2b 8e9d 2f2i+
616 | -3222KI
617 | T3
618 | '** 1704 +8594OU
619 | +8594OU
620 | T6
621 | '** 2228 8e9d 2f2i+ 8i7g 2i9i
622 | -2629RY
623 | T14
624 | '** 1702
625 | +0054KA
626 | T0
627 | '** 2336 B*5d P*3f 4a4b 3e2f
628 | -0036FU
629 | T4
630 | '** 1574 +8382TO -3526OU +8977KE -1415FU +0071KA -1113KY +0045KY -0043FU +9483OU -1516FU +5463UM -4746TO +4142TO -4645TO +6345UM -7879TO +9695FU -1617TO +6463TO
631 | +8977KE
632 | T6
633 | '** 1994 8i7g
634 | -2999RY
635 | T3
636 | '** 1562 +5463UM
637 | +5463UM
638 | T10
639 | '** 2114 5d6c+ 3e2f 5e5d L*7a
640 | -3526OU
641 | T8
642 | '** 1598 +8382TO
643 | +5554FU
644 | T0
645 | '** 2094 5e5d L*7a P*7c 7a7c
646 | -0097HI
647 | T6
648 | '** 1534 +7765KE -9796RY +6396UM -9996RY +0095KY -4738TO +0072HI -2223KI +5453TO -9698RY +0086KI -1113KY +9493OU -4447NY +9382OU -2616OU +6573NK -9899RY +6463TO -7879TO +9592NY
649 | +0086GI
650 | T10
651 | '** 2117 S*8f 9g9f+ P*9e 9f9h
652 | -9796RY
653 | T3
654 | '** 1490
655 | +0095FU
656 | T0
657 | '** 2120 P*9e 9f9h 5d5c+ 7h7i+
658 | -9698RY
659 | T3
660 | '** 1431 +5453TO -9889RY +8382TO -1415FU +7785KE -1516FU +6354UM -0074KY +0077KA -9996RY +7574GI -1617TO +5343TO -1113KY +4333TO -2233KI +0022GI -3334KI +2213NG -4446KY +9483OU -8986RY
661 | +5453TO
662 | T6
663 | '** 1998 5d5c+ 2f2g 6c5d L*7a
664 | -2627OU
665 | T8
666 | '** 1452
667 | +6354UM
668 | T1
669 | '** 1907 6c5d 9h8i 5c4c L*7c
670 | -9889RY
671 | T3
672 | '** 1483 +5343TO
673 | +5343TO
674 | T6
675 | '** 1889 5c4c L*7c 4c3c 2b3c
676 | -0071KY
677 | T7
678 | '** 1541 +4344TO
679 | +4333TO
680 | T10
681 | '** 2003 4c3c 7a7e 8f7e 2b3c
682 | -7175KY
683 | T5
684 | '** 1633 +8675GI
685 | +8675GI
686 | T4
687 | '** 1937 8f7e 2b3c L*3e 3c2d
688 | -2233KI
689 | T4
690 | '** 1614
691 | +7785KE
692 | T6
693 | '** 1916 7g8e 1a1c B*2b S*2d
694 | -4446KY
695 | T6
696 | '** 1518 +5455UM
697 | +0022KA
698 | T10
699 | '** 2218 B*2b 3c2c 2b1a+ 2c1c
700 | -8959RY
701 | T5
702 | '** 1681
703 | +5476UM
704 | T4
705 | '** 2416 5d7f 3c2c 2b1a+ 2c1c
706 | -3323KI
707 | T3
708 | '** 1736
709 | +2211UM
710 | T0
711 | '** 2448 2b1a+ 2c1c 1a2b 1c2d
712 | -2313KI
713 | T3
714 | '** 1758 +7677UM
715 | +1122UM
716 | T6
717 | '** 2353 1a2b 1c2d 2b3c 2d1e
718 | -1324KI
719 | T3
720 | '** 1701 +7584GI
721 | +2233UM
722 | T6
723 | '** 2290 2b3c 2d1e S*2d 5i1i
724 | -2435KI
725 | T9
726 | '** 1730 +6463TO
727 | +3324UM
728 | T4
729 | '** 2428 3c2d 3e2f 2d1d 5i1i
730 | -3526KI
731 | T5
732 | '** 1647 +6463TO
733 | +2414UM
734 | T4
735 | '** 2532 2d1d 5i1i 1d3b P*9f
736 | -4757TO
737 | T7
738 | '** 1687 +1423UM
739 | +1423UM
740 | T10
741 | '** 2780 1d2c 4f4g+ 7f4c 7h7i+
742 | -0067FU
743 | T5
744 | '** 1836 +8382TO -3728TO +6463TO -6768TO +8573NK -0067GI +7677UM -9989RY +9493OU -6756GI +6656KI -5756TO +7722UM -5957RY +9392OU -3637TO +0084GI -7879TO
745 | +7643UM
746 | T4
747 | '** 2745 7f4c 6g6h+ 8c8b+ 6h6g
748 | -4647NY
749 | T5
750 | '** 1717 +6463TO
751 | +6463TO
752 | T4
753 | '** 2955 6d6c+ 6g6h+ 8e7c+ 6h6g
754 | -0096FU
755 | T5
756 | '** 1736
757 | +8573NK
758 | T4
759 | '** 2980 8e7c+ 9f9g+ 9d9c 9g9f
760 | -9697TO
761 | T5
762 | '** 1943 +9493OU
763 | +9493OU
764 | T0
765 | '** 3180 9d9c 9g9f 9e9d 9f9e
766 | -9796TO
767 | T6
768 | '** 2047 +9382OU -9695TO +6667KI -5767TO +2367UM -9989RY +6712UM -0054GI +4342UM -5463GI +7363NK -9585TO +7564GI -8584TO +0073KI -5919RY +0023KI -8483TO +7383KI -1959RY +9192NG -7879TO
769 | +9594FU
770 | T4
771 | '** 3073 9e9d 9f9e G*8d S*8e
772 | -9695TO
773 | T3
774 | '** 1994 +9382OU
775 | +0084KI
776 | T6
777 | '** 2933 G*8d S*8e 9c8b 9e9d
778 | -0085GI
779 | T3
780 | '** 1763 +8485KI
781 | +0098KY
782 | T6
783 | '** 3160 L*9h P*9f 9h9f 9i9f
784 | -9586TO
785 | T8
786 | '** 2020 +9392OU -5969RY +9493TO -7879TO +0022GI -6768TO +7564GI -7978TO +8382TO -7877TO +6675KI -8576GI +9894KY -6878TO +8281TO -6989RY +4151TO -7687GI +2332UM
787 | +0087KY
788 | T10
789 | '** 3679 L*8g 8f8g 4c8g 5i8i
790 | -9989RY
791 | T5
792 | '** 1892 +8485KI
793 | +8786KY
794 | T4
795 | '** 3430 8g8f 8e8f 7e8f 8i8f
796 | -8586GI
797 | T3
798 | '** 1854
799 | +7586GI
800 | T0
801 | '** 3590 7e8f 8i8f S*7e 8f8i
802 | -8986RY
803 | T6
804 | '** 1897 +0076GI
805 | +6676KI
806 | T3
807 | '** 3838 6f7f 8f8i 9h9e 6g6h+
808 | -8689RY
809 | T6
810 | '** 1841 +7685KI
811 | +9895KY
812 | T3
813 | '** 3643 9h9e 6g6h+ 7f8e S*9f
814 | -6768TO
815 | T6
816 | '** 1825 +8382TO
817 | +7685KI
818 | T4
819 | '** 3618 7f8e S*9f G*8f 9f8e
820 | -5767TO
821 | T4
822 | '** 1814 +0022GI
823 | +8574KI
824 | T10
825 | '** 3461 8e7d S*8f G*8e 8f9e
826 | -8986RY
827 | T5
828 | '** 1646 +0085KI
829 | +0085KI
830 | T10
831 | '** 3604 G*8e 8f4f 4c3b 4f3e
832 | -8646RY
833 | T3
834 | '** 1686 +4361UM
835 | +4332UM
836 | T0
837 | '** 3420 4c3b 4f3e 3b4b 3e4f
838 | -5955RY
839 | T7
840 | '** 1654
841 | +8382TO
842 | T10
843 | '** 3281 8c8b+ S*9f 7d7e 9f8e
844 | -3728TO
845 | T5
846 | '** 1635 +7362NK
847 | +7483KI
848 | T4
849 | '** 2823 7d8c S*9f S*7d 9f8e
850 | -0086GI
851 | T6
852 | '** 1597 +0065GI
853 | +8586KI
854 | T10
855 | '** 2761 8e8f 4f8f 9c9b 8f9e
856 | -4686RY
857 | T3
858 | '** 1937 +9392OU
859 | +9392OU
860 | T0
861 | '** 2516 9c9b 8f9e 9d9c+ 5e3e
862 | -8695RY
863 | T6
864 | '** 2051 +9493TO
865 | +9493TO
866 | T3
867 | '** 2461 9d9c+ 5e3e 3b4b 3f3g+
868 | -5535RY
869 | T5
870 | '** 2354 +3222UM
871 | +7362NK
872 | T4
873 | '** 2766 7c6b 3f3g+ 8d7c
874 | -0012KI
875 | T5
876 | '** 2280
877 | +2333UM
878 | T10
879 | '** 2883 2c3c 9e4e 8d7c
880 | -0046KY
881 | T5
882 | '** 2573
883 | +8473KI
884 | T4
885 | '** 5000 8d7c
886 | -3533RY
887 | T4
888 | '** 5475 +3233UM
889 | +3233UM
890 | T10
891 | '** 3892 3b3c 9e3e 3c4b 1b2b
892 | -9535RY
893 | T6
894 | '** 5555 +3342UM
895 | +3342UM
896 | T0
897 | '** 4461 3c4b 1b2b P*5b
898 | -0061KY
899 | T6
900 | '** 5784 +6261NK -0024KA +4252UM -0081FU +8281TO -0051FU +6151NK -2451KA +5251UM -0082FU +8182TO -0061KE +5161UM -0081FU +0051HI -8182FU +0053KA -3533RY +0021GI -8283FU
901 | +6261NK
902 | T4
903 | '** 4097 6b6a 3e6e 6a6b 6e4e
904 | -0033KA
905 | T4
906 | '** 5842 +4252UM
907 | +4253UM
908 | T10
909 | '** 3918 4b5c 3c4d 5c4d 3e4d
910 | -3555RY
911 | T5
912 | '** 5793 +0072HI
913 | +5362UM
914 | T4
915 | '** 4363 5c6b 5e8e P*7b
916 | -3344KA
917 | T5
918 | '** 5835 +0053KE -5553RY +6353TO -0081FU +8281TO -0071KE +0031HI -4453KA +6253UM -0082FU +3136RY -2636KI +0017HI -2738OU +8171TO
919 | +0053GI
920 | T4
921 | '** 5000 S*5c
922 | -4453KA
923 | T4
924 | '** 5781
925 | +6353TO
926 | T10
927 | '** 4229 6c5c 5e6d 5c6c 6d1d
928 | -0081FU
929 | T3
930 | '** 5821 +0051HI
931 | +8281TO
932 | T4
933 | '** 4239 8b8a P*8b 8a8b 1b1a
934 | -0064GI
935 | T8
936 | '** 5843 +0042GI
937 | +5363TO
938 | T4
939 | '** 3898 5c6c 6d7c 6c7c 5e8e
940 | -6473GI
941 | T4
942 | '** 5803 +8373KI
943 | +6373TO
944 | T0
945 | '** 3974 6c7c 5e8e P*7b 8e5e
946 | -0084KI
947 | T7
948 | '** 5747 +8384KI -5564RY +0063KY -6484RY +0083KI -0094KI +0042HI
949 | +0072FU
950 | T4
951 | '** 4316 P*7b 8d8c 7c8c 5e8e
952 | -8483KI
953 | T4
954 | '** 5756 +7383TO -0052KI +6271UM -5251KI +6151NK -5551RY +4151TO -0082FU +0032HI -1222KI +3231RY -8283FU +3122RY -2738OU +0043KA -3637TO
955 | +7383TO
956 | T5
957 | '** 4257 7c8c 5e8e P*5b 8e5e
958 | -0084KI
959 | T5
960 | '** 5808 +0042HI
961 | +8384TO
962 | T10
963 | '** 3735 8c8d 5e8e 8d8e P*1d
964 | -5545RY
965 | T5
966 | '** 5844 +0031HI
967 | +8485TO
968 | T4
969 | '** 3596 8d8e 4e8e G*8d 8e8d
970 | -4541RY
971 | T5
972 | '** 5743 +0052KI
973 | +8594TO
974 | T4
975 | '** 3454 8e9d 4a4e 9d8c 4e4a
976 | -0088FU
977 | T5
978 | '** 5733 +0052KI
979 | +9483TO
980 | T4
981 | '** 3308 9d8c 8h8i+ 8c8d 4a4e
982 | -2637KI
983 | T5
984 | '** 5705 +0052KI -4152RY +6252UM -2738OU +0042HI -0051FU +5253UM -0011FU +0032KI -3727KI +0041KA -3637TO
985 | +8384TO
986 | T4
987 | '** 3240 8c8d 4a4e 8d8e 4e8e
988 | -4111RY
989 | T5
990 | '** 5695 +0052KI -2738OU +0041GI -1141RY +5241KI -0032GI +4142KI -3233GI +7271TO -1222KI +0032HI -2232KI +0016KA -3727KI +4232KI -3637TO +3233KI
991 | +8485TO
992 | T4
993 | '** 3183 8d8e 1b2b 8e9d 1a1h
994 | -2526FU
995 | T5
996 | '** 5632 +0052KI -0051FU +0041HI -1141RY +5241KI -1222KI +0063KA -2738OU +7271TO -0065HI +0042KI -6563HI +6263UM
997 | +8594TO
998 | T10
999 | '** 3324 8e9d 1b2b 9d8c 1a1h
1000 | -7879TO
1001 | T5
1002 | '** 5705 +0071KA -1121RY +0042GI -1222KI +0041GI -2141RY +4241NG -2738OU +0082HI -0077GI +0052HI -2627TO
1003 | +9483TO
1004 | T4
1005 | '** 3049 9d8c 1b2b 8c8d 8h8i+
1006 | -1122RY
1007 | T5
1008 | '** 5740 +0052KI
1009 | +0052FU
1010 | T4
1011 | '** 3900 P*5b 8h8i+ P*4b 2b4b
1012 | -0051FU
1013 | T5
1014 | '** 5883 +0043KI
1015 | +6151NK
1016 | T4
1017 | '** 3669 6a5a 8h8i+ P*4b 2b4b
1018 | -0071FU
1019 | T5
1020 | '** 6006 +0061KA
1021 | +0042FU
1022 | T4
1023 | '** 3842 P*4b 2b4b G*4a 4b4e
1024 | -0061FU
1025 | T5
1026 | '** 6636 +6253UM
1027 | +5161NK
1028 | T4
1029 | '** 4313 5a6a 8h8i+ P*3b
1030 | -2242RY
1031 | T5
1032 | '** 6002 +0051GI
1033 | +0051GI
1034 | T10
1035 | '** 4387 S*5a 4b4e P*4b
1036 | -4251RY
1037 | T3
1038 | '** 6344 +5251TO
1039 | +5251TO
1040 | T4
1041 | '** 3554 5b5a+ 8h8i+ 5a5b 8i8h
1042 | -0063GI
1043 | T5
1044 | '** 5961 +0049KA
1045 | +5152TO
1046 | T4
1047 | '** 3439 5a5b 6c7b 5b4b 7b8c
1048 | -6757TO
1049 | T8
1050 | '** 5805 +0049KI
1051 | +0042FU
1052 | T4
1053 | '** 5000 P*4b 1b1a P*3b
1054 | -6352GI
1055 | T4
1056 | '** 6416 +6252UM
1057 | +0032FU
1058 | T4
1059 | '** 4292 P*3b 5b6a 6b6a N*2c
1060 | -5261GI
1061 | T5
1062 | '** 6030 +6261UM
1063 | +6261UM
1064 | T0
1065 | '** 4267 6b6a N*2c P*5b 8h8i+
1066 | -7172FU
1067 | T6
1068 | '** 6293 +0071KA
1069 | +0052FU
1070 | T10
1071 | '** 3678 P*5b N*2c G*6b 6h5h
1072 | -0087KE
1073 | T4
1074 | '** 6108 +0082GI
1075 | +0062KI
1076 | T4
1077 | '** 3293 G*6b 7i7h 6b5a 7h7i
1078 | -1222KI
1079 | T0
1080 | '** 100000 +0011HI -2232KI +0049KA -3738KI +0017HI
1081 | +6271KI
1082 | T10
1083 | '** 4052 6b7a 2b2a 7a7b 2a3b
1084 | -2232KI
1085 | T0
1086 | '** 100000 +0012HI -2738OU +1232RY -0041FU +0062HI -4142FU +0031GI -2627TO
1087 | +7172KI
1088 | T4
1089 | '** 3251 7a7b 7i7h 7b6b 7h7i
1090 | -3242KI
1091 | T6
1092 | '** 100000 +0012HI -3748KI +0031GI -4252KI +0016KA -2737OU +1652UM -0051FU +0013HI -5152FU +0021GI -2627TO
1093 | +7262KI
1094 | T4
1095 | '** 3267 7b6b 4b5b 6b5b P*1g
1096 | -8889TO
1097 | T14
1098 | '** 5870 +0012HI
1099 | +0072FU
1100 | T4
1101 | '** 3033 P*7b 8i8h G*6c 8h9h
1102 | -0017FU
1103 | T5
1104 | '** 6003 +0021HI
1105 | +0063KI
1106 | T4
1107 | '** 3005 G*6c 2h1h 6c5c 4b5c
1108 | -4252KI
1109 | T3
1110 | '** 100000 +6252KI -0051FU +0032KI -5152FU +0051HI -0084KI +0012HI -8483KI +9383TO -0071FU +0043KA -7172FU +0031GI -1718TO
1111 | +6352KI
1112 | T4
1113 | '** 3111 6c5b 2h1h 5b4a 1h2h
1114 | -0051FU
1115 | T2
1116 | '** 100000 +0032KI -5152FU +0071KA -0094KI +0012HI -9493KI +8393TO -1718TO +0082GI -0013FU
1117 | +5251KI
1118 | T10
1119 | '** 3431 5b5a 2h1h 5a5b 1h2h
1120 | -0082FU
1121 | T0
1122 | '** 100000 +0021HI -0071FU +0032KI -7172FU +0063KA -8283FU +0013HI -1718TO
1123 | +5152KI
1124 | T4
1125 | '** 3437 5a5b 8b8c P*4b 8c8d
1126 | -0051FU
1127 | T3
1128 | '** 100000 +0013HI -5152FU +0071KA -0084KI +8384TO -1718TO +0032KI -0012FU +0021HI -1213FU +0031GI -2738OU
1129 | +5242KI
1130 | T4
1131 | '** 3229 5b4b 5a5b 4b5a 8b8c
1132 | -0041FU
1133 | T2
1134 | '** 100000 +4232KI -0071FU +0021HI -7969TO +0013HI -8283FU +0031GI -7172FU +0071KA -1718TO
1135 | +4251KI
1136 | T4
1137 | '** 3208 4b5a 4a4b 5a5b 4b4c
1138 | -0071FU
1139 | T1
1140 | '** 6056
1141 | +5152KI
1142 | T4
1143 | '** 3320 5a5b 4a4b 5b4b 6h5h
1144 | -0051FU
1145 | T5
1146 | '** 100000 +0012KI -5152FU +0021HI -7172FU +0071KA -8283FU +0051GI -0063KI +6263KI -0062FU +0032KI -6263FU +0082GI -0022KI
1147 | +5251KI
1148 | T4
1149 | '** 3361 5b5a 4a4b 5a5b 6h5h
1150 | -7172FU
1151 | T1
1152 | '** 100000 +0021HI -8283FU +0063KA -1718TO +0013HI -1829TO +6116UM -2718OU +1626UM -0015FU +1315RY -0017FU +1517RY
1153 | +5152KI
1154 | T10
1155 | '** 3434 5a5b 4a4b 5b4b P*4a
1156 | -0051FU
1157 | T9
1158 | '** 100000 +6251KI -6869TO +0032KI -8283FU +0013HI -5758TO +0021HI -1718TO +0082GI -4142FU
1159 | +5251KI
1160 | T4
1161 | '** 3144 5b5a 4a4b 5a5b 6h5h
1162 | -8283FU
1163 | T3
1164 | '** 100000 +0021HI -5756TO +0082GI -0057FU +0032KI -5758TO +0071KA -7273FU
1165 | +5152KI
1166 | T4
1167 | '** 3205 5a5b 4a4b 5b4b P*4a
1168 | -3748KI
1169 | T25
1170 | '** 6153
1171 | +5241KI
1172 | T4
1173 | '** 3142 5b4a 4h5i P*5b 5i4i
1174 | -2737OU
1175 | T6
1176 | '** 6351 +0032KI
1177 | +0052FU
1178 | T4
1179 | '** 3123 P*5b 2f2g+ 4a4b 4h5i
1180 | -0051FU
1181 | T5
1182 | '** 100000
1183 | +5251TO
1184 | T4
1185 | '** 3278 5b5a+ 6h5h 5a5b 4h5i
1186 | -6867TO
1187 | T0
1188 | '** 100000 +0032KI -2839TO +0013HI -8799NK +0021HI -9998NK
1189 | +5152TO
1190 | T10
1191 | '** 3475 5a5b 2f2g+ 5b4b 2g2f
1192 | -2627TO
1193 | T0
1194 | '** 6041
1195 | +5242TO
1196 | T3
1197 | '** 3265 5b4b 2g2f 4b5a 4h5i
1198 | -5758TO
1199 | T1
1200 | '** 100000 +0032KI -6778TO +0021HI -2829TO +0013HI -1718TO
1201 | +0052FU
1202 | T4
1203 | '** 4549 P*5b 4h3h P*3b 4g5g
1204 | -3726OU
1205 | T0
1206 | '** 100000 +0012KI -1718TO +0021HI -2635OU
1207 | +0032FU
1208 | T4
1209 | '** 4601 P*3b 2f3g P*2b
1210 | -2635OU
1211 | T0
1212 | '** 5944 +0023HI
1213 | +0022FU
1214 | T4
1215 | '** 5000 P*2b
1216 | -3637TO
1217 | T0
1218 | '** 100000
1219 | %KACHI,'* -5000 win
1220 | T10
1221 | %KACHI
1222 | 'P1+NG+TO * +UM * +KI * * *
1223 | 'P2+OU * -FU+KI+FU+TO+FU+FU *
1224 | 'P3+TO-FU * * * * * * *
1225 | 'P4 * * * * * * * * *
1226 | 'P5 * * * * * * -OU * *
1227 | 'P6 * * * * * -KY * * *
1228 | 'P7 * -KE * -TO * -NY-TO-TO-FU
1229 | 'P8 * * * * -TO-KI * -TO *
1230 | 'P9 * -TO-TO * * * * * *
1231 | 'P+00FU00FU00GI00GI00GI00HI00HI00KA00KE00KE00KE00KI00KY00KY
1232 | '+
1233 | 'summary:kachi:aobazero win:viper lose
1234 | '$END_TIME:2020/07/19 20:56:29
1235 |
--------------------------------------------------------------------------------
/test/files/csa/example.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/example.csa
--------------------------------------------------------------------------------
/test/files/csa/formal.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/formal.csa
--------------------------------------------------------------------------------
/test/files/csa/illegal_lose.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/illegal_lose.csa
--------------------------------------------------------------------------------
/test/files/csa/illegal_win.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/illegal_win.csa
--------------------------------------------------------------------------------
/test/files/csa/noeol.csa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/csa/noeol.csa
--------------------------------------------------------------------------------
/test/files/jkf/same_move_minimal.jkf:
--------------------------------------------------------------------------------
1 | {
2 | "header": {},
3 | "moves": [
4 | {},
5 | {
6 | "move": {
7 | "from": {
8 | "x": 7,
9 | "y": 7
10 | },
11 | "to": {
12 | "x": 7,
13 | "y": 6
14 | },
15 | "color": 0,
16 | "piece": "FU"
17 | }
18 | },
19 | {
20 | "move": {
21 | "from": {
22 | "x": 3,
23 | "y": 3
24 | },
25 | "to": {
26 | "x": 3,
27 | "y": 4
28 | },
29 | "color": 1,
30 | "piece": "FU"
31 | }
32 | },
33 | {
34 | "move": {
35 | "from": {
36 | "x": 8,
37 | "y": 8
38 | },
39 | "to": {
40 | "x": 2,
41 | "y": 2
42 | },
43 | "promote": true,
44 | "color": 0,
45 | "capture": "KA",
46 | "piece": "KA"
47 | }
48 | },
49 | {
50 | "move": {
51 | "from": {
52 | "x": 3,
53 | "y": 1
54 | },
55 | "to": {
56 | "x": 2,
57 | "y": 2
58 | },
59 | "color": 1,
60 | "same": true,
61 | "capture": "UM",
62 | "piece": "GI"
63 | }
64 | },
65 | {
66 | "move": {
67 | "from": {
68 | "x": 2,
69 | "y": 7
70 | },
71 | "to": {
72 | "x": 2,
73 | "y": 6
74 | },
75 | "color": 0,
76 | "piece": "FU"
77 | }
78 | }
79 | ]
80 | }
81 |
--------------------------------------------------------------------------------
/test/files/ki2/20091203.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/20091203.ki2
--------------------------------------------------------------------------------
/test/files/ki2/7mai.ki2u:
--------------------------------------------------------------------------------
1 | 開始日時:2022/02/06 14:51:20
2 | 終了日時:2022/02/06 14:53:37
3 | 手合割:左七枚落ち
4 | 下手:shitate
5 | 上手:uwate
6 |
7 | △8二銀 ▲7六歩
8 | まで2手で中断
9 |
--------------------------------------------------------------------------------
/test/files/ki2/8mai.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/8mai.ki2
--------------------------------------------------------------------------------
/test/files/ki2/8mai.ki2u:
--------------------------------------------------------------------------------
1 | 開始日時:2014/10/13 18:51:20
2 | 終了日時:2014/10/13 18:53:37
3 | 手合割:八枚落ち
4 | 下手:shitate
5 | 上手:uwate
6 |
7 | △7二金 ▲7六歩 △3二金 ▲2六歩 △6四歩 ▲2五歩
8 | △6五歩 ▲2四歩 △同 歩 ▲同 飛 △2三歩 ▲2八飛
9 | △6三金 ▲3八銀 △5四金 ▲2七銀 △4二玉 ▲2六銀
10 | △4四歩 ▲2五銀 △4三玉 ▲2四歩 △同 歩 ▲同 銀
11 | △3四玉
12 | まで25手で中断
13 |
--------------------------------------------------------------------------------
/test/files/ki2/9fu.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/9fu.ki2
--------------------------------------------------------------------------------
/test/files/ki2/chudan.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/chudan.ki2
--------------------------------------------------------------------------------
/test/files/ki2/denou3-1.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/denou3-1.ki2
--------------------------------------------------------------------------------
/test/files/ki2/fork.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/fork.ki2
--------------------------------------------------------------------------------
/test/files/ki2/illegal.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/illegal.ki2
--------------------------------------------------------------------------------
/test/files/ki2/kobayashi_kinsho196702.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/kobayashi_kinsho196702.ki2
--------------------------------------------------------------------------------
/test/files/ki2/noeol.ki2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/ki2/noeol.ki2
--------------------------------------------------------------------------------
/test/files/kif/20081220kyoochi.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/20081220kyoochi.kif
--------------------------------------------------------------------------------
/test/files/kif/8mai.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/8mai.kif
--------------------------------------------------------------------------------
/test/files/kif/9fu.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/9fu.kif
--------------------------------------------------------------------------------
/test/files/kif/chudan.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/chudan.kif
--------------------------------------------------------------------------------
/test/files/kif/doh_branch.kifu:
--------------------------------------------------------------------------------
1 | #KIF version=2.0 encoding=UTF-8
2 | # ---- Kifu for Windows V7 V7.31 棋譜ファイル ----
3 | 開始日時:2017/10/03 01:12:24
4 | 終了日時:2017/10/03 01:12:54
5 | 手合割:平手
6 | 先手:
7 | 後手:
8 | 手数----指手---------消費時間--
9 | 1 7六歩(77) ( 0:00/00:00:00)
10 | 2 3四歩(33) ( 0:00/00:00:00)
11 | 3 2六歩(27) ( 0:00/00:00:00)+
12 | 4 中断 ( 0:00/00:00:00)
13 | まで3手で中断
14 |
15 | 変化:3手
16 | 3 2二角成(88) ( 0:00/00:00:00)
17 | 4 同 銀(31) ( 0:00/00:00:00)+
18 | 5 中断 ( 0:00/00:00:00)
19 | まで4手で中断
20 |
21 | 変化:4手
22 | 4 同 飛(82) ( 0:00/00:00:00)
23 | 5 中断 ( 0:00/00:00:00)
24 | まで4手で中断
25 |
--------------------------------------------------------------------------------
/test/files/kif/fork-notime.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/fork-notime.kif
--------------------------------------------------------------------------------
/test/files/kif/fork-test.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/fork-test.kif
--------------------------------------------------------------------------------
/test/files/kif/fork.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/fork.kif
--------------------------------------------------------------------------------
/test/files/kif/henka.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/henka.kif
--------------------------------------------------------------------------------
/test/files/kif/illegal.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/illegal.kif
--------------------------------------------------------------------------------
/test/files/kif/jt201409130101.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/jt201409130101.kif
--------------------------------------------------------------------------------
/test/files/kif/kifu_for_iphone.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/kifu_for_iphone.kif
--------------------------------------------------------------------------------
/test/files/kif/kiou201403160101.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/kiou201403160101.kif
--------------------------------------------------------------------------------
/test/files/kif/last-fork.kifu:
--------------------------------------------------------------------------------
1 | #KIF version=2.0 encoding=UTF-8
2 | # ---- Kifu for Windows V7 V7.11 棋譜ファイル ----
3 | 開始日時:2015/08/17 00:32:18
4 | 終了日時:2015/08/17 00:32:35
5 | 手合割:平手
6 | 先手:
7 | 後手:
8 | 手数----指手---------消費時間--
9 | 1 2六歩(27) ( 0:01/00:00:01)
10 | 2 8四歩(83) ( 0:01/00:00:01)
11 | 3 2五歩(26) ( 0:01/00:00:02)
12 | 4 8五歩(84) ( 0:00/00:00:01)
13 | 5 7八金(69) ( 0:01/00:00:03)
14 | 6 3二金(41) ( 0:01/00:00:02)
15 | 7 2四歩(25) ( 0:01/00:00:04)
16 | 8 同 歩(23) ( 0:00/00:00:02)
17 | 9 同 飛(28) ( 0:01/00:00:05)
18 | 10 2三歩打 ( 0:05/00:00:07)+
19 | 11 中断 ( 0:01/00:00:06)
20 | まで10手で中断
21 |
22 | 変化:10手
23 | 10 1四歩(13) ( 0:01/00:00:03)
24 | 11 中断 ( 0:01/00:00:06)
25 | まで10手で中断
26 |
--------------------------------------------------------------------------------
/test/files/kif/last_comment.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/last_comment.kif
--------------------------------------------------------------------------------
/test/files/kif/meijinsen_20180508_M7_10034.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/meijinsen_20180508_M7_10034.kif
--------------------------------------------------------------------------------
/test/files/kif/no_henka.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/no_henka.kif
--------------------------------------------------------------------------------
/test/files/kif/no_henka.kifu:
--------------------------------------------------------------------------------
1 | #KIF version=2.0 encoding=UTF-8
2 | # ---- Kifu for Windows V7 V7.00β 棋譜ファイル ----
3 | 開始日時:2005/08/23 10:12
4 | 終了日時:2005/08/23(火) 22:50
5 | 棋戦:第64期順位戦C級2組2回戦
6 | 持ち時間:6時間
7 | 消費時間:120▲359△329
8 | 場所:東京将棋会館
9 | 手合割:平手
10 | 先手:先手56789012全3456789
11 | 後手:後手567890全123456789
12 | 手数----指手---------消費時間--
13 | *Unicodeのコメント
14 | *森雞二九段
15 | 1 7六歩(77) ( 0:00/00:00:00)
16 | *初手のコメント
17 | *2
18 | *3
19 | *4
20 | *5
21 | *6
22 | *7
23 | *8
24 | *9
25 | *10
26 | *11
27 | *12
28 | 2 8四歩(83) ( 0:00/00:00:00)
29 | *2手目のコメント
30 | 3 2六歩(27) ( 0:00/00:00:00)
31 | 4 8五歩(84) ( 0:00/00:00:00)
32 | 5 7七角(88) ( 0:00/00:00:00)
33 | 6 3四歩(33) ( 0:00/00:00:00)
34 | 7 8八銀(79) ( 0:00/00:00:00)
35 | 8 3二金(41) ( 0:00/00:00:00)
36 | 9 7八金(69) ( 0:00/00:00:00)
37 | 10 7七角(22) ( 0:00/00:00:00)
38 | 11 同 銀(88) ( 0:00/00:00:00)
39 | &読み込み時表示
40 |
41 |
--------------------------------------------------------------------------------
/test/files/kif/noeol.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/noeol.kif
--------------------------------------------------------------------------------
/test/files/kif/oui201407080101.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/oui201407080101.kif
--------------------------------------------------------------------------------
/test/files/kif/ouza201410070101.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/ouza201410070101.kif
--------------------------------------------------------------------------------
/test/files/kif/ryuou201409020101.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/ryuou201409020101.kif
--------------------------------------------------------------------------------
/test/files/kif/ryuou4.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/ryuou4.kif
--------------------------------------------------------------------------------
/test/files/kif/shogidb2.kifu:
--------------------------------------------------------------------------------
1 | 開始日時:2016/12/24 9:00:00
2 | 終了日時:2016/12/24 20:43:00
3 | 棋戦:竜王戦
4 | 手合割:平手
5 | 先手:加藤一二三
6 | 後手:藤井聡太
7 | 手数----指手---------消費時間--
8 | 1 7六歩(77) (00:00/00:00:00)
9 | 2 8四歩(83) (00:00/00:00:00)
10 | 3 6八銀(79) (00:00/00:00:00)
11 | 4 3四歩(33) (00:00/00:00:00)
12 | 5 7七銀(68) (00:00/00:00:00)
13 | 6 6二銀(71) (00:00/00:00:00)
14 | 7 2六歩(27) (00:00/00:00:00)
15 | 8 4二銀(31) (00:00/00:00:00)
16 | 9 4八銀(39) (00:00/00:00:00)
17 | 10 5四歩(53) (00:00/00:00:00)
18 | 11 7八金(69) (00:00/00:00:00)
19 | 12 3二金(41) (00:00/00:00:00)
20 | 13 5六歩(57) (00:00/00:00:00)
21 | 14 4一玉(51) (00:00/00:00:00)
22 | 15 6九玉(59) (00:00/00:00:00)
23 | 16 5二金(61) (00:00/00:00:00)
24 | 17 3六歩(37) (00:00/00:00:00)
25 | 18 3三銀(42) (00:00/00:00:00)
26 | 19 5八金(49) (00:00/00:00:00)
27 | 20 3一角(22) (00:00/00:00:00)
28 | 21 7九角(88) (00:00/00:00:00)
29 | 22 4四歩(43) (00:00/00:00:00)
30 | 23 6六歩(67) (00:00/00:00:00)
31 | 24 7四歩(73) (00:00/00:00:00)
32 | 25 6七金(58) (00:00/00:00:00)
33 | 26 6四角(31) (00:00/00:00:00)
34 | 27 3七銀(48) (00:00/00:00:00)
35 | 28 3一玉(41) (00:00/00:00:00)
36 | 29 6八角(79) (00:00/00:00:00)
37 | 30 4三金(52) (00:00/00:00:00)
38 | 31 7九玉(69) (00:00/00:00:00)
39 | 32 2二玉(31) (00:00/00:00:00)
40 | 33 8八玉(79) (00:00/00:00:00)
41 | 34 9四歩(93) (00:00/00:00:00)
42 | 35 1六歩(17) (00:00/00:00:00)
43 | 36 9五歩(94) (00:00/00:00:00)
44 | 37 1五歩(16) (00:00/00:00:00)
45 | 38 7三桂(81) (00:00/00:00:00)
46 | 39 6五歩(66) (00:00/00:00:00)
47 | 40 5三角(64) (00:00/00:00:00)
48 | 41 4六銀(37) (00:00/00:00:00)
49 | 42 6四歩(63) (00:00/00:00:00)
50 | 43 5五歩(56) (00:00/00:00:00)
51 | 44 同歩(54) (00:00/00:00:00)
52 | 45 同銀(46) (00:00/00:00:00)
53 | 46 6五桂(73) (00:00/00:00:00)
54 | 47 6六銀(77) (00:00/00:00:00)
55 | 48 6三銀(62) (00:00/00:00:00)
56 | 49 3七桂(29) (00:00/00:00:00)
57 | 50 8五歩(84) (00:00/00:00:00)
58 | 51 5四歩打 (00:00/00:00:00)
59 | 52 同銀(63) (00:00/00:00:00)
60 | 53 同銀(55) (00:00/00:00:00)
61 | 54 同金(43) (00:00/00:00:00)
62 | 55 6三銀打 (00:00/00:00:00)
63 | 56 4三銀打 (00:00/00:00:00)
64 | 57 2五桂(37) (00:00/00:00:00)
65 | 58 2四銀(33) (00:00/00:00:00)
66 | 59 1三桂成(25) (00:00/00:00:00)
67 | 60 同銀(24) (00:00/00:00:00)
68 | 61 1四歩(15) (00:00/00:00:00)
69 | 62 同銀(13) (00:00/00:00:00)
70 | 63 5四銀成(63) (00:00/00:00:00)
71 | 64 同銀(43) (00:00/00:00:00)
72 | 65 5五歩打 (00:00/00:00:00)
73 | 66 4三銀(54) (00:00/00:00:00)
74 | 67 1四香(19) (00:00/00:00:00)
75 | 68 同香(11) (00:00/00:00:00)
76 | 69 5四銀打 (00:00/00:00:00)
77 | 70 同銀(43) (00:00/00:00:00)
78 | 71 同歩(55) (00:00/00:00:00)
79 | 72 3一角(53) (00:00/00:00:00)
80 | 73 6五銀(66) (00:00/00:00:00)
81 | 74 同歩(64) (00:00/00:00:00)
82 | 75 4六角(68) (00:00/00:00:00)
83 | 76 8三飛(82) (00:00/00:00:00)
84 | 77 2五桂打 (00:00/00:00:00)
85 | 78 1二歩打 (00:00/00:00:00)
86 | 79 4一金打 (00:00/00:00:00)
87 | 80 6九銀打 (00:00/00:00:00)
88 | 81 3一金(41) (00:00/00:00:00)
89 | 82 同金(32) (00:00/00:00:00)
90 | 83 6四角打 (00:00/00:00:00)
91 | 84 8六桂打 (00:00/00:00:00)
92 | 85 6八金(67) (00:00/00:00:00)
93 | 86 7八桂成(86) (00:00/00:00:00)
94 | 87 同金(68) (00:00/00:00:00)
95 | 88 同銀成(69) (00:00/00:00:00)
96 | 89 同飛(28) (00:00/00:00:00)
97 | 90 6七金打 (00:00/00:00:00)
98 | 91 6九銀打 (00:00/00:00:00)
99 | 92 8六銀打 (00:00/00:00:00)
100 | 93 3一角成(64) (00:00/00:00:00)
101 | 94 同玉(22) (00:00/00:00:00)
102 | 95 6四角(46) (00:00/00:00:00)
103 | 96 4二香打 (00:00/00:00:00)
104 | 97 7七銀打 (00:00/00:00:00)
105 | 98 8七銀成(86) (00:00/00:00:00)
106 | 99 同玉(88) (00:00/00:00:00)
107 | 100 8六銀打 (00:00/00:00:00)
108 | 101 同銀(77) (00:00/00:00:00)
109 | 102 同歩(85) (00:00/00:00:00)
110 | 103 同角(64) (00:00/00:00:00)
111 | 104 同飛(83) (00:00/00:00:00)
112 | 105 同玉(87) (00:00/00:00:00)
113 | 106 6四角打 (00:00/00:00:00)
114 | 107 7五桂打 (00:00/00:00:00)
115 | 108 8五歩打 (00:00/00:00:00)
116 | 109 同玉(86) (00:00/00:00:00)
117 | 110 9四角打 (00:00/00:00:00)
118 | 111 投了 (00:00/00:00:00)
119 |
--------------------------------------------------------------------------------
/test/files/kif/shogidokoro.kifu:
--------------------------------------------------------------------------------
1 | # KIF形式棋譜ファイル
2 | # Generated by Shogidokoro
3 | 後手の持駒:飛 金三 銀三 桂二 香四 歩十三
4 | 9 8 7 6 5 4 3 2 1
5 | +---------------------------+
6 | | ・ ・ ・ ・ 馬 ・ ・v桂 ・|一
7 | | ・ ・ ・ ・ ・ ・ ・v玉 ・|二
8 | | ・ ・ ・ ・ ・ ・v歩v歩 ・|三
9 | | ・ ・ ・ ・ ・ ・ ・v金v歩|四
10 | | ・ ・ ・ ・ 歩 ・ ・ ・ 歩|五
11 | | ・ ・ ・ ・ ・ ・ ・ ・ ・|六
12 | | ・ ・ ・ ・ ・ ・ ・ ・ ・|七
13 | | ・ ・ ・ ・ ・ ・ ・ ・ ・|八
14 | | ・ ・ ・ ・ ・ ・ ・ ・ ・|九
15 | +---------------------------+
16 | 先手の持駒:飛 角 銀 桂
17 | 先手:
18 | 後手:
19 | 手数----指手---------消費時間--
20 | 1 5二飛打 (00:31 / 00:00:31)
21 | 2 3二金打 (00:00 / 00:00:00)
22 | 3 3一角打 (00:00 / 00:00:31)
23 | 4 同 玉(22) (00:00 / 00:00:00)
24 | 5 4二馬(51) (00:00 / 00:00:31)
25 | 6 同 金(32) (00:00 / 00:00:00)
26 | 7 4三桂打 (00:00 / 00:00:31)
27 | 8 2二玉(31) (00:00 / 00:00:00)
28 | 9 4二飛成(52) (00:00 / 00:00:31)
29 | 10 3二飛打 (00:00 / 00:00:00)
30 | 11 3一銀打 (00:00 / 00:00:31)
31 | 12 1三玉(22) (00:00 / 00:00:00)
32 | 13 1二金打 (00:00 / 00:00:31)
33 | 14 同 飛(32) (00:00 / 00:00:00)
34 | 15 同 龍(42) (00:00 / 00:00:31)
35 | 16 同 玉(13) (00:00 / 00:00:00)
36 | 17 3二飛打 (00:00 / 00:00:31)
37 | 18 1三玉(12) (00:00 / 00:00:00)
38 | 19 2二飛成(32) (00:00 / 00:00:31)
--------------------------------------------------------------------------------
/test/files/kif/taichitsume.kif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/na2hiro/json-kifu-format/0df0ed4740e2f3fd1ffc84403bde7926b50ba866/test/files/kif/taichitsume.kif
--------------------------------------------------------------------------------
/test/pegjs-jest.js:
--------------------------------------------------------------------------------
1 | var peg = require("pegjs");
2 | module.exports = {
3 | process(src, filename, config, options) {
4 | return {
5 | code: peg.generate(src, {output: "source", format: "commonjs"}),
6 | };
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["./src"],
3 | "exclude": ["**/__tests__/**/*"],
4 | "compilerOptions": {
5 | "declaration": true,
6 | "module": "commonjs",
7 | "noEmitOnError": true,
8 | "target": "es5"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tsconfig.typecheck.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["./src"],
3 | "compilerOptions": {
4 | "declaration": true,
5 | "module": "commonjs",
6 | "noEmitOnError": true,
7 | "target": "es5"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | const {merge} = require("webpack-merge");
4 | const {CleanWebpackPlugin} = require("clean-webpack-plugin");
5 | const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
6 | const package = require("./package.json");
7 |
8 | module.exports = (env) => {
9 | const IS_ANALYZE = env && env.analyze;
10 | const common = {
11 | entry: {
12 | filename: path.resolve(__dirname, "./src/main.ts"),
13 | //filename: path.resolve(__dirname, './src/tst.js'),
14 | //hoge: path.resolve(__dirname, './src/csa-parser.pegjs')
15 | },
16 | module: {
17 | rules: [
18 | {
19 | test: /\.ts$/,
20 | use: "ts-loader",
21 | exclude: /node_modules/,
22 | },
23 | {
24 | test: /\.pegjs$/,
25 | use: "pegjs-loader",
26 | },
27 | ],
28 | },
29 | resolve: {
30 | extensions: [".ts", ".js", ".pegjs"],
31 | },
32 | };
33 |
34 | // For browsers
35 | const BUNDLE_DIR = path.resolve(__dirname, "./bundle");
36 | const bundle = merge(common, {
37 | output: {
38 | library: "JSONKifuFormat",
39 | filename: `json-kifu-format-${package.version}.min.js`,
40 | path: BUNDLE_DIR,
41 | },
42 | plugins: [new CleanWebpackPlugin()],
43 | optimization: {
44 | minimize: true,
45 | },
46 | });
47 | if (IS_ANALYZE) {
48 | bundle.plugins.push(new BundleAnalyzerPlugin());
49 | }
50 |
51 | // For npm module
52 | const DIST_DIR = path.resolve(__dirname, "./dist");
53 | const dist = merge(common, {
54 | output: {
55 | libraryTarget: "commonjs2",
56 | filename: "json-kifu-format.js",
57 | path: DIST_DIR,
58 | },
59 | plugins: [new CleanWebpackPlugin()],
60 | });
61 | return [bundle, dist];
62 | };
63 |
--------------------------------------------------------------------------------