Merge branch 'm9/bots' into codex/owner/grp_discord_joke-app
This commit is contained in:
53
packages/shared/src/bots.ts
Normal file
53
packages/shared/src/bots.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/** Computer players for solo practice (docs/06-lobby-rooms.md §12). Shared by server and web (no runtime deps). */
|
||||
import type { PublicUser } from './types';
|
||||
|
||||
/** Bot user ids look like `bot:<n>:<ulid>`; real user ids are plain ULIDs, so they can never collide. */
|
||||
export const BOT_PREFIX = 'bot:';
|
||||
|
||||
export const isBotId = (id: string): boolean => id.startsWith(BOT_PREFIX);
|
||||
|
||||
export const makeBotId = (n: number, unique: string): string => `${BOT_PREFIX}${n}:${unique}`;
|
||||
|
||||
/** Number shown in the bot's name ("컴퓨터 2"). */
|
||||
export function botNumber(id: string): number | null {
|
||||
if (!isBotId(id)) return null;
|
||||
const n = Number(id.slice(BOT_PREFIX.length).split(':')[0]);
|
||||
return Number.isInteger(n) && n > 0 ? n : null;
|
||||
}
|
||||
|
||||
/** Public profile of a bot id, or null for anything else. */
|
||||
export function botProfile(id: string): PublicUser | null {
|
||||
const n = botNumber(id);
|
||||
return n === null ? null : { id, nickname: `컴퓨터 ${n}`, avatar: null, kind: 'bot' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Games that offer computer players. Real-time and talk-based games (도블, 할리갈리, 그림 맞히기, 마피아)
|
||||
* don't: a bot there is either unbeatable or meaningless.
|
||||
*/
|
||||
export const BOT_GAMES: readonly string[] = [
|
||||
'omok',
|
||||
'othello',
|
||||
'chess',
|
||||
'janggi',
|
||||
'baduk',
|
||||
'seotda',
|
||||
'poker',
|
||||
'yacht',
|
||||
'yut',
|
||||
'one-card',
|
||||
'rummikub',
|
||||
'gostop',
|
||||
'bang',
|
||||
'davinci-code',
|
||||
'mahjong',
|
||||
'marble',
|
||||
'dalmuti',
|
||||
'splendor',
|
||||
'quoridor',
|
||||
'las-vegas',
|
||||
'cockroach-poker',
|
||||
'ninety-nine',
|
||||
];
|
||||
|
||||
export const botsAllowed = (gameId: string): boolean => BOT_GAMES.includes(gameId);
|
||||
@@ -4,3 +4,4 @@ export * from './nickname';
|
||||
export * from './room-code';
|
||||
export * from './catalog';
|
||||
export * from './view-patch';
|
||||
export * from './bots';
|
||||
|
||||
@@ -27,6 +27,8 @@ export const ClientMessage = z.discriminatedUnion('t', [
|
||||
z.object({ t: z.literal('start') }),
|
||||
z.object({ t: z.literal('kick'), userId }),
|
||||
z.object({ t: z.literal('host'), userId }),
|
||||
z.object({ t: z.literal('addBot'), seat: z.number().int().min(0).max(31).optional() }),
|
||||
z.object({ t: z.literal('removeBot'), userId }),
|
||||
z.object({ t: z.literal('act'), cs: z.number().int().min(1), a: z.unknown() }),
|
||||
z.object({ t: z.literal('chat'), text: z.string().min(1).max(CHAT_MAX) }),
|
||||
z.object({ t: z.literal('emote'), id: z.string().max(20) }),
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface PublicUser {
|
||||
id: string;
|
||||
nickname: string;
|
||||
avatar: string | null;
|
||||
kind: 'guest' | 'member';
|
||||
kind: 'guest' | 'member' | 'bot';
|
||||
}
|
||||
|
||||
export interface SeatView {
|
||||
|
||||
Reference in New Issue
Block a user