Play now against the bot opens a room the server keeps
Every duel then leaves a ledger and shows in the visitors digest; the offline duel in the browser remains as a fallback. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
098dadf482
commit
885dcf56e6
@@ -32,6 +32,20 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function playBot() {
|
async function playBot() {
|
||||||
|
busy = true;
|
||||||
|
error = '';
|
||||||
|
try {
|
||||||
|
rememberName(name);
|
||||||
|
const remote = await Duel.createBotRoom(name.trim());
|
||||||
|
await goto(`/join/${remote.roomId}`);
|
||||||
|
} catch (e) {
|
||||||
|
error = e instanceof Error ? e.message : 'The duel could not be opened.';
|
||||||
|
} finally {
|
||||||
|
busy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function playOffline() {
|
||||||
rememberName(name);
|
rememberName(name);
|
||||||
await goto('/play');
|
await goto('/play');
|
||||||
}
|
}
|
||||||
@@ -66,7 +80,7 @@
|
|||||||
const g = games[held.roomId];
|
const g = games[held.roomId];
|
||||||
if (g === undefined) return 'looking';
|
if (g === undefined) return 'looking';
|
||||||
if (g === null) return 'unreachable';
|
if (g === null) return 'unreachable';
|
||||||
const others = g.seats.filter((s) => s.id !== held.seat).map((s) => s.name);
|
const others = g.seats.filter((s) => s.id !== held.seat).map((s) => (s.bot ? `${s.name} (the bot)` : s.name));
|
||||||
if (!g.state) return `waiting to start${g.seats.length ? ` · ${g.seats.map((s) => s.name).join(', ')}` : ''}`;
|
if (!g.state) return `waiting to start${g.seats.length ? ` · ${g.seats.map((s) => s.name).join(', ')}` : ''}`;
|
||||||
if (g.state.over) {
|
if (g.state.over) {
|
||||||
if (g.state.over.winner === held.seat) return 'you won';
|
if (g.state.over.winner === held.seat) return 'you won';
|
||||||
@@ -105,12 +119,14 @@
|
|||||||
<span>Your wizard's name</span>
|
<span>Your wizard's name</span>
|
||||||
<input type="text" bind:value={name} maxlength={NAME_MAX} placeholder="e.g. Morwenna" autocomplete="nickname" />
|
<input type="text" bind:value={name} maxlength={NAME_MAX} placeholder="e.g. Morwenna" autocomplete="nickname" />
|
||||||
</label>
|
</label>
|
||||||
<button type="button" class="reveal primary" disabled={!ready} onclick={playBot}>
|
<button type="button" class="reveal primary" disabled={!ready || busy} onclick={playBot}>Play now against the bot</button>
|
||||||
{local ? `Continue against ${local.foe.name} (turn ${local.turnNumber})` : 'Play now against the bot'}
|
<p class="muted small">
|
||||||
</button>
|
|
||||||
{#if local}
|
{#if local}
|
||||||
<p class="muted small">Or start afresh from the board's "New duel".</p>
|
<button type="button" class="link" onclick={playOffline}>Continue your offline duel against {local.foe.name} (turn {local.turnNumber})</button>
|
||||||
|
{:else}
|
||||||
|
<button type="button" class="link" onclick={playOffline}>Or play offline, in this browser only</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
</p>
|
||||||
<div class="ways">
|
<div class="ways">
|
||||||
<button type="button" class="quiet" disabled={!ready || busy} onclick={create}>Create a duel</button>
|
<button type="button" class="quiet" disabled={!ready || busy} onclick={create}>Create a duel</button>
|
||||||
<form class="joinform" onsubmit={join}>
|
<form class="joinform" onsubmit={join}>
|
||||||
@@ -149,8 +165,8 @@
|
|||||||
{#if local}
|
{#if local}
|
||||||
<div class="ledger-row your-move">
|
<div class="ledger-row your-move">
|
||||||
<a class="ledger-resume" href="/play">
|
<a class="ledger-resume" href="/play">
|
||||||
<span class="ledger-code">bot</span>
|
<span class="ledger-code">here</span>
|
||||||
<span class="ledger-info">your move · turn {local.turnNumber} · against {local.foe.name}</span>
|
<span class="ledger-info">offline · your move · turn {local.turnNumber} · against {local.foe.name}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -794,6 +794,13 @@ export class Duel {
|
|||||||
return remoteFrom(seated.view, seated.token);
|
return remoteFrom(seated.view, seated.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A room with the bot already seated: a duel the server keeps, so it can be replayed and seen. */
|
||||||
|
static async createBotRoom(name: string): Promise<Remote> {
|
||||||
|
const seated = await api.create(name, 2);
|
||||||
|
rememberSeat(seated.view.roomId, { seat: seated.seat, token: seated.token });
|
||||||
|
return remoteFrom(await api.addBot(seated.view.roomId, seated.token), seated.token);
|
||||||
|
}
|
||||||
|
|
||||||
static async joinRoom(roomId: string, name: string): Promise<Remote> {
|
static async joinRoom(roomId: string, name: string): Promise<Remote> {
|
||||||
const seated = await api.join(roomId, name);
|
const seated = await api.join(roomId, name);
|
||||||
rememberSeat(roomId, { seat: seated.seat, token: seated.token });
|
rememberSeat(roomId, { seat: seated.seat, token: seated.token });
|
||||||
|
|||||||
Reference in New Issue
Block a user