shogiops 0.2.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/LICENSE.txt +0 -0
  2. package/README.md +0 -0
  3. package/attacks.d.ts +0 -0
  4. package/attacks.js +7 -7
  5. package/attacks.js.map +1 -1
  6. package/board.d.ts +3 -0
  7. package/board.js +31 -0
  8. package/board.js.map +1 -1
  9. package/compat.d.ts +6 -24
  10. package/compat.js +15 -171
  11. package/compat.js.map +1 -1
  12. package/csa.d.ts +4 -3
  13. package/csa.js +38 -33
  14. package/csa.js.map +1 -1
  15. package/csaUtil.d.ts +1 -1
  16. package/csaUtil.js +3 -3
  17. package/csaUtil.js.map +1 -1
  18. package/debug.d.ts +0 -0
  19. package/debug.js +29 -55
  20. package/debug.js.map +1 -1
  21. package/fen.d.ts +6 -5
  22. package/fen.js +53 -49
  23. package/fen.js.map +1 -1
  24. package/hand.d.ts +38 -0
  25. package/hand.js +74 -0
  26. package/hand.js.map +1 -0
  27. package/hash.d.ts +4 -3
  28. package/hash.js +9 -9
  29. package/hash.js.map +1 -1
  30. package/index.d.ts +4 -3
  31. package/index.js +3 -7
  32. package/index.js.map +1 -1
  33. package/kif.d.ts +5 -4
  34. package/kif.js +70 -65
  35. package/kif.js.map +1 -1
  36. package/kifHandicaps.d.ts +0 -0
  37. package/kifHandicaps.js +5 -0
  38. package/kifHandicaps.js.map +1 -1
  39. package/kifUtil.d.ts +0 -0
  40. package/kifUtil.js +4 -4
  41. package/kifUtil.js.map +1 -1
  42. package/package.json +9 -5
  43. package/setup.d.ts +2 -32
  44. package/setup.js +3 -74
  45. package/setup.js.map +1 -1
  46. package/shogi.d.ts +14 -7
  47. package/shogi.js +109 -106
  48. package/shogi.js.map +1 -1
  49. package/squareSet.d.ts +1 -5
  50. package/squareSet.js +0 -13
  51. package/squareSet.js.map +1 -1
  52. package/src/attacks.ts +0 -0
  53. package/src/board.ts +34 -0
  54. package/src/compat.ts +18 -197
  55. package/src/csa.ts +29 -21
  56. package/src/csaUtil.ts +3 -2
  57. package/src/debug.ts +25 -55
  58. package/src/fen.ts +42 -35
  59. package/src/hand.ts +94 -0
  60. package/src/hash.ts +8 -7
  61. package/src/index.ts +5 -5
  62. package/src/kif.ts +51 -43
  63. package/src/kifHandicaps.ts +5 -0
  64. package/src/kifUtil.ts +0 -0
  65. package/src/setup.ts +4 -91
  66. package/src/shogi.ts +75 -75
  67. package/src/squareSet.ts +1 -18
  68. package/src/transform.ts +3 -1
  69. package/src/types.ts +13 -16
  70. package/src/util.ts +2 -68
  71. package/src/variant.ts +44 -3
  72. package/src/variantUtil.ts +160 -0
  73. package/transform.d.ts +0 -0
  74. package/transform.js +3 -1
  75. package/transform.js.map +1 -1
  76. package/types.d.ts +3 -7
  77. package/types.js +13 -14
  78. package/types.js.map +1 -1
  79. package/util.d.ts +1 -4
  80. package/util.js +2 -54
  81. package/util.js.map +1 -1
  82. package/variant.d.ts +12 -1
  83. package/variant.js +38 -3
  84. package/variant.js.map +1 -1
  85. package/variantUtil.d.ts +13 -0
  86. package/variantUtil.js +160 -0
  87. package/variantUtil.js.map +1 -0
  88. package/san.d.ts +0 -6
  89. package/san.js +0 -135
  90. package/san.js.map +0 -1
  91. package/src/san.ts +0 -136
package/src/fen.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { Result } from '@badrap/result';
2
- import { Piece, Color, POCKET_ROLES, PocketRole } from './types';
2
+ import { Piece, Color } from './types';
3
3
  import { Board } from './board';
4
- import { Setup, MaterialSide, Material } from './setup';
4
+ import { Setup } from './setup';
5
5
  import { defined, roleToChar, charToRole, toBW } from './util';
6
+ import { Hand, Hands } from './hand';
7
+ import { ROLES } from './types';
6
8
 
7
9
  export const INITIAL_BOARD_FEN = 'lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL';
8
10
  export const INITIAL_EPD = INITIAL_BOARD_FEN + ' b -';
@@ -14,7 +16,7 @@ export const EMPTY_FEN = EMPTY_EPD + ' 1';
14
16
  export enum InvalidFen {
15
17
  Fen = 'ERR_FEN',
16
18
  Board = 'ERR_BOARD',
17
- Pockets = 'ERR_POCKETS',
19
+ Hands = 'ERR_HANDS',
18
20
  Turn = 'ERR_TURN',
19
21
  Fullmoves = 'ERR_FULLMOVES',
20
22
  }
@@ -32,12 +34,18 @@ function charToPiece(ch: string): Piece | undefined {
32
34
 
33
35
  export function parseBoardFen(boardPart: string): Result<Board, FenError> {
34
36
  const board = Board.empty();
37
+ const ranks = boardPart.split('/');
38
+ board.numberOfRanks = ranks.length;
39
+ // we assume the board is square
40
+ // since that's good enough for now...
41
+ board.numberOfFiles = board.numberOfRanks;
42
+ const offset = 9 - board.numberOfFiles;
35
43
  let rank = 8;
36
- let file = 0;
44
+ let file = offset;
37
45
  for (let i = 0; i < boardPart.length; i++) {
38
46
  let c = boardPart[i];
39
47
  if (c === '/' && file === 9) {
40
- file = 0;
48
+ file = offset;
41
49
  rank--;
42
50
  } else {
43
51
  const step = parseInt(c, 10);
@@ -53,26 +61,25 @@ export function parseBoardFen(boardPart: string): Result<Board, FenError> {
53
61
  }
54
62
  }
55
63
  }
56
- if (rank !== 0 || file !== 9) return Result.err(new FenError(InvalidFen.Board));
64
+ if (rank !== offset || file !== 9) return Result.err(new FenError(InvalidFen.Board));
57
65
  return Result.ok(board);
58
66
  }
59
67
 
60
- export function parsePockets(pocketPart: string): Result<Material, FenError> {
61
- if (pocketPart.toLowerCase().includes('k')) return Result.err(new FenError(InvalidFen.Pockets));
62
- const pockets = Material.empty();
63
- for (let i = 0; i < pocketPart.length; i++) {
64
- if (pocketPart[i] === '-') break;
68
+ export function parseHands(handsPart: string): Result<Hands, FenError> {
69
+ const hands = Hands.empty();
70
+ for (let i = 0; i < handsPart.length; i++) {
71
+ if (handsPart[i] === '-') break;
65
72
  // max 99
66
73
  let count: number;
67
- if (parseInt(pocketPart[i]) >= 0) {
68
- count = parseInt(pocketPart[i++], 10);
69
- if (parseInt(pocketPart[i]) >= 0) count = count * 10 + parseInt(pocketPart[i++], 10);
74
+ if (parseInt(handsPart[i]) >= 0) {
75
+ count = parseInt(handsPart[i++], 10);
76
+ if (parseInt(handsPart[i]) >= 0) count = count * 10 + parseInt(handsPart[i++], 10);
70
77
  } else count = 1;
71
- const piece = charToPiece(pocketPart[i]);
72
- if (!piece) return Result.err(new FenError(InvalidFen.Pockets));
73
- pockets[piece.color][piece.role as PocketRole] += count;
78
+ const piece = charToPiece(handsPart[i]);
79
+ if (!piece) return Result.err(new FenError(InvalidFen.Hands));
80
+ hands[piece.color][piece.role] += count;
74
81
  }
75
- return Result.ok(pockets);
82
+ return Result.ok(hands);
76
83
  }
77
84
 
78
85
  export function parseFen(fen: string): Result<Setup, FenError> {
@@ -89,11 +96,11 @@ export function parseFen(fen: string): Result<Setup, FenError> {
89
96
  else if (turnPart === 'w') turn = 'gote';
90
97
  else return Result.err(new FenError(InvalidFen.Turn));
91
98
 
92
- // Pocket
93
- const pocketPart = parts.shift();
94
- let pockets: Result<Material, FenError>;
95
- if (!defined(pocketPart)) pockets = Result.ok(Material.empty());
96
- else pockets = parsePockets(pocketPart);
99
+ // Hands
100
+ const handsPart = parts.shift();
101
+ let hands: Result<Hands, FenError>;
102
+ if (!defined(handsPart)) hands = Result.ok(Hands.empty());
103
+ else hands = parseHands(handsPart);
97
104
 
98
105
  // Turn
99
106
  const fullmovesPart = parts.shift();
@@ -103,10 +110,10 @@ export function parseFen(fen: string): Result<Setup, FenError> {
103
110
  if (parts.length > 0) return Result.err(new FenError(InvalidFen.Fen));
104
111
 
105
112
  return board.chain(board =>
106
- pockets.map(pockets => {
113
+ hands.map(hands => {
107
114
  return {
108
115
  board,
109
- pockets,
116
+ hands,
110
117
  turn,
111
118
  fullmoves: Math.max(1, fullmoves),
112
119
  };
@@ -135,8 +142,8 @@ export function makePiece(piece: Piece): string {
135
142
  export function makeBoardFen(board: Board): string {
136
143
  let fen = '';
137
144
  let empty = 0;
138
- for (let rank = 8; rank >= 0; rank--) {
139
- for (let file = 0; file < 9; file++) {
145
+ for (let rank = 8; rank >= 9 - board.numberOfRanks; rank--) {
146
+ for (let file = 9 - board.numberOfFiles; file < 9; file++) {
140
147
  const square = file + rank * 9;
141
148
  const piece = board.get(square);
142
149
  if (!piece) empty++;
@@ -153,31 +160,31 @@ export function makeBoardFen(board: Board): string {
153
160
  fen += empty;
154
161
  empty = 0;
155
162
  }
156
- if (rank !== 0) fen += '/';
163
+ if (rank !== 9 - board.numberOfRanks) fen += '/';
157
164
  }
158
165
  }
159
166
  }
160
167
  return fen;
161
168
  }
162
169
 
163
- export function makePocket(material: MaterialSide): string {
164
- return POCKET_ROLES.map(role => {
170
+ export function makeHand(hand: Hand): string {
171
+ return ROLES.map(role => {
165
172
  const r = roleToChar(role);
166
- const n = material[role];
173
+ const n = hand[role];
167
174
  return n > 1 ? n + r : n === 1 ? r : '';
168
175
  }).join('');
169
176
  }
170
177
 
171
- export function makePockets(pocket: Material): string {
172
- const pockets = makePocket(pocket.sente).toUpperCase() + makePocket(pocket.gote);
173
- return pockets === '' ? '-' : pockets;
178
+ export function makeHands(hands: Hands): string {
179
+ const handsStr = makeHand(hands.sente).toUpperCase() + makeHand(hands.gote);
180
+ return handsStr === '' ? '-' : handsStr;
174
181
  }
175
182
 
176
183
  export function makeFen(setup: Setup, opts?: FenOpts): string {
177
184
  return [
178
185
  makeBoardFen(setup.board),
179
186
  toBW(setup.turn),
180
- makePockets(setup.pockets),
187
+ makeHands(setup.hands),
181
188
  ...(opts?.epd ? [] : [Math.max(1, Math.min(setup.fullmoves, 9999))]),
182
189
  ].join(' ');
183
190
  }
package/src/hand.ts ADDED
@@ -0,0 +1,94 @@
1
+ import { Role, ROLES } from './types';
2
+
3
+ // Hand alone can store anything
4
+ // let the variants decide what to store and what not
5
+ export class Hand {
6
+ pawn: number;
7
+ lance: number;
8
+ knight: number;
9
+ silver: number;
10
+ gold: number;
11
+ bishop: number;
12
+ rook: number;
13
+ tokin: number;
14
+ promotedlance: number;
15
+ promotedknight: number;
16
+ promotedsilver: number;
17
+ horse: number;
18
+ dragon: number;
19
+ king: number;
20
+
21
+ private constructor() {}
22
+
23
+ static empty(): Hand {
24
+ const m = new Hand();
25
+ for (const role of ROLES) m[role] = 0;
26
+ return m;
27
+ }
28
+
29
+ clone(): Hand {
30
+ const m = new Hand();
31
+ for (const role of ROLES) m[role] = this[role];
32
+ return m;
33
+ }
34
+
35
+ equals(other: Hand): boolean {
36
+ return ROLES.every(role => this[role] === other[role]);
37
+ }
38
+
39
+ add(other: Hand): Hand {
40
+ const m = new Hand();
41
+ for (const role of ROLES) m[role] = this[role] + other[role];
42
+ return m;
43
+ }
44
+
45
+ nonEmpty(): boolean {
46
+ return ROLES.some(role => this[role] > 0);
47
+ }
48
+
49
+ isEmpty(): boolean {
50
+ return !this.nonEmpty();
51
+ }
52
+
53
+ count(): number {
54
+ return ROLES.map(role => this[role]).reduce((acc, cur) => acc + cur);
55
+ }
56
+
57
+ *[Symbol.iterator](): Iterator<[Role, number]> {
58
+ for (const role of ROLES.filter(r => this[r] > 0)) {
59
+ yield [role, this[role]];
60
+ }
61
+ }
62
+ }
63
+
64
+ export class Hands {
65
+ constructor(public gote: Hand, public sente: Hand) {}
66
+
67
+ static empty(): Hands {
68
+ return new Hands(Hand.empty(), Hand.empty());
69
+ }
70
+
71
+ clone(): Hands {
72
+ return new Hands(this.gote.clone(), this.sente.clone());
73
+ }
74
+
75
+ equals(other: Hands): boolean {
76
+ return this.gote.equals(other.gote) && this.sente.equals(other.sente);
77
+ }
78
+
79
+ add(other: Hands): Hands {
80
+ return new Hands(this.gote.add(other.gote), this.sente.add(other.sente));
81
+ }
82
+
83
+ count(): number {
84
+ return this.gote.count() + this.sente.count();
85
+ }
86
+
87
+ isEmpty(): boolean {
88
+ return this.gote.isEmpty() && this.sente.isEmpty();
89
+ }
90
+
91
+ nonEmpty(): boolean {
92
+ return !this.isEmpty();
93
+ }
94
+ }
package/src/hash.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { COLORS, POCKET_ROLES, ROLES } from './types';
1
+ import { COLORS, ROLES } from './types';
2
2
  import { Board } from './board';
3
- import { Setup, MaterialSide, Material } from './setup';
3
+ import { Setup } from './setup';
4
+ import { Hand, Hands } from './hand';
4
5
 
5
6
  function rol32(n: number, left: number): number {
6
7
  return (n << left) | (n >>> (32 - left));
@@ -17,19 +18,19 @@ export function hashBoard(board: Board, state = 0): number {
17
18
  return state;
18
19
  }
19
20
 
20
- export function hashMaterialSide(side: MaterialSide, state = 0): number {
21
- for (const role of POCKET_ROLES) state = fxhash32(side[role], state);
21
+ export function hashHand(hand: Hand, state = 0): number {
22
+ for (const role of ROLES) state = fxhash32(hand[role], state);
22
23
  return state;
23
24
  }
24
25
 
25
- export function hashMaterial(material: Material, state = 0): number {
26
- for (const color of COLORS) state = hashMaterialSide(material[color], state);
26
+ export function hashHands(hands: Hands, state = 0): number {
27
+ for (const color of COLORS) state = hashHand(hands[color], state);
27
28
  return state;
28
29
  }
29
30
 
30
31
  export function hashSetup(setup: Setup, state = 0): number {
31
32
  state = hashBoard(setup.board, state);
32
- state = hashMaterial(setup.pockets, state);
33
+ state = hashHands(setup.hands, state);
33
34
  if (setup.turn === 'sente') state = fxhash32(1, state);
34
35
  return state;
35
36
  }
package/src/index.ts CHANGED
@@ -11,8 +11,6 @@ export {
11
11
  ByColor,
12
12
  Role,
13
13
  ROLES,
14
- POCKET_ROLES,
15
- PROMOTABLE_ROLES,
16
14
  ByRole,
17
15
  Piece,
18
16
  NormalMove,
@@ -58,7 +56,7 @@ export {
58
56
 
59
57
  export { Board } from './board';
60
58
 
61
- export { Material, MaterialSide, Setup, defaultSetup } from './setup';
59
+ export { Setup, defaultSetup } from './setup';
62
60
 
63
61
  export { IllegalSetup, Shogi, Position, PositionError, Context } from './shogi';
64
62
 
@@ -70,6 +68,8 @@ export * as fen from './fen';
70
68
 
71
69
  export * as handicaps from './kifHandicaps';
72
70
 
71
+ export * as hand from './hand';
72
+
73
73
  export * as hash from './hash';
74
74
 
75
75
  export * as kif from './kif';
@@ -80,8 +80,8 @@ export * as csa from './csa';
80
80
 
81
81
  export * as csaUtil from './csaUtil';
82
82
 
83
- export * as san from './san';
84
-
85
83
  export * as transform from './transform';
86
84
 
87
85
  export * as variant from './variant';
86
+
87
+ export * as variantUtil from './variantUtil';
package/src/kif.ts CHANGED
@@ -2,10 +2,10 @@ import { Result } from '@badrap/result';
2
2
  import { Board } from './board';
3
3
  import { INITIAL_FEN, makeFen, parseFen } from './fen';
4
4
  import { handicapNameToSfen, sfenToHandicapName } from './kifHandicaps';
5
- import { Material, MaterialSide, Setup } from './setup';
5
+ import { Setup } from './setup';
6
6
  import { Position } from './shogi';
7
- import { Color, isDrop, Move, PocketRole, POCKET_ROLES, Square } from './types';
8
- import { defined, kanjiToRole, promote, roleTo1Kanji, roleTo2Kanji } from './util';
7
+ import { Color, isDrop, Move, Square } from './types';
8
+ import { defined, kanjiToRole, roleTo1Kanji, roleTo2Kanji } from './util';
9
9
 
10
10
  import {
11
11
  kanjiToNumber,
@@ -15,6 +15,8 @@ import {
15
15
  numberToKanji,
16
16
  parseKifSquare,
17
17
  } from './kifUtil';
18
+ import { Hand, Hands } from './hand';
19
+ import { allRoles, handRoles, promote } from './variantUtil';
18
20
 
19
21
  //
20
22
  // KIF HEADER
@@ -24,7 +26,7 @@ export enum InvalidKif {
24
26
  Kif = 'ERR_KIF',
25
27
  Board = 'ERR_BOARD',
26
28
  Handicap = 'ERR_HANDICAP',
27
- Pockets = 'ERR_POCKETS',
29
+ Hands = 'ERR_HANDS',
28
30
  }
29
31
 
30
32
  export class KifError extends Error {}
@@ -38,20 +40,23 @@ export function makeKifHeader(setup: Setup): string {
38
40
 
39
41
  export function makeKifPositionHeader(setup: Setup): string {
40
42
  return [
41
- '後手の持駒:' + makeKifPocket(setup.pockets.gote),
43
+ '後手の持駒:' + makeKifHand(setup.hands.gote),
42
44
  makeKifBoard(setup.board),
43
- '先手の持駒:' + makeKifPocket(setup.pockets.sente),
45
+ '先手の持駒:' + makeKifHand(setup.hands.sente),
44
46
  ...(setup.turn === 'gote' ? ['後手番'] : []),
45
47
  ].join('\n');
46
48
  }
47
49
 
48
50
  export function makeKifBoard(board: Board): string {
49
- let kifBoard = ' 9 8 7 6 5 4 3 2 1\n+---------------------------+\n';
50
- for (let rank = 8; rank >= 0; rank--) {
51
- for (let file = 0; file < 9; file++) {
51
+ const kifFiles = ' 9 8 7 6 5 4 3 2 '.slice(-(board.numberOfFiles * 2));
52
+ const separator = '+' + '-'.repeat(board.numberOfFiles * 3) + '+';
53
+ const offset = 9 - board.numberOfFiles;
54
+ let kifBoard = ' ' + kifFiles + `\n${separator}\n`;
55
+ for (let rank = 8; rank >= 9 - board.numberOfRanks; rank--) {
56
+ for (let file = offset; file < 9; file++) {
52
57
  const square = file + rank * 9;
53
58
  const piece = board.get(square);
54
- if (file === 0) {
59
+ if (file === offset) {
55
60
  kifBoard += '|';
56
61
  }
57
62
  if (!piece) kifBoard += ' ・';
@@ -64,17 +69,18 @@ export function makeKifBoard(board: Board): string {
64
69
  }
65
70
  }
66
71
  }
67
- kifBoard += '+---------------------------+';
72
+ kifBoard += separator;
68
73
  return kifBoard;
69
74
  }
70
75
 
71
- export function makeKifPocket(material: MaterialSide): string {
72
- if (material.isEmpty()) return 'なし';
73
- return POCKET_ROLES.map(role => {
74
- const r = roleTo1Kanji(role);
75
- const n = material[role];
76
- return n > 1 ? r + numberToKanji(n) : n === 1 ? r : '';
77
- })
76
+ export function makeKifHand(hand: Hand): string {
77
+ if (hand.isEmpty()) return 'なし';
78
+ return handRoles('shogi')
79
+ .map(role => {
80
+ const r = roleTo1Kanji(role);
81
+ const n = hand[role];
82
+ return n > 1 ? r + numberToKanji(n) : n === 1 ? r : '';
83
+ })
78
84
  .filter(p => p.length > 0)
79
85
  .join(' ');
80
86
  }
@@ -96,25 +102,21 @@ export function parseKifHeader(kif: string): Result<Setup, KifError> {
96
102
  export function parseKifPositionHeader(kif: string): Result<Setup, KifError> {
97
103
  const lines = normalizedKifLines(kif);
98
104
 
99
- const gotePocketStr = lines.find(l => l.startsWith('後手の持駒:'));
100
- const sentePocketStr = lines.find(l => l.startsWith('先手の持駒:'));
105
+ const goteHandStr = lines.find(l => l.startsWith('後手の持駒:'));
106
+ const senteHandStr = lines.find(l => l.startsWith('先手の持駒:'));
101
107
  const turn = lines.some(l => l.startsWith('後手番')) ? 'gote' : 'sente';
102
108
 
103
109
  const board: Result<Board, KifError> = parseKifBoard(kif);
104
110
 
105
- const gotePocket = defined(gotePocketStr)
106
- ? parseKifHand(gotePocketStr.split(':')[1])
107
- : Result.ok(MaterialSide.empty());
108
- const sentePocket = defined(sentePocketStr)
109
- ? parseKifHand(sentePocketStr.split(':')[1])
110
- : Result.ok(MaterialSide.empty());
111
+ const goteHand = defined(goteHandStr) ? parseKifHand(goteHandStr.split(':')[1]) : Result.ok(Hand.empty());
112
+ const senteHand = defined(senteHandStr) ? parseKifHand(senteHandStr.split(':')[1]) : Result.ok(Hand.empty());
111
113
 
112
114
  return board.chain(board =>
113
- gotePocket.chain(gPocket =>
114
- sentePocket.map(sPocket => {
115
+ goteHand.chain(gHand =>
116
+ senteHand.map(sHand => {
115
117
  return {
116
118
  board,
117
- pockets: new Material(gPocket, sPocket),
119
+ hands: new Hands(gHand, sHand),
118
120
  turn,
119
121
  fullmoves: 1,
120
122
  };
@@ -125,13 +127,19 @@ export function parseKifPositionHeader(kif: string): Result<Setup, KifError> {
125
127
 
126
128
  export function parseKifBoard(kifBoard: string): Result<Board, KifError> {
127
129
  const lines = normalizedKifLines(kifBoard).filter(l => l.startsWith('|'));
128
- if (lines.length !== 9) return Result.err(new KifError(InvalidKif.Board));
130
+ if (lines.length === 0) return Result.err(new KifError(InvalidKif.Board));
129
131
  const board = Board.empty();
130
- let file = 0;
132
+
133
+ // assuming square board
134
+ board.numberOfRanks = lines.length;
135
+ board.numberOfFiles = lines.length;
136
+
137
+ const offset = 9 - lines.length;
138
+ let file = offset;
131
139
  let rank = 9;
132
140
 
133
141
  for (const l of lines) {
134
- file = 0;
142
+ file = offset;
135
143
  rank--;
136
144
  let gote = false;
137
145
  let prom = false;
@@ -149,9 +157,9 @@ export function parseKifBoard(kifBoard: string): Result<Board, KifError> {
149
157
  default:
150
158
  if (file > 9 || rank < 0) return Result.err(new KifError(InvalidKif.Board));
151
159
  const role = kanjiToRole(c);
152
- if (defined(role)) {
160
+ if (defined(role) && allRoles('shogi').includes(role)) {
153
161
  const square = file + rank * 9;
154
- const piece = { role: prom ? promote(role) : role, color: (gote ? 'gote' : 'sente') as Color };
162
+ const piece = { role: prom ? promote('shogi')(role) : role, color: (gote ? 'gote' : 'sente') as Color };
155
163
  board.set(square, piece);
156
164
  prom = false;
157
165
  gote = false;
@@ -160,27 +168,27 @@ export function parseKifBoard(kifBoard: string): Result<Board, KifError> {
160
168
  }
161
169
  }
162
170
  }
163
- if (rank !== 0 || file !== 9) return Result.err(new KifError(InvalidKif.Board));
171
+ if (rank !== 9 - lines.length || file !== 9) return Result.err(new KifError(InvalidKif.Board));
164
172
  return Result.ok(board);
165
173
  }
166
174
 
167
- export function parseKifHand(pocketPart: string): Result<MaterialSide, KifError> {
168
- const pockets = MaterialSide.empty();
169
- const pieces = pocketPart.replace(/ /g, ' ').trim().split(' ');
175
+ export function parseKifHand(handPart: string): Result<Hand, KifError> {
176
+ const hand = Hand.empty();
177
+ const pieces = handPart.replace(/ /g, ' ').trim().split(' ');
170
178
 
171
- if (pocketPart.includes('なし')) return Result.ok(pockets);
179
+ if (handPart.includes('なし')) return Result.ok(hand);
172
180
  for (const piece of pieces) {
173
181
  for (let i = 0; i < piece.length; i++) {
174
182
  const role = kanjiToRole(piece[i++]);
175
- if (!role) return Result.err(new KifError(InvalidKif.Pockets));
183
+ if (!role || !handRoles('shogi').includes(role)) return Result.err(new KifError(InvalidKif.Hands));
176
184
  let countStr = '';
177
185
  while (i < piece.length && ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'].includes(piece[i]))
178
186
  countStr += piece[i++];
179
187
  const count = kanjiToNumber(countStr) || 1;
180
- pockets[role as PocketRole] += count;
188
+ hand[role] += count;
181
189
  }
182
190
  }
183
- return Result.ok(pockets);
191
+ return Result.ok(hand);
184
192
  }
185
193
 
186
194
  export function parseTags(kif: string): [string, string][] {
@@ -204,7 +212,7 @@ export function parseKifMove(kifMove: string, lastDest: Square | undefined = und
204
212
  const match = kifMove.match(/((?:[123456789][一二三四五六七八九]|同\s?))(飛|角|金|銀|桂|香|歩)打/);
205
213
  if (!match) return;
206
214
  const move = {
207
- role: kanjiToRole(match[2]) as PocketRole,
215
+ role: kanjiToRole(match[2])!,
208
216
  to: parseKifSquare(match[1]) ?? lastDest!,
209
217
  };
210
218
  return move;
@@ -23,6 +23,9 @@ export function sfenToHandicapName(sfen: string): string | undefined {
23
23
  return '八枚落ち';
24
24
  case '4k4/9/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w -':
25
25
  return '十枚落ち';
26
+ // minishogi
27
+ case 'rbsgk/4p/5/P4/KGSBR b -':
28
+ return '五々将棋';
26
29
  default:
27
30
  return undefined;
28
31
  }
@@ -79,6 +82,8 @@ export function handicapNameToSfen(name: string): string | undefined {
79
82
  return '2sgkgs2/9/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w RB2N2L 1';
80
83
  case '八枚得':
81
84
  return '3gkg3/9/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w RB2S2N2L 1';
85
+ case '五々将棋':
86
+ return 'rbsgk/4p/5/P4/KGSBR b - 1';
82
87
  default:
83
88
  return undefined;
84
89
  }
package/src/kifUtil.ts CHANGED
File without changes
package/src/setup.ts CHANGED
@@ -1,97 +1,10 @@
1
- import { Color, POCKET_ROLES } from './types';
1
+ import { Color } from './types';
2
2
  import { Board } from './board';
3
-
4
- export class MaterialSide {
5
- pawn: number;
6
- lance: number;
7
- knight: number;
8
- silver: number;
9
- gold: number;
10
- bishop: number;
11
- rook: number;
12
-
13
- private constructor() {}
14
-
15
- static empty(): MaterialSide {
16
- const m = new MaterialSide();
17
- for (const role of POCKET_ROLES) m[role] = 0;
18
- return m;
19
- }
20
-
21
- static fromBoard(board: Board, color: Color): MaterialSide {
22
- const m = new MaterialSide();
23
- for (const role of POCKET_ROLES) m[role] = board.pieces(color, role).size();
24
- return m;
25
- }
26
-
27
- clone(): MaterialSide {
28
- const m = new MaterialSide();
29
- for (const role of POCKET_ROLES) m[role] = this[role];
30
- return m;
31
- }
32
-
33
- equals(other: MaterialSide): boolean {
34
- return POCKET_ROLES.every(role => this[role] === other[role]);
35
- }
36
-
37
- add(other: MaterialSide): MaterialSide {
38
- const m = new MaterialSide();
39
- for (const role of POCKET_ROLES) m[role] = this[role] + other[role];
40
- return m;
41
- }
42
-
43
- nonEmpty(): boolean {
44
- return POCKET_ROLES.some(role => this[role] > 0);
45
- }
46
-
47
- isEmpty(): boolean {
48
- return !this.nonEmpty();
49
- }
50
-
51
- count(): number {
52
- return this.pawn + this.lance + this.knight + this.silver + this.gold + this.bishop + this.rook;
53
- }
54
- }
55
-
56
- export class Material {
57
- constructor(public gote: MaterialSide, public sente: MaterialSide) {}
58
-
59
- static empty(): Material {
60
- return new Material(MaterialSide.empty(), MaterialSide.empty());
61
- }
62
-
63
- static fromBoard(board: Board): Material {
64
- return new Material(MaterialSide.fromBoard(board, 'gote'), MaterialSide.fromBoard(board, 'sente'));
65
- }
66
-
67
- clone(): Material {
68
- return new Material(this.gote.clone(), this.sente.clone());
69
- }
70
-
71
- equals(other: Material): boolean {
72
- return this.gote.equals(other.gote) && this.sente.equals(other.sente);
73
- }
74
-
75
- add(other: Material): Material {
76
- return new Material(this.gote.add(other.gote), this.sente.add(other.sente));
77
- }
78
-
79
- count(): number {
80
- return this.gote.count() + this.sente.count();
81
- }
82
-
83
- isEmpty(): boolean {
84
- return this.gote.isEmpty() && this.sente.isEmpty();
85
- }
86
-
87
- nonEmpty(): boolean {
88
- return !this.isEmpty();
89
- }
90
- }
3
+ import { Hands } from './hand';
91
4
 
92
5
  export interface Setup {
93
6
  board: Board;
94
- pockets: Material;
7
+ hands: Hands;
95
8
  turn: Color;
96
9
  fullmoves: number;
97
10
  }
@@ -99,7 +12,7 @@ export interface Setup {
99
12
  export function defaultSetup(): Setup {
100
13
  return {
101
14
  board: Board.default(),
102
- pockets: Material.empty(),
15
+ hands: Hands.empty(),
103
16
  turn: 'sente',
104
17
  fullmoves: 1,
105
18
  };