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/snake.js
DELETED
|
@@ -1,406 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Snake Game implementation for /vibe
|
|
3
|
-
* Classic arcade action! Control the snake, eat food, grow longer, avoid walls and yourself.
|
|
4
|
-
* Simple controls, endless fun, and high score tracking.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Game configuration
|
|
8
|
-
const BOARD_WIDTH = 15;
|
|
9
|
-
const BOARD_HEIGHT = 12;
|
|
10
|
-
const INITIAL_SNAKE_LENGTH = 3;
|
|
11
|
-
|
|
12
|
-
// Direction constants
|
|
13
|
-
const DIRECTIONS = {
|
|
14
|
-
'UP': 'UP',
|
|
15
|
-
'DOWN': 'DOWN',
|
|
16
|
-
'LEFT': 'LEFT',
|
|
17
|
-
'RIGHT': 'RIGHT'
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// Opposite directions (to prevent instant death by going backwards)
|
|
21
|
-
const OPPOSITE_DIRECTIONS = {
|
|
22
|
-
'UP': 'DOWN',
|
|
23
|
-
'DOWN': 'UP',
|
|
24
|
-
'LEFT': 'RIGHT',
|
|
25
|
-
'RIGHT': 'LEFT'
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Visual representation
|
|
29
|
-
const SYMBOLS = {
|
|
30
|
-
empty: '⬜',
|
|
31
|
-
snake: '🟩', // Snake body
|
|
32
|
-
head: '🟢', // Snake head (brighter green)
|
|
33
|
-
food: '🍎', // Food
|
|
34
|
-
wall: '⬛' // Walls (if we add them later)
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Create initial snake game state
|
|
38
|
-
function createInitialSnakeState(playerHandle) {
|
|
39
|
-
// Start snake in the middle of the board
|
|
40
|
-
const centerX = Math.floor(BOARD_WIDTH / 2);
|
|
41
|
-
const centerY = Math.floor(BOARD_HEIGHT / 2);
|
|
42
|
-
|
|
43
|
-
const initialSnake = [];
|
|
44
|
-
for (let i = 0; i < INITIAL_SNAKE_LENGTH; i++) {
|
|
45
|
-
initialSnake.push({ x: centerX - i, y: centerY });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
player: playerHandle,
|
|
50
|
-
snake: initialSnake,
|
|
51
|
-
direction: 'RIGHT',
|
|
52
|
-
food: generateRandomFood(initialSnake),
|
|
53
|
-
score: 0,
|
|
54
|
-
gameOver: false,
|
|
55
|
-
moves: 0,
|
|
56
|
-
speed: 1, // Game speed level (1-5)
|
|
57
|
-
createdAt: new Date().toISOString(),
|
|
58
|
-
lastMove: new Date().toISOString(),
|
|
59
|
-
highScore: 0, // Track personal best
|
|
60
|
-
reason: null // Game over reason
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Generate random food position that doesn't overlap with snake
|
|
65
|
-
function generateRandomFood(snake) {
|
|
66
|
-
let attempts = 0;
|
|
67
|
-
const maxAttempts = 100;
|
|
68
|
-
|
|
69
|
-
while (attempts < maxAttempts) {
|
|
70
|
-
const x = Math.floor(Math.random() * BOARD_WIDTH);
|
|
71
|
-
const y = Math.floor(Math.random() * BOARD_HEIGHT);
|
|
72
|
-
|
|
73
|
-
// Check if this position is occupied by snake
|
|
74
|
-
const occupied = snake.some(segment => segment.x === x && segment.y === y);
|
|
75
|
-
|
|
76
|
-
if (!occupied) {
|
|
77
|
-
return { x, y };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
attempts++;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Fallback if we can't find a spot (board is almost full)
|
|
84
|
-
for (let y = 0; y < BOARD_HEIGHT; y++) {
|
|
85
|
-
for (let x = 0; x < BOARD_WIDTH; x++) {
|
|
86
|
-
const occupied = snake.some(segment => segment.x === x && segment.y === y);
|
|
87
|
-
if (!occupied) {
|
|
88
|
-
return { x, y };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// If board is completely full (shouldn't happen with our board size)
|
|
94
|
-
return { x: 0, y: 0 };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Change snake direction
|
|
98
|
-
function changeDirection(gameState, newDirection, playerHandle) {
|
|
99
|
-
const { player, direction, gameOver } = gameState;
|
|
100
|
-
|
|
101
|
-
if (playerHandle !== player) {
|
|
102
|
-
return { error: 'Not your game!' };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (gameOver) {
|
|
106
|
-
return { error: 'Game is over! Start a new game to play again.' };
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Validate direction
|
|
110
|
-
if (!DIRECTIONS[newDirection.toUpperCase()]) {
|
|
111
|
-
return { error: 'Invalid direction. Use: up, down, left, right' };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const normalizedDirection = newDirection.toUpperCase();
|
|
115
|
-
|
|
116
|
-
// Prevent going in opposite direction (instant death)
|
|
117
|
-
if (OPPOSITE_DIRECTIONS[direction] === normalizedDirection) {
|
|
118
|
-
return { error: 'Cannot reverse direction!' };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
success: true,
|
|
123
|
-
gameState: {
|
|
124
|
-
...gameState,
|
|
125
|
-
direction: normalizedDirection,
|
|
126
|
-
lastMove: new Date().toISOString()
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Move the snake one step
|
|
132
|
-
function moveSnake(gameState) {
|
|
133
|
-
const { snake, direction, food, score, moves, speed } = gameState;
|
|
134
|
-
|
|
135
|
-
if (gameState.gameOver) {
|
|
136
|
-
return { success: true, gameState }; // Already over, no change
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Calculate new head position
|
|
140
|
-
const head = snake[0];
|
|
141
|
-
let newX = head.x;
|
|
142
|
-
let newY = head.y;
|
|
143
|
-
|
|
144
|
-
switch (direction) {
|
|
145
|
-
case 'UP':
|
|
146
|
-
newY -= 1;
|
|
147
|
-
break;
|
|
148
|
-
case 'DOWN':
|
|
149
|
-
newY += 1;
|
|
150
|
-
break;
|
|
151
|
-
case 'LEFT':
|
|
152
|
-
newX -= 1;
|
|
153
|
-
break;
|
|
154
|
-
case 'RIGHT':
|
|
155
|
-
newX += 1;
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Check wall collision
|
|
160
|
-
if (newX < 0 || newX >= BOARD_WIDTH || newY < 0 || newY >= BOARD_HEIGHT) {
|
|
161
|
-
return {
|
|
162
|
-
success: true,
|
|
163
|
-
gameState: {
|
|
164
|
-
...gameState,
|
|
165
|
-
gameOver: true,
|
|
166
|
-
reason: 'Hit the wall!',
|
|
167
|
-
highScore: Math.max(gameState.highScore || 0, score)
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Check self collision
|
|
173
|
-
const selfCollision = snake.some(segment => segment.x === newX && segment.y === newY);
|
|
174
|
-
if (selfCollision) {
|
|
175
|
-
return {
|
|
176
|
-
success: true,
|
|
177
|
-
gameState: {
|
|
178
|
-
...gameState,
|
|
179
|
-
gameOver: true,
|
|
180
|
-
reason: 'Bit yourself!',
|
|
181
|
-
highScore: Math.max(gameState.highScore || 0, score)
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Create new head
|
|
187
|
-
const newHead = { x: newX, y: newY };
|
|
188
|
-
const newSnake = [newHead, ...snake];
|
|
189
|
-
|
|
190
|
-
// Check if food was eaten
|
|
191
|
-
const ateFood = newX === food.x && newY === food.y;
|
|
192
|
-
|
|
193
|
-
let finalSnake;
|
|
194
|
-
let newScore = score;
|
|
195
|
-
let newFood = food;
|
|
196
|
-
let newSpeed = speed;
|
|
197
|
-
|
|
198
|
-
if (ateFood) {
|
|
199
|
-
// Keep tail (snake grows)
|
|
200
|
-
finalSnake = newSnake;
|
|
201
|
-
newScore = score + 10;
|
|
202
|
-
newFood = generateRandomFood(newSnake);
|
|
203
|
-
|
|
204
|
-
// Increase speed every 50 points (max speed 5)
|
|
205
|
-
newSpeed = Math.min(5, Math.floor(newScore / 50) + 1);
|
|
206
|
-
} else {
|
|
207
|
-
// Remove tail (snake moves)
|
|
208
|
-
finalSnake = newSnake.slice(0, -1);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
success: true,
|
|
213
|
-
gameState: {
|
|
214
|
-
...gameState,
|
|
215
|
-
snake: finalSnake,
|
|
216
|
-
food: newFood,
|
|
217
|
-
score: newScore,
|
|
218
|
-
moves: moves + 1,
|
|
219
|
-
speed: newSpeed,
|
|
220
|
-
lastMove: new Date().toISOString(),
|
|
221
|
-
highScore: Math.max(gameState.highScore || 0, newScore)
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Auto-move the snake (for continuous play)
|
|
227
|
-
function autoMove(gameState) {
|
|
228
|
-
if (gameState.gameOver) {
|
|
229
|
-
return { success: true, gameState };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return moveSnake(gameState);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Format snake game for display
|
|
236
|
-
function formatSnakeDisplay(gameState) {
|
|
237
|
-
const { snake, food, score, gameOver, moves, speed, reason, highScore, player } = gameState;
|
|
238
|
-
|
|
239
|
-
let display = `🐍 **Snake Game** - @${player}\n\n`;
|
|
240
|
-
|
|
241
|
-
if (gameOver) {
|
|
242
|
-
display += `💀 **Game Over!** ${reason}\n`;
|
|
243
|
-
display += `**Final Score:** ${score} points\n`;
|
|
244
|
-
if (highScore > score) {
|
|
245
|
-
display += `**High Score:** ${highScore} points\n`;
|
|
246
|
-
} else {
|
|
247
|
-
display += `🎉 **New High Score!** ${score} points\n`;
|
|
248
|
-
}
|
|
249
|
-
display += '\n';
|
|
250
|
-
} else {
|
|
251
|
-
display += `**Score:** ${score} | **Speed:** ${speed} | **Length:** ${snake.length}\n`;
|
|
252
|
-
if (highScore > 0) {
|
|
253
|
-
display += `**High Score:** ${highScore} points\n`;
|
|
254
|
-
}
|
|
255
|
-
display += '\n';
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Draw the game board
|
|
259
|
-
display += '```\n';
|
|
260
|
-
|
|
261
|
-
// Create empty board
|
|
262
|
-
const board = Array(BOARD_HEIGHT).fill(null).map(() => Array(BOARD_WIDTH).fill(SYMBOLS.empty));
|
|
263
|
-
|
|
264
|
-
// Place snake body
|
|
265
|
-
for (let i = 1; i < snake.length; i++) {
|
|
266
|
-
const segment = snake[i];
|
|
267
|
-
if (segment.x >= 0 && segment.x < BOARD_WIDTH && segment.y >= 0 && segment.y < BOARD_HEIGHT) {
|
|
268
|
-
board[segment.y][segment.x] = SYMBOLS.snake;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Place snake head (on top of body if needed)
|
|
273
|
-
const head = snake[0];
|
|
274
|
-
if (head.x >= 0 && head.x < BOARD_WIDTH && head.y >= 0 && head.y < BOARD_HEIGHT) {
|
|
275
|
-
board[head.y][head.x] = SYMBOLS.head;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Place food
|
|
279
|
-
if (food.x >= 0 && food.x < BOARD_WIDTH && food.y >= 0 && food.y < BOARD_HEIGHT) {
|
|
280
|
-
board[food.y][food.x] = SYMBOLS.food;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Add border for clarity
|
|
284
|
-
display += '┌' + '─'.repeat(BOARD_WIDTH * 2) + '┐\n';
|
|
285
|
-
|
|
286
|
-
// Draw board rows
|
|
287
|
-
for (let y = 0; y < BOARD_HEIGHT; y++) {
|
|
288
|
-
display += '│';
|
|
289
|
-
for (let x = 0; x < BOARD_WIDTH; x++) {
|
|
290
|
-
display += board[y][x];
|
|
291
|
-
if (x < BOARD_WIDTH - 1) display += '';
|
|
292
|
-
}
|
|
293
|
-
display += '│\n';
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
display += '└' + '─'.repeat(BOARD_WIDTH * 2) + '┘\n';
|
|
297
|
-
display += '```\n\n';
|
|
298
|
-
|
|
299
|
-
if (!gameOver) {
|
|
300
|
-
display += '**Controls:**\n';
|
|
301
|
-
display += '• `w` or `up` - Move up\n';
|
|
302
|
-
display += '• `s` or `down` - Move down\n';
|
|
303
|
-
display += '• `a` or `left` - Move left\n';
|
|
304
|
-
display += '• `d` or `right` - Move right\n\n';
|
|
305
|
-
|
|
306
|
-
display += '💡 **Tip:** Eat the 🍎 to grow and score points!\n';
|
|
307
|
-
|
|
308
|
-
// Show game stats
|
|
309
|
-
const totalCells = BOARD_WIDTH * BOARD_HEIGHT;
|
|
310
|
-
const coverage = Math.round((snake.length / totalCells) * 100);
|
|
311
|
-
display += `**Board Coverage:** ${coverage}%\n`;
|
|
312
|
-
} else {
|
|
313
|
-
display += 'Start a new game to play again! 🎮\n';
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return display;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Get game statistics
|
|
320
|
-
function getSnakeStats(gameState) {
|
|
321
|
-
const { snake, score, moves, speed } = gameState;
|
|
322
|
-
|
|
323
|
-
const totalCells = BOARD_WIDTH * BOARD_HEIGHT;
|
|
324
|
-
const coverage = (snake.length / totalCells) * 100;
|
|
325
|
-
|
|
326
|
-
return {
|
|
327
|
-
score,
|
|
328
|
-
snakeLength: snake.length,
|
|
329
|
-
moves,
|
|
330
|
-
speed,
|
|
331
|
-
boardCoverage: Math.round(coverage * 100) / 100,
|
|
332
|
-
efficiency: moves > 0 ? Math.round((score / moves) * 100) / 100 : 0
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Check if game is won (snake fills entire board - nearly impossible!)
|
|
337
|
-
function checkWin(gameState) {
|
|
338
|
-
const totalCells = BOARD_WIDTH * BOARD_HEIGHT;
|
|
339
|
-
return gameState.snake.length >= totalCells - 1; // -1 for food
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Get direction from various input formats
|
|
343
|
-
function parseDirection(input) {
|
|
344
|
-
const normalized = input.toLowerCase().trim();
|
|
345
|
-
|
|
346
|
-
const directionMap = {
|
|
347
|
-
'w': 'UP',
|
|
348
|
-
'up': 'UP',
|
|
349
|
-
'u': 'UP',
|
|
350
|
-
's': 'DOWN',
|
|
351
|
-
'down': 'DOWN',
|
|
352
|
-
'd': 'RIGHT',
|
|
353
|
-
'a': 'LEFT',
|
|
354
|
-
'left': 'LEFT',
|
|
355
|
-
'right': 'RIGHT',
|
|
356
|
-
'l': 'LEFT',
|
|
357
|
-
'r': 'RIGHT'
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
return directionMap[normalized] || null;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Generate tips based on game state
|
|
364
|
-
function getGameTips(gameState) {
|
|
365
|
-
const { snake, score, moves, speed } = gameState;
|
|
366
|
-
|
|
367
|
-
const tips = [];
|
|
368
|
-
|
|
369
|
-
if (snake.length < 5) {
|
|
370
|
-
tips.push('🎯 Focus on eating food to grow your snake!');
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (score > 0 && moves > score * 2) {
|
|
374
|
-
tips.push('⚡ Try to be more efficient - plan your path to the food!');
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (speed >= 3) {
|
|
378
|
-
tips.push('🏃 You\'re getting fast! Be careful with turns.');
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
if (snake.length > 10) {
|
|
382
|
-
tips.push('🐍 Getting long! Watch out for your own tail.');
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const coverage = (snake.length / (BOARD_WIDTH * BOARD_HEIGHT)) * 100;
|
|
386
|
-
if (coverage > 30) {
|
|
387
|
-
tips.push('📈 Great coverage! Space is getting tight.');
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return tips;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
module.exports = {
|
|
394
|
-
createInitialSnakeState,
|
|
395
|
-
changeDirection,
|
|
396
|
-
moveSnake,
|
|
397
|
-
autoMove,
|
|
398
|
-
formatSnakeDisplay,
|
|
399
|
-
getSnakeStats,
|
|
400
|
-
checkWin,
|
|
401
|
-
parseDirection,
|
|
402
|
-
getGameTips,
|
|
403
|
-
DIRECTIONS,
|
|
404
|
-
BOARD_WIDTH,
|
|
405
|
-
BOARD_HEIGHT
|
|
406
|
-
};
|