Skip to content

Commit dddd427

Browse files
Abalone
1 parent e25da1c commit dddd427

27 files changed

+853
-277
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules/
22
dist/
3+
.env.local

README.md

+35-6
Original file line numberDiff line numberDiff line change
@@ -91,40 +91,69 @@ More? Please make a pull request to include it here.
9191
Commands are listed in package.json.
9292
In case you want to see the possibilities from the console, run `pnpm run`
9393

94-
Install build dependencies:
94+
### Install build dependencies:
9595

9696
```sh
9797
pnpm install
9898
```
9999

100-
Update deps:
100+
### Update deps:
101101

102102
```sh
103103
rm -rf node_modules pnpm-lock.yaml && pnpm store prune && pnpm install
104104
```
105105

106-
Build the node module:
106+
### Build the node module:
107107

108108
```sh
109109
pnpm prepare --watch
110110
```
111111

112-
Build the minified bundled dist files: (NOTE: from lila you will likely then need to restart the build as it does not watch for changes on the minified file):
112+
### Build the minified bundled dist files: (NOTE: from lila you will likely then need to restart the build as it does not watch for changes on the minified file):
113113

114114
```sh
115115
pnpm dist
116116
```
117117

118-
run tests:
118+
### Run tests:
119119

120120
```sh
121121
pnpm run test --watch
122122
pnpm run test fen --watch
123123
```
124124

125-
Before committing:
125+
### Before committing:
126126

127127
```sh
128128
pnpm run lint
129129
pnpm run format
130130
```
131+
132+
### Watch chessground changes from another project:
133+
134+
In other project:
135+
136+
- declare the link towards chessground (from project's `package.json`)
137+
138+
```json
139+
"dependencies": {
140+
...
141+
"chessground": "link:/path/to/chessground",
142+
...
143+
}
144+
```
145+
146+
In chessground:
147+
148+
- create `.env.local` file based on .env.local.default
149+
- link back:
150+
151+
```sh
152+
pnpm run link
153+
```
154+
155+
- trigger compilation and generate minified file:
156+
157+
```sh
158+
pnpm run local-dist
159+
```

build.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as cps from 'node:child_process';
2+
import * as ps from 'node:process';
3+
4+
const consumerRoot = process.env.CONSUMER_ROOT;
5+
const args = ps.argv.slice(2);
6+
7+
if (args.includes('--link')) {
8+
cps.execSync('pnpm link --dir ' + consumerRoot);
9+
console.log('\x1b[36m%s\x1b[0m', 'Linked to ' + consumerRoot);
10+
}
11+
if (args.includes('--bundle')) {
12+
cps.execSync(
13+
'esbuild src/chessground.ts --bundle --format=esm --outfile=' +
14+
consumerRoot +
15+
'/node_modules/chessground/dist/chessground.min.js',
16+
);
17+
console.log(
18+
'\x1b[32m%s\x1b[0m',
19+
'Compiled and built ' + consumerRoot + '/node_modules/chessground/dist/chessground.min.js',
20+
);
21+
}

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
"check-format": "prettier --check .",
5050
"bundle": "esbuild src/chessground.ts --bundle --format=esm --outfile=dist/chessground.min.js --minify",
5151
"dist": "$npm_execpath run compile && $npm_execpath run bundle",
52+
"link": "node --env-file=.env.local ./build.js --link",
53+
"local-bundle": "node --env-file=.env.local ./build.js --bundle",
54+
"local-dist": "$npm_execpath run compile && $npm_execpath run local-bundle",
5255
"postinstall": "$npm_execpath run bundle"
5356
},
5457
"files": [

src/api.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,11 @@ export function start(state: State, redrawAll: cg.Redraw): Api {
144144
},
145145

146146
move(orig, dest): void {
147-
anim(state => board.baseMove(state, orig, dest), state);
147+
anim(state => state.baseMove(state, orig, dest), state);
148148
},
149149

150150
moveNoAnim(orig, dest): void {
151-
board.baseMove(state, orig, dest);
151+
state.baseMove(state, orig, dest);
152152
state.dom.redraw();
153153
},
154154

src/board.ts

+11-15
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import { premove, queen, knight } from './premove';
2323
import predrop from './predrop';
2424
import * as cg from './types';
2525
import * as T from './transformations';
26+
2627
import { getKeyAtDomPos as abaloneGetKeyAtDomPos } from './variants/abalone/board';
27-
import { abaloneUpdatePiecesFromMove } from './variants/abalone/util';
2828

2929
export function setOrientation(state: HeadlessState, o: cg.Orientation): void {
3030
state.orientation = o;
@@ -183,17 +183,15 @@ function updatePocketPieces(
183183
state.pocketPieces = newPocketPieces;
184184
}
185185

186-
// returns false in case the move could not be processed
186+
/**
187+
* called when a piece is moved from orig to dest
188+
* @returns: false if the move is invalid, true if the move is valid but no capture happened, or the captured piece if a capture happened
189+
*/
187190
export function baseMove(state: HeadlessState, orig: cg.Key, dest: cg.Key): cg.Piece | boolean {
188191
const origPiece = state.pieces.get(orig),
189192
destPiece = state.pieces.get(dest);
190193
if ((orig === dest && state.variant !== 'togyzkumalak' && state.variant !== 'bestemshe') || !origPiece) return false;
191-
let abalonePieces: cg.PiecesDiff = state.pieces, // because captures are computed in abaloneUpdatePiecesFromMove, it is better to store the updated pieces and the capture before the switch responsible of setPieces
192-
abaloneCapture: boolean = false;
193-
if (state.variant === 'abalone') {
194-
[abalonePieces, abaloneCapture] = abaloneUpdatePiecesFromMove(state.pieces, orig, dest);
195-
}
196-
const captured = isCapture(state.variant, destPiece, origPiece) || abaloneCapture;
194+
const captured = isCapture(state.variant, destPiece, origPiece);
197195
if (dest === state.selected) unselect(state);
198196
callUserFunction(state.events.move, orig, dest, captured);
199197

@@ -216,9 +214,6 @@ export function baseMove(state: HeadlessState, orig: cg.Key, dest: cg.Key): cg.P
216214
}
217215
setPieces(state, backgammonUpdatePiecesFromMove(state.pieces, orig, dest));
218216
break;
219-
case 'abalone':
220-
setPieces(state, abalonePieces);
221-
break;
222217
default:
223218
if (!tryAutoCastle(state, orig, dest)) {
224219
state.pieces.set(dest, origPiece);
@@ -246,7 +241,7 @@ function isCapture(variant: cg.Variant, destPiece: cg.Piece | undefined, origPie
246241
//TODO this is more complicated to calculate... (but its only used for sound in lila atm)
247242
return destPiece && destPiece.playerIndex !== origPiece.playerIndex ? destPiece : undefined;
248243
case 'abalone':
249-
return undefined; // we compute it from abaloneUpdatePiecesFromMove instead
244+
return undefined; // we compute it from Abalone namespace using HOF
250245
default:
251246
return destPiece && destPiece.playerIndex !== origPiece.playerIndex ? destPiece : undefined;
252247
}
@@ -276,7 +271,7 @@ export function baseNewPiece(state: HeadlessState, piece: cg.Piece, key: cg.Key,
276271
}
277272

278273
function baseUserMove(state: HeadlessState, orig: cg.Key, dest: cg.Key): cg.Piece | boolean {
279-
const result = baseMove(state, orig, dest);
274+
const result = state.baseMove(state, orig, dest);
280275
if (result) {
281276
state.movable.dests = undefined;
282277
state.dropmode.dropDests = undefined;
@@ -638,7 +633,8 @@ export function stop(state: HeadlessState): void {
638633
cancelMove(state);
639634
}
640635

641-
// triggered when we click on the svg area (a piece, a square or even an area outside the board drawn can be below the cursor)
636+
// triggered when we click on the svg area (a piece, a square or even an area outside the board drawn can be below the cursor).
637+
// @return the key of the square that was clicked, or undefined if the click was outside the board.
642638
export function getKeyAtDomPos(
643639
pos: cg.NumberPair,
644640
orientation: cg.Orientation,
@@ -647,7 +643,7 @@ export function getKeyAtDomPos(
647643
variant: cg.Variant = 'chess',
648644
): cg.Key | undefined {
649645
if (variant === 'abalone') {
650-
return abaloneGetKeyAtDomPos(pos, orientation, bounds, bd);
646+
return abaloneGetKeyAtDomPos(pos, orientation, bounds);
651647
}
652648
const bgBorder = 1 / 15;
653649
const file =

src/chessground.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Config, configure } from './config';
33
import { HeadlessState, State, defaults } from './state';
44
import { renderWrap } from './wrap';
55
import * as events from './events';
6-
import { render, updateBounds } from './render';
6+
import { updateBounds } from './render';
77
import * as svg from './svg';
88
import * as util from './util';
99

@@ -18,7 +18,7 @@ export function Chessground(element: HTMLElement, config?: Config): Api {
1818
elements = renderWrap(element, maybeState, relative),
1919
bounds = util.memo(() => elements.board.getBoundingClientRect()),
2020
redrawNow = (skipSvg?: boolean): void => {
21-
render(state);
21+
maybeState.render(state);
2222
if (!skipSvg && elements.svg) svg.renderSvg(state, elements.svg, elements.customSvg!);
2323
},
2424
boundsUpdated = (): void => {

src/config.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import * as cg from './types';
12
import { HeadlessState } from './state';
23
import { setSelected, setGoScore } from './board';
34
import { read as fenRead, readPocket as fenReadPocket } from './fen';
45
import { DrawShape, DrawBrush } from './draw';
5-
import * as cg from './types';
6+
7+
import { configure as abaloneConfigure } from './variants/abalone/config';
68

79
export interface Config {
810
fen?: cg.FEN; // chess position in Forsyth notation
@@ -181,6 +183,11 @@ export function configure(state: HeadlessState, config: Config): void {
181183
),
182184
);
183185
}
186+
187+
// configure variants
188+
if (state.variant === 'abalone') {
189+
abaloneConfigure(state);
190+
}
184191
}
185192

186193
function setCheck(state: HeadlessState, playerIndex: cg.PlayerIndex | boolean): void {

src/drag.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export function dragNewPiece(s: State, piece: cg.Piece, e: cg.MouchEvent, force?
152152

153153
function processDrag(s: State): void {
154154
requestAnimationFrame(() => {
155-
if (s.variant === 'abalone') return abaloneProcessDrag(s);
155+
if (s.variant === 'abalone') return abaloneProcessDrag(s); // "working" WIP: have to use HOF
156156
const cur = s.draggable.current;
157157
if (!cur) return;
158158
// cancel animations while dragging

src/fen.ts

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export function read(fen: cg.FEN, dimensions: cg.BoardDimensions, variant: cg.Va
2626
let promoted = false;
2727
let num = 0;
2828

29-
// @TODO: try to refactor using Higher Order Functions with a default square board
3029
if (!commaFenVariants.includes(variant)) {
3130
let skipNext = false;
3231
for (let i = 0; i < fen.length; i++) {

src/render.ts

-7
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,12 @@ import { DragCurrent } from './drag';
1414
import * as cg from './types';
1515
import * as T from './transformations';
1616

17-
import { render as renderAbalone } from './variants/abalone/render';
18-
1917
export type PieceName = string; // `$playerIndex $role`
2018
export type SquareClasses = Map<cg.Key, string>;
2119

2220
// ported from https://github.com/veloce/lichobile/blob/master/src/js/chessground/view.js
2321
// in case of bugs, blame @veloce
2422
export function render(s: State): void {
25-
if (s.variant === 'abalone') {
26-
renderAbalone(s);
27-
return;
28-
}
29-
3023
const orientation = s.orientation,
3124
asP1: boolean = p1Pov(s),
3225
posToTranslate = s.dom.relative ? posToTranslateRel : posToTranslateAbs(s.dom.bounds(), s.dimensions, s.variant),

src/state.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import * as fen from './fen';
22
import { AnimCurrent } from './anim';
3+
import { baseMove } from './board';
34
import { DragCurrent } from './drag';
45
import { Drawable } from './draw';
6+
import { render } from './render';
57
import { timer } from './util';
68
import * as cg from './types';
79

@@ -136,6 +138,8 @@ export interface HeadlessState {
136138
notation: cg.Notation;
137139
onlyDropsVariant: boolean;
138140
singleClickMoveVariant: boolean;
141+
baseMove: (state: HeadlessState, orig: cg.Key, dest: cg.Key) => cg.Piece | boolean;
142+
render: (state: State) => void;
139143
}
140144

141145
export interface State extends HeadlessState {
@@ -248,5 +252,7 @@ export function defaults(): HeadlessState {
248252
notation: cg.Notation.DEFAULT,
249253
onlyDropsVariant: false,
250254
singleClickMoveVariant: false,
255+
baseMove,
256+
render,
251257
};
252258
}

src/util.ts

+5-19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import * as cg from './types';
22
import * as T from './transformations';
33

4-
import {
5-
posToTranslateAbs as abalonePosToTranslateAbs,
6-
posToTranslateRel as abalonePosToTranslateRel,
7-
} from './variants/abalone/util';
4+
import { posToTranslateRel as abalonePosToTranslateRel, posToTranslateBase2 } from './variants/abalone/util';
85

96
export const playerIndexs: cg.PlayerIndex[] = ['p1', 'p2'];
107
export const invRanks: readonly cg.Rank[] = [...cg.ranks19].reverse();
@@ -23,18 +20,6 @@ export function allKeys(bd: cg.BoardDimensions = { width: 8, height: 8 }) {
2320
return Array.prototype.concat(...files(bd.width).map(c => ranks(bd.height).map(r => c + r)));
2421
}
2522

26-
// @TODO VFR: example of how pos2key could be rewritten
27-
// export const pos2key = (variant: cg.Variant, pos: cg.Pos): cg.Key => {
28-
// switch(variant) {
29-
// case "abalone": {
30-
// return (cg.files[pos[0] - 1] + cg.ranks19[pos[1] - 1]) as cg.Key;
31-
// }
32-
// default: {
33-
// return (cg.files[pos[0] - 1] + cg.ranks19[pos[1] - 1]) as cg.Key;
34-
// }
35-
// }
36-
// }
37-
3823
export function pos2key(pos: cg.Pos): cg.Key {
3924
return (cg.files[pos[0] - 1] + cg.ranks19[pos[1] - 1]) as cg.Key;
4025
}
@@ -177,7 +162,8 @@ export const posToTranslateAbs = (
177162
(variant === 'backgammon' || variant === 'hyper' || variant === 'nackgammon' ? 0.8 : 1),
178163
yFactor = bounds.height / bt.height;
179164
if (variant === 'abalone') {
180-
return (pos, orientation) => abalonePosToTranslateAbs(pos, orientation, xFactor, yFactor, bt);
165+
// "working" WIP: have to use HOF
166+
return (pos, orientation) => posToTranslateBase2(bounds, pos, orientation);
181167
}
182168
return (pos, orientation) => posToTranslateBase(pos, orientation, xFactor, yFactor, bt);
183169
};
@@ -189,7 +175,8 @@ export const posToTranslateRel = (
189175
v: cg.Variant,
190176
): cg.NumberPair => {
191177
if (v === 'abalone') {
192-
return abalonePosToTranslateRel(pos, orientation, bt);
178+
// "working" WIP: have to use HOF
179+
return abalonePosToTranslateRel(pos, orientation);
193180
}
194181
return posToTranslateBase(
195182
pos,
@@ -238,7 +225,6 @@ export const createEl = (tagName: string, className?: string): HTMLElement => {
238225
return el;
239226
};
240227

241-
// @TODO VFR investigations: could be interesting to see if it can be used to move Abalone squares ??
242228
export function computeSquareCenter(
243229
key: cg.Key,
244230
orientation: cg.Orientation,

src/variants/abalone/README.md

-1
This file was deleted.

0 commit comments

Comments
 (0)