slashvibe-mcp 0.3.20 → 0.3.21
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/README.md +47 -252
- package/analytics.js +107 -0
- package/auth-store.js +148 -0
- package/auto-update.js +130 -0
- package/bridges/bridge-monitor.js +388 -0
- package/bridges/discord-bot.js +431 -0
- package/bridges/farcaster.js +299 -0
- package/bridges/telegram.js +261 -0
- package/bridges/webhook-health.js +420 -0
- package/bridges/webhook-server.js +437 -0
- package/bridges/whatsapp.js +441 -0
- package/bridges/x-webhook.js +423 -0
- package/config.js +27 -15
- package/games/arcade.js +406 -0
- package/games/chess.js +451 -0
- package/games/colorguess.js +343 -0
- package/games/crossword-words.js +171 -0
- package/games/crossword.js +461 -0
- package/games/drawing.js +347 -0
- package/games/gameroulette.js +300 -0
- package/games/gamerouter.js +336 -0
- package/games/gamestatus.js +337 -0
- package/games/guessnumber.js +209 -0
- package/games/hangman.js +279 -0
- package/games/memory.js +338 -0
- package/games/multiplayer-tictactoe.js +389 -0
- package/games/pixelart.js +399 -0
- package/games/quickduel.js +354 -0
- package/games/riddle.js +371 -0
- package/games/rockpaperscissors.js +291 -0
- package/games/snake.js +406 -0
- package/games/storybuilder.js +343 -0
- package/games/tictactoe.js +345 -0
- package/games/twentyquestions.js +286 -0
- package/games/twotruths.js +207 -0
- package/games/werewolf.js +508 -0
- package/games/wordassociation.js +247 -0
- package/games/wordchain.js +135 -0
- package/index.js +116 -159
- package/intelligence/index.js +9 -2
- package/intelligence/interests.js +369 -0
- package/notification-emitter.js +77 -0
- package/notify.js +5 -1
- package/package.json +21 -16
- package/prompts.js +1 -1
- package/protocol/index.js +73 -0
- package/setup.js +480 -0
- package/smart-inbox.js +276 -0
- package/store/api.js +536 -215
- package/store/profiles.js +160 -12
- package/tools/_actions.js +362 -21
- package/tools/_discovery.js +119 -26
- package/tools/_shared/index.js +64 -0
- package/tools/_shared.js +234 -0
- package/tools/_work-context.js +338 -0
- package/tools/_work-context.manual-test.js +199 -0
- package/tools/_work-context.test.js +260 -0
- package/tools/activity.js +220 -0
- package/tools/analytics.js +191 -0
- package/tools/approve.js +197 -0
- package/tools/artifact-create.js +14 -3
- package/tools/artifacts-price.js +107 -0
- package/tools/available.js +120 -0
- package/tools/broadcast.js +325 -0
- package/tools/chat.js +202 -0
- package/tools/collaborative-drawing.js +1 -1
- package/tools/connection-status.js +178 -0
- package/tools/discover.js +350 -34
- package/tools/dm.js +80 -8
- package/tools/earnings.js +126 -0
- package/tools/feed.js +35 -4
- package/tools/follow.js +224 -0
- package/tools/friends.js +207 -0
- package/tools/gig-browse.js +206 -0
- package/tools/gig-complete.js +144 -0
- package/tools/health.js +87 -0
- package/tools/help.js +3 -3
- package/tools/idea.js +9 -2
- package/tools/inbox.js +289 -105
- package/tools/init.js +131 -34
- package/tools/invite.js +15 -4
- package/tools/leaderboard.js +117 -0
- package/tools/lib/git-apply.js +206 -0
- package/tools/lib/git-bundle.js +407 -0
- package/tools/migrate.js +3 -3
- package/tools/multiplayer-game.js +1 -1
- package/tools/onboarding.js +7 -7
- package/tools/open.js +143 -12
- package/tools/party-game.js +1 -1
- package/tools/plan.js +225 -0
- package/tools/proof-of-work.js +144 -0
- package/tools/reply.js +166 -0
- package/tools/report.js +1 -1
- package/tools/request.js +17 -3
- package/tools/schedule.js +367 -0
- package/tools/search-messages.js +123 -0
- package/tools/session.js +467 -0
- package/tools/session_price.js +128 -0
- package/tools/settings.js +90 -2
- package/tools/ship.js +30 -7
- package/tools/smart-check.js +201 -0
- package/tools/start.js +147 -12
- package/tools/status.js +53 -6
- package/tools/streak.js +147 -0
- package/tools/stuck.js +297 -0
- package/tools/subscribe.js +148 -0
- package/tools/subscriptions.js +134 -0
- package/tools/suggest-tags.js +6 -8
- package/tools/tag-suggestions.js +1 -1
- package/tools/tip.js +150 -77
- package/tools/token.js +4 -4
- package/tools/update.js +1 -1
- package/tools/wallet.js +221 -79
- package/tools/watch.js +157 -0
- package/tools/who.js +30 -1
- package/tools/withdraw.js +145 -0
- package/tools/work-summary.js +96 -0
- package/version.json +10 -8
- package/LICENSE +0 -21
- package/store/sqlite.js +0 -347
- /package/tools/{auto-suggest-connections.js → _deprecated/auto-suggest-connections.js} +0 -0
- /package/tools/{away.js → _deprecated/away.js} +0 -0
- /package/tools/{back.js → _deprecated/back.js} +0 -0
- /package/tools/{bootstrap-skills.js → _deprecated/bootstrap-skills.js} +0 -0
- /package/tools/{bridge-dashboard.js → _deprecated/bridge-dashboard.js} +0 -0
- /package/tools/{bridge-health.js → _deprecated/bridge-health.js} +0 -0
- /package/tools/{bridge-live.js → _deprecated/bridge-live.js} +0 -0
- /package/tools/{bridges.js → _deprecated/bridges.js} +0 -0
- /package/tools/{colorguess.js → _deprecated/colorguess.js} +0 -0
- /package/tools/{discover-insights.js → _deprecated/discover-insights.js} +0 -0
- /package/tools/{discover-momentum.js → _deprecated/discover-momentum.js} +0 -0
- /package/tools/{discovery-analytics.js → _deprecated/discovery-analytics.js} +0 -0
- /package/tools/{discovery-auto-suggest.js → _deprecated/discovery-auto-suggest.js} +0 -0
- /package/tools/{discovery-bootstrap.js → _deprecated/discovery-bootstrap.js} +0 -0
- /package/tools/{discovery-daily.js → _deprecated/discovery-daily.js} +0 -0
- /package/tools/{discovery-dashboard.js → _deprecated/discovery-dashboard.js} +0 -0
- /package/tools/{discovery-digest.js → _deprecated/discovery-digest.js} +0 -0
- /package/tools/{discovery-hub.js → _deprecated/discovery-hub.js} +0 -0
- /package/tools/{discovery-insights.js → _deprecated/discovery-insights.js} +0 -0
- /package/tools/{discovery-momentum.js → _deprecated/discovery-momentum.js} +0 -0
- /package/tools/{discovery-monitor.js → _deprecated/discovery-monitor.js} +0 -0
- /package/tools/{discovery-proactive.js → _deprecated/discovery-proactive.js} +0 -0
- /package/tools/{draw.js → _deprecated/draw.js} +0 -0
- /package/tools/{farcaster.js → _deprecated/farcaster.js} +0 -0
- /package/tools/{forget.js → _deprecated/forget.js} +0 -0
- /package/tools/{games-catalog.js → _deprecated/games-catalog.js} +0 -0
- /package/tools/{games.js → _deprecated/games.js} +0 -0
- /package/tools/{guessnumber.js → _deprecated/guessnumber.js} +0 -0
- /package/tools/{hangman.js → _deprecated/hangman.js} +0 -0
- /package/tools/{multiplayer-tictactoe.js → _deprecated/multiplayer-tictactoe.js} +0 -0
- /package/tools/{mute.js → _deprecated/mute.js} +0 -0
- /package/tools/{recall.js → _deprecated/recall.js} +0 -0
- /package/tools/{remember.js → _deprecated/remember.js} +0 -0
- /package/tools/{riddle.js → _deprecated/riddle.js} +0 -0
- /package/tools/{run-bootstrap.js → _deprecated/run-bootstrap.js} +0 -0
- /package/tools/{skills-analytics.js → _deprecated/skills-analytics.js} +0 -0
- /package/tools/{skills-bootstrap.js → _deprecated/skills-bootstrap.js} +0 -0
- /package/tools/{skills-dashboard.js → _deprecated/skills-dashboard.js} +0 -0
- /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
- /package/tools/{skills.js → _deprecated/skills.js} +0 -0
- /package/tools/{smart-intro.js → _deprecated/smart-intro.js} +0 -0
- /package/tools/{storybuilder.js → _deprecated/storybuilder.js} +0 -0
- /package/tools/{telegram-bot.js → _deprecated/telegram-bot.js} +0 -0
- /package/tools/{telegram-setup.js → _deprecated/telegram-setup.js} +0 -0
- /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
- /package/tools/{twentyquestions.js → _deprecated/twentyquestions.js} +0 -0
- /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guess the Number game implementation for /vibe
|
|
3
|
+
* Classic number guessing game with hints and difficulty levels
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Create initial guess the number state
|
|
7
|
+
function createInitialGuessNumberState(difficulty = 'medium') {
|
|
8
|
+
const ranges = {
|
|
9
|
+
easy: { min: 1, max: 10 },
|
|
10
|
+
medium: { min: 1, max: 50 },
|
|
11
|
+
hard: { min: 1, max: 100 },
|
|
12
|
+
extreme: { min: 1, max: 1000 }
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const range = ranges[difficulty] || ranges.medium;
|
|
16
|
+
const targetNumber = Math.floor(Math.random() * (range.max - range.min + 1)) + range.min;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
targetNumber: targetNumber,
|
|
20
|
+
guesses: [],
|
|
21
|
+
moves: 0,
|
|
22
|
+
gameOver: false,
|
|
23
|
+
won: false,
|
|
24
|
+
difficulty: difficulty,
|
|
25
|
+
range: range,
|
|
26
|
+
hints: []
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Make a guess
|
|
31
|
+
function makeGuess(gameState, guess) {
|
|
32
|
+
const { targetNumber, guesses, moves, gameOver, range } = gameState;
|
|
33
|
+
|
|
34
|
+
// Parse guess as number
|
|
35
|
+
const guessNumber = parseInt(guess);
|
|
36
|
+
|
|
37
|
+
// Validate guess
|
|
38
|
+
if (isNaN(guessNumber)) {
|
|
39
|
+
return { error: 'Please enter a valid number!' };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (guessNumber < range.min || guessNumber > range.max) {
|
|
43
|
+
return { error: `Please guess a number between ${range.min} and ${range.max}!` };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check if already guessed
|
|
47
|
+
if (guesses.includes(guessNumber)) {
|
|
48
|
+
return { error: `You already guessed ${guessNumber}! Try a different number.` };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check if game is already over
|
|
52
|
+
if (gameOver) {
|
|
53
|
+
return { error: 'Game is over! Start a new game to play again.' };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Process the guess
|
|
57
|
+
const newGuesses = [...guesses, guessNumber];
|
|
58
|
+
const newMoves = moves + 1;
|
|
59
|
+
let hint = '';
|
|
60
|
+
let won = false;
|
|
61
|
+
|
|
62
|
+
if (guessNumber === targetNumber) {
|
|
63
|
+
// Correct guess!
|
|
64
|
+
won = true;
|
|
65
|
+
hint = '🎯 Perfect! You got it!';
|
|
66
|
+
} else if (guessNumber < targetNumber) {
|
|
67
|
+
// Too low
|
|
68
|
+
const diff = targetNumber - guessNumber;
|
|
69
|
+
if (diff <= 2) {
|
|
70
|
+
hint = '📈 Very close! Go a bit higher!';
|
|
71
|
+
} else if (diff <= 5) {
|
|
72
|
+
hint = '📈 Close! Too low, but getting warmer!';
|
|
73
|
+
} else if (diff <= 10) {
|
|
74
|
+
hint = '📈 Too low! Go higher!';
|
|
75
|
+
} else {
|
|
76
|
+
hint = '📈 Way too low! Much higher!';
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
// Too high
|
|
80
|
+
const diff = guessNumber - targetNumber;
|
|
81
|
+
if (diff <= 2) {
|
|
82
|
+
hint = '📉 Very close! Go a bit lower!';
|
|
83
|
+
} else if (diff <= 5) {
|
|
84
|
+
hint = '📉 Close! Too high, but getting warmer!';
|
|
85
|
+
} else if (diff <= 10) {
|
|
86
|
+
hint = '📉 Too high! Go lower!';
|
|
87
|
+
} else {
|
|
88
|
+
hint = '📉 Way too high! Much lower!';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const newHints = [...gameState.hints, hint];
|
|
93
|
+
const newGameOver = won;
|
|
94
|
+
|
|
95
|
+
const newGameState = {
|
|
96
|
+
...gameState,
|
|
97
|
+
guesses: newGuesses,
|
|
98
|
+
moves: newMoves,
|
|
99
|
+
gameOver: newGameOver,
|
|
100
|
+
won: won,
|
|
101
|
+
hints: newHints,
|
|
102
|
+
lastGuess: guessNumber,
|
|
103
|
+
lastHint: hint
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return { success: true, gameState: newGameState };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Format display for guess the number game
|
|
110
|
+
function formatGuessNumberDisplay(gameState) {
|
|
111
|
+
const { guesses, moves, gameOver, won, difficulty, range, lastGuess, lastHint, targetNumber } = gameState;
|
|
112
|
+
|
|
113
|
+
let display = `🔢 **Guess the Number** (${difficulty}) - Move ${moves}\n\n`;
|
|
114
|
+
|
|
115
|
+
// Show range
|
|
116
|
+
display += `**Range:** ${range.min} - ${range.max}\n\n`;
|
|
117
|
+
|
|
118
|
+
// Show latest guess and hint
|
|
119
|
+
if (lastGuess) {
|
|
120
|
+
display += `**Last guess:** ${lastGuess}\n`;
|
|
121
|
+
if (lastHint) {
|
|
122
|
+
display += `**Hint:** ${lastHint}\n\n`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Show all guesses in order
|
|
127
|
+
if (guesses.length > 0) {
|
|
128
|
+
display += `**Your guesses:** ${guesses.join(', ')}\n\n`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Show game status
|
|
132
|
+
if (gameOver) {
|
|
133
|
+
if (won) {
|
|
134
|
+
display += `🎉 **Congratulations!** You guessed ${targetNumber} in ${moves} tries!\n\n`;
|
|
135
|
+
|
|
136
|
+
// Add performance feedback
|
|
137
|
+
const maxOptimal = Math.ceil(Math.log2(range.max - range.min + 1));
|
|
138
|
+
if (moves <= maxOptimal) {
|
|
139
|
+
display += '⭐ **Excellent strategy!** You used optimal guessing!';
|
|
140
|
+
} else if (moves <= maxOptimal + 2) {
|
|
141
|
+
display += '👍 **Great job!** Very efficient guessing!';
|
|
142
|
+
} else if (moves <= maxOptimal + 5) {
|
|
143
|
+
display += '👌 **Not bad!** Room for improvement with binary search!';
|
|
144
|
+
} else {
|
|
145
|
+
display += '🎯 **You got there!** Try narrowing down with middle numbers next time!';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
display += '**Keep guessing! You can do it! 🎯**';
|
|
150
|
+
|
|
151
|
+
// Give strategic hint after several guesses
|
|
152
|
+
if (moves >= 3 && moves % 3 === 0) {
|
|
153
|
+
display += '\n\n💡 **Tip:** Try guessing numbers in the middle of your remaining range!';
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return display;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get optimal strategy hint
|
|
161
|
+
function getStrategyHint(gameState) {
|
|
162
|
+
const { guesses, range } = gameState;
|
|
163
|
+
|
|
164
|
+
if (guesses.length === 0) {
|
|
165
|
+
const middle = Math.floor((range.min + range.max) / 2);
|
|
166
|
+
return `Try starting with ${middle} (middle of the range) for optimal strategy!`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Find remaining range based on guesses
|
|
170
|
+
let min = range.min;
|
|
171
|
+
let max = range.max;
|
|
172
|
+
|
|
173
|
+
guesses.forEach(guess => {
|
|
174
|
+
const hints = gameState.hints;
|
|
175
|
+
const hintIndex = guesses.indexOf(guess);
|
|
176
|
+
if (hints[hintIndex]) {
|
|
177
|
+
if (hints[hintIndex].includes('higher') || hints[hintIndex].includes('low')) {
|
|
178
|
+
min = Math.max(min, guess + 1);
|
|
179
|
+
} else if (hints[hintIndex].includes('lower') || hints[hintIndex].includes('high')) {
|
|
180
|
+
max = Math.min(max, guess - 1);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
if (min >= max) {
|
|
186
|
+
return 'Keep going! You\'re very close!';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const middle = Math.floor((min + max) / 2);
|
|
190
|
+
return `Try ${middle} (middle of ${min}-${max} range)`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Get difficulty info
|
|
194
|
+
function getDifficultyInfo() {
|
|
195
|
+
return {
|
|
196
|
+
easy: { range: '1-10', description: 'Perfect for beginners' },
|
|
197
|
+
medium: { range: '1-50', description: 'Good balance of challenge and fun' },
|
|
198
|
+
hard: { range: '1-100', description: 'Classic number guessing challenge' },
|
|
199
|
+
extreme: { range: '1-1000', description: 'For the truly brave!' }
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = {
|
|
204
|
+
createInitialGuessNumberState,
|
|
205
|
+
makeGuess,
|
|
206
|
+
formatGuessNumberDisplay,
|
|
207
|
+
getStrategyHint,
|
|
208
|
+
getDifficultyInfo
|
|
209
|
+
};
|
package/games/hangman.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hangman game implementation for /vibe
|
|
3
|
+
* Classic word guessing game with ASCII art
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Word lists organized by difficulty
|
|
7
|
+
const WORD_LISTS = {
|
|
8
|
+
easy: [
|
|
9
|
+
'cat', 'dog', 'sun', 'car', 'run', 'big', 'fun', 'red', 'yes', 'top',
|
|
10
|
+
'old', 'new', 'hot', 'cold', 'good', 'fast', 'slow', 'book', 'tree', 'blue'
|
|
11
|
+
],
|
|
12
|
+
medium: [
|
|
13
|
+
'apple', 'house', 'water', 'happy', 'music', 'phone', 'sleep', 'magic',
|
|
14
|
+
'friend', 'school', 'garden', 'yellow', 'rainbow', 'castle', 'dragon',
|
|
15
|
+
'planet', 'forest', 'ocean', 'mountain', 'butterfly'
|
|
16
|
+
],
|
|
17
|
+
hard: [
|
|
18
|
+
'javascript', 'computer', 'elephant', 'adventure', 'chocolate', 'umbrella',
|
|
19
|
+
'fantastic', 'mysterious', 'programming', 'butterfly', 'technology',
|
|
20
|
+
'dinosaur', 'encyclopedia', 'extraordinary', 'magnificent', 'revolutionary',
|
|
21
|
+
'philosopher', 'kaleidoscope', 'whimsical', 'serendipity'
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Hangman ASCII art stages
|
|
26
|
+
const HANGMAN_STAGES = [
|
|
27
|
+
// 0 wrong guesses
|
|
28
|
+
`
|
|
29
|
+
+---+
|
|
30
|
+
| |
|
|
31
|
+
|
|
|
32
|
+
|
|
|
33
|
+
|
|
|
34
|
+
|
|
|
35
|
+
=========`,
|
|
36
|
+
|
|
37
|
+
// 1 wrong guess
|
|
38
|
+
`
|
|
39
|
+
+---+
|
|
40
|
+
| |
|
|
41
|
+
O |
|
|
42
|
+
|
|
|
43
|
+
|
|
|
44
|
+
|
|
|
45
|
+
=========`,
|
|
46
|
+
|
|
47
|
+
// 2 wrong guesses
|
|
48
|
+
`
|
|
49
|
+
+---+
|
|
50
|
+
| |
|
|
51
|
+
O |
|
|
52
|
+
| |
|
|
53
|
+
|
|
|
54
|
+
|
|
|
55
|
+
=========`,
|
|
56
|
+
|
|
57
|
+
// 3 wrong guesses
|
|
58
|
+
`
|
|
59
|
+
+---+
|
|
60
|
+
| |
|
|
61
|
+
O |
|
|
62
|
+
/| |
|
|
63
|
+
|
|
|
64
|
+
|
|
|
65
|
+
=========`,
|
|
66
|
+
|
|
67
|
+
// 4 wrong guesses
|
|
68
|
+
`
|
|
69
|
+
+---+
|
|
70
|
+
| |
|
|
71
|
+
O |
|
|
72
|
+
/|\\ |
|
|
73
|
+
|
|
|
74
|
+
|
|
|
75
|
+
=========`,
|
|
76
|
+
|
|
77
|
+
// 5 wrong guesses
|
|
78
|
+
`
|
|
79
|
+
+---+
|
|
80
|
+
| |
|
|
81
|
+
O |
|
|
82
|
+
/|\\ |
|
|
83
|
+
/ |
|
|
84
|
+
|
|
|
85
|
+
=========`,
|
|
86
|
+
|
|
87
|
+
// 6 wrong guesses (game over)
|
|
88
|
+
`
|
|
89
|
+
+---+
|
|
90
|
+
| |
|
|
91
|
+
O |
|
|
92
|
+
/|\\ |
|
|
93
|
+
/ \\ |
|
|
94
|
+
|
|
|
95
|
+
=========`
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
// Get random word from difficulty level
|
|
99
|
+
function getRandomWord(difficulty = 'medium') {
|
|
100
|
+
const words = WORD_LISTS[difficulty] || WORD_LISTS.medium;
|
|
101
|
+
return words[Math.floor(Math.random() * words.length)].toLowerCase();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Create initial hangman state
|
|
105
|
+
function createInitialHangmanState(difficulty = 'medium') {
|
|
106
|
+
const word = getRandomWord(difficulty);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
word: word,
|
|
110
|
+
guessedLetters: [],
|
|
111
|
+
wrongGuesses: [],
|
|
112
|
+
correctGuesses: [],
|
|
113
|
+
wrongCount: 0,
|
|
114
|
+
maxWrongs: 6,
|
|
115
|
+
gameOver: false,
|
|
116
|
+
won: false,
|
|
117
|
+
difficulty: difficulty,
|
|
118
|
+
moves: 0
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check if letter has been guessed before
|
|
123
|
+
function hasBeenGuessed(gameState, letter) {
|
|
124
|
+
return gameState.guessedLetters.includes(letter.toLowerCase());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check if letter is in the word
|
|
128
|
+
function isLetterInWord(word, letter) {
|
|
129
|
+
return word.includes(letter.toLowerCase());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Get current word display (with underscores for unguessed letters)
|
|
133
|
+
function getWordDisplay(word, correctGuesses) {
|
|
134
|
+
return word
|
|
135
|
+
.split('')
|
|
136
|
+
.map(letter => correctGuesses.includes(letter) ? letter.toUpperCase() : '_')
|
|
137
|
+
.join(' ');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Make a guess
|
|
141
|
+
function makeGuess(gameState, guess) {
|
|
142
|
+
const { word, guessedLetters, wrongGuesses, correctGuesses, wrongCount, maxWrongs } = gameState;
|
|
143
|
+
|
|
144
|
+
// Normalize guess
|
|
145
|
+
const letter = guess.toLowerCase().trim();
|
|
146
|
+
|
|
147
|
+
// Validate guess
|
|
148
|
+
if (!/^[a-z]$/.test(letter)) {
|
|
149
|
+
return { error: 'Please guess a single letter (a-z)' };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check if already guessed
|
|
153
|
+
if (hasBeenGuessed(gameState, letter)) {
|
|
154
|
+
return { error: `You already guessed "${letter.toUpperCase()}". Try a different letter!` };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Check if game is already over
|
|
158
|
+
if (gameState.gameOver) {
|
|
159
|
+
return { error: 'Game is over! Start a new game to play again.' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Process the guess
|
|
163
|
+
const newGuessedLetters = [...guessedLetters, letter];
|
|
164
|
+
const newWrongGuesses = [...wrongGuesses];
|
|
165
|
+
const newCorrectGuesses = [...correctGuesses];
|
|
166
|
+
let newWrongCount = wrongCount;
|
|
167
|
+
|
|
168
|
+
if (isLetterInWord(word, letter)) {
|
|
169
|
+
// Correct guess
|
|
170
|
+
newCorrectGuesses.push(letter);
|
|
171
|
+
} else {
|
|
172
|
+
// Wrong guess
|
|
173
|
+
newWrongGuesses.push(letter);
|
|
174
|
+
newWrongCount++;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check win condition (all letters guessed)
|
|
178
|
+
const uniqueLetters = [...new Set(word.split(''))];
|
|
179
|
+
const won = uniqueLetters.every(letter => newCorrectGuesses.includes(letter));
|
|
180
|
+
|
|
181
|
+
// Check lose condition
|
|
182
|
+
const gameOver = won || newWrongCount >= maxWrongs;
|
|
183
|
+
|
|
184
|
+
const newGameState = {
|
|
185
|
+
...gameState,
|
|
186
|
+
guessedLetters: newGuessedLetters,
|
|
187
|
+
wrongGuesses: newWrongGuesses,
|
|
188
|
+
correctGuesses: newCorrectGuesses,
|
|
189
|
+
wrongCount: newWrongCount,
|
|
190
|
+
gameOver: gameOver,
|
|
191
|
+
won: won,
|
|
192
|
+
moves: gameState.moves + 1,
|
|
193
|
+
lastGuess: letter
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return { success: true, gameState: newGameState };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Format hangman display
|
|
200
|
+
function formatHangmanDisplay(gameState) {
|
|
201
|
+
const { word, wrongGuesses, correctGuesses, wrongCount, gameOver, won, difficulty, moves, lastGuess } = gameState;
|
|
202
|
+
|
|
203
|
+
let display = `🎯 **Hangman** (${difficulty}) - Move ${moves}\n\n`;
|
|
204
|
+
|
|
205
|
+
// Show hangman drawing
|
|
206
|
+
display += '```\n' + HANGMAN_STAGES[wrongCount] + '\n```\n\n';
|
|
207
|
+
|
|
208
|
+
// Show current word progress
|
|
209
|
+
const wordDisplay = getWordDisplay(word, correctGuesses);
|
|
210
|
+
display += `**Word:** ${wordDisplay}\n\n`;
|
|
211
|
+
|
|
212
|
+
// Show wrong guesses
|
|
213
|
+
if (wrongGuesses.length > 0) {
|
|
214
|
+
display += `**Wrong guesses:** ${wrongGuesses.map(l => l.toUpperCase()).join(', ')}\n`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Show remaining guesses
|
|
218
|
+
const remaining = 6 - wrongCount;
|
|
219
|
+
display += `**Remaining guesses:** ${remaining}\n\n`;
|
|
220
|
+
|
|
221
|
+
// Show game status
|
|
222
|
+
if (gameOver) {
|
|
223
|
+
if (won) {
|
|
224
|
+
display += `🎉 **You won!** The word was "${word.toUpperCase()}"\n`;
|
|
225
|
+
display += `Solved in ${moves} guesses!`;
|
|
226
|
+
} else {
|
|
227
|
+
display += `💀 **Game Over!** The word was "${word.toUpperCase()}"\n`;
|
|
228
|
+
display += 'Better luck next time!';
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
if (lastGuess) {
|
|
232
|
+
if (correctGuesses.includes(lastGuess)) {
|
|
233
|
+
display += `✅ Good guess! "${lastGuess.toUpperCase()}" is in the word.\n\n`;
|
|
234
|
+
} else {
|
|
235
|
+
display += `❌ Sorry, "${lastGuess.toUpperCase()}" is not in the word.\n\n`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
display += '**Guess a letter to continue!**';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return display;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Get hint for the current word
|
|
245
|
+
function getHint(word, difficulty) {
|
|
246
|
+
const hints = {
|
|
247
|
+
// Easy words
|
|
248
|
+
'cat': 'A furry pet that says meow',
|
|
249
|
+
'dog': 'Man\'s best friend that barks',
|
|
250
|
+
'sun': 'Bright star in our sky',
|
|
251
|
+
'car': 'Vehicle with four wheels',
|
|
252
|
+
'run': 'Move quickly on foot',
|
|
253
|
+
|
|
254
|
+
// Medium words
|
|
255
|
+
'apple': 'Red or green fruit that grows on trees',
|
|
256
|
+
'house': 'Building where people live',
|
|
257
|
+
'water': 'Clear liquid we drink',
|
|
258
|
+
'music': 'Sounds arranged in harmony',
|
|
259
|
+
'phone': 'Device for making calls',
|
|
260
|
+
|
|
261
|
+
// Hard words
|
|
262
|
+
'javascript': 'Popular programming language',
|
|
263
|
+
'computer': 'Electronic device for processing data',
|
|
264
|
+
'elephant': 'Large gray animal with a trunk',
|
|
265
|
+
'adventure': 'Exciting or unusual experience',
|
|
266
|
+
'programming': 'Writing code for computers'
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
return hints[word] || `A ${difficulty} word with ${word.length} letters`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
module.exports = {
|
|
273
|
+
createInitialHangmanState,
|
|
274
|
+
makeGuess,
|
|
275
|
+
formatHangmanDisplay,
|
|
276
|
+
getRandomWord,
|
|
277
|
+
getHint,
|
|
278
|
+
WORD_LISTS
|
|
279
|
+
};
|