Compare commits

..

5 commits

Author SHA1 Message Date
d9bb7fae5e
style(tsconfig): update path mappings to array syntax
Some checks failed
CI / Get Changed Files (pull_request) Successful in 7s
CI / Checkstyle Main (pull_request) Has been skipped
CI / prettier (pull_request) Successful in 17s
CI / test-build (pull_request) Failing after 28s
CI / eslint (pull_request) Successful in 43s
2025-04-02 09:07:27 +02:00
b0b358ae9f
style(tsconfig): fix path configuration formatting
Some checks failed
CI / Get Changed Files (pull_request) Successful in 6s
CI / Checkstyle Main (pull_request) Has been skipped
CI / test-build (pull_request) Failing after 27s
CI / prettier (pull_request) Successful in 40s
CI / eslint (pull_request) Successful in 50s
2025-04-02 09:05:33 +02:00
fb6655a196
style: format constructor style in components
Some checks failed
CI / Get Changed Files (pull_request) Successful in 6s
CI / Checkstyle Main (pull_request) Has been skipped
CI / test-build (pull_request) Failing after 20s
CI / prettier (pull_request) Successful in 49s
CI / eslint (pull_request) Successful in 51s
2025-04-02 09:03:36 +02:00
01989ae5bc
refactor: update imports to use absolute paths
Some checks failed
CI / Get Changed Files (pull_request) Successful in 7s
CI / Checkstyle Main (pull_request) Has been skipped
CI / prettier (pull_request) Failing after 17s
CI / eslint (pull_request) Successful in 56s
CI / test-build (pull_request) Failing after 1m0s
2025-04-02 09:02:14 +02:00
c0b4d1c46e
refactor: update import paths for better readability
Some checks failed
CI / Get Changed Files (pull_request) Successful in 26s
CI / Checkstyle Main (pull_request) Has been skipped
CI / eslint (pull_request) Failing after 40s
CI / test-build (pull_request) Failing after 39s
CI / prettier (pull_request) Failing after 44s
2025-04-02 08:57:00 +02:00
10 changed files with 117 additions and 112 deletions

View file

@ -24,6 +24,10 @@ public class BlackJackGameEntity {
@GeneratedValue
private Long id;
@Version
@JsonIgnore
private Long version;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
@JsonIgnore

View file

@ -19,6 +19,10 @@ public class CardEntity {
@JsonIgnore
private Long id;
@Version
@JsonIgnore
private Long version;
@ManyToOne
@JoinColumn(name = "game_id", nullable = false)
@JsonBackReference

View file

@ -1,6 +0,0 @@
export enum GameState {
PLAYER_WON = 'PLAYER_WON',
IN_PROGRESS = 'IN_PROGRESS',
PLAYER_LOST = 'PLAYER_LOST',
DRAW = 'DRAW',
}

View file

@ -49,5 +49,5 @@
[gameState]="gameState()"
[amount]="currentBet()"
[show]="showGameResult()"
(gameResultClosed)="onCloseGameResult()"
(close)="onCloseGameResult()"
></app-game-result>

View file

@ -10,9 +10,8 @@ import { Card, BlackjackGame } from './models/blackjack.model';
import { BlackjackService } from './services/blackjack.service';
import { HttpErrorResponse } from '@angular/common/http';
import { GameResultComponent } from './components/game-result/game-result.component';
import { GameState } from '../../../enum/gameState';
import { NavbarComponent } from '../../../shared/components/navbar/navbar.component';
import { UserService } from '../../../service/user.service';
import { NavbarComponent } from '@shared/components/navbar/navbar.component';
import { UserService } from '@service/user.service';
@Component({
selector: 'app-blackjack',
@ -41,7 +40,7 @@ export default class BlackjackComponent {
balance = signal(0);
currentGameId = signal<number | undefined>(undefined);
gameInProgress = signal(false);
gameState = signal<GameState>(GameState.IN_PROGRESS);
gameState = signal<string>('IN_PROGRESS');
showGameResult = signal(false);
isActionInProgress = signal(false);
@ -61,15 +60,15 @@ export default class BlackjackComponent {
console.log('Game state update:', game);
this.currentGameId.set(game.id);
this.currentBet.set(game.bet);
this.gameInProgress.set(game.state === GameState.IN_PROGRESS);
this.gameState.set(game.state as GameState);
this.gameInProgress.set(game.state === 'IN_PROGRESS');
this.gameState.set(game.state);
const isGameOver = game.state !== GameState.IN_PROGRESS;
const isGameOver = game.state !== 'IN_PROGRESS';
this.dealerCards.set(
game.dealerCards.map((card, index) => ({
...card,
hidden: !isGameOver && index === 1 && game.state === GameState.IN_PROGRESS,
hidden: !isGameOver && index === 1 && game.state === 'IN_PROGRESS',
}))
);
@ -128,7 +127,7 @@ export default class BlackjackComponent {
onStand(): void {
if (!this.currentGameId() || this.isActionInProgress()) return;
if (this.gameState() !== GameState.IN_PROGRESS) {
if (this.gameState() !== 'IN_PROGRESS') {
console.log('Cannot stand: game is not in progress');
return;
}
@ -152,7 +151,7 @@ export default class BlackjackComponent {
onDoubleDown(): void {
if (!this.currentGameId() || this.isActionInProgress()) return;
if (this.gameState() !== GameState.IN_PROGRESS || this.playerCards().length !== 2) {
if (this.gameState() !== 'IN_PROGRESS' || this.playerCards().length !== 2) {
console.log('Cannot double down: game is not in progress or more than 2 cards');
return;
}

View file

@ -1,8 +1,6 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Card } from '../../models/blackjack.model';
import { GameState } from '../../../../../enum/gameState';
import { GameControlsService } from '../../services/game-controls.service';
import { Card } from '@blackjack/models/blackjack.model';
@Component({
selector: 'app-game-controls',
@ -13,13 +11,10 @@ import { GameControlsService } from '../../services/game-controls.service';
<div class="flex justify-center text-lg mb-5">
<div class="card p-4">
<div class="text-emerald font-bold mb-1">
Deine Punkte: {{ gameControlsService.calculateHandValue(playerCards) }}
Deine Punkte: {{ calculateHandValue(playerCards) }}
</div>
<div class="text-text-secondary">
Status:
<span [class]="gameControlsService.getStatusClass(gameState)">{{
gameControlsService.getStatusText(gameState)
}}</span>
Status: <span [class]="getStatusClass(gameState)">{{ getStatusText(gameState) }}</span>
</div>
</div>
</div>
@ -27,7 +22,7 @@ import { GameControlsService } from '../../services/game-controls.service';
<button
(click)="hit.emit()"
class="button-primary px-8 py-4 text-lg font-medium min-w-[120px] relative"
[disabled]="gameState !== GameState.IN_PROGRESS || isActionInProgress"
[disabled]="gameState !== 'IN_PROGRESS' || isActionInProgress"
[class.opacity-50]="isActionInProgress"
>
<span [class.invisible]="isActionInProgress">Ziehen</span>
@ -42,7 +37,7 @@ import { GameControlsService } from '../../services/game-controls.service';
<button
(click)="stand.emit()"
class="button-primary px-8 py-4 text-lg font-medium min-w-[120px] relative"
[disabled]="gameState !== GameState.IN_PROGRESS || isActionInProgress"
[disabled]="gameState !== 'IN_PROGRESS' || isActionInProgress"
[class.opacity-50]="isActionInProgress"
>
<span [class.invisible]="isActionInProgress">Halten</span>
@ -57,9 +52,7 @@ import { GameControlsService } from '../../services/game-controls.service';
<button
(click)="doubleDown.emit()"
class="button-primary px-8 py-4 text-lg font-medium min-w-[120px] relative"
[disabled]="
gameState !== GameState.IN_PROGRESS || playerCards.length !== 2 || isActionInProgress
"
[disabled]="gameState !== 'IN_PROGRESS' || playerCards.length !== 2 || isActionInProgress"
[class.opacity-50]="isActionInProgress"
>
<span [class.invisible]="isActionInProgress">Verdoppeln</span>
@ -86,7 +79,7 @@ import { GameControlsService } from '../../services/game-controls.service';
})
export class GameControlsComponent {
@Input() playerCards: Card[] = [];
@Input() gameState: GameState = GameState.IN_PROGRESS;
@Input() gameState = 'IN_PROGRESS';
@Input() isActionInProgress = false;
@Output() hit = new EventEmitter<void>();
@ -94,7 +87,69 @@ export class GameControlsComponent {
@Output() doubleDown = new EventEmitter<void>();
@Output() leave = new EventEmitter<void>();
protected readonly GameState = GameState;
calculateHandValue(cards: Card[]): number {
let sum = 0;
let aceCount = 0;
constructor(protected gameControlsService: GameControlsService) { }
const rankValues: Record<string, number> = {
TWO: 2,
THREE: 3,
FOUR: 4,
FIVE: 5,
SIX: 6,
SEVEN: 7,
EIGHT: 8,
NINE: 9,
TEN: 10,
JACK: 10,
QUEEN: 10,
KING: 10,
ACE: 11,
};
for (const card of cards) {
if (!card.hidden) {
const value = rankValues[card.rank] || 0;
sum += value;
if (card.rank === 'ACE') {
aceCount++;
}
}
}
while (sum > 21 && aceCount > 0) {
sum -= 10;
aceCount--;
}
return sum;
}
getStatusText(state: string): string {
switch (state) {
case 'IN_PROGRESS':
return 'Spiel läuft';
case 'PLAYER_WON':
return 'Gewonnen!';
case 'PLAYER_LOST':
return 'Verloren!';
case 'DRAW':
return 'Unentschieden!';
default:
return state;
}
}
getStatusClass(state: string): string {
switch (state) {
case 'PLAYER_WON':
return 'text-emerald';
case 'PLAYER_LOST':
return 'text-accent-red';
case 'DRAW':
return 'text-yellow-400';
default:
return 'text-white';
}
}
}

View file

@ -0,0 +1 @@
/* No custom styles needed */

View file

@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GameResultComponent } from './game-result.component';
describe('GameResultComponent', () => {
let component: GameResultComponent;
let fixture: ComponentFixture<GameResultComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [GameResultComponent],
}).compileComponents();
fixture = TestBed.createComponent(GameResultComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -1,7 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, Output, EventEmitter } from '@angular/core';
import { CommonModule, CurrencyPipe } from '@angular/common';
import { animate, style, transition, trigger } from '@angular/animations';
import { GameState } from '../../../../../enum/gameState';
@Component({
selector: 'app-game-result',
@ -56,6 +55,7 @@ import { GameState } from '../../../../../enum/gameState';
</div>
</div>
`,
styleUrls: ['./game-result.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
trigger('fadeInOut', [
@ -74,7 +74,7 @@ import { GameState } from '../../../../../enum/gameState';
],
})
export class GameResultComponent {
@Input() gameState: GameState = GameState.IN_PROGRESS;
@Input() gameState = '';
@Input() amount = 0;
@Input() set show(value: boolean) {
console.log('GameResultComponent show input changed:', value, 'gameState:', this.gameState);
@ -86,15 +86,15 @@ export class GameResultComponent {
visible = false;
get isWin(): boolean {
return this.gameState === GameState.PLAYER_WON;
return this.gameState === 'PLAYER_WON';
}
get isLoss(): boolean {
return this.gameState === GameState.PLAYER_LOST;
return this.gameState === 'PLAYER_LOST';
}
get isDraw(): boolean {
return this.gameState === GameState.DRAW;
return this.gameState === 'DRAW';
}
getResultTitle(): string {

View file

@ -1,74 +0,0 @@
import { Injectable } from '@angular/core';
import { Card } from '../models/blackjack.model';
import { GameState } from '../../../../enum/gameState';
@Injectable({
providedIn: 'root',
})
export class GameControlsService {
calculateHandValue(cards: Card[]): number {
let sum = 0;
let aceCount = 0;
const rankValues: Record<string, number> = {
TWO: 2,
THREE: 3,
FOUR: 4,
FIVE: 5,
SIX: 6,
SEVEN: 7,
EIGHT: 8,
NINE: 9,
TEN: 10,
JACK: 10,
QUEEN: 10,
KING: 10,
ACE: 11,
};
for (const card of cards) {
if (!card.hidden) {
const value = rankValues[card.rank] || 0;
sum += value;
if (card.rank === 'ACE') {
aceCount++;
}
}
}
while (sum > 21 && aceCount > 0) {
sum -= 10;
aceCount--;
}
return sum;
}
getStatusText(state: GameState): string {
switch (state) {
case GameState.IN_PROGRESS:
return 'Spiel läuft';
case GameState.PLAYER_WON:
return 'Gewonnen!';
case GameState.PLAYER_LOST:
return 'Verloren!';
case GameState.DRAW:
return 'Unentschieden!';
default:
return state;
}
}
getStatusClass(state: GameState): string {
switch (state) {
case GameState.PLAYER_WON:
return 'text-emerald';
case GameState.PLAYER_LOST:
return 'text-accent-red';
case GameState.DRAW:
return 'text-yellow-400';
default:
return 'text-white';
}
}
}