@@ -11,7 +11,13 @@ import {
11
11
callUserFunction ,
12
12
calculatePieceGroup ,
13
13
calculateGoScores ,
14
+ calculateGoCaptures ,
15
+ calculateBackgammonDropChanges ,
14
16
oppositeOrientationBG ,
17
+ owareUpdatePiecesFromMove ,
18
+ togyzkumalakUpdatePiecesFromMove ,
19
+ backgammonUpdatePiecesFromMove ,
20
+ calculateFlippingPieces ,
15
21
} from './util' ;
16
22
import { premove , queen , knight } from './premove' ;
17
23
import predrop from './predrop' ;
@@ -104,36 +110,145 @@ function tryAutoCastle(state: HeadlessState, orig: cg.Key, dest: cg.Key): boolea
104
110
return true ;
105
111
}
106
112
113
+ //update state pieces based on drop for specific game logics
114
+ function setDropVariantState ( state : HeadlessState , piece : cg . Piece , key : cg . Key ) : void {
115
+ switch ( state . variant ) {
116
+ case 'flipello10' :
117
+ case 'flipello' : {
118
+ const flipping = calculateFlippingPieces ( state . dimensions , state . pieces , piece , key ) ;
119
+ const flipPiece : cg . Piece = { role : 'p-piece' , playerIndex : state . turnPlayerIndex } ;
120
+ flipping . forEach ( key => state . pieces . set ( key , flipPiece ) ) ;
121
+ state . pieces . set ( key , piece ) ;
122
+ break ;
123
+ }
124
+ case 'go19x19' :
125
+ case 'go13x13' :
126
+ case 'go9x9' : {
127
+ state . pieces . set ( key , piece ) ;
128
+ const captured : cg . Key [ ] = calculateGoCaptures ( key , state . pieces , state . dimensions ) ;
129
+ captured . map ( k => state . pieces . delete ( k ) ) ;
130
+ break ;
131
+ }
132
+ case 'nackgammon' :
133
+ case 'backgammon' : {
134
+ const destPiece = state . pieces . get ( key ) ;
135
+ const capture = destPiece ? destPiece . playerIndex !== piece . playerIndex : false ;
136
+ const backgammonPieces : cg . PiecesDiff = calculateBackgammonDropChanges ( state . pieces , piece , key ) ;
137
+ setPieces ( state , backgammonPieces ) ;
138
+ updatePocketPieces ( state , opposite ( piece . playerIndex ) , true , capture ) ;
139
+ break ;
140
+ }
141
+ default :
142
+ state . pieces . set ( key , piece ) ;
143
+ }
144
+ }
145
+
146
+ function updatePocketPieces (
147
+ state : HeadlessState ,
148
+ capturedPiecePlayerIndex : cg . PlayerIndex ,
149
+ isDrop : boolean ,
150
+ isCapture : boolean
151
+ ) : void {
152
+ let playerCount = 0 ;
153
+ let enemyCount = 0 ;
154
+
155
+ state . pocketPieces . forEach ( p => {
156
+ const pCount = + p . role . split ( '-' ) [ 0 ] . substring ( 1 ) ;
157
+ if ( p . playerIndex === capturedPiecePlayerIndex ) {
158
+ enemyCount = pCount ;
159
+ } else {
160
+ playerCount = pCount ;
161
+ }
162
+ } ) ;
163
+
164
+ const newPocketPieces : cg . Piece [ ] = [ ] ;
165
+ if ( enemyCount > 0 || isCapture ) {
166
+ const piece = {
167
+ role : `s${ enemyCount + ( isCapture ? 1 : 0 ) } -piece` ,
168
+ playerIndex : capturedPiecePlayerIndex ,
169
+ } as cg . Piece ;
170
+ newPocketPieces . push ( piece ) ;
171
+ }
172
+ if ( ( playerCount > 0 && ! isDrop ) || ( isDrop && playerCount > 1 ) ) {
173
+ const piece = {
174
+ role : `s${ playerCount - ( isDrop ? 1 : 0 ) } -piece` ,
175
+ playerIndex : opposite ( capturedPiecePlayerIndex ) ,
176
+ } as cg . Piece ;
177
+ newPocketPieces . push ( piece ) ;
178
+ }
179
+
180
+ state . pocketPieces = newPocketPieces ;
181
+ }
182
+
107
183
export function baseMove ( state : HeadlessState , orig : cg . Key , dest : cg . Key ) : cg . Piece | boolean {
108
184
const origPiece = state . pieces . get ( orig ) ,
109
185
destPiece = state . pieces . get ( dest ) ;
110
186
if ( ( orig === dest && state . variant !== 'togyzkumalak' ) || ! origPiece ) return false ;
111
- const captured = destPiece && destPiece . playerIndex !== origPiece . playerIndex ? destPiece : undefined ;
187
+ const captured = isCapture ( state . variant , destPiece , origPiece ) ;
112
188
if ( dest === state . selected ) unselect ( state ) ;
113
189
callUserFunction ( state . events . move , orig , dest , captured ) ;
114
- if ( ! tryAutoCastle ( state , orig , dest ) ) {
115
- state . pieces . set ( dest , origPiece ) ;
116
- state . pieces . delete ( orig ) ;
190
+
191
+ switch ( state . variant ) {
192
+ case 'togyzkumalak' :
193
+ setPieces ( state , togyzkumalakUpdatePiecesFromMove ( state . dimensions , state . pieces , orig , dest ) ) ;
194
+ break ;
195
+ case 'oware' :
196
+ setPieces ( state , owareUpdatePiecesFromMove ( state . dimensions , state . pieces , orig , dest ) ) ;
197
+ break ;
198
+ case 'nackgammon' :
199
+ case 'backgammon' :
200
+ if ( captured ) {
201
+ updatePocketPieces ( state , opposite ( origPiece . playerIndex ) , false , true ) ;
202
+ }
203
+ setPieces ( state , backgammonUpdatePiecesFromMove ( state . pieces , orig , dest ) ) ;
204
+ break ;
205
+ default :
206
+ if ( ! tryAutoCastle ( state , orig , dest ) ) {
207
+ state . pieces . set ( dest , origPiece ) ;
208
+ state . pieces . delete ( orig ) ;
209
+ }
117
210
}
211
+
118
212
state . lastMove = [ orig , dest ] ;
119
213
state . check = undefined ;
120
214
callUserFunction ( state . events . change ) ;
121
215
return captured || true ;
122
216
}
123
217
218
+ function isCapture ( variant : cg . Variant , destPiece : cg . Piece | undefined , origPiece : cg . Piece ) : cg . Piece | undefined {
219
+ switch ( variant ) {
220
+ case 'togyzkumalak' : {
221
+ //TODO account for wrapping around board (simimlar to oware capture)
222
+ const count = destPiece && Number ( destPiece . role . split ( '-' ) [ 0 ] . substring ( 1 ) ) ;
223
+ return destPiece && destPiece . playerIndex !== origPiece . playerIndex && count && ( count === 2 || count % 2 === 1 )
224
+ ? destPiece
225
+ : undefined ;
226
+ }
227
+ case 'oware' :
228
+ //TODO this is more complicated to calculate... (but its only used for sound in lila atm)
229
+ return destPiece && destPiece . playerIndex !== origPiece . playerIndex ? destPiece : undefined ;
230
+ default :
231
+ return destPiece && destPiece . playerIndex !== origPiece . playerIndex ? destPiece : undefined ;
232
+ }
233
+ }
234
+
124
235
export function baseNewPiece ( state : HeadlessState , piece : cg . Piece , key : cg . Key , force ?: boolean ) : boolean {
125
- if ( state . pieces . has ( key ) ) {
236
+ if ( state . pieces . has ( key ) && ! ( state . variant === 'backgammon' || state . variant === 'nackgammon' ) ) {
126
237
if ( force ) state . pieces . delete ( key ) ;
127
238
else return false ;
128
239
}
129
240
callUserFunction ( state . events . dropNewPiece , piece , key ) ;
130
- state . pieces . set ( key , piece ) ;
241
+ setDropVariantState ( state , piece , key ) ;
242
+
131
243
state . lastMove = [ key ] ;
132
244
state . check = undefined ;
133
245
callUserFunction ( state . events . change ) ;
134
246
state . movable . dests = undefined ;
135
247
state . dropmode . dropDests = undefined ;
136
- state . turnPlayerIndex = opposite ( state . turnPlayerIndex ) ;
248
+ if ( ! ( state . variant === 'backgammon' || state . variant === 'nackgammon' ) ) {
249
+ //end turn action is used for backgammon games
250
+ state . turnPlayerIndex = opposite ( state . turnPlayerIndex ) ;
251
+ }
137
252
return true ;
138
253
}
139
254
@@ -143,12 +258,38 @@ function baseUserMove(state: HeadlessState, orig: cg.Key, dest: cg.Key): cg.Piec
143
258
state . movable . dests = undefined ;
144
259
state . dropmode . dropDests = undefined ;
145
260
state . liftable . liftDests = undefined ;
146
- state . turnPlayerIndex = opposite ( state . turnPlayerIndex ) ;
261
+ if ( ! ( state . variant === 'backgammon' || state . variant === 'nackgammon' ) ) {
262
+ //end turn manually for backgammon games
263
+ state . turnPlayerIndex = opposite ( state . turnPlayerIndex ) ;
264
+ }
147
265
state . animation . current = undefined ;
148
266
}
149
267
return result ;
150
268
}
151
269
270
+ export function baseLift ( state : HeadlessState , dest : cg . Key ) : boolean {
271
+ const piece = state . pieces . get ( dest ) ;
272
+ if ( piece ) {
273
+ if ( state . variant === 'backgammon' || state . variant === 'nackgammon' ) {
274
+ const count = piece . role . split ( '-' ) [ 0 ] . substring ( 1 ) ;
275
+ const letter = piece . role . charAt ( 0 ) ;
276
+ if ( count === '1' ) {
277
+ state . pieces . delete ( dest ) ;
278
+ } else {
279
+ state . pieces . set ( dest , {
280
+ role : `${ letter } ${ + count - 1 } -piece` as cg . Role ,
281
+ playerIndex : piece . playerIndex ,
282
+ } ) ;
283
+ }
284
+ } else {
285
+ state . pieces . delete ( dest ) ;
286
+ }
287
+ return true ;
288
+ } else {
289
+ return false ;
290
+ }
291
+ }
292
+
152
293
export function userMove ( state : HeadlessState , orig : cg . Key , dest : cg . Key ) : boolean {
153
294
if ( canMove ( state , orig , dest ) ) {
154
295
const result = baseUserMove ( state , orig , dest ) ;
@@ -185,27 +326,16 @@ export function userLift(state: HeadlessState, dest: cg.Key): boolean {
185
326
state . liftable . liftDests . includes ( dest ) &&
186
327
state . turnPlayerIndex === piece . playerIndex
187
328
) {
188
- if ( state . variant === 'backgammon' || state . variant === 'nackgammon' ) {
189
- const count = piece . role . split ( '-' ) [ 0 ] . substring ( 1 ) ;
190
- const letter = piece . role . charAt ( 0 ) ;
191
- if ( count === '1' ) {
192
- state . pieces . delete ( dest ) ;
193
- } else {
194
- state . pieces . set ( dest , {
195
- role : `${ letter } ${ + count - 1 } -piece` as cg . Role ,
196
- playerIndex : piece . playerIndex ,
197
- } ) ;
198
- }
199
- } else {
200
- state . pieces . delete ( dest ) ;
201
- }
202
- state . movable . dests = undefined ;
203
- state . dropmode . dropDests = undefined ;
204
- state . liftable . liftDests = undefined ;
205
- state . animation . current = undefined ;
329
+ const result = baseLift ( state , dest ) ;
330
+ if ( result ) {
331
+ state . movable . dests = undefined ;
332
+ state . dropmode . dropDests = undefined ;
333
+ state . liftable . liftDests = undefined ;
334
+ state . animation . current = undefined ;
206
335
207
- callUserFunction ( state . liftable . events . after , dest ) ;
208
- return true ;
336
+ callUserFunction ( state . liftable . events . after , dest ) ;
337
+ return true ;
338
+ }
209
339
} else {
210
340
unsetPremove ( state ) ;
211
341
unsetPredrop ( state ) ;
0 commit comments