PokeR/src/lib/components/DecisionTimer.svelte
Veit F. 422fa5b3ab feat: implement bot intelligence system with 8 archetypes and coaching
Add personality-driven bots with 8 archetypes (Nit, TAG, LAG, Maniac, Calling Station, Loose Fish, Old Man, Monster TAG) across 5 skill levels.

Includes:

- Three-layer decision pipeline (base strategy → personality filter → skill noise)

- Decision timer system with archetype-specific timeout defaults

- Observation tracking engine (VPIP, PFR, Fold-to-CBet, WTSD, bet sizing, timing tells)

- Player classification engine with weighted scoring and confidence scaling

- Table setup UI with visual seat editor and quick presets

- Info display system with 4 visibility levels

- Teaching coach with post-hand analysis and real-time suggestions

Archives bot-intelligence change and syncs all 8 delta specs to main specs.
2026-05-17 22:41:09 +02:00

116 lines
2.5 KiB
Svelte

<script lang="ts">
import type { BotArchetype } from '$lib/types/bot-archetype';
let {
duration = 10,
active = false,
playerName = '',
archetype = undefined,
onTimeout
} = $props<{
duration?: number;
active?: boolean;
playerName?: string;
archetype?: BotArchetype | undefined;
onTimeout?: () => void;
}>();
let remaining = $state(0);
let intervalId: ReturnType<typeof setInterval> | null = null;
const percent = $derived(duration > 0 ? (remaining / duration) * 100 : 0);
const warning = $derived(remaining <= 3 && active);
$effect(() => {
if (active) {
remaining = duration;
intervalId = setInterval(() => {
remaining--;
if (remaining <= 0) {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
onTimeout?.();
}
}, 1000);
} else {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
remaining = 0;
}
return () => {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
};
});
</script>
<div class="timer" class:warning>
<div class="player-name">{playerName}</div>
<div class="countdown">{remaining}s</div>
<div class="bar-track">
<div class="bar-fill" style="width: {percent}%"></div>
</div>
{#if archetype}
<div class="archetype-badge">{archetype}</div>
{/if}
</div>
<style>
.timer {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
padding: 0.5rem;
border-radius: 0.5rem;
background: rgba(0, 0, 0, 0.3);
min-width: 100px;
}
.timer.warning {
background: rgba(255, 50, 50, 0.2);
border: 1px solid rgba(255, 50, 50, 0.5);
}
.player-name {
font-size: 0.75rem;
color: #aaa;
}
.countdown {
font-size: 1.5rem;
font-weight: bold;
font-variant-numeric: tabular-nums;
}
.warning .countdown {
color: #ff4444;
}
.bar-track {
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
}
.bar-fill {
height: 100%;
background: #4ade80;
transition: width 0.3s linear, background 0.3s;
border-radius: 2px;
}
:global(.warning) .bar-fill {
background: #ff4444;
}
.archetype-badge {
font-size: 0.6rem;
padding: 0.1rem 0.4rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 0.25rem;
color: #888;
}
</style>