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
|
@@ -1,389 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Multiplayer Tic-Tac-Toe game implementation for /vibe
|
|
3
|
-
* Room-based system where players can create/join games and play together
|
|
4
|
-
* Multiple rooms can run simultaneously with spectators allowed
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const BOARD_SIZE = 9; // 3x3 grid
|
|
8
|
-
|
|
9
|
-
// Create initial multiplayer tic-tac-toe state
|
|
10
|
-
function createInitialMultiplayerTicTacToeState(host, roomName = null) {
|
|
11
|
-
return {
|
|
12
|
-
roomName: roomName || `${host}'s game`,
|
|
13
|
-
host: host,
|
|
14
|
-
players: [], // [player1, player2] - exactly 2 players to play
|
|
15
|
-
spectators: [], // Can watch the game
|
|
16
|
-
board: Array(BOARD_SIZE).fill(''), // 3x3 grid as flat array
|
|
17
|
-
turn: 'X', // X always starts
|
|
18
|
-
moves: 0,
|
|
19
|
-
winner: null,
|
|
20
|
-
gameOver: false,
|
|
21
|
-
isDraw: false,
|
|
22
|
-
currentPlayerIndex: 0, // 0 or 1, indexes into players array
|
|
23
|
-
playerSymbols: {}, // { handle: 'X' | 'O' }
|
|
24
|
-
gameStarted: false,
|
|
25
|
-
createdAt: new Date().toISOString(),
|
|
26
|
-
lastActivity: new Date().toISOString(),
|
|
27
|
-
history: [] // Move history for replay/analysis
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Join a game room (as player or spectator)
|
|
32
|
-
function joinRoom(gameState, playerHandle, asSpectator = false) {
|
|
33
|
-
const { players, spectators, gameStarted, host } = gameState;
|
|
34
|
-
|
|
35
|
-
// Check if already in the game
|
|
36
|
-
if (players.includes(playerHandle) || spectators.includes(playerHandle)) {
|
|
37
|
-
return { error: 'Already in this game room' };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (!asSpectator && players.length < 2 && !gameStarted) {
|
|
41
|
-
// Join as player
|
|
42
|
-
const newPlayers = [...players, playerHandle];
|
|
43
|
-
const playerSymbols = { ...gameState.playerSymbols };
|
|
44
|
-
|
|
45
|
-
// Assign symbols - host gets X (goes first), second player gets O
|
|
46
|
-
if (playerHandle === host) {
|
|
47
|
-
playerSymbols[playerHandle] = 'X';
|
|
48
|
-
} else if (newPlayers.length === 1) {
|
|
49
|
-
playerSymbols[playerHandle] = 'X';
|
|
50
|
-
} else {
|
|
51
|
-
// Second player gets O
|
|
52
|
-
playerSymbols[playerHandle] = 'O';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Start game if we now have 2 players
|
|
56
|
-
const shouldStart = newPlayers.length === 2;
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
success: true,
|
|
60
|
-
gameState: {
|
|
61
|
-
...gameState,
|
|
62
|
-
players: newPlayers,
|
|
63
|
-
playerSymbols,
|
|
64
|
-
gameStarted: shouldStart,
|
|
65
|
-
lastActivity: new Date().toISOString()
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
} else {
|
|
69
|
-
// Join as spectator
|
|
70
|
-
return {
|
|
71
|
-
success: true,
|
|
72
|
-
gameState: {
|
|
73
|
-
...gameState,
|
|
74
|
-
spectators: [...spectators, playerHandle],
|
|
75
|
-
lastActivity: new Date().toISOString()
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Leave the room
|
|
82
|
-
function leaveRoom(gameState, playerHandle) {
|
|
83
|
-
const { players, spectators, gameStarted, gameOver } = gameState;
|
|
84
|
-
|
|
85
|
-
const wasPlayer = players.includes(playerHandle);
|
|
86
|
-
const wasSpectator = spectators.includes(playerHandle);
|
|
87
|
-
|
|
88
|
-
if (!wasPlayer && !wasSpectator) {
|
|
89
|
-
return { error: 'Not in this game room' };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// If game is in progress and a player leaves, end the game
|
|
93
|
-
if (wasPlayer && gameStarted && !gameOver) {
|
|
94
|
-
const remainingPlayer = players.find(p => p !== playerHandle);
|
|
95
|
-
return {
|
|
96
|
-
success: true,
|
|
97
|
-
gameState: {
|
|
98
|
-
...gameState,
|
|
99
|
-
players: players.filter(p => p !== playerHandle),
|
|
100
|
-
spectators: spectators.filter(s => s !== playerHandle),
|
|
101
|
-
winner: remainingPlayer,
|
|
102
|
-
gameOver: true,
|
|
103
|
-
lastActivity: new Date().toISOString(),
|
|
104
|
-
history: [...gameState.history, {
|
|
105
|
-
type: 'forfeit',
|
|
106
|
-
player: playerHandle,
|
|
107
|
-
timestamp: new Date().toISOString()
|
|
108
|
-
}]
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Normal leave
|
|
114
|
-
return {
|
|
115
|
-
success: true,
|
|
116
|
-
gameState: {
|
|
117
|
-
...gameState,
|
|
118
|
-
players: players.filter(p => p !== playerHandle),
|
|
119
|
-
spectators: spectators.filter(s => s !== playerHandle),
|
|
120
|
-
lastActivity: new Date().toISOString()
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Check for winner in tic-tac-toe
|
|
126
|
-
function checkWinner(board) {
|
|
127
|
-
const lines = [
|
|
128
|
-
[0, 1, 2], [3, 4, 5], [6, 7, 8], // rows
|
|
129
|
-
[0, 3, 6], [1, 4, 7], [2, 5, 8], // cols
|
|
130
|
-
[0, 4, 8], [2, 4, 6] // diagonals
|
|
131
|
-
];
|
|
132
|
-
|
|
133
|
-
for (const [a, b, c] of lines) {
|
|
134
|
-
if (board[a] && board[a] === board[b] && board[a] === board[c]) {
|
|
135
|
-
return board[a];
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Make a move
|
|
142
|
-
function makeMove(gameState, position, playerHandle) {
|
|
143
|
-
const { board, players, turn, moves, winner, gameOver, gameStarted, playerSymbols } = gameState;
|
|
144
|
-
|
|
145
|
-
// Validate game state
|
|
146
|
-
if (!gameStarted) {
|
|
147
|
-
return { error: 'Game hasn\'t started yet. Need 2 players.' };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (gameOver) {
|
|
151
|
-
return { error: 'Game is already over' };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Validate player
|
|
155
|
-
if (!players.includes(playerHandle)) {
|
|
156
|
-
return { error: 'You are not a player in this game' };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Check if it's player's turn
|
|
160
|
-
const playerSymbol = playerSymbols[playerHandle];
|
|
161
|
-
if (playerSymbol !== turn) {
|
|
162
|
-
return { error: `Not your turn! Waiting for ${turn}` };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Validate position
|
|
166
|
-
if (position < 1 || position > 9) {
|
|
167
|
-
return { error: 'Position must be 1-9' };
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const index = position - 1; // Convert to 0-based index
|
|
171
|
-
if (board[index]) {
|
|
172
|
-
return { error: `Position ${position} is already taken` };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Make the move
|
|
176
|
-
const newBoard = [...board];
|
|
177
|
-
newBoard[index] = playerSymbol;
|
|
178
|
-
|
|
179
|
-
const newMoves = moves + 1;
|
|
180
|
-
const newWinner = checkWinner(newBoard);
|
|
181
|
-
const newIsDraw = !newWinner && newBoard.every(cell => cell !== '');
|
|
182
|
-
const newGameOver = newWinner || newIsDraw;
|
|
183
|
-
const nextTurn = playerSymbol === 'X' ? 'O' : 'X';
|
|
184
|
-
|
|
185
|
-
// Record move in history
|
|
186
|
-
const moveRecord = {
|
|
187
|
-
type: 'move',
|
|
188
|
-
player: playerHandle,
|
|
189
|
-
symbol: playerSymbol,
|
|
190
|
-
position: position,
|
|
191
|
-
timestamp: new Date().toISOString(),
|
|
192
|
-
moveNumber: newMoves
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const newGameState = {
|
|
196
|
-
...gameState,
|
|
197
|
-
board: newBoard,
|
|
198
|
-
turn: newGameOver ? playerSymbol : nextTurn,
|
|
199
|
-
moves: newMoves,
|
|
200
|
-
winner: newWinner,
|
|
201
|
-
gameOver: newGameOver,
|
|
202
|
-
isDraw: newIsDraw,
|
|
203
|
-
lastActivity: new Date().toISOString(),
|
|
204
|
-
history: [...gameState.history, moveRecord]
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
return { success: true, gameState: newGameState };
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Restart the game (keep same players)
|
|
211
|
-
function restartGame(gameState, playerHandle) {
|
|
212
|
-
const { players, host, spectators, playerSymbols } = gameState;
|
|
213
|
-
|
|
214
|
-
// Only players can restart
|
|
215
|
-
if (!players.includes(playerHandle)) {
|
|
216
|
-
return { error: 'Only players can restart the game' };
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Need 2 players to restart
|
|
220
|
-
if (players.length !== 2) {
|
|
221
|
-
return { error: 'Need 2 players to restart the game' };
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
success: true,
|
|
226
|
-
gameState: {
|
|
227
|
-
...gameState,
|
|
228
|
-
board: Array(BOARD_SIZE).fill(''),
|
|
229
|
-
turn: 'X', // X always starts
|
|
230
|
-
moves: 0,
|
|
231
|
-
winner: null,
|
|
232
|
-
gameOver: false,
|
|
233
|
-
isDraw: false,
|
|
234
|
-
currentPlayerIndex: 0,
|
|
235
|
-
gameStarted: true,
|
|
236
|
-
lastActivity: new Date().toISOString(),
|
|
237
|
-
history: []
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Format multiplayer tic-tac-toe for display
|
|
243
|
-
function formatMultiplayerTicTacToeDisplay(gameState, viewerHandle = null) {
|
|
244
|
-
const {
|
|
245
|
-
roomName, host, players, spectators, board, turn, moves, winner, isDraw,
|
|
246
|
-
gameStarted, playerSymbols, gameOver, history
|
|
247
|
-
} = gameState;
|
|
248
|
-
|
|
249
|
-
let display = `🎯 **Multiplayer Tic-Tac-Toe** - ${roomName}\n\n`;
|
|
250
|
-
|
|
251
|
-
// Game status
|
|
252
|
-
if (!gameStarted) {
|
|
253
|
-
display += `**Waiting for players...** (${players.length}/2)\n`;
|
|
254
|
-
if (players.length > 0) {
|
|
255
|
-
display += `Players: ${players.map(p => `@${p} (${playerSymbols[p] || '?'})`).join(', ')}\n`;
|
|
256
|
-
}
|
|
257
|
-
if (spectators.length > 0) {
|
|
258
|
-
display += `Spectators: ${spectators.map(s => `@${s}`).join(', ')}\n`;
|
|
259
|
-
}
|
|
260
|
-
display += '\n';
|
|
261
|
-
|
|
262
|
-
if (players.length < 2) {
|
|
263
|
-
display += 'Join with `/game multiplayer-tictactoe --join`\n';
|
|
264
|
-
display += 'Or spectate with `/game multiplayer-tictactoe --spectate`\n';
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return display;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Show board
|
|
271
|
-
const symbols = board.map((cell, i) => cell || (i + 1).toString());
|
|
272
|
-
|
|
273
|
-
display += `**Move ${moves}**\n`;
|
|
274
|
-
display += '```\n';
|
|
275
|
-
for (let row = 0; row < 3; row++) {
|
|
276
|
-
const line = [];
|
|
277
|
-
for (let col = 0; col < 3; col++) {
|
|
278
|
-
const index = row * 3 + col;
|
|
279
|
-
line.push(symbols[index]);
|
|
280
|
-
}
|
|
281
|
-
display += line.join(' │ ') + '\n';
|
|
282
|
-
if (row < 2) display += '──┼───┼──\n';
|
|
283
|
-
}
|
|
284
|
-
display += '```\n\n';
|
|
285
|
-
|
|
286
|
-
// Game status
|
|
287
|
-
if (winner) {
|
|
288
|
-
const winnerHandle = Object.keys(playerSymbols).find(h => playerSymbols[h] === winner);
|
|
289
|
-
display += `🎉 **@${winnerHandle} wins!** (${winner})\n\n`;
|
|
290
|
-
} else if (isDraw) {
|
|
291
|
-
display += `🤝 **Draw!** Well played!\n\n`;
|
|
292
|
-
} else if (gameStarted) {
|
|
293
|
-
const currentPlayerHandle = Object.keys(playerSymbols).find(h => playerSymbols[h] === turn);
|
|
294
|
-
display += `**@${currentPlayerHandle}'s turn** (${turn})\n\n`;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Show players
|
|
298
|
-
display += `**Players:**\n`;
|
|
299
|
-
for (const player of players) {
|
|
300
|
-
const symbol = playerSymbols[player];
|
|
301
|
-
const status = symbol === turn && !gameOver ? ' ← current turn' : '';
|
|
302
|
-
display += `• @${player} (${symbol})${status}\n`;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Show spectators if any
|
|
306
|
-
if (spectators.length > 0) {
|
|
307
|
-
display += `\n**Spectators (${spectators.length}):** ${spectators.map(s => `@${s}`).join(', ')}\n`;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Show controls based on viewer's role
|
|
311
|
-
display += '\n';
|
|
312
|
-
if (players.includes(viewerHandle)) {
|
|
313
|
-
if (!gameOver) {
|
|
314
|
-
display += `**Your commands:**\n`;
|
|
315
|
-
display += `• \`/game multiplayer-tictactoe --move N\` - Play position 1-9\n`;
|
|
316
|
-
display += `• \`/game multiplayer-tictactoe --leave\` - Leave game\n`;
|
|
317
|
-
} else {
|
|
318
|
-
display += `**Game over!** Use \`/game multiplayer-tictactoe --restart\` to play again\n`;
|
|
319
|
-
}
|
|
320
|
-
} else if (spectators.includes(viewerHandle)) {
|
|
321
|
-
display += `**You are spectating.** Use \`/game multiplayer-tictactoe --leave\` to stop watching\n`;
|
|
322
|
-
} else {
|
|
323
|
-
if (!gameStarted) {
|
|
324
|
-
display += `**Join:** \`/game multiplayer-tictactoe --join\` or \`/game multiplayer-tictactoe --spectate\`\n`;
|
|
325
|
-
} else {
|
|
326
|
-
display += `**Spectate:** \`/game multiplayer-tictactoe --spectate\`\n`;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Show recent moves
|
|
331
|
-
if (history.length > 0) {
|
|
332
|
-
const recentMoves = history.filter(h => h.type === 'move').slice(-3);
|
|
333
|
-
if (recentMoves.length > 0) {
|
|
334
|
-
display += `\n**Recent moves:**\n`;
|
|
335
|
-
for (const move of recentMoves) {
|
|
336
|
-
display += `• @${move.player} played ${move.symbol} at position ${move.position}\n`;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return display;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Get game statistics
|
|
345
|
-
function getGameStats(gameState) {
|
|
346
|
-
const { moves, history, players, spectators } = gameState;
|
|
347
|
-
|
|
348
|
-
const moveHistory = history.filter(h => h.type === 'move');
|
|
349
|
-
const playerMoves = {};
|
|
350
|
-
|
|
351
|
-
for (const move of moveHistory) {
|
|
352
|
-
playerMoves[move.player] = (playerMoves[move.player] || 0) + 1;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
return {
|
|
356
|
-
totalMoves: moves,
|
|
357
|
-
totalPlayers: players.length,
|
|
358
|
-
totalSpectators: spectators.length,
|
|
359
|
-
playerMoves,
|
|
360
|
-
gameStarted: gameState.gameStarted,
|
|
361
|
-
gameOver: gameState.gameOver,
|
|
362
|
-
winner: gameState.winner
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Check if game is ready to start
|
|
367
|
-
function canStartGame(gameState) {
|
|
368
|
-
return gameState.players.length === 2 && !gameState.gameStarted;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// Get available positions
|
|
372
|
-
function getAvailablePositions(gameState) {
|
|
373
|
-
return gameState.board
|
|
374
|
-
.map((cell, index) => cell === '' ? index + 1 : null)
|
|
375
|
-
.filter(pos => pos !== null);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
module.exports = {
|
|
379
|
-
createInitialMultiplayerTicTacToeState,
|
|
380
|
-
joinRoom,
|
|
381
|
-
leaveRoom,
|
|
382
|
-
makeMove,
|
|
383
|
-
restartGame,
|
|
384
|
-
formatMultiplayerTicTacToeDisplay,
|
|
385
|
-
checkWinner,
|
|
386
|
-
getGameStats,
|
|
387
|
-
canStartGame,
|
|
388
|
-
getAvailablePositions
|
|
389
|
-
};
|