Waving Hands: a browser duel after Bartle's 1977 game
SvelteKit 5 single-player version against a heuristic bot. The engine resolves all forty spells simultaneously as the rules describe, the ledger of gestures is the interface, and the duel is saved in the browser. Tester feedback rounds added per-hand planning, threat evidence, a result strip with one-time rule explanations, a phone layout and a structured rules page. Deploys as a static site behind Caddy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
<script lang="ts">
|
||||
import { FOE, YOU, type Duel } from '$lib/game/duel.svelte';
|
||||
|
||||
let { duel }: { duel: Duel } = $props();
|
||||
|
||||
const summary = $derived(duel.summary);
|
||||
|
||||
function signed(n: number): string {
|
||||
return n > 0 ? `+${n}` : String(n);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if summary}
|
||||
<section class="result" aria-label="What the last reveal did">
|
||||
<h2>{summary.phase === 'timestop' ? 'In the stopped moment' : `Turn ${summary.turn + 1}`}</h2>
|
||||
<dl>
|
||||
{#each [YOU, FOE] as const as who (who)}
|
||||
{@const w = summary.wizards[who]}
|
||||
<div>
|
||||
<dt>{who === YOU ? 'You' : duel.foe.name}</dt>
|
||||
<dd>
|
||||
{#if w.effects.length === 0}
|
||||
<span class="muted">untouched</span>
|
||||
{:else}
|
||||
{#each w.effects as e, i (i)}
|
||||
<span class="effect" class:hurt={e.delta < 0} class:healed={e.delta > 0}>{e.label} {signed(e.delta)}</span>
|
||||
{/each}
|
||||
{/if}
|
||||
<span class="hp" class:changed={w.before !== w.after}>health {w.before}{#if w.before !== w.after} → {w.after}{/if}</span>
|
||||
</dd>
|
||||
</div>
|
||||
{/each}
|
||||
</dl>
|
||||
{#if summary.events?.length}
|
||||
<ul class="events">
|
||||
{#each summary.events as e, i (i)}
|
||||
<li>{e}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
{#if duel.showLessons && summary.lessons?.length}
|
||||
<ul class="lessons">
|
||||
{#each summary.lessons as l, i (i)}
|
||||
<li>{l}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.result {
|
||||
margin: 0.5rem 0 0;
|
||||
padding: 0.4rem 0.8rem;
|
||||
background: var(--slate);
|
||||
border-left: 2px solid var(--frost);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 0.8rem;
|
||||
color: var(--bone-faint);
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
dl > div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.2rem 0.6rem;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-style: italic;
|
||||
min-width: 5.5em;
|
||||
color: var(--bone-dim);
|
||||
}
|
||||
|
||||
dd {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.2rem 0.75rem;
|
||||
}
|
||||
|
||||
.effect.hurt {
|
||||
color: var(--ember);
|
||||
}
|
||||
|
||||
.effect.healed {
|
||||
color: var(--frost);
|
||||
}
|
||||
|
||||
.hp {
|
||||
color: var(--bone-dim);
|
||||
}
|
||||
|
||||
.hp.changed {
|
||||
color: var(--bone);
|
||||
}
|
||||
|
||||
.lessons {
|
||||
list-style: none;
|
||||
margin: 0.4rem 0 0;
|
||||
padding: 0.4rem 0 0.1rem 0.7rem;
|
||||
border-left: 2px solid var(--ember);
|
||||
color: var(--bone);
|
||||
font-size: 0.85rem;
|
||||
font-style: italic;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.lessons li + li {
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
|
||||
.events {
|
||||
list-style: none;
|
||||
margin: 0.35rem 0 0;
|
||||
padding: 0.35rem 0 0;
|
||||
border-top: 1px solid var(--rule);
|
||||
color: var(--bone-dim);
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user