feat: piece pormotion

This commit is contained in:
shahdlala66
2026-04-22 08:35:58 +02:00
parent 25b69fd7b6
commit 4f76bcc7c6
6 changed files with 239 additions and 3 deletions
+6
View File
@@ -1,4 +1,10 @@
<main class="game-shell">
<app-promotion-dialog
[isOpen]="facade.isPromotionDialogOpen"
(promotionSelected)="facade.onPromotionSelected($event)"
(closed)="facade.onPromotionClosed()"
/>
<section class="game-card">
<header class="mb-3">
<a routerLink="/" class="back-link">Back</a>
+3 -2
View File
@@ -4,13 +4,14 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { ChessBoardComponent } from '../../components/chess-board/chess-board.component';
import { InputCardComponent } from '../../components/input-card/input-card.component';
import { InputCardComponent } from '../../components/input-card/input-card.component';
import { PromotionDialogComponent } from '../../components/promotion-dialog/promotion-dialog.component';
import { GameFacade } from './game.facade';
@Component({
selector: 'app-game',
standalone: true,
imports: [CommonModule, FormsModule, RouterLink, ChessBoardComponent, InputCardComponent],
imports: [CommonModule, FormsModule, RouterLink, ChessBoardComponent, InputCardComponent, PromotionDialogComponent],
providers: [GameFacade],
templateUrl: './game.component.html',
styleUrl: './game.component.css'
+34 -1
View File
@@ -1,7 +1,7 @@
import { DestroyRef, Injectable, OnDestroy, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { getErrorMessage } from '../../core/http/error-message.util';
import { GameFull, GameState, GameStreamEvent } from '../../models/game.models';
import { GameFull, GameState, GameStreamEvent, LegalMove } from '../../models/game.models';
import { GameApiService } from '../../services/game-api.service';
import { BotMoveService } from '../../services/bot-move.service';
import { GameCompletionService } from '../../services/game-completion.service';
@@ -20,12 +20,14 @@ export class GameFacade implements OnDestroy {
loading = true;
gameCompletionMessage = '';
isGameFinished = false;
isPromotionDialogOpen = false;
private boardSelection: BoardSelection = {
selectedSquare: null,
highlightedSquares: [],
selectedSquareMoves: []
};
private pendingPromotionMoves: LegalMove[] = [];
private readonly gameApi = inject(GameApiService);
private readonly destroyRef = inject(DestroyRef);
@@ -66,6 +68,15 @@ export class GameFacade implements OnDestroy {
if (this.boardSelection.selectedSquare && this.boardSelection.highlightedSquares.includes(square)) {
const selectedMove = this.boardSelection.selectedSquareMoves.find((move) => move.to === square);
if (selectedMove) {
// If multiple promotion outcomes exist for the target, ask player to choose one.
const promotionMoves = this.boardSelection.selectedSquareMoves.filter(
(move) => move.to === square && !!move.promotion
);
if (promotionMoves.length > 0) {
this.pendingPromotionMoves = promotionMoves;
this.isPromotionDialogOpen = true;
return;
}
this.moveInput = selectedMove.uci;
this.submitMove();
}
@@ -134,6 +145,28 @@ export class GameFacade implements OnDestroy {
});
}
onPromotionSelected(promotionPiece: 'queen' | 'rook' | 'bishop' | 'knight'): void {
const selectedPromotionMove = this.pendingPromotionMoves.find((move) => move.promotion === promotionPiece);
if (!selectedPromotionMove) {
this.errorMessage = 'Selected promotion move is unavailable.';
this.isPromotionDialogOpen = false;
this.pendingPromotionMoves = [];
return;
}
this.moveInput = selectedPromotionMove.uci;
this.isPromotionDialogOpen = false;
this.boardSelection = this.boardSelectionService.clearSelection();
this.pendingPromotionMoves = [];
this.submitMove();
}
onPromotionClosed(): void {
this.isPromotionDialogOpen = false;
this.boardSelection = this.boardSelectionService.clearSelection();
this.pendingPromotionMoves = [];
}
importFen(): void {
this.errorMessage = '';
this.importService.importFen(