Files
yarn-ng/bots/BotFactory.js
T

79 lines
2.2 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const RandomBot = require('./RandomBot');
const LLMBot = require('./LLMBot');
const NAMES_PATH = path.join(__dirname, 'bot_names.txt');
const FALLBACK_NAMES = ['DefaultBot', 'BackupBot', 'SpareBot', 'ExtraBot'];
function readNames() {
try {
const names = fs
.readFileSync(NAMES_PATH, 'utf8')
.split('\n')
.map((name) => name.trim())
.filter((name) => name.length > 0);
return names.length ? names : FALLBACK_NAMES;
} catch (err) {
console.warn(`[BotFactory] Could not read ${NAMES_PATH}: ${err.message}`);
return FALLBACK_NAMES;
}
}
class BotFactory {
/**
* Create a bot instance. LLM bots fall back to RandomBot when the LLM
* client cannot be constructed (missing API key/config) so a game can
* always start.
*/
static createBot(type = 'llm') {
switch (String(type).toLowerCase()) {
case 'random':
return new RandomBot();
case 'llm':
try {
return new LLMBot();
} catch (err) {
console.warn(
`[BotFactory] LLM bot unavailable (${err.message}); using RandomBot instead.`
);
return new RandomBot();
}
default:
throw new Error(`Unknown bot type: ${type}`);
}
}
/**
* Pick a random bot name, avoiding names already used in the room.
*/
static generateBotName(exclude = []) {
const names = readNames();
const taken = new Set(
(Array.isArray(exclude) ? exclude : [exclude])
.filter(Boolean)
.map((name) => String(name).toLowerCase())
);
const available = names.filter((name) => !taken.has(name.toLowerCase()));
const pool = available.length ? available : names;
const pick = pool[Math.floor(Math.random() * pool.length)] || FALLBACK_NAMES[0];
// If every themed name is taken, disambiguate with a number.
if (taken.has(pick.toLowerCase())) {
let suffix = 2;
while (taken.has(`${pick}${suffix}`.toLowerCase())) suffix += 1;
return `${pick}${suffix}`;
}
return pick;
}
static getAvailableBotTypes() {
return ['llm', 'random'];
}
}
module.exports = BotFactory;