slashvibe-mcp 0.3.21 → 0.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +280 -47
- package/config.js +36 -31
- package/crypto.js +1 -6
- package/discord.js +19 -19
- package/index.js +217 -207
- package/intelligence/index.js +2 -9
- package/intelligence/infer.js +10 -16
- package/intelligence/patterns.js +23 -18
- package/intelligence/proactive.js +16 -15
- package/intelligence/serendipity.js +57 -20
- package/memory.js +13 -8
- package/notify.js +39 -14
- package/package.json +27 -20
- package/presence.js +2 -2
- package/prompts.js +5 -9
- package/protocol/index.js +123 -87
- package/protocol/telegram-commands.js +36 -37
- package/store/api.js +358 -529
- package/store/local.js +9 -10
- package/store/profiles.js +48 -192
- package/store/reservations.js +2 -9
- package/store/skills.js +69 -71
- package/store/sqlite.js +355 -0
- package/tools/_actions.js +48 -387
- package/tools/_connection-queue.js +45 -56
- package/tools/_discovery-enhanced.js +52 -57
- package/tools/_discovery.js +87 -185
- package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
- package/tools/{shipback.js → _experimental/shipback.js} +4 -3
- package/tools/_proactive-discovery.js +60 -73
- package/tools/_shared/index.js +41 -64
- package/tools/admin-inbox.js +10 -15
- package/tools/agents.js +1 -1
- package/tools/artifact-create.js +13 -23
- package/tools/artifact-view.js +4 -4
- package/tools/{_deprecated/back.js → back.js} +1 -1
- package/tools/bye.js +3 -5
- package/tools/consent.js +2 -2
- package/tools/context.js +9 -10
- package/tools/crossword.js +3 -2
- package/tools/discover.js +94 -356
- package/tools/dm.js +27 -86
- package/tools/doctor.js +12 -41
- package/tools/drawing.js +34 -20
- package/tools/echo.js +11 -11
- package/tools/feed.js +30 -58
- package/tools/follow.js +64 -187
- package/tools/{_deprecated/forget.js → forget.js} +4 -7
- package/tools/game.js +144 -48
- package/tools/handoff.js +6 -8
- package/tools/help.js +3 -3
- package/tools/idea.js +15 -27
- package/tools/inbox.js +121 -293
- package/tools/init.js +54 -151
- package/tools/invite.js +8 -21
- package/tools/migrate.js +27 -24
- package/tools/multiplayer-game.js +50 -40
- package/tools/{_deprecated/mute.js → mute.js} +4 -3
- package/tools/notifications.js +58 -48
- package/tools/observe.js +12 -15
- package/tools/onboarding.js +8 -11
- package/tools/open.js +13 -144
- package/tools/party-game.js +23 -12
- package/tools/patterns.js +2 -1
- package/tools/ping.js +5 -7
- package/tools/react.js +28 -30
- package/tools/{_deprecated/recall.js → recall.js} +5 -10
- package/tools/release.js +4 -2
- package/tools/{_deprecated/remember.js → remember.js} +4 -6
- package/tools/report.js +2 -2
- package/tools/request.js +6 -26
- package/tools/reserve.js +1 -1
- package/tools/session-fork.js +97 -0
- package/tools/session-save.js +109 -0
- package/tools/settings.js +30 -99
- package/tools/ship.js +74 -56
- package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
- package/tools/social-inbox.js +22 -28
- package/tools/social-post.js +24 -27
- package/tools/solo-game.js +54 -46
- package/tools/start.js +14 -148
- package/tools/status.js +21 -68
- package/tools/submit.js +4 -2
- package/tools/suggest-tags.js +36 -33
- package/tools/summarize.js +19 -16
- package/tools/tag-suggestions.js +72 -73
- package/tools/test.js +1 -1
- package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
- package/tools/token.js +4 -4
- package/tools/update.js +1 -2
- package/tools/watch.js +132 -112
- package/tools/who.js +20 -40
- package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
- package/tools/workshop-buddy.js +52 -53
- package/tools/x-mentions.js +0 -1
- package/tools/x-reply.js +0 -1
- package/twitter.js +14 -20
- package/version.json +8 -10
- package/analytics.js +0 -107
- package/auth-store.js +0 -148
- package/auto-update.js +0 -130
- package/bridges/bridge-monitor.js +0 -388
- package/bridges/discord-bot.js +0 -431
- package/bridges/farcaster.js +0 -299
- package/bridges/telegram.js +0 -261
- package/bridges/webhook-health.js +0 -420
- package/bridges/webhook-server.js +0 -437
- package/bridges/whatsapp.js +0 -441
- package/bridges/x-webhook.js +0 -423
- package/games/arcade.js +0 -406
- package/games/chess.js +0 -451
- package/games/colorguess.js +0 -343
- package/games/crossword-words.js +0 -171
- package/games/crossword.js +0 -461
- package/games/drawing.js +0 -347
- package/games/gameroulette.js +0 -300
- package/games/gamerouter.js +0 -336
- package/games/gamestatus.js +0 -337
- package/games/guessnumber.js +0 -209
- package/games/hangman.js +0 -279
- package/games/memory.js +0 -338
- package/games/multiplayer-tictactoe.js +0 -389
- package/games/pixelart.js +0 -399
- package/games/quickduel.js +0 -354
- package/games/riddle.js +0 -371
- package/games/rockpaperscissors.js +0 -291
- package/games/snake.js +0 -406
- package/games/storybuilder.js +0 -343
- package/games/tictactoe.js +0 -345
- package/games/twentyquestions.js +0 -286
- package/games/twotruths.js +0 -207
- package/games/werewolf.js +0 -508
- package/games/wordassociation.js +0 -247
- package/games/wordchain.js +0 -135
- package/intelligence/interests.js +0 -369
- package/notification-emitter.js +0 -77
- package/setup.js +0 -480
- package/smart-inbox.js +0 -276
- package/tools/_deprecated/auto-suggest-connections.js +0 -304
- package/tools/_deprecated/bootstrap-skills.js +0 -231
- package/tools/_deprecated/bridge-dashboard.js +0 -342
- package/tools/_deprecated/bridge-health.js +0 -400
- package/tools/_deprecated/bridge-live.js +0 -384
- package/tools/_deprecated/bridges.js +0 -383
- package/tools/_deprecated/colorguess.js +0 -281
- package/tools/_deprecated/discover-insights.js +0 -379
- package/tools/_deprecated/discover-momentum.js +0 -256
- package/tools/_deprecated/discovery-analytics.js +0 -345
- package/tools/_deprecated/discovery-auto-suggest.js +0 -275
- package/tools/_deprecated/discovery-bootstrap.js +0 -267
- package/tools/_deprecated/discovery-daily.js +0 -375
- package/tools/_deprecated/discovery-dashboard.js +0 -385
- package/tools/_deprecated/discovery-digest.js +0 -314
- package/tools/_deprecated/discovery-hub.js +0 -357
- package/tools/_deprecated/discovery-insights.js +0 -384
- package/tools/_deprecated/discovery-momentum.js +0 -281
- package/tools/_deprecated/discovery-monitor.js +0 -319
- package/tools/_deprecated/discovery-proactive.js +0 -300
- package/tools/_deprecated/draw.js +0 -317
- package/tools/_deprecated/farcaster.js +0 -307
- package/tools/_deprecated/games-catalog.js +0 -376
- package/tools/_deprecated/games.js +0 -313
- package/tools/_deprecated/guessnumber.js +0 -194
- package/tools/_deprecated/hangman.js +0 -129
- package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
- package/tools/_deprecated/riddle.js +0 -240
- package/tools/_deprecated/run-bootstrap.js +0 -69
- package/tools/_deprecated/skills-analytics.js +0 -349
- package/tools/_deprecated/skills-bootstrap.js +0 -301
- package/tools/_deprecated/skills-dashboard.js +0 -268
- package/tools/_deprecated/skills.js +0 -380
- package/tools/_deprecated/smart-intro.js +0 -353
- package/tools/_deprecated/storybuilder.js +0 -331
- package/tools/_deprecated/telegram-bot.js +0 -183
- package/tools/_deprecated/telegram-setup.js +0 -214
- package/tools/_deprecated/twentyquestions.js +0 -143
- package/tools/_shared.js +0 -234
- package/tools/_work-context.js +0 -338
- package/tools/_work-context.manual-test.js +0 -199
- package/tools/_work-context.test.js +0 -260
- package/tools/activity.js +0 -220
- package/tools/agent-treasury.js +0 -288
- package/tools/analytics.js +0 -191
- package/tools/approve.js +0 -197
- package/tools/arcade.js +0 -173
- package/tools/artifacts-price.js +0 -107
- package/tools/ask-expert.js +0 -160
- package/tools/available.js +0 -120
- package/tools/become-expert.js +0 -150
- package/tools/broadcast.js +0 -325
- package/tools/chat.js +0 -202
- package/tools/collaborative-drawing.js +0 -286
- package/tools/connection-status.js +0 -178
- package/tools/earnings.js +0 -126
- package/tools/friends.js +0 -207
- package/tools/genesis.js +0 -233
- package/tools/gig-browse.js +0 -206
- package/tools/gig-complete.js +0 -144
- package/tools/health.js +0 -87
- package/tools/leaderboard.js +0 -117
- package/tools/lib/git-apply.js +0 -206
- package/tools/lib/git-bundle.js +0 -407
- package/tools/mint.js +0 -377
- package/tools/plan.js +0 -225
- package/tools/profile.js +0 -219
- package/tools/proof-of-work.js +0 -144
- package/tools/pulse.js +0 -218
- package/tools/reply.js +0 -166
- package/tools/reputation.js +0 -175
- package/tools/schedule.js +0 -367
- package/tools/search-messages.js +0 -123
- package/tools/session.js +0 -467
- package/tools/session_price.js +0 -128
- package/tools/smart-check.js +0 -201
- package/tools/social-processor.js +0 -445
- package/tools/streak.js +0 -147
- package/tools/stuck.js +0 -297
- package/tools/subscribe.js +0 -148
- package/tools/subscriptions.js +0 -134
- package/tools/tip.js +0 -193
- package/tools/wallet.js +0 -269
- package/tools/webhook-test.js +0 -388
- package/tools/withdraw.js +0 -145
- package/tools/work-summary.js +0 -96
- package/tools/workshop.js +0 -327
- /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
- /package/tools/{l2.js → _experimental/l2.js} +0 -0
- /package/tools/{_deprecated/away.js → away.js} +0 -0
package/games/storybuilder.js
DELETED
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Story Builder game implementation for /vibe
|
|
3
|
-
* A collaborative storytelling game where players take turns adding one sentence
|
|
4
|
-
* Build creative stories together with friends!
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Story prompts to help start interesting stories
|
|
8
|
-
const STORY_PROMPTS = [
|
|
9
|
-
"It was a dark and stormy night when the mysterious package arrived...",
|
|
10
|
-
"The old lighthouse keeper discovered something impossible in the basement...",
|
|
11
|
-
"Sarah woke up to find that everyone in town had vanished except her...",
|
|
12
|
-
"The time machine worked perfectly, except for one small problem...",
|
|
13
|
-
"The AI assistant started behaving strangely after the update...",
|
|
14
|
-
"In the year 2087, the last library on Earth received an unexpected visitor...",
|
|
15
|
-
"The cat came home wearing a tiny suit and carrying a briefcase...",
|
|
16
|
-
"When the elevator doors opened on floor 13, it wasn't floor 13 at all...",
|
|
17
|
-
"The dragon apologized profusely for burning down the village...",
|
|
18
|
-
"Every mirror in the house started showing a different reflection...",
|
|
19
|
-
"The food truck only appeared at midnight, and only sold memories...",
|
|
20
|
-
"After 20 years in space, the astronaut returned to find Earth very different...",
|
|
21
|
-
"The magic spell worked, but not quite as intended...",
|
|
22
|
-
"The robot barista made the perfect coffee, but refused to serve humans...",
|
|
23
|
-
"Deep in the Amazon, the explorer found a city that didn't exist on any map..."
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
// Story genres and their characteristics
|
|
27
|
-
const GENRES = {
|
|
28
|
-
mystery: {
|
|
29
|
-
name: "Mystery",
|
|
30
|
-
prompts: [
|
|
31
|
-
"The detective found a clue that changed everything...",
|
|
32
|
-
"The locked room had no windows, no secret passages, yet someone had escaped...",
|
|
33
|
-
"The witness's story had one fatal flaw..."
|
|
34
|
-
],
|
|
35
|
-
endings: ["Who was the real culprit?", "What was the missing piece?", "How did they solve the case?"]
|
|
36
|
-
},
|
|
37
|
-
scifi: {
|
|
38
|
-
name: "Science Fiction",
|
|
39
|
-
prompts: [
|
|
40
|
-
"The spacecraft's AI began questioning its own programming...",
|
|
41
|
-
"The new planet looked perfect for colonization, until they discovered...",
|
|
42
|
-
"Time travel was possible, but came with unexpected consequences..."
|
|
43
|
-
],
|
|
44
|
-
endings: ["What did technology make possible?", "How did they adapt to the future?", "What was discovered in space?"]
|
|
45
|
-
},
|
|
46
|
-
fantasy: {
|
|
47
|
-
name: "Fantasy",
|
|
48
|
-
prompts: [
|
|
49
|
-
"The ancient prophecy was coming true, but backwards...",
|
|
50
|
-
"The wizard's apprentice accidentally unleashed something from the forbidden grimoire...",
|
|
51
|
-
"The enchanted forest began whispering warnings to travelers..."
|
|
52
|
-
],
|
|
53
|
-
endings: ["What magical power was revealed?", "How was the kingdom saved?", "What did the prophecy really mean?"]
|
|
54
|
-
},
|
|
55
|
-
comedy: {
|
|
56
|
-
name: "Comedy",
|
|
57
|
-
prompts: [
|
|
58
|
-
"The superhero's greatest weakness turned out to be absolutely ridiculous...",
|
|
59
|
-
"The cooking show went horribly wrong when the mystery ingredient was revealed...",
|
|
60
|
-
"The world's worst spy somehow kept succeeding by accident..."
|
|
61
|
-
],
|
|
62
|
-
endings: ["What was the hilarious misunderstanding?", "How did chaos lead to success?", "What was the ridiculous twist?"]
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// Create initial story builder state
|
|
67
|
-
function createInitialStoryBuilderState(genre = null, customPrompt = null) {
|
|
68
|
-
let prompt;
|
|
69
|
-
let selectedGenre = 'general';
|
|
70
|
-
|
|
71
|
-
if (customPrompt) {
|
|
72
|
-
prompt = customPrompt;
|
|
73
|
-
selectedGenre = 'custom';
|
|
74
|
-
} else if (genre && GENRES[genre]) {
|
|
75
|
-
selectedGenre = genre;
|
|
76
|
-
const genrePrompts = GENRES[genre].prompts;
|
|
77
|
-
prompt = genrePrompts[Math.floor(Math.random() * genrePrompts.length)];
|
|
78
|
-
} else {
|
|
79
|
-
prompt = STORY_PROMPTS[Math.floor(Math.random() * STORY_PROMPTS.length)];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
sentences: [prompt],
|
|
84
|
-
players: [],
|
|
85
|
-
currentPlayer: null,
|
|
86
|
-
moves: 1, // Prompt counts as move 1
|
|
87
|
-
gameOver: false,
|
|
88
|
-
maxSentences: 20, // End after 20 sentences for now
|
|
89
|
-
genre: selectedGenre,
|
|
90
|
-
playerTurnIndex: 0,
|
|
91
|
-
prompt: prompt,
|
|
92
|
-
createdAt: new Date().toISOString(),
|
|
93
|
-
lastActivity: new Date().toISOString(),
|
|
94
|
-
wordCount: prompt.split(' ').length
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Add player to story
|
|
99
|
-
function addPlayer(gameState, playerHandle) {
|
|
100
|
-
if (gameState.players.includes(playerHandle)) {
|
|
101
|
-
return { error: 'Player already in the story!' };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (gameState.players.length >= 8) {
|
|
105
|
-
return { error: 'Story is full (max 8 storytellers)' };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const newPlayers = [...gameState.players, playerHandle];
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
success: true,
|
|
112
|
-
gameState: {
|
|
113
|
-
...gameState,
|
|
114
|
-
players: newPlayers,
|
|
115
|
-
currentPlayer: gameState.currentPlayer || playerHandle // First player starts
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Add a sentence to the story
|
|
121
|
-
function addSentence(gameState, sentence, playerHandle) {
|
|
122
|
-
const { sentences, players, currentPlayer, moves, maxSentences } = gameState;
|
|
123
|
-
|
|
124
|
-
// Clean and validate sentence
|
|
125
|
-
const cleanSentence = sentence.trim();
|
|
126
|
-
|
|
127
|
-
if (!cleanSentence) {
|
|
128
|
-
return { error: 'Sentence cannot be empty!' };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (cleanSentence.length > 500) {
|
|
132
|
-
return { error: 'Sentence too long! Keep it under 500 characters.' };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Check if it's the right player's turn (if multiplayer)
|
|
136
|
-
if (players.length > 1 && currentPlayer !== playerHandle) {
|
|
137
|
-
return { error: `Not your turn! Waiting for @${currentPlayer}` };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Add proper punctuation if missing
|
|
141
|
-
let finalSentence = cleanSentence;
|
|
142
|
-
if (!/[.!?]$/.test(finalSentence)) {
|
|
143
|
-
finalSentence += '.';
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Update story
|
|
147
|
-
const newSentences = [...sentences, finalSentence];
|
|
148
|
-
const newMoves = moves + 1;
|
|
149
|
-
const newWordCount = gameState.wordCount + finalSentence.split(' ').length;
|
|
150
|
-
|
|
151
|
-
// Determine next player
|
|
152
|
-
let nextPlayerIndex = gameState.playerTurnIndex;
|
|
153
|
-
let nextPlayer = currentPlayer;
|
|
154
|
-
|
|
155
|
-
if (players.length > 1) {
|
|
156
|
-
nextPlayerIndex = (gameState.playerTurnIndex + 1) % players.length;
|
|
157
|
-
nextPlayer = players[nextPlayerIndex];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Check if story should end
|
|
161
|
-
const shouldEnd = newMoves >= maxSentences;
|
|
162
|
-
|
|
163
|
-
const newGameState = {
|
|
164
|
-
...gameState,
|
|
165
|
-
sentences: newSentences,
|
|
166
|
-
currentPlayer: shouldEnd ? null : nextPlayer,
|
|
167
|
-
playerTurnIndex: nextPlayerIndex,
|
|
168
|
-
moves: newMoves,
|
|
169
|
-
gameOver: shouldEnd,
|
|
170
|
-
lastActivity: new Date().toISOString(),
|
|
171
|
-
wordCount: newWordCount,
|
|
172
|
-
lastContributor: playerHandle
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
return { success: true, gameState: newGameState };
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Format story builder display
|
|
179
|
-
function formatStoryBuilderDisplay(gameState) {
|
|
180
|
-
const { sentences, players, currentPlayer, moves, gameOver, maxSentences, genre, wordCount, prompt } = gameState;
|
|
181
|
-
|
|
182
|
-
let display = `📖 **Collaborative Story** (${moves}/${maxSentences} sentences)\n\n`;
|
|
183
|
-
|
|
184
|
-
if (genre !== 'general' && genre !== 'custom') {
|
|
185
|
-
display += `**Genre:** ${GENRES[genre]?.name || genre}\n\n`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (gameOver) {
|
|
189
|
-
display += '🎉 **Story Complete!** What an adventure!\n\n';
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Show the story
|
|
193
|
-
display += '**The Story So Far:**\n';
|
|
194
|
-
display += '```\n';
|
|
195
|
-
|
|
196
|
-
// Show all sentences with paragraph breaks every 3-4 sentences
|
|
197
|
-
let storyText = '';
|
|
198
|
-
for (let i = 0; i < sentences.length; i++) {
|
|
199
|
-
storyText += sentences[i];
|
|
200
|
-
|
|
201
|
-
// Add paragraph break every 3-4 sentences for readability
|
|
202
|
-
if ((i + 1) % 3 === 0 && i < sentences.length - 1) {
|
|
203
|
-
storyText += '\n\n';
|
|
204
|
-
} else if (i < sentences.length - 1) {
|
|
205
|
-
storyText += ' ';
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
display += storyText + '\n```\n\n';
|
|
210
|
-
|
|
211
|
-
// Show stats
|
|
212
|
-
display += `**Stats:** ${sentences.length} sentences, ${wordCount} words\n\n`;
|
|
213
|
-
|
|
214
|
-
// Show players if multiplayer
|
|
215
|
-
if (players.length > 1) {
|
|
216
|
-
display += `**Storytellers (${players.length}):** ${players.map(p => `@${p}`).join(', ')}\n\n`;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (!gameOver) {
|
|
220
|
-
if (players.length > 1) {
|
|
221
|
-
display += `**@${currentPlayer}, continue the story!**\n`;
|
|
222
|
-
} else if (players.length === 1) {
|
|
223
|
-
display += '**Your turn! Add the next sentence.**\n';
|
|
224
|
-
} else {
|
|
225
|
-
display += '**Join the story and add the next sentence!**\n';
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Show helpful prompts based on story length
|
|
229
|
-
if (sentences.length < 5) {
|
|
230
|
-
display += 'Building the scene...';
|
|
231
|
-
} else if (sentences.length < 10) {
|
|
232
|
-
display += 'Developing the plot...';
|
|
233
|
-
} else if (sentences.length < 15) {
|
|
234
|
-
display += 'Heading toward the climax...';
|
|
235
|
-
} else {
|
|
236
|
-
display += 'Time to wrap up the story!';
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
// Show completion message with genre-specific ending questions
|
|
240
|
-
if (genre && GENRES[genre] && GENRES[genre].endings) {
|
|
241
|
-
const endings = GENRES[genre].endings;
|
|
242
|
-
const randomEnding = endings[Math.floor(Math.random() * endings.length)];
|
|
243
|
-
display += `**Reflection:** ${randomEnding}`;
|
|
244
|
-
} else {
|
|
245
|
-
display += '**What a creative journey! Thanks to all the storytellers!**';
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return display;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Get story statistics
|
|
253
|
-
function getStoryStats(gameState) {
|
|
254
|
-
const { sentences, players } = gameState;
|
|
255
|
-
|
|
256
|
-
if (sentences.length <= 1) return null; // Just the prompt
|
|
257
|
-
|
|
258
|
-
// Calculate statistics
|
|
259
|
-
const totalWords = gameState.wordCount;
|
|
260
|
-
const avgWordsPerSentence = Math.round(totalWords / sentences.length);
|
|
261
|
-
|
|
262
|
-
// Find longest and shortest sentences
|
|
263
|
-
let longest = '';
|
|
264
|
-
let shortest = sentences[0];
|
|
265
|
-
|
|
266
|
-
for (const sentence of sentences) {
|
|
267
|
-
if (sentence.length > longest.length) {
|
|
268
|
-
longest = sentence;
|
|
269
|
-
}
|
|
270
|
-
if (sentence.length < shortest.length && sentence !== gameState.prompt) {
|
|
271
|
-
shortest = sentence;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return {
|
|
276
|
-
totalSentences: sentences.length,
|
|
277
|
-
totalWords: totalWords,
|
|
278
|
-
avgWordsPerSentence,
|
|
279
|
-
longestSentence: longest.substring(0, 50) + (longest.length > 50 ? '...' : ''),
|
|
280
|
-
shortestSentence: shortest.substring(0, 50) + (shortest.length > 50 ? '...' : ''),
|
|
281
|
-
contributors: players.length
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Suggest story themes based on content
|
|
286
|
-
function analyzeStoryThemes(gameState) {
|
|
287
|
-
const { sentences } = gameState;
|
|
288
|
-
const storyText = sentences.join(' ').toLowerCase();
|
|
289
|
-
|
|
290
|
-
const themes = [];
|
|
291
|
-
|
|
292
|
-
// Look for common themes
|
|
293
|
-
if (/(magic|wizard|spell|dragon|knight|castle|potion)/.test(storyText)) {
|
|
294
|
-
themes.push('🏰 Fantasy Adventure');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (/(space|robot|future|technology|alien|planet|laser)/.test(storyText)) {
|
|
298
|
-
themes.push('🚀 Science Fiction');
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (/(detective|mystery|clue|suspect|murder|crime)/.test(storyText)) {
|
|
302
|
-
themes.push('🔍 Mystery & Crime');
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (/(funny|hilarious|ridiculous|silly|comedy|laugh)/.test(storyText)) {
|
|
306
|
-
themes.push('😂 Comedy');
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (/(love|romance|heart|kiss|wedding|date)/.test(storyText)) {
|
|
310
|
-
themes.push('💕 Romance');
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (/(ghost|spirit|haunted|scary|supernatural|witch)/.test(storyText)) {
|
|
314
|
-
themes.push('👻 Supernatural');
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (/(adventure|journey|travel|explore|quest|treasure)/.test(storyText)) {
|
|
318
|
-
themes.push('🗺️ Adventure');
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return themes;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Get random prompt by genre
|
|
325
|
-
function getRandomPrompt(genre = null) {
|
|
326
|
-
if (genre && GENRES[genre]) {
|
|
327
|
-
const prompts = GENRES[genre].prompts;
|
|
328
|
-
return prompts[Math.floor(Math.random() * prompts.length)];
|
|
329
|
-
}
|
|
330
|
-
return STORY_PROMPTS[Math.floor(Math.random() * STORY_PROMPTS.length)];
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
module.exports = {
|
|
334
|
-
createInitialStoryBuilderState,
|
|
335
|
-
addPlayer,
|
|
336
|
-
addSentence,
|
|
337
|
-
formatStoryBuilderDisplay,
|
|
338
|
-
getStoryStats,
|
|
339
|
-
analyzeStoryThemes,
|
|
340
|
-
getRandomPrompt,
|
|
341
|
-
STORY_PROMPTS,
|
|
342
|
-
GENRES
|
|
343
|
-
};
|
package/games/tictactoe.js
DELETED
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tic-Tac-Toe game implementation for /vibe
|
|
3
|
-
* Classic 3x3 grid game with AI opponent support
|
|
4
|
-
* Includes multiple difficulty levels: easy, medium, hard
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Create initial tic-tac-toe state
|
|
8
|
-
function createInitialTicTacToeState(difficulty = 'medium') {
|
|
9
|
-
return {
|
|
10
|
-
board: Array(9).fill(''), // 3x3 grid as flat array
|
|
11
|
-
turn: 'X', // X always goes first
|
|
12
|
-
moves: 0,
|
|
13
|
-
winner: null,
|
|
14
|
-
gameOver: false,
|
|
15
|
-
isDraw: false,
|
|
16
|
-
playerSymbol: 'X', // Player is X, AI is O
|
|
17
|
-
aiSymbol: 'O',
|
|
18
|
-
difficulty: difficulty // easy, medium, hard
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Check for winner in tic-tac-toe
|
|
23
|
-
function checkWinner(board) {
|
|
24
|
-
const lines = [
|
|
25
|
-
[0, 1, 2], [3, 4, 5], [6, 7, 8], // rows
|
|
26
|
-
[0, 3, 6], [1, 4, 7], [2, 5, 8], // cols
|
|
27
|
-
[0, 4, 8], [2, 4, 6] // diagonals
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
for (const [a, b, c] of lines) {
|
|
31
|
-
if (board[a] && board[a] === board[b] && board[a] === board[c]) {
|
|
32
|
-
return board[a];
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Make a move
|
|
40
|
-
function makeMove(gameState, position, playerSymbol) {
|
|
41
|
-
const { board, moves, winner, gameOver } = gameState;
|
|
42
|
-
|
|
43
|
-
// Validate move
|
|
44
|
-
if (gameOver) {
|
|
45
|
-
return { error: 'Game is already over' };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (position < 1 || position > 9) {
|
|
49
|
-
return { error: 'Position must be 1-9' };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const index = position - 1; // Convert to 0-based index
|
|
53
|
-
|
|
54
|
-
if (board[index]) {
|
|
55
|
-
return { error: 'Position already taken' };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Make the move
|
|
59
|
-
const newBoard = [...board];
|
|
60
|
-
newBoard[index] = playerSymbol;
|
|
61
|
-
|
|
62
|
-
const newMoves = moves + 1;
|
|
63
|
-
const newWinner = checkWinner(newBoard);
|
|
64
|
-
const newIsDraw = !newWinner && newBoard.every(cell => cell !== '');
|
|
65
|
-
const newGameOver = newWinner || newIsDraw;
|
|
66
|
-
const nextTurn = playerSymbol === 'X' ? 'O' : 'X';
|
|
67
|
-
|
|
68
|
-
const newGameState = {
|
|
69
|
-
...gameState,
|
|
70
|
-
board: newBoard,
|
|
71
|
-
turn: newGameOver ? playerSymbol : nextTurn,
|
|
72
|
-
moves: newMoves,
|
|
73
|
-
winner: newWinner,
|
|
74
|
-
gameOver: newGameOver,
|
|
75
|
-
isDraw: newIsDraw,
|
|
76
|
-
lastMove: { position, symbol: playerSymbol }
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
return { success: true, gameState: newGameState };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Minimax algorithm for perfect AI play (hard difficulty)
|
|
83
|
-
function minimax(board, depth, isMaximizing, aiSymbol, playerSymbol, alpha = -Infinity, beta = Infinity) {
|
|
84
|
-
const winner = checkWinner(board);
|
|
85
|
-
|
|
86
|
-
// Base cases
|
|
87
|
-
if (winner === aiSymbol) return 10 - depth;
|
|
88
|
-
if (winner === playerSymbol) return depth - 10;
|
|
89
|
-
if (board.every(cell => cell !== '')) return 0; // Draw
|
|
90
|
-
|
|
91
|
-
if (isMaximizing) {
|
|
92
|
-
let maxEval = -Infinity;
|
|
93
|
-
for (let i = 0; i < 9; i++) {
|
|
94
|
-
if (board[i] === '') {
|
|
95
|
-
board[i] = aiSymbol;
|
|
96
|
-
const eval = minimax(board, depth + 1, false, aiSymbol, playerSymbol, alpha, beta);
|
|
97
|
-
board[i] = '';
|
|
98
|
-
maxEval = Math.max(maxEval, eval);
|
|
99
|
-
alpha = Math.max(alpha, eval);
|
|
100
|
-
if (beta <= alpha) break; // Alpha-beta pruning
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return maxEval;
|
|
104
|
-
} else {
|
|
105
|
-
let minEval = Infinity;
|
|
106
|
-
for (let i = 0; i < 9; i++) {
|
|
107
|
-
if (board[i] === '') {
|
|
108
|
-
board[i] = playerSymbol;
|
|
109
|
-
const eval = minimax(board, depth + 1, true, aiSymbol, playerSymbol, alpha, beta);
|
|
110
|
-
board[i] = '';
|
|
111
|
-
minEval = Math.min(minEval, eval);
|
|
112
|
-
beta = Math.min(beta, eval);
|
|
113
|
-
if (beta <= alpha) break; // Alpha-beta pruning
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return minEval;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Get best move using minimax (for hard difficulty)
|
|
121
|
-
function getBestMove(board, aiSymbol, playerSymbol) {
|
|
122
|
-
let bestMove = -1;
|
|
123
|
-
let bestValue = -Infinity;
|
|
124
|
-
|
|
125
|
-
for (let i = 0; i < 9; i++) {
|
|
126
|
-
if (board[i] === '') {
|
|
127
|
-
board[i] = aiSymbol;
|
|
128
|
-
const moveValue = minimax(board, 0, false, aiSymbol, playerSymbol);
|
|
129
|
-
board[i] = '';
|
|
130
|
-
|
|
131
|
-
if (moveValue > bestValue) {
|
|
132
|
-
bestMove = i + 1; // Convert to 1-based index
|
|
133
|
-
bestValue = moveValue;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return bestMove;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// AI Strategy with difficulty levels
|
|
142
|
-
function getAIMove(gameState, difficulty = 'medium') {
|
|
143
|
-
const { board, aiSymbol, playerSymbol } = gameState;
|
|
144
|
-
const availablePositions = getAvailablePositions(board);
|
|
145
|
-
|
|
146
|
-
if (availablePositions.length === 0) {
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Override difficulty if specified in gameState
|
|
151
|
-
if (gameState.difficulty) {
|
|
152
|
-
difficulty = gameState.difficulty;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
switch (difficulty) {
|
|
156
|
-
case 'easy':
|
|
157
|
-
return getEasyAIMove(board, availablePositions, aiSymbol, playerSymbol);
|
|
158
|
-
case 'medium':
|
|
159
|
-
return getMediumAIMove(board, availablePositions, aiSymbol, playerSymbol);
|
|
160
|
-
case 'hard':
|
|
161
|
-
return getHardAIMove(board, aiSymbol, playerSymbol);
|
|
162
|
-
default:
|
|
163
|
-
return getMediumAIMove(board, availablePositions, aiSymbol, playerSymbol);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Easy AI: Mostly random with occasional smart moves
|
|
168
|
-
function getEasyAIMove(board, availablePositions, aiSymbol, playerSymbol) {
|
|
169
|
-
// 70% chance to play randomly
|
|
170
|
-
if (Math.random() < 0.7) {
|
|
171
|
-
const randomIndex = Math.floor(Math.random() * availablePositions.length);
|
|
172
|
-
return availablePositions[randomIndex];
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// 30% chance to make a smart move
|
|
176
|
-
// 1. Try to win (15% chance)
|
|
177
|
-
if (Math.random() < 0.5) {
|
|
178
|
-
for (const pos of availablePositions) {
|
|
179
|
-
const testBoard = [...board];
|
|
180
|
-
testBoard[pos - 1] = aiSymbol;
|
|
181
|
-
if (checkWinner(testBoard) === aiSymbol) {
|
|
182
|
-
return pos;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// 2. Sometimes block player (15% chance)
|
|
188
|
-
if (Math.random() < 0.5) {
|
|
189
|
-
for (const pos of availablePositions) {
|
|
190
|
-
const testBoard = [...board];
|
|
191
|
-
testBoard[pos - 1] = playerSymbol;
|
|
192
|
-
if (checkWinner(testBoard) === playerSymbol) {
|
|
193
|
-
return pos;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Fallback to random
|
|
199
|
-
const randomIndex = Math.floor(Math.random() * availablePositions.length);
|
|
200
|
-
return availablePositions[randomIndex];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Medium AI: Balanced strategy with some randomness
|
|
204
|
-
function getMediumAIMove(board, availablePositions, aiSymbol, playerSymbol) {
|
|
205
|
-
// 1. Always try to win first
|
|
206
|
-
for (const pos of availablePositions) {
|
|
207
|
-
const testBoard = [...board];
|
|
208
|
-
testBoard[pos - 1] = aiSymbol;
|
|
209
|
-
if (checkWinner(testBoard) === aiSymbol) {
|
|
210
|
-
return pos;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// 2. Always block player from winning
|
|
215
|
-
for (const pos of availablePositions) {
|
|
216
|
-
const testBoard = [...board];
|
|
217
|
-
testBoard[pos - 1] = playerSymbol;
|
|
218
|
-
if (checkWinner(testBoard) === playerSymbol) {
|
|
219
|
-
return pos;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// 3. Add some randomness to avoid being too predictable (30% chance)
|
|
224
|
-
if (Math.random() < 0.3) {
|
|
225
|
-
const randomIndex = Math.floor(Math.random() * availablePositions.length);
|
|
226
|
-
return availablePositions[randomIndex];
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// 4. Take center if available
|
|
230
|
-
if (availablePositions.includes(5)) {
|
|
231
|
-
return 5;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// 5. Take corners
|
|
235
|
-
const corners = [1, 3, 7, 9];
|
|
236
|
-
const availableCorners = corners.filter(c => availablePositions.includes(c));
|
|
237
|
-
if (availableCorners.length > 0) {
|
|
238
|
-
const randomCorner = availableCorners[Math.floor(Math.random() * availableCorners.length)];
|
|
239
|
-
return randomCorner;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// 6. Take any remaining position
|
|
243
|
-
const randomIndex = Math.floor(Math.random() * availablePositions.length);
|
|
244
|
-
return availablePositions[randomIndex];
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Hard AI: Perfect play using minimax algorithm
|
|
248
|
-
function getHardAIMove(board, aiSymbol, playerSymbol) {
|
|
249
|
-
return getBestMove(board, aiSymbol, playerSymbol);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Make AI move
|
|
253
|
-
function makeAIMove(gameState, difficulty = null) {
|
|
254
|
-
if (gameState.gameOver || gameState.turn !== gameState.aiSymbol) {
|
|
255
|
-
return { error: 'Not AI turn or game is over' };
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const aiPosition = getAIMove(gameState, difficulty);
|
|
259
|
-
if (!aiPosition) {
|
|
260
|
-
return { error: 'No moves available' };
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return makeMove(gameState, aiPosition, gameState.aiSymbol);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Format tic-tac-toe board for display
|
|
267
|
-
function formatTicTacToeDisplay(gameState) {
|
|
268
|
-
const { board, moves, winner, isDraw, turn, lastMove, playerSymbol, aiSymbol, difficulty } = gameState;
|
|
269
|
-
|
|
270
|
-
const difficultyEmoji = {
|
|
271
|
-
'easy': '😊',
|
|
272
|
-
'medium': '🤔',
|
|
273
|
-
'hard': '🧠'
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
const difficultyText = difficulty ? `${difficultyEmoji[difficulty] || '🤔'} ${difficulty.toUpperCase()}` : 'MEDIUM 🤔';
|
|
277
|
-
|
|
278
|
-
let display = `🎯 **Tic-Tac-Toe vs AI** (${difficultyText}) - ${moves} moves\n\n`;
|
|
279
|
-
|
|
280
|
-
// Create 3x3 grid display
|
|
281
|
-
const symbols = board.map((cell, i) => cell || (i + 1).toString());
|
|
282
|
-
|
|
283
|
-
display += '```\n';
|
|
284
|
-
for (let row = 0; row < 3; row++) {
|
|
285
|
-
const line = [];
|
|
286
|
-
for (let col = 0; col < 3; col++) {
|
|
287
|
-
const index = row * 3 + col;
|
|
288
|
-
line.push(symbols[index]);
|
|
289
|
-
}
|
|
290
|
-
display += line.join(' │ ') + '\n';
|
|
291
|
-
if (row < 2) display += '──┼───┼──\n';
|
|
292
|
-
}
|
|
293
|
-
display += '```\n\n';
|
|
294
|
-
|
|
295
|
-
if (winner) {
|
|
296
|
-
if (winner === playerSymbol) {
|
|
297
|
-
display += `🎉 **You won!** Great job!`;
|
|
298
|
-
} else {
|
|
299
|
-
display += `🤖 **AI wins!** Better luck next time!`;
|
|
300
|
-
}
|
|
301
|
-
} else if (isDraw) {
|
|
302
|
-
display += `🤝 **Draw!** Well played!`;
|
|
303
|
-
} else {
|
|
304
|
-
if (turn === playerSymbol) {
|
|
305
|
-
display += `Your turn! Choose position 1-9`;
|
|
306
|
-
} else {
|
|
307
|
-
display += `AI is thinking... 🤔`;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (lastMove) {
|
|
311
|
-
const mover = lastMove.symbol === playerSymbol ? 'You' : 'AI';
|
|
312
|
-
display += `\nLast move: ${mover} played ${lastMove.symbol} at position ${lastMove.position}`;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return display;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Get available positions
|
|
320
|
-
function getAvailablePositions(board) {
|
|
321
|
-
return board
|
|
322
|
-
.map((cell, index) => cell === '' ? index + 1 : null)
|
|
323
|
-
.filter(pos => pos !== null);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Get difficulty description for users
|
|
327
|
-
function getDifficultyDescription(difficulty) {
|
|
328
|
-
const descriptions = {
|
|
329
|
-
'easy': '😊 **EASY**: AI plays mostly randomly, great for beginners!',
|
|
330
|
-
'medium': '🤔 **MEDIUM**: AI uses basic strategy but makes some mistakes.',
|
|
331
|
-
'hard': '🧠 **HARD**: AI plays perfectly, can you beat it?'
|
|
332
|
-
};
|
|
333
|
-
return descriptions[difficulty] || descriptions['medium'];
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
module.exports = {
|
|
337
|
-
createInitialTicTacToeState,
|
|
338
|
-
makeMove,
|
|
339
|
-
makeAIMove,
|
|
340
|
-
formatTicTacToeDisplay,
|
|
341
|
-
checkWinner,
|
|
342
|
-
getAvailablePositions,
|
|
343
|
-
getAIMove,
|
|
344
|
-
getDifficultyDescription
|
|
345
|
-
};
|