slashvibe-mcp 0.3.21 → 0.3.23
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/auto-update.js +10 -15
- package/config.js +36 -31
- package/crypto.js +1 -6
- package/debug.js +12 -0
- package/discord.js +19 -19
- package/eslint.config.js +54 -0
- 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/migrate-v2.js +72 -0
- package/notification-emitter.js +2 -2
- package/notify.js +39 -14
- package/package.json +28 -29
- package/post-install.js +141 -0
- 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/test-skills-bootstrap.js +20 -0
- package/test-v2-integration.js +385 -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/webhook-runner.js +132 -0
- package/auth-store.js +0 -148
- 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/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/drawing.js
DELETED
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Collaborative Drawing game implementation for /vibe
|
|
3
|
-
* A shared canvas where multiple users can draw together in real-time
|
|
4
|
-
* Create art, doodles, or play drawing games like Pictionary!
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Drawing canvas dimensions (character-based art)
|
|
8
|
-
const CANVAS_WIDTH = 20;
|
|
9
|
-
const CANVAS_HEIGHT = 12;
|
|
10
|
-
|
|
11
|
-
// Drawing tools and colors (using Unicode characters)
|
|
12
|
-
const DRAWING_CHARS = {
|
|
13
|
-
empty: '⬜', // Empty space
|
|
14
|
-
dot: '⚫', // Small dot
|
|
15
|
-
circle: '⚪', // Circle
|
|
16
|
-
square: '⬛', // Filled square
|
|
17
|
-
star: '⭐', // Star
|
|
18
|
-
heart: '❤️', // Heart
|
|
19
|
-
tree: '🌲', // Tree
|
|
20
|
-
house: '🏠', // House
|
|
21
|
-
sun: '☀️', // Sun
|
|
22
|
-
moon: '🌙', // Moon
|
|
23
|
-
water: '🌊', // Wave
|
|
24
|
-
mountain: '⛰️', // Mountain
|
|
25
|
-
person: '🧍', // Person
|
|
26
|
-
cat: '🐱', // Cat
|
|
27
|
-
dog: '🐕', // Dog
|
|
28
|
-
car: '🚗', // Car
|
|
29
|
-
plane: '✈️', // Plane
|
|
30
|
-
flower: '🌸', // Flower
|
|
31
|
-
umbrella: '☂️', // Umbrella
|
|
32
|
-
rainbow: '🌈' // Rainbow
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// Create initial drawing game state
|
|
36
|
-
function createInitialDrawingState() {
|
|
37
|
-
// Initialize empty canvas
|
|
38
|
-
const canvas = Array(CANVAS_HEIGHT).fill(null).map(() =>
|
|
39
|
-
Array(CANVAS_WIDTH).fill(DRAWING_CHARS.empty)
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
canvas: canvas,
|
|
44
|
-
players: [],
|
|
45
|
-
moves: [],
|
|
46
|
-
maxPlayers: 8,
|
|
47
|
-
gameOver: false,
|
|
48
|
-
createdAt: new Date().toISOString(),
|
|
49
|
-
lastActivity: new Date().toISOString(),
|
|
50
|
-
theme: null, // Optional drawing theme/prompt
|
|
51
|
-
mode: 'freeform' // 'freeform', 'pictionary', 'collaborative'
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Add player to drawing session
|
|
56
|
-
function addPlayer(gameState, playerHandle) {
|
|
57
|
-
if (gameState.players.includes(playerHandle)) {
|
|
58
|
-
return { error: 'Player already in the drawing session' };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (gameState.players.length >= gameState.maxPlayers) {
|
|
62
|
-
return { error: `Drawing session is full (max ${gameState.maxPlayers} players)` };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const newPlayers = [...gameState.players, playerHandle];
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
success: true,
|
|
69
|
-
gameState: {
|
|
70
|
-
...gameState,
|
|
71
|
-
players: newPlayers,
|
|
72
|
-
lastActivity: new Date().toISOString()
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Make a drawing move (place character at position)
|
|
78
|
-
function makeMove(gameState, x, y, char, playerHandle) {
|
|
79
|
-
const { canvas, players, moves } = gameState;
|
|
80
|
-
|
|
81
|
-
// Validate player
|
|
82
|
-
if (!players.includes(playerHandle)) {
|
|
83
|
-
return { error: 'You need to join the drawing session first!' };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Validate position
|
|
87
|
-
if (x < 0 || x >= CANVAS_WIDTH || y < 0 || y >= CANVAS_HEIGHT) {
|
|
88
|
-
return { error: `Position out of bounds. Canvas is ${CANVAS_WIDTH}x${CANVAS_HEIGHT}` };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Validate character
|
|
92
|
-
const validChars = Object.values(DRAWING_CHARS);
|
|
93
|
-
if (!validChars.includes(char)) {
|
|
94
|
-
return { error: `Invalid character. Use one of: ${Object.keys(DRAWING_CHARS).join(', ')}` };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Update canvas
|
|
98
|
-
const newCanvas = canvas.map(row => [...row]);
|
|
99
|
-
newCanvas[y][x] = char;
|
|
100
|
-
|
|
101
|
-
// Record the move
|
|
102
|
-
const move = {
|
|
103
|
-
x, y, char,
|
|
104
|
-
player: playerHandle,
|
|
105
|
-
timestamp: new Date().toISOString(),
|
|
106
|
-
moveNumber: moves.length + 1
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const newMoves = [...moves, move];
|
|
110
|
-
|
|
111
|
-
const newGameState = {
|
|
112
|
-
...gameState,
|
|
113
|
-
canvas: newCanvas,
|
|
114
|
-
moves: newMoves,
|
|
115
|
-
lastActivity: new Date().toISOString()
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
return { success: true, gameState: newGameState };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Draw a line between two points (simple Bresenham-like algorithm)
|
|
122
|
-
function drawLine(gameState, x0, y0, x1, y1, char, playerHandle) {
|
|
123
|
-
const moves = [];
|
|
124
|
-
|
|
125
|
-
// Simple line drawing - just plot points between start and end
|
|
126
|
-
const dx = Math.abs(x1 - x0);
|
|
127
|
-
const dy = Math.abs(y1 - y0);
|
|
128
|
-
const steps = Math.max(dx, dy);
|
|
129
|
-
|
|
130
|
-
if (steps === 0) {
|
|
131
|
-
// Single point
|
|
132
|
-
const result = makeMove(gameState, x0, y0, char, playerHandle);
|
|
133
|
-
return result;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const xInc = (x1 - x0) / steps;
|
|
137
|
-
const yInc = (y1 - y0) / steps;
|
|
138
|
-
|
|
139
|
-
let currentGameState = gameState;
|
|
140
|
-
|
|
141
|
-
for (let i = 0; i <= steps; i++) {
|
|
142
|
-
const x = Math.round(x0 + i * xInc);
|
|
143
|
-
const y = Math.round(y0 + i * yInc);
|
|
144
|
-
|
|
145
|
-
const result = makeMove(currentGameState, x, y, char, playerHandle);
|
|
146
|
-
if (result.error) {
|
|
147
|
-
// Stop on first error but return what we accomplished
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
currentGameState = result.gameState;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return { success: true, gameState: currentGameState };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Clear a region of the canvas
|
|
157
|
-
function clearRegion(gameState, x0, y0, x1, y1, playerHandle) {
|
|
158
|
-
const { players } = gameState;
|
|
159
|
-
|
|
160
|
-
if (!players.includes(playerHandle)) {
|
|
161
|
-
return { error: 'You need to join the drawing session first!' };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Ensure coordinates are in bounds and properly ordered
|
|
165
|
-
const minX = Math.max(0, Math.min(x0, x1));
|
|
166
|
-
const maxX = Math.min(CANVAS_WIDTH - 1, Math.max(x0, x1));
|
|
167
|
-
const minY = Math.max(0, Math.min(y0, y1));
|
|
168
|
-
const maxY = Math.min(CANVAS_HEIGHT - 1, Math.max(y0, y1));
|
|
169
|
-
|
|
170
|
-
const newCanvas = gameState.canvas.map(row => [...row]);
|
|
171
|
-
|
|
172
|
-
for (let y = minY; y <= maxY; y++) {
|
|
173
|
-
for (let x = minX; x <= maxX; x++) {
|
|
174
|
-
newCanvas[y][x] = DRAWING_CHARS.empty;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const clearMove = {
|
|
179
|
-
action: 'clear',
|
|
180
|
-
x0: minX, y0: minY, x1: maxX, y1: maxY,
|
|
181
|
-
player: playerHandle,
|
|
182
|
-
timestamp: new Date().toISOString(),
|
|
183
|
-
moveNumber: gameState.moves.length + 1
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
success: true,
|
|
188
|
-
gameState: {
|
|
189
|
-
...gameState,
|
|
190
|
-
canvas: newCanvas,
|
|
191
|
-
moves: [...gameState.moves, clearMove],
|
|
192
|
-
lastActivity: new Date().toISOString()
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Format drawing canvas for display
|
|
198
|
-
function formatDrawingDisplay(gameState) {
|
|
199
|
-
const { canvas, players, moves, theme, mode } = gameState;
|
|
200
|
-
|
|
201
|
-
let display = `🎨 **Collaborative Drawing** (${players.length} artist${players.length !== 1 ? 's' : ''})\n\n`;
|
|
202
|
-
|
|
203
|
-
// Show theme if set
|
|
204
|
-
if (theme) {
|
|
205
|
-
display += `🎯 **Theme:** ${theme}\n\n`;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Show canvas with coordinate labels
|
|
209
|
-
display += '```\n';
|
|
210
|
-
|
|
211
|
-
// Top coordinate labels
|
|
212
|
-
let topLabels = ' ';
|
|
213
|
-
for (let x = 0; x < CANVAS_WIDTH; x++) {
|
|
214
|
-
if (x < 10) {
|
|
215
|
-
topLabels += x + ' ';
|
|
216
|
-
} else {
|
|
217
|
-
topLabels += String.fromCharCode(55 + x); // A, B, C... for 10+
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
display += topLabels + '\n';
|
|
221
|
-
|
|
222
|
-
// Canvas rows with left coordinate labels
|
|
223
|
-
for (let y = 0; y < CANVAS_HEIGHT; y++) {
|
|
224
|
-
let row = (y < 10 ? ' ' + y : String.fromCharCode(55 + y)) + ' ';
|
|
225
|
-
row += canvas[y].join('');
|
|
226
|
-
display += row + '\n';
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
display += '```\n\n';
|
|
230
|
-
|
|
231
|
-
// Show players
|
|
232
|
-
if (players.length > 0) {
|
|
233
|
-
display += `**Artists:** ${players.map(p => `@${p}`).join(', ')}\n\n`;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Show recent activity
|
|
237
|
-
if (moves.length > 0) {
|
|
238
|
-
const recentMoves = moves.slice(-3); // Last 3 moves
|
|
239
|
-
display += '**Recent activity:**\n';
|
|
240
|
-
for (const move of recentMoves) {
|
|
241
|
-
if (move.action === 'clear') {
|
|
242
|
-
display += `• @${move.player} cleared region (${move.x0},${move.y0}) to (${move.x1},${move.y1})\n`;
|
|
243
|
-
} else {
|
|
244
|
-
display += `• @${move.player} drew ${move.char} at (${move.x},${move.y})\n`;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
display += '\n';
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Show available characters
|
|
251
|
-
display += '**Available characters:**\n';
|
|
252
|
-
const charEntries = Object.entries(DRAWING_CHARS);
|
|
253
|
-
const charGroups = [];
|
|
254
|
-
for (let i = 0; i < charEntries.length; i += 5) {
|
|
255
|
-
const group = charEntries.slice(i, i + 5);
|
|
256
|
-
charGroups.push(group.map(([name, char]) => `${char} (${name})`).join(' '));
|
|
257
|
-
}
|
|
258
|
-
display += charGroups.join('\n') + '\n\n';
|
|
259
|
-
|
|
260
|
-
return display;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Set drawing theme/prompt
|
|
264
|
-
function setTheme(gameState, theme, playerHandle) {
|
|
265
|
-
const { players } = gameState;
|
|
266
|
-
|
|
267
|
-
if (!players.includes(playerHandle)) {
|
|
268
|
-
return { error: 'You need to join the drawing session first!' };
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return {
|
|
272
|
-
success: true,
|
|
273
|
-
gameState: {
|
|
274
|
-
...gameState,
|
|
275
|
-
theme: theme,
|
|
276
|
-
lastActivity: new Date().toISOString()
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Generate drawing tips based on theme
|
|
282
|
-
function getDrawingTips(theme) {
|
|
283
|
-
const tips = {
|
|
284
|
-
'house': ['Start with a ⬛ base', 'Add a roof with ⛰️', 'Use 🏠 for details'],
|
|
285
|
-
'landscape': ['Use 🌲 for trees', '☀️ for sun', '🌊 for water', '⛰️ for mountains'],
|
|
286
|
-
'portrait': ['Use 🧍 for people', '⚪⚫ for eyes', '❤️ for heart'],
|
|
287
|
-
'animals': ['Try 🐱🐕 for pets', '🌲 for habitat', '⭐ for magical touches'],
|
|
288
|
-
'vehicle': ['🚗 for cars', '✈️ for planes', '⬛ for roads'],
|
|
289
|
-
'nature': ['🌸 for flowers', '🌲 for trees', '☀️🌙 for sky', '🌈 for color']
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const defaultTips = ['Use ⬛⬜ for shapes', 'Add ⭐❤️ for details', 'Try 🌸🌲 for nature'];
|
|
293
|
-
|
|
294
|
-
return tips[theme?.toLowerCase()] || defaultTips;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Get canvas statistics
|
|
298
|
-
function getCanvasStats(gameState) {
|
|
299
|
-
const { canvas, moves, players } = gameState;
|
|
300
|
-
|
|
301
|
-
// Count characters used
|
|
302
|
-
const charCount = {};
|
|
303
|
-
for (const row of canvas) {
|
|
304
|
-
for (const char of row) {
|
|
305
|
-
if (char !== DRAWING_CHARS.empty) {
|
|
306
|
-
charCount[char] = (charCount[char] || 0) + 1;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Count moves per player
|
|
312
|
-
const playerMoves = {};
|
|
313
|
-
for (const move of moves) {
|
|
314
|
-
if (move.player) {
|
|
315
|
-
playerMoves[move.player] = (playerMoves[move.player] || 0) + 1;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const totalDrawnCells = Object.values(charCount).reduce((a, b) => a + b, 0);
|
|
320
|
-
const totalCells = CANVAS_WIDTH * CANVAS_HEIGHT;
|
|
321
|
-
const fillPercentage = Math.round((totalDrawnCells / totalCells) * 100);
|
|
322
|
-
|
|
323
|
-
return {
|
|
324
|
-
totalMoves: moves.length,
|
|
325
|
-
totalDrawnCells,
|
|
326
|
-
fillPercentage,
|
|
327
|
-
uniqueCharsUsed: Object.keys(charCount).length,
|
|
328
|
-
charCount,
|
|
329
|
-
playerMoves,
|
|
330
|
-
mostUsedChar: Object.entries(charCount).sort(([,a], [,b]) => b - a)[0]
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
module.exports = {
|
|
335
|
-
createInitialDrawingState,
|
|
336
|
-
addPlayer,
|
|
337
|
-
makeMove,
|
|
338
|
-
drawLine,
|
|
339
|
-
clearRegion,
|
|
340
|
-
formatDrawingDisplay,
|
|
341
|
-
setTheme,
|
|
342
|
-
getDrawingTips,
|
|
343
|
-
getCanvasStats,
|
|
344
|
-
DRAWING_CHARS,
|
|
345
|
-
CANVAS_WIDTH,
|
|
346
|
-
CANVAS_HEIGHT
|
|
347
|
-
};
|
package/games/gameroulette.js
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Game Roulette - Discover random games from the /vibe workshop
|
|
3
|
-
* Perfect for when you want to play something but don't know what!
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const arcade = require('./arcade');
|
|
7
|
-
|
|
8
|
-
// Game difficulty weights for smart recommendations
|
|
9
|
-
const DIFFICULTY_WEIGHTS = {
|
|
10
|
-
'Easy': 0.4,
|
|
11
|
-
'Medium': 0.4,
|
|
12
|
-
'Hard': 0.2
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// Player count weights for recommendations
|
|
16
|
-
const PLAYER_WEIGHTS = {
|
|
17
|
-
'Solo': 0.3,
|
|
18
|
-
'1v1': 0.4,
|
|
19
|
-
'1v2': 0.1,
|
|
20
|
-
'Multiplayer': 0.2
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// Create initial game roulette state
|
|
24
|
-
function createInitialGameRouletteState() {
|
|
25
|
-
return {
|
|
26
|
-
lastRecommendation: null,
|
|
27
|
-
userPreferences: {},
|
|
28
|
-
playHistory: [],
|
|
29
|
-
sessionStarted: new Date().toISOString(),
|
|
30
|
-
totalRecommendations: 0
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Get a random game recommendation
|
|
35
|
-
function getRandomGame(gameState, userHandle = null, preferences = {}) {
|
|
36
|
-
const { GAMES } = arcade;
|
|
37
|
-
const gameIds = Object.keys(GAMES);
|
|
38
|
-
|
|
39
|
-
// Apply preferences if provided
|
|
40
|
-
let filteredGames = gameIds;
|
|
41
|
-
|
|
42
|
-
if (preferences.difficulty) {
|
|
43
|
-
filteredGames = filteredGames.filter(id =>
|
|
44
|
-
GAMES[id].difficulty.toLowerCase() === preferences.difficulty.toLowerCase()
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (preferences.category) {
|
|
49
|
-
filteredGames = filteredGames.filter(id =>
|
|
50
|
-
GAMES[id].category === preferences.category
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (preferences.players) {
|
|
55
|
-
filteredGames = filteredGames.filter(id =>
|
|
56
|
-
GAMES[id].players === preferences.players
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Avoid recommending the same game twice in a row
|
|
61
|
-
if (gameState.lastRecommendation && filteredGames.length > 1) {
|
|
62
|
-
filteredGames = filteredGames.filter(id => id !== gameState.lastRecommendation.id);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (filteredGames.length === 0) {
|
|
66
|
-
return { error: 'No games match your preferences!' };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Pick random game
|
|
70
|
-
const randomId = filteredGames[Math.floor(Math.random() * filteredGames.length)];
|
|
71
|
-
const game = { id: randomId, ...GAMES[randomId] };
|
|
72
|
-
|
|
73
|
-
// Update game state
|
|
74
|
-
const newGameState = {
|
|
75
|
-
...gameState,
|
|
76
|
-
lastRecommendation: game,
|
|
77
|
-
totalRecommendations: gameState.totalRecommendations + 1,
|
|
78
|
-
playHistory: [...gameState.playHistory.slice(-9), game] // Keep last 10
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
if (userHandle) {
|
|
82
|
-
// Track user preferences
|
|
83
|
-
newGameState.userPreferences[userHandle] = {
|
|
84
|
-
...gameState.userPreferences[userHandle],
|
|
85
|
-
lastSeen: new Date().toISOString(),
|
|
86
|
-
totalRequests: (gameState.userPreferences[userHandle]?.totalRequests || 0) + 1
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return { success: true, gameState: newGameState, recommendation: game };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Get smart recommendation based on user history
|
|
94
|
-
function getSmartRecommendation(gameState, userHandle, mood = null) {
|
|
95
|
-
const { GAMES, CATEGORIES } = arcade;
|
|
96
|
-
|
|
97
|
-
// Mood-based filtering
|
|
98
|
-
const moodCategories = {
|
|
99
|
-
'chill': ['word', 'puzzle'],
|
|
100
|
-
'competitive': ['classic', 'action'],
|
|
101
|
-
'social': ['social', 'creative'],
|
|
102
|
-
'quick': ['classic', 'action'],
|
|
103
|
-
'thoughtful': ['puzzle', 'word'],
|
|
104
|
-
'creative': ['creative', 'social']
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const preferences = {};
|
|
108
|
-
if (mood && moodCategories[mood]) {
|
|
109
|
-
preferences.categoryList = moodCategories[mood];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return getRecommendationWithFilters(gameState, userHandle, preferences);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Get recommendation with advanced filters
|
|
116
|
-
function getRecommendationWithFilters(gameState, userHandle, filters = {}) {
|
|
117
|
-
const { GAMES } = arcade;
|
|
118
|
-
let candidateGames = Object.keys(GAMES);
|
|
119
|
-
|
|
120
|
-
// Apply category list filter
|
|
121
|
-
if (filters.categoryList) {
|
|
122
|
-
candidateGames = candidateGames.filter(id =>
|
|
123
|
-
filters.categoryList.includes(GAMES[id].category)
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Apply other filters
|
|
128
|
-
if (filters.difficulty) {
|
|
129
|
-
candidateGames = candidateGames.filter(id =>
|
|
130
|
-
GAMES[id].difficulty.toLowerCase() === filters.difficulty.toLowerCase()
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (filters.maxPlayers && filters.maxPlayers < 4) {
|
|
135
|
-
candidateGames = candidateGames.filter(id =>
|
|
136
|
-
GAMES[id].players === 'Solo' || GAMES[id].players === '1v1'
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Weighted random selection
|
|
141
|
-
if (candidateGames.length > 1) {
|
|
142
|
-
const weights = candidateGames.map(id => {
|
|
143
|
-
const game = GAMES[id];
|
|
144
|
-
let weight = 1.0;
|
|
145
|
-
|
|
146
|
-
// Prefer easier games slightly
|
|
147
|
-
if (game.difficulty === 'Easy') weight *= 1.2;
|
|
148
|
-
else if (game.difficulty === 'Hard') weight *= 0.8;
|
|
149
|
-
|
|
150
|
-
// Prefer variety - reduce weight if recently recommended
|
|
151
|
-
const recentHistory = gameState.playHistory.slice(-3);
|
|
152
|
-
if (recentHistory.some(h => h.id === id)) {
|
|
153
|
-
weight *= 0.3;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return weight;
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Weighted random selection
|
|
160
|
-
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
|
161
|
-
let random = Math.random() * totalWeight;
|
|
162
|
-
|
|
163
|
-
for (let i = 0; i < candidateGames.length; i++) {
|
|
164
|
-
random -= weights[i];
|
|
165
|
-
if (random <= 0) {
|
|
166
|
-
const gameId = candidateGames[i];
|
|
167
|
-
const game = { id: gameId, ...GAMES[gameId] };
|
|
168
|
-
|
|
169
|
-
const newGameState = {
|
|
170
|
-
...gameState,
|
|
171
|
-
lastRecommendation: game,
|
|
172
|
-
totalRecommendations: gameState.totalRecommendations + 1,
|
|
173
|
-
playHistory: [...gameState.playHistory.slice(-9), game]
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
return { success: true, gameState: newGameState, recommendation: game };
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Fallback to simple random
|
|
182
|
-
return getRandomGame(gameState, userHandle, filters);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Get games by current "vibe"
|
|
186
|
-
function getGamesByVibe(vibe) {
|
|
187
|
-
const { GAMES } = arcade;
|
|
188
|
-
const vibes = {
|
|
189
|
-
'competitive': ['chess', 'tictactoe', 'multiplayer-tictactoe', 'quickduel', 'rockpaperscissors'],
|
|
190
|
-
'social': ['storybuilder', 'drawing', 'twotruths', 'werewolf', 'wordassociation'],
|
|
191
|
-
'solo': ['snake', 'memory', 'hangman', 'riddle', 'guessnumber', 'colorguess'],
|
|
192
|
-
'quick': ['rockpaperscissors', 'guessnumber', 'colorguess', 'quickduel'],
|
|
193
|
-
'creative': ['drawing', 'storybuilder'],
|
|
194
|
-
'thinking': ['chess', 'riddle', 'twentyquestions', 'hangman'],
|
|
195
|
-
'party': ['werewolf', 'twotruths', 'wordassociation', 'drawing']
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const gameIds = vibes[vibe.toLowerCase()] || [];
|
|
199
|
-
return gameIds.map(id => ({ id, ...GAMES[id] })).filter(g => g.name);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Format roulette display
|
|
203
|
-
function formatRouletteDisplay(gameState, recommendation, userHandle = null) {
|
|
204
|
-
if (!recommendation) {
|
|
205
|
-
return 'Game Roulette ready! Spin the wheel to discover your next game!';
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const { icon, name, description, category, players, difficulty } = recommendation;
|
|
209
|
-
|
|
210
|
-
let display = `🎲 **GAME ROULETTE** 🎲\n\n`;
|
|
211
|
-
display += `${icon} **${name}**\n\n`;
|
|
212
|
-
display += `**Description:** ${description}\n`;
|
|
213
|
-
display += `**Category:** ${category.charAt(0).toUpperCase() + category.slice(1)}\n`;
|
|
214
|
-
display += `**Players:** ${players}\n`;
|
|
215
|
-
display += `**Difficulty:** ${difficulty}\n\n`;
|
|
216
|
-
|
|
217
|
-
// Add contextual launch instructions
|
|
218
|
-
if (['tictactoe', 'chess'].includes(recommendation.id)) {
|
|
219
|
-
display += `**How to play:** \`vibe game @username\` to challenge someone!\n`;
|
|
220
|
-
} else if (recommendation.id === 'drawing') {
|
|
221
|
-
display += `**How to play:** Join the collaborative canvas and start drawing!\n`;
|
|
222
|
-
} else if (recommendation.id === 'arcade') {
|
|
223
|
-
display += `**How to play:** Browse all games in the Workshop Arcade!\n`;
|
|
224
|
-
} else {
|
|
225
|
-
display += `**How to play:** Launch ${name} and dive in!\n`;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
display += `\n🎯 **Feeling lucky?** Spin again for another recommendation!`;
|
|
229
|
-
|
|
230
|
-
if (gameState.totalRecommendations > 1) {
|
|
231
|
-
display += `\n\n*This is recommendation #${gameState.totalRecommendations} in your session*`;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return display;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Get roulette statistics
|
|
238
|
-
function getRouletteStats(gameState) {
|
|
239
|
-
const { playHistory, totalRecommendations } = gameState;
|
|
240
|
-
|
|
241
|
-
if (playHistory.length === 0) {
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Category distribution
|
|
246
|
-
const categoryCount = {};
|
|
247
|
-
const difficultyCount = {};
|
|
248
|
-
const playerCount = {};
|
|
249
|
-
|
|
250
|
-
for (const game of playHistory) {
|
|
251
|
-
categoryCount[game.category] = (categoryCount[game.category] || 0) + 1;
|
|
252
|
-
difficultyCount[game.difficulty] = (difficultyCount[game.difficulty] || 0) + 1;
|
|
253
|
-
playerCount[game.players] = (playerCount[game.players] || 0) + 1;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const mostRecommendedCategory = Object.entries(categoryCount)
|
|
257
|
-
.sort(([,a], [,b]) => b - a)[0];
|
|
258
|
-
|
|
259
|
-
const mostRecommendedDifficulty = Object.entries(difficultyCount)
|
|
260
|
-
.sort(([,a], [,b]) => b - a)[0];
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
totalRecommendations,
|
|
264
|
-
sessionGames: playHistory.length,
|
|
265
|
-
favoriteCategory: mostRecommendedCategory ? mostRecommendedCategory[0] : null,
|
|
266
|
-
favoriteDifficulty: mostRecommendedDifficulty ? mostRecommendedDifficulty[0] : null,
|
|
267
|
-
categoryDistribution: categoryCount,
|
|
268
|
-
difficultyDistribution: difficultyCount,
|
|
269
|
-
playerDistribution: playerCount
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Generate fun recommendation messages
|
|
274
|
-
function getRandomRouletteMessage() {
|
|
275
|
-
const messages = [
|
|
276
|
-
"🎲 The roulette wheel is spinning...",
|
|
277
|
-
"🎯 Searching for your perfect game match...",
|
|
278
|
-
"🎰 Rolling the dice of destiny...",
|
|
279
|
-
"🔮 Consulting the gaming crystal ball...",
|
|
280
|
-
"🎪 Welcome to the game carnival!",
|
|
281
|
-
"🚀 Launching game discovery sequence...",
|
|
282
|
-
"🎨 Painting your gaming adventure...",
|
|
283
|
-
"⚡ Generating gaming lightning in a bottle...",
|
|
284
|
-
"🧩 Assembling your perfect game puzzle...",
|
|
285
|
-
"🌟 Aligning the gaming stars for you..."
|
|
286
|
-
];
|
|
287
|
-
|
|
288
|
-
return messages[Math.floor(Math.random() * messages.length)];
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
module.exports = {
|
|
292
|
-
createInitialGameRouletteState,
|
|
293
|
-
getRandomGame,
|
|
294
|
-
getSmartRecommendation,
|
|
295
|
-
getRecommendationWithFilters,
|
|
296
|
-
getGamesByVibe,
|
|
297
|
-
formatRouletteDisplay,
|
|
298
|
-
getRouletteStats,
|
|
299
|
-
getRandomRouletteMessage
|
|
300
|
-
};
|