Skip to content

Commit

Permalink
feat(web): handle pieces positioning using FEN
Browse files Browse the repository at this point in the history
### Description

- Rename utils containing `engine` by removing it
- Add `fenToCoords` convertor
  - Use it to place pieces
  • Loading branch information
Neosoulink committed Oct 13, 2024
1 parent bd6f258 commit 4a7395e
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 188 deletions.
6 changes: 6 additions & 0 deletions apps/web/src/core/core.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import { AppModule } from "@quick-threejs/reactive";
import { Physics, RapierPhysics } from "@chess-d/rapier-physics";

import { CoreModule } from "./core.module";
import { Chess } from "chess.js";

export const setupCoreModule = async (app: AppModule) => {
if (!isObject(app))
throw new Error("Unable to retrieve the application context.");

container.register(AppModule, { useValue: app });
container.register(Chess, {
useValue: new Chess(
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1"
)
});
container.register(Physics, { useValue: await RapierPhysics() });

return container.resolve<CoreModule>(CoreModule);
Expand Down
12 changes: 6 additions & 6 deletions apps/web/src/core/engine/engine.component.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Chess } from "chess.js";
import { singleton } from "tsyringe";
import { inject, singleton } from "tsyringe";

import { fenToCoords } from "../../shared";

@singleton()
export class EngineComponent {
public readonly game = new Chess(
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1"
);
constructor(@inject(Chess) public readonly game: Chess) {}

constructor() {
console.log("EngineComponent", this.game.turn());
public getFenCoords() {
return fenToCoords(this.game.fen());
}
}
8 changes: 4 additions & 4 deletions apps/web/src/core/engine/engine.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { Move } from "chess.js";
import { EngineComponent } from "./engine.component";
import { PiecesController } from "../pieces/pieces.controller";
import {
coordToEngineSquare,
coordToSquare,
EngineNotificationPayload,
engineSquareToCoord,
squareToCoord,
ObservablePayload,
PieceNotificationPayload
} from "../../shared";
Expand Down Expand Up @@ -60,13 +60,13 @@ export class EngineController {
private _getEnginePayLoadFromPiece<
PiecePayload extends PieceNotificationPayload
>(piecePayload: PiecePayload): EngineNotificationPayload & PiecePayload {
const pgnSquare = coordToEngineSquare(piecePayload.piece.coord);
const pgnSquare = coordToSquare(piecePayload.piece.coord);
const possibleMoves = this.component.game.moves({
square: pgnSquare,
verbose: true
});
const possibleCoords = possibleMoves.map((moves) =>
engineSquareToCoord(moves.to)
squareToCoord(moves.to)
);

return {
Expand Down
210 changes: 44 additions & 166 deletions apps/web/src/core/pieces/pieces.component.ts
Original file line number Diff line number Diff line change
@@ -1,213 +1,91 @@
import { inject, singleton } from "tsyringe";
import { BufferGeometry, Vector3Like } from "three";
import { Vector3Like } from "three";
import { Physics } from "@chess-d/rapier-physics";

import {
InstancedPieceModel,
PiecesGroups,
PieceType,
ColorVariant,
BOARD_MATRIX_RANGE_SIZE,
MatrixPieceModel,
BoardCoord,
DroppedPiecesGroups
} from "../../shared";
import { EngineComponent } from "../engine/engine.component";
import { BoardComponent } from "../board/board.component";
import { ResourceComponent } from "../resource/resource.component";

@singleton()
export class PiecesComponent {
public groups?: PiecesGroups;
public droppedGroups?: DroppedPiecesGroups;
public readonly groups: PiecesGroups = {
[ColorVariant.white]: {},
[ColorVariant.black]: {}
};
public readonly droppedGroups: DroppedPiecesGroups = {
[ColorVariant.white]: {},
[ColorVariant.black]: {}
};

constructor(
@inject(EngineComponent)
private readonly engineComponent: EngineComponent,
@inject(BoardComponent)
private readonly boardComponent: BoardComponent,
@inject(ResourceComponent)
private readonly resourceComponent: ResourceComponent,
@inject(Physics) private readonly physics: Physics
) {}

private _initPawns<Color extends ColorVariant>(color: Color) {
const group = this.createGroup(
PieceType.pawn,
color,
BOARD_MATRIX_RANGE_SIZE,
this.resourceComponent.getGeometryByPieceType(PieceType.pawn)
);
const isBlack = color === ColorVariant.black;

group.pieces.forEach((piece, index) => {
group.setPieceCoord(piece.instanceId, this.boardComponent.instancedCell, {
col: isBlack ? BOARD_MATRIX_RANGE_SIZE - 1 - index : index,
row: isBlack ? BOARD_MATRIX_RANGE_SIZE - 2 : 1
});
});

return group;
}

private _initRocks<Color extends ColorVariant>(color: Color) {
const group = this.createGroup(
PieceType.rock,
color,
2,
this.resourceComponent.getGeometryByPieceType(PieceType.rock)
);
const isBlack = color === ColorVariant.black;

group.pieces.forEach((piece, index) => {
group.setPieceCoord(piece.instanceId, this.boardComponent.instancedCell, {
col: index === 0 ? 0 : BOARD_MATRIX_RANGE_SIZE - 1,
row: isBlack ? BOARD_MATRIX_RANGE_SIZE - 1 : 0
});
});

return group;
}

private _initKnights<Color extends ColorVariant>(color: Color) {
const group = this.createGroup(
PieceType.knight,
color,
2,
this.resourceComponent.getGeometryByPieceType(PieceType.knight)
);
const isBlack = color === ColorVariant.black;

group.pieces.forEach((piece, index) => {
group.setPieceCoord(piece.instanceId, this.boardComponent.instancedCell, {
col: isBlack
? index === 0
? BOARD_MATRIX_RANGE_SIZE - 2
: 1
: index === 0
? 1
: BOARD_MATRIX_RANGE_SIZE - 2,
row: isBlack ? BOARD_MATRIX_RANGE_SIZE - 1 : 0
});
});

return group;
}

private _initBishops<Color extends ColorVariant>(color: Color) {
const group = this.createGroup(
PieceType.bishop,
color,
2,
this.resourceComponent.getGeometryByPieceType(PieceType.bishop)
);
const isBlack = color === ColorVariant.black;

group.pieces.forEach((piece, index) => {
group.setPieceCoord(piece.instanceId, this.boardComponent.instancedCell, {
col: isBlack
? index === 0
? BOARD_MATRIX_RANGE_SIZE - 3
: 2
: index === 0
? 2
: BOARD_MATRIX_RANGE_SIZE - 3,
row: isBlack ? BOARD_MATRIX_RANGE_SIZE - 1 : 0
});
});

return group;
}

private _initQueens<Color extends ColorVariant>(color: Color) {
const group = this.createGroup(
PieceType.queen,
color,
1,
this.resourceComponent.getGeometryByPieceType(PieceType.queen)
);
const isBlack = color === ColorVariant.black;

group.pieces.forEach((piece) => {
group.setPieceCoord(piece.instanceId, this.boardComponent.instancedCell, {
col: 3,
row: isBlack ? BOARD_MATRIX_RANGE_SIZE - 1 : 0
});
});

return group;
}

private _initKings<Color extends ColorVariant>(color: Color) {
const group = this.createGroup(
PieceType.king,
color,
1,
this.resourceComponent.getGeometryByPieceType(PieceType.king)
);
const isBlack = color === ColorVariant.black;

group.pieces.forEach((piece) => {
group.setPieceCoord(piece.instanceId, this.boardComponent.instancedCell, {
col: 4,
row: isBlack ? BOARD_MATRIX_RANGE_SIZE - 1 : 0
});
});

return group;
}

public createGroup<Type extends PieceType, Color extends ColorVariant>(
type: Type,
color: Color,
count: number,
geometry: BufferGeometry,
coords: BoardCoord[],
pieces?: InstancedPieceModel<Type, Color>["pieces"]
) {
const group = new InstancedPieceModel(type, color, count, geometry, pieces);
const group = new InstancedPieceModel(
type,
color,
coords.length,
this.resourceComponent.getGeometryByPieceType(type),
pieces
);

coords.forEach((coord, instanceId) => {
group.setPieceCoord(instanceId, this.boardComponent.instancedCell, coord);
});

group.initPhysics(this.physics);

return group;
}

public setGroupType<Type extends PieceType, Color extends ColorVariant>(
type: Type,
color: Color,
public setGroup<Type extends PieceType, Color extends ColorVariant>(
newGroup: InstancedPieceModel<Type, Color>
): InstancedPieceModel<Type, Color> | undefined {
if (!(this.groups?.[color][type] instanceof InstancedPieceModel)) return;

// @ts-ignore <unsupported never type>
this.groups[color][type] = newGroup;
this.groups[newGroup.piecesColor][newGroup.piecesType] = newGroup;

return this.groups[color][type] as unknown as typeof newGroup;
return this.groups[newGroup.piecesColor][
newGroup.piecesType
] as unknown as typeof newGroup;
}

public initPieces() {
const createGroup = <C extends ColorVariant = ColorVariant>(color: C) => ({
[PieceType.pawn]: this._initPawns(color),
[PieceType.rock]: this._initRocks(color),
[PieceType.knight]: this._initKnights(color),
[PieceType.bishop]: this._initBishops(color),
[PieceType.queen]: this._initQueens(color),
[PieceType.king]: this._initKings(color)
});
const fenCoords = this.engineComponent.getFenCoords();

const createDroppedGroup = () => ({
[PieceType.pawn]: [],
[PieceType.rock]: [],
[PieceType.knight]: [],
[PieceType.bishop]: [],
[PieceType.queen]: [],
[PieceType.king]: []
});
if (fenCoords)
[ColorVariant.black, ColorVariant.white].forEach((color) => {
const pieceCoords = fenCoords[color];
Object.keys(pieceCoords).forEach((_pieceType) => {
const coords = pieceCoords[_pieceType];
const pieceType = _pieceType.toLowerCase() as PieceType;

this.groups = {
[ColorVariant.black]: createGroup(ColorVariant.black),
[ColorVariant.white]: createGroup(ColorVariant.white)
};
const newGroup = this.createGroup(pieceType, color, coords ?? []);

this.droppedGroups = {
[ColorVariant.black]: createDroppedGroup(),
[ColorVariant.white]: createDroppedGroup()
};
this.setGroup(newGroup);
this.droppedGroups[color][pieceType] = [];
});
});
}

public getPieceByCoord<Type extends PieceType, Color extends ColorVariant>(
Expand Down Expand Up @@ -274,7 +152,7 @@ export class PiecesComponent {

if (!newGroup) return undefined;

this.setGroupType(newGroup.piecesType, newGroup.piecesColor, newGroup);
this.setGroup(newGroup);

// @ts-ignore <unsupported never type>
droppedPiecesGroup?.push(piece);
Expand Down Expand Up @@ -306,6 +184,6 @@ export class PiecesComponent {
const newGroup = promotedPieceGroup.addPiece(newPiece, this.physics);
if (!newGroup) return;

this.setGroupType(newGroup.piecesType, newGroup.piecesColor, newGroup);
this.setGroup(newGroup);
}
}
11 changes: 4 additions & 7 deletions apps/web/src/core/pieces/pieces.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { copyProperties } from "@quick-threejs/utils";

import { PiecesComponent } from "./pieces.component";
import { PiecesController } from "./pieces.controller";
import { ColorVariant, VECTOR } from "../../shared";
import { ColorVariant, PieceType, VECTOR } from "../../shared";
import { EngineController } from "../engine/engine.controller";

@singleton()
Expand All @@ -26,13 +26,10 @@ export class PiecesModule implements Module {
[...Object.keys(this.component.groups[ColorVariant.black])].forEach(
(key) => {
const blackGroup =
this.component.groups?.[ColorVariant.black][
key as unknown as keyof (typeof this.component.groups)[ColorVariant.black]
];
this.component.groups?.[ColorVariant.black][key as PieceType];

const whiteGroup =
this.component.groups?.[ColorVariant.white][
key as unknown as keyof (typeof this.component.groups)[ColorVariant.white]
];
this.component.groups?.[ColorVariant.white][key as PieceType];

if (blackGroup) this.appModule.world.scene().add(blackGroup);
if (whiteGroup) this.appModule.world.scene().add(whiteGroup);
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/shared/interfaces/piece.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export type DroppedPiecesGroup<Color extends ColorVariant> = {
};

export type PiecesGroups = {
[Color in ColorVariant]: PiecesGroup<Color>;
[Color in ColorVariant]: Partial<PiecesGroup<Color>>;
};

export type DroppedPiecesGroups = {
[Color in ColorVariant]: DroppedPiecesGroup<Color>;
[Color in ColorVariant]: Partial<DroppedPiecesGroup<Color>>;
};
Loading

0 comments on commit 4a7395e

Please sign in to comment.