Skip to content

Commit

Permalink
Merge pull request #26 from Mind-Sports-Games/pla-840-add-breakthroug…
Browse files Browse the repository at this point in the history
…h-lilalila-ws

Pla 840 add breakthrough lilalila ws
  • Loading branch information
vincentfrochot authored Jun 24, 2024
2 parents a576ff4 + ad6a619 commit 02a3e0a
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 122 deletions.
52 changes: 1 addition & 51 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,4 @@ node_modules/
docs/
coverage/
package-lock.json

/attacks.js
/board.js
/chess.js
/compat.js
/variant.js
/transform.js
/setup.js
/squareSet.js
/types.js
/util.js
/debug.js
/fen.js
/fp.js
/san.js
/hash.js
/index.js

/attacks.d.ts
/board.d.ts
/chess.d.ts
/compat.d.ts
/variant.d.ts
/transform.d.ts
/setup.d.ts
/squareSet.d.ts
/types.d.ts
/util.d.ts
/debug.d.ts
/fen.d.ts
/fp.d.ts
/san.d.ts
/hash.d.ts
/index.d.ts

/attacks.js.map
/board.js.map
/chess.js.map
/compat.js.map
/variant.js.map
/transform.js.map
/setup.js.map
/squareSet.js.map
/types.js.map
/util.js.map
/debug.js.map
/fen.js.map
/fp.js.map
/san.js.map
/hash.js.map
/index.js.map
build/
9 changes: 9 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const jestConfig: { [K: string]: unknown } = {
preset: 'ts-jest',
moduleDirectories: ['node_modules'],
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/src/$1',
},
};

export default jestConfig;
71 changes: 5 additions & 66 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stratops",
"version": "0.8.1-pstrat4.1",
"version": "0.8.1-pstrat4.2",
"description": "Strategy game rules and operations",
"keywords": [
"chess",
Expand All @@ -16,7 +16,7 @@
"type": "commonjs",
"sideEffects": false,
"dependencies": {
"@badrap/result": "^0.2.8"
"@badrap/result": "0.2.8"
},
"devDependencies": {
"@types/jest": "^26.0.22",
Expand All @@ -26,6 +26,7 @@
"jest": "^26.6.3",
"prettier": "^2.2.1",
"ts-jest": "^26.5.4",
"ts-node": "^10.9.2",
"typedoc": "^0.23.22",
"typescript": "4.8.4"
},
Expand All @@ -39,70 +40,8 @@
"check-format": "prettier --check ."
},
"files": [
"attacks.js",
"board.js",
"chess.js",
"compat.js",
"variant.js",
"transform.js",
"setup.js",
"squareSet.js",
"types.js",
"util.js",
"fp.js",
"debug.js",
"fen.js",
"san.js",
"hash.js",
"index.js",
"attacks.d.ts",
"board.d.ts",
"chess.d.ts",
"compat.d.ts",
"variant.d.ts",
"transform.d.ts",
"setup.d.ts",
"squareSet.d.ts",
"types.d.ts",
"util.d.ts",
"fp.d.ts",
"debug.d.ts",
"fen.d.ts",
"san.d.ts",
"hash.d.ts",
"index.d.ts",
"attacks.js.map",
"board.js.map",
"chess.js.map",
"compat.js.map",
"variant.js.map",
"transform.js.map",
"setup.js.map",
"squareSet.js.map",
"types.js.map",
"util.js.map",
"fp.js.map",
"debug.js.map",
"fen.js.map",
"san.js.map",
"hash.js.map",
"index.js.map",
"src/attacks.ts",
"src/board.ts",
"src/chess.ts",
"src/compat.ts",
"src/variant.ts",
"src/transform.ts",
"src/setup.ts",
"src/squareSet.ts",
"src/types.ts",
"src/util.ts",
"src/fp.ts",
"src/debug.ts",
"src/fen.ts",
"src/san.ts",
"src/hash.ts",
"src/index.ts"
"build/**/*",
"src/**/*"
],
"jest": {
"transform": {
Expand Down
10 changes: 9 additions & 1 deletion src/compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ export function playstrategyRules(
| 'go19x19'
| 'backgammon'
| 'nackgammon'
| 'breakthroughtroyka'
| 'minibreakthroughtroyka'
): Rules {
switch (variant) {
case 'standard':
Expand All @@ -108,6 +110,10 @@ export function playstrategyRules(
return 'linesofaction';
case 'scrambledEggs':
return 'scrambledeggs';
case 'breakthroughtroyka':
return 'breakthrough';
case 'minibreakthroughtroyka':
return 'minibreakthrough';
default:
return variant;
}
Expand Down Expand Up @@ -142,7 +148,9 @@ export function playstrategyVariants(
| 'go13x13'
| 'go19x19'
| 'backgammon'
| 'nackgammon' {
| 'nackgammon'
| 'breakthrough'
| 'minibreakthrough' {
switch (rules) {
case 'chess':
return 'standard';
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ export const RULES = [
'go19x19',
'backgammon',
'nackgammon',
'breakthrough',
'minibreakthrough',
] as const;

export type Rules = typeof RULES[number];
Expand Down
2 changes: 2 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,12 @@ export const dimensionsForRules = (rules: Rules): BoardDimensions => {
case 'monster':
case 'linesofaction':
case 'scrambledeggs':
case 'breakthrough':
return { ranks: 8, files: 8 };
case 'shogi':
return { ranks: 9, files: 9 };
case 'minishogi':
case 'minibreakthrough':
return { ranks: 5, files: 5 };
case 'xiangqi':
return { ranks: 10, files: 9 };
Expand Down
10 changes: 10 additions & 0 deletions src/variant.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Result } from '@badrap/result';

import { Square, Outcome, PlayerIndex, PLAYERINDEXES, Piece, Rules } from './types';
import { defined, opposite } from './util';
import { between, kingAttacks } from './attacks';
import { SquareSet } from './squareSet';
import { Board } from './board';
import { Setup, RemainingChecks, Material } from './setup';
import { PositionError, Position, IllegalSetup, Context, Castles, Chess } from './chess';
import { Breakthrough, MiniBreakthrough } from './variants/breakthrough';

export { Position, PositionError, IllegalSetup, Context, Chess, Castles };

Expand Down Expand Up @@ -1107,6 +1109,10 @@ export function defaultPosition(rules: Rules): Position {
return Backgammon.default();
case 'nackgammon':
return Nackgammon.default();
case 'breakthrough':
return Breakthrough.default();
case 'minibreakthrough':
return MiniBreakthrough.default();
}
}

Expand Down Expand Up @@ -1166,5 +1172,9 @@ export function setupPosition(rules: Rules, setup: Setup): Result<Position, Posi
return Backgammon.fromSetup(setup);
case 'nackgammon':
return Nackgammon.fromSetup(setup);
case 'breakthrough':
return Breakthrough.fromSetup(setup);
case 'minibreakthrough':
return MiniBreakthrough.fromSetup(setup);
}
}
72 changes: 72 additions & 0 deletions src/variants/breakthrough/Breakthrough.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Result } from '@badrap/result';
import { Chess, Context, IllegalSetup, PositionError } from '../../chess';
import { Setup } from '../../setup';
import { Outcome, PLAYERINDEXES, PlayerIndex } from '../../types';
import { SquareSet } from '../../squareSet';
import { opposite } from '../../util';

export class Breakthrough extends Chess {
protected constructor() {
super('breakthrough');
}

static default(): Breakthrough {
return super.default() as Breakthrough;
}

static fromSetup(setup: Setup): Result<Breakthrough, PositionError> {
return super.fromSetup(setup) as Result<Breakthrough, PositionError>;
}

clone(): Breakthrough {
return super.clone() as Breakthrough;
}

protected validate(): Result<undefined, PositionError> {
if (this.board.occupied.isEmpty()) return Result.err(new PositionError(IllegalSetup.Empty));
return Result.ok(undefined);
}

/*
* seems like outcome and the other related f() below are related to src/san makeSanAndPlay()
* makeSanAndPlay() is actually not called from within strategygames
*/
outcome(ctx?: Context): Outcome | undefined {
const variantOutcome = this.variantOutcome(ctx);
if (variantOutcome) return variantOutcome;
ctx = ctx || this.ctx();
if (this.isInsufficientMaterial()) return { winner: opposite(this.turn) };
else return;
}

isInsufficientMaterial(): boolean {
return this.hasInsufficientMaterial(this.turn);
}

hasInsufficientMaterial(_playerIndex: PlayerIndex): boolean {
if (this.board[_playerIndex].intersect(this.board['p-piece']).isEmpty()) return true;
return false;
}

isVariantEnd(): boolean {
const goalP1 = SquareSet.fromRank64(7);
const goalP2 = SquareSet.fromRank64(0);
const p2InGoal = this.board.pieces('p2', 'p-piece').intersects(goalP2);
const p1InGoal = this.board.pieces('p1', 'p-piece').intersects(goalP1);
if (p2InGoal || p1InGoal) {
return true;
}
return false;
}

variantOutcome(ctx?: Context): Outcome | undefined {
if (ctx ? !ctx.variantEnd : !this.isVariantEnd()) return;
const goalP1 = SquareSet.fromRank64(7);
const goalP2 = SquareSet.fromRank64(0);
const p2InGoal = this.board.pieces('p2', 'p-piece').intersects(goalP2);
const p1InGoal = this.board.pieces('p1', 'p-piece').intersects(goalP1);
if (p2InGoal && !p1InGoal) return { winner: 'p2' };
if (p1InGoal && !p2InGoal) return { winner: 'p1' };
return { winner: undefined };
}
}
31 changes: 31 additions & 0 deletions src/variants/breakthrough/MiniBreakthrough.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Result } from '@badrap/result';
import { Chess, IllegalSetup, PositionError } from '../../chess';
import { Setup } from '../../setup';
import { PlayerIndex } from '../../types';

export class MiniBreakthrough extends Chess {
protected constructor() {
super('minibreakthrough');
}

static default(): MiniBreakthrough {
return super.default() as MiniBreakthrough;
}

static fromSetup(setup: Setup): Result<MiniBreakthrough, PositionError> {
return super.fromSetup(setup) as Result<MiniBreakthrough, PositionError>;
}

clone(): MiniBreakthrough {
return super.clone() as MiniBreakthrough;
}

hasInsufficientMaterial(_playerIndex: PlayerIndex): boolean {
return false;
}

protected validate(): Result<undefined, PositionError> {
if (this.board.occupied.isEmpty()) return Result.err(new PositionError(IllegalSetup.Empty));
return Result.ok(undefined);
}
}
5 changes: 5 additions & 0 deletions src/variants/breakthrough/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Breakthrough } from './Breakthrough';
import { MiniBreakthrough } from './MiniBreakthrough';

export { Breakthrough };
export { MiniBreakthrough };
31 changes: 31 additions & 0 deletions test/variant.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,34 @@ test('lines of action wins', () => {
expect(pos.isVariantEnd()).toBe(true);
expect(pos.outcome()).toStrictEqual({ winner: 'p2' });
});

test('breakthrough wins', () => {
let pos = setupPosition(
'breakthrough',
parseFen('breakthrough')('pppppppp/pppppppp/8/8/8/8/PPPPPPPP/PPPPPPPP w - - 0 1').unwrap()
).unwrap();
expect(pos.isEnd()).toBe(false);
expect(pos.isVariantEnd()).toBe(false);
expect(pos.outcome()).toBeUndefined();

pos = setupPosition(
'breakthrough',
parseFen('breakthrough')('ppppppp1/pppppppp/8/8/8/8/PPPPPPPP/PPPPPPPp w - - 0 1').unwrap()
).unwrap();
expect(pos.isEnd()).toBe(true);
expect(pos.outcome()).toStrictEqual({ winner: 'p2' });

pos = setupPosition(
'breakthrough',
parseFen('breakthrough')('ppppPppp/pppppppp/8/8/8/8/PPPPPPPP/PPPPPPP1 w - - 0 1').unwrap()
).unwrap();
expect(pos.isEnd()).toBe(true);
expect(pos.outcome()).toStrictEqual({ winner: 'p1' });

pos = setupPosition(
'breakthrough',
parseFen('breakthrough')('pppppppp/pppppppp/8/8/8/8/8/8 w - - 0 1').unwrap()
).unwrap();
expect(pos.isEnd()).toBe(true);
expect(pos.outcome()).toStrictEqual({ winner: 'p2' });
});
Loading

0 comments on commit 02a3e0a

Please sign in to comment.