style: format code
This commit is contained in:
parent
b2053acdfe
commit
f2aa81b6d2
5 changed files with 274 additions and 221 deletions
|
@ -63,7 +63,7 @@ export const routes: Routes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'game/dice',
|
path: 'game/dice',
|
||||||
loadComponent: () => import('./feature/game/dice/dice.component').then(m => m.DiceComponent),
|
loadComponent: () => import('./feature/game/dice/dice.component').then((m) => m.DiceComponent),
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,74 +1,121 @@
|
||||||
<div class="container mx-auto px-4 py-8 space-y-8">
|
<div class="container mx-auto px-4 py-8 space-y-8">
|
||||||
<h1 class="text-3xl font-bold text-white mb-6">Dice Game</h1>
|
<h1 class="text-3xl font-bold text-white mb-6">Dice Game</h1>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
<div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||||
<div class="lg:col-span-1 card p-8 space-y-6">
|
<div class="lg:col-span-1 card p-8 space-y-6">
|
||||||
<form [formGroup]="diceForm" (ngSubmit)="roll()" class="space-y-6">
|
<form [formGroup]="diceForm" (ngSubmit)="roll()" class="space-y-6">
|
||||||
<div class="controls space-y-4">
|
<div class="controls space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="betAmount" class="block text-text-secondary mb-2">Bet Amount:</label>
|
<label for="betAmount" class="block text-text-secondary mb-2">Bet Amount:</label>
|
||||||
<input id="betAmount" type="number" formControlName="betAmount" min="0.01" step="0.01"
|
<input
|
||||||
class="w-full bg-deep-blue-light text-white rounded-lg p-2 focus:outline-none focus:ring-1 focus:ring-emerald">
|
id="betAmount"
|
||||||
@if (hasError('betAmount', 'required')) {
|
type="number"
|
||||||
<span class="text-accent-red text-sm mt-1 block">Bet Amount is required</span>
|
formControlName="betAmount"
|
||||||
}
|
min="0.01"
|
||||||
@if (hasError('betAmount', 'min')) {
|
step="0.01"
|
||||||
<span class="text-accent-red text-sm mt-1 block">Bet Amount must be at least 0.01</span>
|
class="w-full bg-deep-blue-light text-white rounded-lg p-2 focus:outline-none focus:ring-1 focus:ring-emerald"
|
||||||
}
|
/>
|
||||||
</div>
|
@if (hasError('betAmount', 'required')) {
|
||||||
|
<span class="text-accent-red text-sm mt-1 block">Bet Amount is required</span>
|
||||||
<div>
|
}
|
||||||
<label class="block text-text-secondary mb-2">Roll Mode:</label>
|
@if (hasError('betAmount', 'min')) {
|
||||||
<div class="roll-mode flex rounded-lg overflow-hidden">
|
<span class="text-accent-red text-sm mt-1 block"
|
||||||
<button type="button" (click)="toggleRollMode()" [ngClass]="{'bg-emerald text-white': diceForm.get('rollOver')?.value, 'bg-deep-blue-light text-text-secondary': !diceForm.get('rollOver')?.value}"
|
>Bet Amount must be at least 0.01</span
|
||||||
class="flex-1 py-2 text-center font-semibold transition-colors duration-200">Roll Over</button>
|
>
|
||||||
<button type="button" (click)="toggleRollMode()" [ngClass]="{'bg-emerald text-white': !diceForm.get('rollOver')?.value, 'bg-deep-blue-light text-text-secondary': diceForm.get('rollOver')?.value}"
|
}
|
||||||
class="flex-1 py-2 text-center font-semibold transition-colors duration-200">Roll Under</button>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
<div>
|
||||||
|
<label class="block text-text-secondary mb-2">Roll Mode:</label>
|
||||||
<div>
|
<div class="roll-mode flex rounded-lg overflow-hidden">
|
||||||
<label for="targetValue" class="block text-text-secondary mb-2">Target Value: {{ diceForm.get('targetValue')?.value | number:'1.0-2' }}</label>
|
<button
|
||||||
<input id="targetValue" type="range" formControlName="targetValue" min="1" max="100" step="0.01"
|
type="button"
|
||||||
class="w-full h-2 bg-deep-blue-light rounded-lg appearance-none cursor-pointer range-lg accent-emerald">
|
(click)="toggleRollMode()"
|
||||||
@if (hasError('targetValue', 'required')) {
|
[ngClass]="{
|
||||||
<span class="text-accent-red text-sm mt-1 block">Target Value is required</span>
|
'bg-emerald text-white': diceForm.get('rollOver')?.value,
|
||||||
}
|
'bg-deep-blue-light text-text-secondary': !diceForm.get('rollOver')?.value,
|
||||||
@if (hasError('targetValue', 'min')) {
|
}"
|
||||||
<span class="text-accent-red text-sm mt-1 block">Target Value must be at least 1</span>
|
class="flex-1 py-2 text-center font-semibold transition-colors duration-200"
|
||||||
}
|
>
|
||||||
@if (hasError('targetValue', 'max')) {
|
Roll Over
|
||||||
<span class="text-accent-red text-sm mt-1 block">Target Value must be at most 100</span>
|
</button>
|
||||||
}
|
<button
|
||||||
</div>
|
type="button"
|
||||||
</div>
|
(click)="toggleRollMode()"
|
||||||
|
[ngClass]="{
|
||||||
<div class="info space-y-2 text-text-secondary">
|
'bg-emerald text-white': !diceForm.get('rollOver')?.value,
|
||||||
<p>Win Chance: <span class="text-white">{{ winChance() | number:'1.0-2' }}%</span></p>
|
'bg-deep-blue-light text-text-secondary': diceForm.get('rollOver')?.value,
|
||||||
<p>Potential Win: <span class="text-white">{{ potentialWin() | currency:'EUR':'symbol':'1.2-2' }}</span></p>
|
}"
|
||||||
</div>
|
class="flex-1 py-2 text-center font-semibold transition-colors duration-200"
|
||||||
|
>
|
||||||
<button type="submit" class="button-primary w-full py-2 font-bold">Roll Dice</button>
|
Roll Under
|
||||||
</form>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="lg:col-span-3 card p-8 flex items-center justify-center">
|
|
||||||
@if (rolledValue() !== null) {
|
<div>
|
||||||
<div class="text-white text-center text-8xl font-bold">
|
<label for="targetValue" class="block text-text-secondary mb-2"
|
||||||
{{ rolledValue() }}
|
>Target Value: {{ diceForm.get('targetValue')?.value | number: '1.0-2' }}</label
|
||||||
</div>
|
>
|
||||||
}
|
<input
|
||||||
</div>
|
id="targetValue"
|
||||||
</div>
|
type="range"
|
||||||
|
formControlName="targetValue"
|
||||||
|
min="1"
|
||||||
@if (rolledValue() !== null) {
|
max="100"
|
||||||
<div class="result max-w-sm mx-auto card p-6 mt-8 text-center">
|
step="0.01"
|
||||||
@if (win()) {
|
class="w-full h-2 bg-deep-blue-light rounded-lg appearance-none cursor-pointer range-lg accent-emerald"
|
||||||
<p class="text-emerald text-base font-semibold">You Won! Payout: {{ payout() | currency:'EUR':'symbol':'1.2-2' }}</p>
|
/>
|
||||||
} @else {
|
@if (hasError('targetValue', 'required')) {
|
||||||
<p class="text-accent-red text-base font-semibold">You Lost.</p>
|
<span class="text-accent-red text-sm mt-1 block">Target Value is required</span>
|
||||||
}
|
}
|
||||||
</div>
|
@if (hasError('targetValue', 'min')) {
|
||||||
}
|
<span class="text-accent-red text-sm mt-1 block"
|
||||||
</div>
|
>Target Value must be at least 1</span
|
||||||
|
>
|
||||||
|
}
|
||||||
|
@if (hasError('targetValue', 'max')) {
|
||||||
|
<span class="text-accent-red text-sm mt-1 block"
|
||||||
|
>Target Value must be at most 100</span
|
||||||
|
>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info space-y-2 text-text-secondary">
|
||||||
|
<p>
|
||||||
|
Win Chance: <span class="text-white">{{ winChance() | number: '1.0-2' }}%</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Potential Win:
|
||||||
|
<span class="text-white">{{
|
||||||
|
potentialWin() | currency: 'EUR' : 'symbol' : '1.2-2'
|
||||||
|
}}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="button-primary w-full py-2 font-bold">Roll Dice</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lg:col-span-3 card p-8 flex items-center justify-center">
|
||||||
|
@if (rolledValue() !== null) {
|
||||||
|
<div class="text-white text-center text-8xl font-bold">
|
||||||
|
{{ rolledValue() }}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (rolledValue() !== null) {
|
||||||
|
<div class="result max-w-sm mx-auto card p-6 mt-8 text-center">
|
||||||
|
@if (win()) {
|
||||||
|
<p class="text-emerald text-base font-semibold">
|
||||||
|
You Won! Payout: {{ payout() | currency: 'EUR' : 'symbol' : '1.2-2' }}
|
||||||
|
</p>
|
||||||
|
} @else {
|
||||||
|
<p class="text-accent-red text-base font-semibold">You Lost.</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,118 +1,124 @@
|
||||||
import { Component, signal, inject, OnInit } from '@angular/core';
|
import { Component, signal, inject, OnInit } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import {
|
||||||
import { DiceService } from './dice.service';
|
FormBuilder,
|
||||||
import { DiceDto, DiceResult } from './dice.model';
|
FormControl,
|
||||||
import { debounceTime, tap } from 'rxjs/operators';
|
FormGroup,
|
||||||
import {UserService} from "@service/user.service";
|
ReactiveFormsModule,
|
||||||
|
Validators,
|
||||||
type DiceFormGroup = FormGroup<{
|
} from '@angular/forms';
|
||||||
betAmount: FormControl<number | null>;
|
import { DiceService } from './dice.service';
|
||||||
rollOver: FormControl<boolean>;
|
import { DiceDto, DiceResult } from './dice.model';
|
||||||
targetValue: FormControl<number | null>;
|
import { debounceTime, tap } from 'rxjs/operators';
|
||||||
}>;
|
import { UserService } from '@service/user.service';
|
||||||
|
|
||||||
@Component({
|
type DiceFormGroup = FormGroup<{
|
||||||
selector: 'app-dice',
|
betAmount: FormControl<number | null>;
|
||||||
standalone: true,
|
rollOver: FormControl<boolean>;
|
||||||
imports: [CommonModule, ReactiveFormsModule],
|
targetValue: FormControl<number | null>;
|
||||||
templateUrl: './dice.component.html',
|
}>;
|
||||||
})
|
|
||||||
export class DiceComponent implements OnInit {
|
@Component({
|
||||||
private readonly formBuilder = inject(FormBuilder);
|
selector: 'app-dice',
|
||||||
private readonly diceService = inject(DiceService);
|
standalone: true,
|
||||||
private readonly userService = inject(UserService);
|
imports: [CommonModule, ReactiveFormsModule],
|
||||||
|
templateUrl: './dice.component.html',
|
||||||
|
})
|
||||||
rolledValue = signal<number | null>(null);
|
export class DiceComponent implements OnInit {
|
||||||
win = signal<boolean | null>(null);
|
private readonly formBuilder = inject(FormBuilder);
|
||||||
payout = signal<number | null>(null);
|
private readonly diceService = inject(DiceService);
|
||||||
winChance = signal(0);
|
private readonly userService = inject(UserService);
|
||||||
potentialWin = signal(0);
|
|
||||||
|
rolledValue = signal<number | null>(null);
|
||||||
readonly diceForm: DiceFormGroup = this.createDiceForm();
|
win = signal<boolean | null>(null);
|
||||||
|
payout = signal<number | null>(null);
|
||||||
private readonly MAX_DICE_VALUE = 100;
|
winChance = signal(0);
|
||||||
|
potentialWin = signal(0);
|
||||||
constructor() {
|
|
||||||
}
|
readonly diceForm: DiceFormGroup = this.createDiceForm();
|
||||||
|
|
||||||
ngOnInit(): void {
|
private readonly MAX_DICE_VALUE = 100;
|
||||||
this.diceForm.valueChanges.pipe(
|
|
||||||
debounceTime(100),
|
constructor() {}
|
||||||
tap(() => this.calculateWinChanceAndPotentialWin())
|
|
||||||
).subscribe();
|
ngOnInit(): void {
|
||||||
|
this.diceForm.valueChanges
|
||||||
this.calculateWinChanceAndPotentialWin();
|
.pipe(
|
||||||
}
|
debounceTime(100),
|
||||||
|
tap(() => this.calculateWinChanceAndPotentialWin())
|
||||||
createDiceForm(): DiceFormGroup {
|
)
|
||||||
return this.formBuilder.group({
|
.subscribe();
|
||||||
betAmount: new FormControl<number | null>(1.00, {
|
|
||||||
validators: [Validators.required, Validators.min(0.01)],
|
this.calculateWinChanceAndPotentialWin();
|
||||||
nonNullable: true,
|
}
|
||||||
}),
|
|
||||||
rollOver: new FormControl<boolean>(true, {
|
createDiceForm(): DiceFormGroup {
|
||||||
validators: [Validators.required],
|
return this.formBuilder.group({
|
||||||
nonNullable: true,
|
betAmount: new FormControl<number | null>(1.0, {
|
||||||
}),
|
validators: [Validators.required, Validators.min(0.01)],
|
||||||
targetValue: new FormControl<number | null>(50.50, {
|
nonNullable: true,
|
||||||
validators: [Validators.required, Validators.min(1), Validators.max(100)],
|
}),
|
||||||
nonNullable: true,
|
rollOver: new FormControl<boolean>(true, {
|
||||||
}),
|
validators: [Validators.required],
|
||||||
});
|
nonNullable: true,
|
||||||
}
|
}),
|
||||||
|
targetValue: new FormControl<number | null>(50.5, {
|
||||||
toggleRollMode(): void {
|
validators: [Validators.required, Validators.min(1), Validators.max(100)],
|
||||||
const currentMode = this.diceForm.get('rollOver')?.value;
|
nonNullable: true,
|
||||||
this.diceForm.get('rollOver')?.setValue(!currentMode);
|
}),
|
||||||
}
|
});
|
||||||
|
}
|
||||||
calculateWinChanceAndPotentialWin(): void {
|
|
||||||
const formValues = this.diceForm.value;
|
toggleRollMode(): void {
|
||||||
const target = formValues.targetValue ?? 0;
|
const currentMode = this.diceForm.get('rollOver')?.value;
|
||||||
const bet = formValues.betAmount ?? 0;
|
this.diceForm.get('rollOver')?.setValue(!currentMode);
|
||||||
const isOver = formValues.rollOver ?? true;
|
}
|
||||||
|
|
||||||
const calculatedWinChance = isOver ? this.MAX_DICE_VALUE - target : target - 1;
|
calculateWinChanceAndPotentialWin(): void {
|
||||||
|
const formValues = this.diceForm.value;
|
||||||
this.winChance.set(Math.max(0, calculatedWinChance));
|
const target = formValues.targetValue ?? 0;
|
||||||
|
const bet = formValues.betAmount ?? 0;
|
||||||
let multiplier = 0;
|
const isOver = formValues.rollOver ?? true;
|
||||||
if (calculatedWinChance > 0) {
|
|
||||||
multiplier = (this.MAX_DICE_VALUE - 1) / calculatedWinChance;
|
const calculatedWinChance = isOver ? this.MAX_DICE_VALUE - target : target - 1;
|
||||||
}
|
|
||||||
|
this.winChance.set(Math.max(0, calculatedWinChance));
|
||||||
this.potentialWin.set(bet * multiplier);
|
|
||||||
}
|
let multiplier = 0;
|
||||||
|
if (calculatedWinChance > 0) {
|
||||||
roll(): void {
|
multiplier = (this.MAX_DICE_VALUE - 1) / calculatedWinChance;
|
||||||
if (this.diceForm.invalid) {
|
}
|
||||||
this.diceForm.markAllAsTouched();
|
|
||||||
return;
|
this.potentialWin.set(bet * multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
const diceDto: DiceDto = this.diceForm.getRawValue() as DiceDto;
|
roll(): void {
|
||||||
|
if (this.diceForm.invalid) {
|
||||||
this.rolledValue.set(null);
|
this.diceForm.markAllAsTouched();
|
||||||
this.win.set(null);
|
return;
|
||||||
this.payout.set(null);
|
}
|
||||||
|
|
||||||
this.diceService.rollDice(diceDto).subscribe({
|
const diceDto: DiceDto = this.diceForm.getRawValue() as DiceDto;
|
||||||
next: (result: DiceResult) => {
|
|
||||||
this.rolledValue.set(result.rolledValue);
|
this.rolledValue.set(null);
|
||||||
this.win.set(result.win);
|
this.win.set(null);
|
||||||
this.payout.set(result.payout);
|
this.payout.set(null);
|
||||||
this.userService.refreshCurrentUser();
|
|
||||||
},
|
this.diceService.rollDice(diceDto).subscribe({
|
||||||
error: (error) => {
|
next: (result: DiceResult) => {
|
||||||
console.error('Dice roll failed:', error);
|
this.rolledValue.set(result.rolledValue);
|
||||||
}
|
this.win.set(result.win);
|
||||||
});
|
this.payout.set(result.payout);
|
||||||
}
|
this.userService.refreshCurrentUser();
|
||||||
|
},
|
||||||
hasError(controlName: string, errorName: string): boolean {
|
error: (error) => {
|
||||||
const control = this.diceForm.get(controlName);
|
console.error('Dice roll failed:', error);
|
||||||
return control !== null && control.touched && control.hasError(errorName);
|
},
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasError(controlName: string, errorName: string): boolean {
|
||||||
|
const control = this.diceForm.get(controlName);
|
||||||
|
return control !== null && control.touched && control.hasError(errorName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
export interface DiceDto {
|
export interface DiceDto {
|
||||||
betAmount: number;
|
betAmount: number;
|
||||||
rollOver: boolean;
|
rollOver: boolean;
|
||||||
targetValue: number;
|
targetValue: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DiceResult {
|
export interface DiceResult {
|
||||||
win: boolean;
|
win: boolean;
|
||||||
payout: number;
|
payout: number;
|
||||||
rolledValue: number;
|
rolledValue: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { DiceDto, DiceResult } from './dice.model';
|
import { DiceDto, DiceResult } from './dice.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class DiceService {
|
export class DiceService {
|
||||||
private apiUrl = '/backend/dice';
|
private apiUrl = '/backend/dice';
|
||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
rollDice(diceDto: DiceDto): Observable<DiceResult> {
|
rollDice(diceDto: DiceDto): Observable<DiceResult> {
|
||||||
return this.http.post<DiceResult>(this.apiUrl, diceDto);
|
return this.http.post<DiceResult>(this.apiUrl, diceDto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue