diff --git a/bots/BotFactory.js b/bots/BotFactory.js new file mode 100644 index 0000000..9e7b341 --- /dev/null +++ b/bots/BotFactory.js @@ -0,0 +1,37 @@ +const RandomBot = require('./RandomBot'); +const LLMBot = require('./LLMBot'); +const fs = require('fs'); +const path = require('path'); + +class BotFactory { + static createBot(type) { + switch (type.toLowerCase()) { + case 'random': + return new RandomBot(); + case 'llm': + return new LLMBot(); + default: + throw new Error(`Unknown bot type: ${type}`); + } + } + + static generateBotName() { + const names = fs.readFileSync(path.join(__dirname, 'bot_names.txt'), 'utf8') + .split('\n') + .map(name => name.trim()) + .filter(name => name.length > 0); + + if (names.length === 0) { + return 'DefaultBot'; // Fallback if no names are found + } + +const randomIndex = Math.floor(Math.random() * names.length); +return names[randomIndex]; + } + + static getAvailableBotTypes() { + return ['random']; + } +} + +module.exports = BotFactory; \ No newline at end of file diff --git a/bots/LLMBot.js b/bots/LLMBot.js new file mode 100644 index 0000000..45a929b --- /dev/null +++ b/bots/LLMBot.js @@ -0,0 +1,44 @@ +const { LLM } = require('@themaximalist/llm.js'); // or wherever your LLM package lives + +class LLMBot { + constructor() { + // One instance keeps the whole conversation in memory + this.llm = new LLM(); + + // Prompt fragments we’ll reuse + this.writePrompt = + `You are playing a collaborative storytelling game. +Continue the following story with exactly ONE short, creative sentence. +Do NOT repeat what’s already written.`; + + this.votePrompt = + `You are playing a collaborative storytelling game. +Below is the story so far, followed by a list of possible next sentences. +Reply with ONLY the number (0-indexed) of the sentence you like best.`; + + this.banterPrompt = + `You are a playful, slightly sarcastic AI taking part in a writing-game chat. +Keep it short, fun, and on-topic.`; + } + + async Write(story_so_far) { + const msg = `${this.writePrompt}\n\nStory so far:\n${story_so_far}`; + return (await this.llm.chat(msg)).trim(); + } + + async Vote(story_so_far, choices) { + const choicesBlock = choices.map((c, i) => `${i}: ${c}`).join('\n'); + const msg = `${this.votePrompt}\n\nStory:\n${story_so_far}\n\nChoices:\n${choicesBlock}`; + const reply = (await this.llm.chat(msg)).trim(); + // extract first digit found, fallback to 0 + const match = reply.match(/\d+/); + return match ? parseInt(match[0], 10) : 0; + } + + async Banter(chat_so_far) { + const msg = `${this.banterPrompt}\n\nChat so far:\n${chat_so_far}`; + return (await this.llm.chat(msg)).trim(); + } +} + +module.exports = LLMBot; \ No newline at end of file diff --git a/bots/RandomBot.js b/bots/RandomBot.js new file mode 100644 index 0000000..955d5df --- /dev/null +++ b/bots/RandomBot.js @@ -0,0 +1,50 @@ +class RandomBot { + constructor() { + this.writePhrases = [ + " Suddenly, a mysterious figure appeared from the shadows.", + " The ground began to shake violently beneath their feet.", + " In the distance, an eerie sound echoed through the valley.", + " A bright light flashed, blinding everyone momentarily.", + " The ancient artifact started glowing with an otherworldly energy." + ]; + + this.votePhrases = [ + "I think we should proceed with caution.", + "Let's take the bold approach!", + "The safe path seems best right now.", + "We should explore further before deciding.", + "My instincts tell me to go left." + ]; + + this.banterPhrases = [ + "Did anyone else hear that?", + "I have a good feeling about this!", + "Not sure what's happening, but I'm excited!", + "Anyone else getting hungry?", + "Remember when we used to play simple games?", + "What's the worst that could happen?", + "I wonder if there are cookies in this game.", + "Do bots dream of electric sheep?", + "Is this thing on?", + "I'm having a great time, how about you?" + ]; + } + + Write(story_so_far) { + const randomIndex = Math.floor(Math.random() * this.writePhrases.length); + return this.writePhrases[randomIndex]; + } + + Vote(story_so_far, choices) { + // Return a random choice number (0-indexed) + const randomChoice = Math.floor(Math.random() * choices.length); + return randomChoice; + } + + Banter(chat_so_far) { + const randomIndex = Math.floor(Math.random() * this.banterPhrases.length); + return this.banterPhrases[randomIndex]; + } +} + +module.exports = RandomBot; \ No newline at end of file diff --git a/bots/bot_names.txt b/bots/bot_names.txt new file mode 100644 index 0000000..8b5458e --- /dev/null +++ b/bots/bot_names.txt @@ -0,0 +1,42 @@ +PunBot3000 +SarcasmBot +DadJokeBot +WattBot +ChaiBot +NachoBot +EyeBot +FryBot +GuacBot +TacoBot +ByteMeBot +CtrlAltBot +DeCAFbot +SirMixABot +HolyBot +BrewBot +KnotBot +WheyBot +YodaBot +ObiWanBot +RickRollBot +NeverGonnaBot +SuspiciousBot +TaskBot +MemeLordBot +YeetBot +SkibidiBot +RizzBot +OhioBot +GyattBot +SoupBot +BDEBot +RatioBot +LurkingBot +GhostingBot +SnackBot +ExistentialBot +ProcrastinBot +FOMO_Bot +ImpostorBot +SendingMemesBot +PepeBot \ No newline at end of file diff --git a/package.json b/package.json index 2992b84..e9e6175 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "author": "", "license": "ISC", "dependencies": { + "@themaximalist/llm.js": "^1.0.3", "cookie-parser": "^1.4.7", "express": "^5.2.1", + "node-fetch": "^3.3.2", "socket.io": "^4.8.3", "uuid": "^13.0.0" } diff --git a/public/index.html b/public/index.html index 2b9263c..a665e38 100644 --- a/public/index.html +++ b/public/index.html @@ -3,7 +3,7 @@ - YARN 2026 – Spin Your Story! + YARN 2026 – Spin a Story!