-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathremaining_logics.txt
More file actions
125 lines (104 loc) · 5.83 KB
/
Copy pathremaining_logics.txt
File metadata and controls
125 lines (104 loc) · 5.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
================================================================================
DOODLE DASH (SKRIBBL CLONE) - REMAINING LOGICS & ALGORITHMS
================================================================================
This document outlines the core architectures, states, and algorithms required
to take the current Skribbl prototype to a fully featured, production-ready
multiplayer game.
---
1. TIMER & TURN MANAGEMENT ALGORITHM
------------------------------------
Currently, turns only end if everyone guesses correctly. A real game requires a
server-controlled timer countdown for each turn.
* State Additions (backend/services/room.service.js):
Add `timer` (interval ref), `timeLeft` (integer), and `totalTime` (integer) to the room.
* Algorithm:
1. When start() is called, clear any active timer: `clearInterval(room.timer)`.
2. Set `room.timeLeft = 60` (or dynamic round time).
3. Initialize a `setInterval` running every 1000ms:
a. Decrement `room.timeLeft`.
b. Emit `timer_update` event containing `room.timeLeft` to all players in the roomId.
c. If `room.timeLeft === 0`:
- Clear the interval.
- Send a message to chat: "Time is up! The word was [currentWord]."
- Call `next(room)` and trigger a new turn with `start()`.
4. If all players guess correctly before `timeLeft === 0`:
- Clear the interval immediately.
- Proceed to `next(room)` and `start()`.
---
2. WORD SELECTION / CHOICE STAGE (DRAWER'S SELECT WORD PHASE)
-------------------------------------------------------------
Currently, the server automatically picks a word and starts the round. To give
the drawer agency, add a "Word Selection" phase:
* State Additions:
Change `room.gameState` to 'selecting' during this phase.
* Algorithm:
1. In `start()`, instead of immediately choosing one word:
- Generate 3 random, distinct words from the word bank.
- Store these on `room.wordChoices = [word1, word2, word3]`.
- Change `room.gameState = 'selecting'`.
- Emit a `word_choices` event *only* to the drawer: `io.to(drawer.socketId).emit('word_choices', room.wordChoices)`.
- Start a short selection countdown (e.g., 15 seconds).
2. Frontend Drawer UI:
- Display a modal with the 3 words. Clicking one fires a `select_word` socket event with the selected word.
3. Backend `select_word` Event:
- Verify the sender is indeed the current drawer.
- Set `room.currentWord = selectedWord`.
- Change `room.gameState = 'playing'`.
- Start the main round countdown (60s) and send the masked word to guessers.
4. Timeout Fallback:
- If the 15-second selection timer hits 0 and no word was chosen, the server auto-selects a random one from the choices and proceeds.
---
3. DYNAMIC SCORING SYSTEM ALGORITHM
-----------------------------------
Currently, guessers get a flat +10 points. Skribbl scales points by speed and
rewards the drawer for recognizable drawings.
* Algorithm:
When player P guesses correctly:
1. Calculate speed percentage: `const timeRatio = room.timeLeft / room.totalTime;`
2. Guesser Score: Award points dynamically based on speed:
`const guesserPoints = Math.round(100 + (200 * timeRatio));` // Max 300, Min 100
`p.score += guesserPoints;`
3. Drawer Bonus: The drawer should be rewarded when players guess successfully to encourage recognizable drawings:
`const drawerPoints = Math.round(50 * timeRatio);`
`drawer.score += drawerPoints;`
---
4. DYNAMIC HINT SYSTEM
----------------------
Provide letters to guessers as time progresses to assist with difficult words.
* Algorithm:
1. Calculate hint milestones during the 60s countdown:
- At 45 seconds remaining (25% elapsed): Reveal 1 random letter.
- At 30 seconds remaining (50% elapsed): Reveal a 2nd random letter.
- At 15 seconds remaining (75% elapsed): Reveal a 3rd random letter (if word length >= 6).
2. Hint Generation:
- Maintain an array or mask tracking revealed indexes (e.g., `[false, false, true, false, false]`).
- Pick a random unrevealed index, mark it true, and build the hint string:
e.g., "A P P L E" -> "_ _ P _ _".
- Emit `word` containing the newly updated masked string *only* to the guessers.
---
5. GAME OVER & WINNER LEADERBOARD STAGE
---------------------------------------
Currently, the rounds keep incrementing infinitely. We need a defined conclusion.
* Algorithm:
1. In the `next(room)` function, check if the rounds limit is exceeded:
`if (room.round > maxRounds) { room.gameState = 'finished'; }`
2. When `room.gameState === 'finished'`:
- Clear all active timers.
- Sort players by score: `const podium = [...room.players].sort((a, b) => b.score - a.score);`
- Emit a `game_over` event to the room containing the sorted `podium` list.
3. Frontend:
- Display a beautiful podium UI (1st, 2nd, 3rd) with animations, fireworks, or trophy graphics.
- Show a "Back to Lobby" or "Restart Game" button (visible only to the room host).
4. Restart Event:
- When the host clicks restart, reset `room.players.forEach(p => p.score = 0)`, reset `room.round = 1`, set `room.gameState = 'waiting'`, and broadcast to all clients to return to the waiting state.
---
6. CHAT GUESS RECOGNITION (CLOSE GUESSES)
-----------------------------------------
Improve chat UX by recognizing and notifying players when they are "extremely close" to the word (e.g., typo or off by one letter).
* Algorithm:
1. Use the Levenshtein Distance algorithm to compare the typed chat text with the secret word.
2. If the user hasn't guessed correctly yet:
`const distance = levenshteinDistance(text.toLowerCase(), currentWord.toLowerCase());`
3. If `distance === 1`:
- Do NOT send the message to the room (keeps it secret).
- Emit a private warning event to the typing player: `socket.emit('msg', { user: 'System', text: 'You are very close!' });`