shogiops 0.2.8 → 0.5.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 (115) hide show
  1. package/attacks.js +32 -48
  2. package/attacks.js.map +1 -1
  3. package/board.d.ts +3 -0
  4. package/board.js +60 -33
  5. package/board.js.map +1 -1
  6. package/compat.d.ts +6 -24
  7. package/compat.js +20 -185
  8. package/compat.js.map +1 -1
  9. package/debug.js +37 -72
  10. package/debug.js.map +1 -1
  11. package/hand.d.ts +38 -0
  12. package/hand.js +69 -0
  13. package/hand.js.map +1 -0
  14. package/hash.d.ts +4 -3
  15. package/hash.js +12 -20
  16. package/hash.js.map +1 -1
  17. package/index.d.ts +13 -10
  18. package/index.js +22 -64
  19. package/index.js.map +1 -1
  20. package/{csa.d.ts → notation/csa/csa.d.ts} +9 -9
  21. package/notation/csa/csa.js +213 -0
  22. package/notation/csa/csa.js.map +1 -0
  23. package/notation/japanese.d.ts +3 -0
  24. package/notation/japanese.js +61 -0
  25. package/notation/japanese.js.map +1 -0
  26. package/{kif.d.ts → notation/kif/kif.d.ts} +10 -9
  27. package/notation/kif/kif.js +232 -0
  28. package/notation/kif/kif.js.map +1 -0
  29. package/{kifHandicaps.d.ts → notation/kif/kifHandicaps.d.ts} +0 -0
  30. package/{kifHandicaps.js → notation/kif/kifHandicaps.js} +7 -7
  31. package/notation/kif/kifHandicaps.js.map +1 -0
  32. package/notation/kitaoKawasaki.d.ts +3 -0
  33. package/notation/kitaoKawasaki.js +26 -0
  34. package/notation/kitaoKawasaki.js.map +1 -0
  35. package/notation/kitaokawasaki.d.ts +0 -0
  36. package/notation/kitaokawasaki.js +2 -0
  37. package/notation/kitaokawasaki.js.map +1 -0
  38. package/notation/notationUtil.d.ts +12 -0
  39. package/{kifUtil.js → notation/notationUtil.js} +39 -40
  40. package/notation/notationUtil.js.map +1 -0
  41. package/notation/western.d.ts +3 -0
  42. package/notation/western.js +23 -0
  43. package/notation/western.js.map +1 -0
  44. package/notation/westernEngine.d.ts +3 -0
  45. package/notation/westernEngine.js +23 -0
  46. package/notation/westernEngine.js.map +1 -0
  47. package/package.json +54 -37
  48. package/setup.d.ts +2 -32
  49. package/setup.js +5 -80
  50. package/setup.js.map +1 -1
  51. package/sfen.d.ts +33 -0
  52. package/sfen.js +188 -0
  53. package/sfen.js.map +1 -0
  54. package/shogi.d.ts +14 -7
  55. package/shogi.js +138 -141
  56. package/shogi.js.map +1 -1
  57. package/squareSet.d.ts +1 -5
  58. package/squareSet.js +1 -18
  59. package/squareSet.js.map +1 -1
  60. package/src/board.ts +34 -0
  61. package/src/compat.ts +18 -197
  62. package/src/debug.ts +26 -56
  63. package/src/hand.ts +94 -0
  64. package/src/hash.ts +8 -7
  65. package/src/index.ts +16 -12
  66. package/src/{csa.ts → notation/csa/csa.ts} +53 -52
  67. package/src/notation/japanese.ts +74 -0
  68. package/src/{kif.ts → notation/kif/kif.ts} +77 -78
  69. package/src/{kifHandicaps.ts → notation/kif/kifHandicaps.ts} +5 -0
  70. package/src/notation/kitaoKawasaki.ts +24 -0
  71. package/src/{kifUtil.ts → notation/notationUtil.ts} +37 -28
  72. package/src/notation/western.ts +21 -0
  73. package/src/notation/westernEngine.ts +21 -0
  74. package/src/setup.ts +4 -91
  75. package/src/sfen.ts +190 -0
  76. package/src/shogi.ts +75 -75
  77. package/src/squareSet.ts +1 -18
  78. package/src/transform.ts +3 -1
  79. package/src/types.ts +13 -16
  80. package/src/util.ts +8 -73
  81. package/src/variant.ts +44 -3
  82. package/src/variantUtil.ts +160 -0
  83. package/transform.js +18 -24
  84. package/transform.js.map +1 -1
  85. package/types.d.ts +3 -7
  86. package/types.js +18 -24
  87. package/types.js.map +1 -1
  88. package/util.d.ts +4 -7
  89. package/util.js +24 -96
  90. package/util.js.map +1 -1
  91. package/variant.d.ts +12 -1
  92. package/variant.js +42 -16
  93. package/variant.js.map +1 -1
  94. package/variantUtil.d.ts +13 -0
  95. package/variantUtil.js +147 -0
  96. package/variantUtil.js.map +1 -0
  97. package/csa.js +0 -229
  98. package/csa.js.map +0 -1
  99. package/csaUtil.d.ts +0 -3
  100. package/csaUtil.js +0 -21
  101. package/csaUtil.js.map +0 -1
  102. package/fen.d.ts +0 -32
  103. package/fen.js +0 -197
  104. package/fen.js.map +0 -1
  105. package/kif.js +0 -247
  106. package/kif.js.map +0 -1
  107. package/kifHandicaps.js.map +0 -1
  108. package/kifUtil.d.ts +0 -9
  109. package/kifUtil.js.map +0 -1
  110. package/san.d.ts +0 -6
  111. package/san.js +0 -135
  112. package/san.js.map +0 -1
  113. package/src/csaUtil.ts +0 -15
  114. package/src/fen.ts +0 -183
  115. package/src/san.ts +0 -136
package/src/csaUtil.ts DELETED
@@ -1,15 +0,0 @@
1
- import { Square } from './types';
2
- import { squareFile, squareRank } from './util';
3
-
4
- export function parseCsaSquare(str: string): Square | undefined {
5
- if (str.length !== 2) return;
6
- if (str === '00') return 0; // for pocket
7
- const file = Math.abs(str.charCodeAt(0) - '9'.charCodeAt(0));
8
- const rank = Math.abs(str.charCodeAt(1) - '9'.charCodeAt(0));
9
- if (file < 0 || file >= 9 || rank < 0 || rank >= 9) return;
10
- return file + 9 * rank;
11
- }
12
-
13
- export function makeCsaSquare(sq: Square): string {
14
- return (9 - squareFile(sq)).toString() + (9 - squareRank(sq));
15
- }
package/src/fen.ts DELETED
@@ -1,183 +0,0 @@
1
- import { Result } from '@badrap/result';
2
- import { Piece, Color, POCKET_ROLES, PocketRole } from './types';
3
- import { Board } from './board';
4
- import { Setup, MaterialSide, Material } from './setup';
5
- import { defined, roleToChar, charToRole, toBW } from './util';
6
-
7
- export const INITIAL_BOARD_FEN = 'lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL';
8
- export const INITIAL_EPD = INITIAL_BOARD_FEN + ' b -';
9
- export const INITIAL_FEN = INITIAL_EPD + ' 1';
10
- export const EMPTY_BOARD_FEN = '9/9/9/9/9/9/9/9/9';
11
- export const EMPTY_EPD = EMPTY_BOARD_FEN + ' b -';
12
- export const EMPTY_FEN = EMPTY_EPD + ' 1';
13
-
14
- export enum InvalidFen {
15
- Fen = 'ERR_FEN',
16
- Board = 'ERR_BOARD',
17
- Pockets = 'ERR_POCKETS',
18
- Turn = 'ERR_TURN',
19
- Fullmoves = 'ERR_FULLMOVES',
20
- }
21
-
22
- export class FenError extends Error {}
23
-
24
- function parseSmallUint(str: string): number | undefined {
25
- return /^\d{1,4}$/.test(str) ? parseInt(str, 10) : undefined;
26
- }
27
-
28
- function charToPiece(ch: string): Piece | undefined {
29
- const role = charToRole(ch);
30
- return role && { role, color: ch.toLowerCase() === ch ? 'gote' : 'sente' };
31
- }
32
-
33
- export function parseBoardFen(boardPart: string): Result<Board, FenError> {
34
- const board = Board.empty();
35
- let rank = 8;
36
- let file = 0;
37
- for (let i = 0; i < boardPart.length; i++) {
38
- let c = boardPart[i];
39
- if (c === '/' && file === 9) {
40
- file = 0;
41
- rank--;
42
- } else {
43
- const step = parseInt(c, 10);
44
- if (step > 0) file += step;
45
- else {
46
- if (file >= 9 || rank < 0) return Result.err(new FenError(InvalidFen.Board));
47
- if (c === '+' && i + 1 < boardPart.length) c += boardPart[++i];
48
- const square = file + rank * 9;
49
- const piece = charToPiece(c);
50
- if (!piece) return Result.err(new FenError(InvalidFen.Board));
51
- board.set(square, piece);
52
- file++;
53
- }
54
- }
55
- }
56
- if (rank !== 0 || file !== 9) return Result.err(new FenError(InvalidFen.Board));
57
- return Result.ok(board);
58
- }
59
-
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;
65
- // max 99
66
- 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);
70
- } 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;
74
- }
75
- return Result.ok(pockets);
76
- }
77
-
78
- export function parseFen(fen: string): Result<Setup, FenError> {
79
- const parts = fen.split(' ');
80
-
81
- // Board
82
- const boardPart = parts.shift()!;
83
- const board: Result<Board, FenError> = parseBoardFen(boardPart);
84
-
85
- // Turn
86
- const turnPart = parts.shift();
87
- let turn: Color;
88
- if (!defined(turnPart) || turnPart === 'b') turn = 'sente';
89
- else if (turnPart === 'w') turn = 'gote';
90
- else return Result.err(new FenError(InvalidFen.Turn));
91
-
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);
97
-
98
- // Turn
99
- const fullmovesPart = parts.shift();
100
- const fullmoves = defined(fullmovesPart) ? parseSmallUint(fullmovesPart) : 1;
101
- if (!defined(fullmoves)) return Result.err(new FenError(InvalidFen.Fullmoves));
102
-
103
- if (parts.length > 0) return Result.err(new FenError(InvalidFen.Fen));
104
-
105
- return board.chain(board =>
106
- pockets.map(pockets => {
107
- return {
108
- board,
109
- pockets,
110
- turn,
111
- fullmoves: Math.max(1, fullmoves),
112
- };
113
- })
114
- );
115
- }
116
-
117
- interface FenOpts {
118
- epd?: boolean;
119
- }
120
-
121
- export function parsePiece(str: string): Piece | undefined {
122
- if (!str) return;
123
- const piece = charToPiece(str[0]);
124
- if (!piece) return;
125
- else if (str.length > 1 && str[1] !== '+') return;
126
- return piece;
127
- }
128
-
129
- export function makePiece(piece: Piece): string {
130
- let r = roleToChar(piece.role);
131
- if (piece.color === 'sente') r = r.toUpperCase();
132
- return r;
133
- }
134
-
135
- export function makeBoardFen(board: Board): string {
136
- let fen = '';
137
- let empty = 0;
138
- for (let rank = 8; rank >= 0; rank--) {
139
- for (let file = 0; file < 9; file++) {
140
- const square = file + rank * 9;
141
- const piece = board.get(square);
142
- if (!piece) empty++;
143
- else {
144
- if (empty > 0) {
145
- fen += empty;
146
- empty = 0;
147
- }
148
- fen += makePiece(piece);
149
- }
150
-
151
- if (file === 8) {
152
- if (empty > 0) {
153
- fen += empty;
154
- empty = 0;
155
- }
156
- if (rank !== 0) fen += '/';
157
- }
158
- }
159
- }
160
- return fen;
161
- }
162
-
163
- export function makePocket(material: MaterialSide): string {
164
- return POCKET_ROLES.map(role => {
165
- const r = roleToChar(role);
166
- const n = material[role];
167
- return n > 1 ? n + r : n === 1 ? r : '';
168
- }).join('');
169
- }
170
-
171
- export function makePockets(pocket: Material): string {
172
- const pockets = makePocket(pocket.sente).toUpperCase() + makePocket(pocket.gote);
173
- return pockets === '' ? '-' : pockets;
174
- }
175
-
176
- export function makeFen(setup: Setup, opts?: FenOpts): string {
177
- return [
178
- makeBoardFen(setup.board),
179
- toBW(setup.turn),
180
- makePockets(setup.pockets),
181
- ...(opts?.epd ? [] : [Math.max(1, Math.min(setup.fullmoves, 9999))]),
182
- ].join(' ');
183
- }
package/src/san.ts DELETED
@@ -1,136 +0,0 @@
1
- import { isDrop, Move, PocketRole, PROMOTABLE_ROLES } from './types';
2
- import { defined, makeSquare } from './util';
3
- import { SquareSet } from './squareSet';
4
- import { Position } from './shogi';
5
- import { lishogiCharToRole, roleToLishogiChar, shogiCoordToChessCord, parseChessSquare } from './compat';
6
-
7
- function makeSanWithoutSuffix(pos: Position, move: Move): string {
8
- let san = '';
9
- if (isDrop(move)) {
10
- san = roleToLishogiChar(move.role).toUpperCase() + '*' + shogiCoordToChessCord(makeSquare(move.to));
11
- } else {
12
- const role = pos.board.getRole(move.from);
13
- if (!role) return '--';
14
- const capture = pos.board.occupied.has(move.to);
15
- san = roleToLishogiChar(role).toUpperCase();
16
-
17
- if (role !== 'pawn' && role !== 'lance' && role !== 'king') {
18
- // Disambiguation
19
- let others = SquareSet.empty();
20
- for (const s of pos.board.pieces(pos.turn, role)) {
21
- if (pos.dests(s).has(move.to)) others = others.union(SquareSet.fromSquare(s));
22
- }
23
- others = others.without(move.from);
24
- if (others.nonEmpty()) {
25
- san += shogiCoordToChessCord(makeSquare(move.from));
26
- }
27
- }
28
- if (capture) san += 'x';
29
- san += shogiCoordToChessCord(makeSquare(move.to));
30
- if (move.promotion) san += '+';
31
- // Do we need to force promotion?
32
- else if (
33
- (role === 'knight' && SquareSet.backrank2(pos.turn).has(move.to)) ||
34
- ((role === 'pawn' || role === 'lance') && SquareSet.backrank(pos.turn).has(move.to))
35
- )
36
- san += '+';
37
- else if (
38
- (PROMOTABLE_ROLES as ReadonlyArray<string>).includes(role) &&
39
- SquareSet.promotionZone(pos.turn).has(move.to)
40
- )
41
- san += '=';
42
- }
43
- return san;
44
- }
45
-
46
- export function makeSanAndPlay(pos: Position, move: Move): string {
47
- const san = makeSanWithoutSuffix(pos, move);
48
- pos.play(move);
49
- return san;
50
- }
51
-
52
- export function makeSanVariation(pos: Position, variation: Move[]): string {
53
- pos = pos.clone();
54
- const line = [];
55
- for (let i = 0; i < variation.length; i++) {
56
- if (i !== 0) line.push(' ');
57
- line.push(pos.fullmoves, '. ');
58
- const san = makeSanWithoutSuffix(pos, variation[i]);
59
- pos.play(variation[i]);
60
- line.push(san);
61
- if (san === '--') return line.join('');
62
- if (i === variation.length - 1 && pos.outcome()?.winner) line.push('投了');
63
- }
64
- return line.join('');
65
- }
66
-
67
- export function makeSan(pos: Position, move: Move): string {
68
- return makeSanAndPlay(pos.clone(), move);
69
- }
70
-
71
- export function parseSan(pos: Position, san: string): Move | undefined {
72
- const ctx = pos.ctx();
73
-
74
- // Normal move
75
- const match = san.match(/^([PLNSGKBRTUMAHD])([a-i][1-9])?[x]?([a-i][1-9])(\+|\=)?$/);
76
- if (!match) {
77
- // Drop
78
- const match = san.match(/^([PLNSGBRplsgbr])\*([a-i][1-9])$/);
79
- if (!match) return;
80
- const move = {
81
- role: lishogiCharToRole(match[1]) as PocketRole,
82
- to: parseChessSquare(match[2])!,
83
- };
84
- return pos.isLegal(move, ctx) ? move : undefined;
85
- }
86
- const role = lishogiCharToRole(match[1])!;
87
- const to = parseChessSquare(match[3])!;
88
- const fromStr = match[2];
89
-
90
- let candidates = pos.board.pieces(pos.turn, role);
91
- if (fromStr) candidates = candidates.intersect(SquareSet.fromSquare(parseChessSquare(fromStr)!));
92
-
93
- // Check uniqueness and legality
94
- let from;
95
- for (const candidate of candidates) {
96
- if (pos.dests(candidate, ctx).has(to)) {
97
- if (defined(from)) return; // Ambiguous
98
- from = candidate;
99
- }
100
- }
101
- if (!defined(from)) return; // Illegal
102
-
103
- const promotionStr = match[4];
104
- let promotion: boolean;
105
- if (promotionStr === '+') promotion = true;
106
- else promotion = false;
107
-
108
- // Promotion needs to be specified in san
109
- if (
110
- !defined(promotionStr) &&
111
- (PROMOTABLE_ROLES as ReadonlyArray<string>).includes(role) &&
112
- (SquareSet.promotionZone(pos.turn).has(to) || SquareSet.promotionZone(pos.turn).has(from))
113
- )
114
- return;
115
-
116
- // role can't be promoted/unpromoted or it isn't in/from the promotion zone
117
- if (
118
- promotionStr &&
119
- (!(PROMOTABLE_ROLES as ReadonlyArray<string>).includes(role) ||
120
- (!SquareSet.promotionZone(pos.turn).has(to) && !SquareSet.promotionZone(pos.turn).has(from)))
121
- )
122
- return;
123
- // force promotion
124
- else if (
125
- !promotion &&
126
- (((role === 'pawn' || role === 'lance') && SquareSet.backrank(pos.turn).has(to)) ||
127
- (role === 'knight' && SquareSet.backrank2(pos.turn).has(to)))
128
- )
129
- return;
130
-
131
- return {
132
- from,
133
- to,
134
- promotion,
135
- };
136
- }