MikeDoes commited on
Commit
2ce42d3
·
verified ·
1 Parent(s): 65bf72a

Upload 6 files

Browse files
eleven_labs_script.js ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ function injectElevenLabsWidget() {
3
+ const script = document.createElement('script');
4
+ script.src = 'https://elevenlabs.io/convai-widget/index.js';
5
+ script.async = true;
6
+ script.type = 'text/javascript';
7
+ document.head.appendChild(script);
8
+
9
+ // Create the wrapper and widget
10
+ const wrapper = document.createElement('div');
11
+ wrapper.className = 'desktop';
12
+
13
+ const widget = document.createElement('elevenlabs-convai');
14
+ widget.setAttribute('agent-id', 'SoMyS9WQqWMY0o3jhcfr');
15
+
16
+ // Set initial colors based on current theme
17
+ updateWidgetColors(widget);
18
+
19
+ // Watch for theme changes
20
+ const observer = new MutationObserver(() => {
21
+ updateWidgetColors(widget);
22
+ });
23
+
24
+ observer.observe(document.documentElement, {
25
+ attributes: true,
26
+ attributeFilter: ['class'],
27
+ });
28
+
29
+ function updateWidgetColors(widget) {
30
+ const isDarkMode = !document.documentElement.classList.contains('light');
31
+ if (isDarkMode) {
32
+ widget.setAttribute('avatar-orb-color-1', '#2E2E2E');
33
+ widget.setAttribute('avatar-orb-color-2', '#B8B8B8');
34
+ } else {
35
+ widget.setAttribute('avatar-orb-color-1', '#4D9CFF');
36
+ widget.setAttribute('avatar-orb-color-2', '#9CE6E6');
37
+ }
38
+ }
39
+
40
+ widget.innerHTML = `\
41
+ <form slot="terms" class="prose text-sm">
42
+ <h3>Terms and conditions</h3>
43
+ <p>
44
+ By clicking "Continue," and each time I interact with this AI agent, I
45
+ consent to ElevenLabs collecting and using my voice and data derived from
46
+ it to interpret my speech, and provide the support services I request, and
47
+ to the recording, storage, and sharing of my communications with
48
+ third-party service providers, and as described in the
49
+ <a href="/terms-of-use">Privacy Policy</a>. If you do not wish to have
50
+ your conversations recorded, please refrain from using this service.
51
+ </form>`;
52
+
53
+ // Listen for the widget's "call" event to inject client tools
54
+ widget.addEventListener('elevenlabs-convai:call', (event) => {
55
+ event.detail.config.clientTools = {
56
+ submitAdviceSentence: (
57
+ {adviceText}
58
+ ) => {
59
+ submitAdvice(adviceText)
60
+ }
61
+ };
62
+ });
63
+
64
+ // Attach widget to the DOM
65
+ wrapper.appendChild(widget);
66
+ document.body.appendChild(wrapper);
67
+ }
68
+
69
+ if (document.readyState === 'loading') {
70
+ document.addEventListener('DOMContentLoaded', injectElevenLabsWidget);
71
+ } else {
72
+ injectElevenLabsWidget();
73
+ }
74
+
game_logic.js ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Core game elements
2
+ const statusEl = document.getElementById('status');
3
+ const timerEl = document.getElementById('timer');
4
+ const gameInfoEl = document.getElementById('game-info');
5
+ const guessInput = document.getElementById('guess-input');
6
+ const submitGuessBtn = document.getElementById('submit-guess-btn');
7
+ const feedbackSection = document.getElementById('feedback-section');
8
+ const guessedWordEl = document.getElementById('guessed-word');
9
+ const guessScoreEl = document.getElementById('guess-score');
10
+ const feedbackTextEl = document.getElementById('feedback-text');
11
+ const chatContent = document.getElementById('chat-content');
12
+ const gridContainer = document.getElementById('grid-container');
13
+
14
+ // Game state variables
15
+ let countdownInterval;
16
+ const playerName = 'Player1';
17
+ let squareCount = 0;
18
+ let chatCount = 1;
19
+
20
+ // Grid management functions
21
+ const updateGrid = () => {
22
+ const cols = Math.ceil(Math.sqrt(squareCount));
23
+ const rows = Math.ceil(squareCount / cols);
24
+ gridContainer.style.gridTemplateColumns = `repeat(${cols}, minmax(0, 1fr))`;
25
+ };
26
+
27
+ const addSquare = () => {
28
+ squareCount++;
29
+ updateGrid();
30
+ const colors = ["cerulean", "light-sea-green", "sunset", "floral-white", "light-red"];
31
+
32
+ const square = document.createElement("div");
33
+ square.className = `${colors[(squareCount - 1) % colors.length]} w-16 h-16 rounded-md shadow-md flex flex-col items-center justify-center text-black font-bold pop-in`;
34
+ square.innerHTML = `
35
+ <span class="iconify" data-icon="mdi:account" data-width="24" data-height="24"></span>
36
+ Student ${squareCount}
37
+ `;
38
+ gridContainer.appendChild(square);
39
+ };
40
+
41
+ const removeSquare = () => {
42
+ if (squareCount > 0) {
43
+ const squareToRemove = gridContainer.lastElementChild;
44
+ squareToRemove.classList.add("pop-out");
45
+ setTimeout(() => {
46
+ squareToRemove.remove();
47
+ squareCount--;
48
+ updateGrid();
49
+ }, 300);
50
+ }
51
+ };
52
+
53
+ // Chat functionality
54
+ const addStreamMessage = (message, type = 'user') => {
55
+ if (!message.trim()) return;
56
+
57
+ const chatLine = document.createElement("div");
58
+ chatLine.className = "text-gray-700 flex items-center gap-2";
59
+
60
+ if (type === 'system') {
61
+ chatLine.innerHTML = `<span class="font-bold text-blue-600">${message}</span>`;
62
+ } else {
63
+ chatLine.innerHTML = `<span class="font-bold">${chatCount}.</span> <span>${message}</span>`;
64
+ chatCount++;
65
+ }
66
+
67
+ chatContent.appendChild(chatLine);
68
+ chatContent.scrollTop = chatContent.scrollHeight;
69
+ };
70
+
71
+ // Timer functionality
72
+ function startCountdown(endTimestamp) {
73
+ if (countdownInterval) {
74
+ clearInterval(countdownInterval);
75
+ }
76
+
77
+ function updateTimer() {
78
+ const now = new Date().getTime();
79
+ const timeLeft = endTimestamp - now;
80
+
81
+ if (timeLeft <= 0) {
82
+ clearInterval(countdownInterval);
83
+ timerEl.textContent = 'Time\'s up!';
84
+ timerEl.classList.add('text-red-600');
85
+ //addStreamMessage('Time\'s up! Starting new round...', 'system');
86
+ fetchGameState();
87
+ return;
88
+ }
89
+
90
+ const seconds = Math.floor(timeLeft / 1000);
91
+ timerEl.textContent = `${seconds}s`;
92
+ }
93
+
94
+ updateTimer();
95
+ countdownInterval = setInterval(updateTimer, 1000);
96
+ }
97
+
98
+ // Game state management
99
+ function updateGameState(data) {
100
+ gameInfoEl.innerHTML = '';
101
+ statusEl.textContent = data.message || '';
102
+
103
+ if (data.session_id) {
104
+ const infoHtml = `
105
+ <div>
106
+ Session: ${data.session_id || ''}<br>
107
+ Turn: ${data.turn_number || ''}<br>
108
+ Word: ${data.word || ''}
109
+ </div>
110
+ `;
111
+ gameInfoEl.innerHTML = infoHtml;
112
+
113
+ // Display hints based on turn number
114
+ const turnNumber = parseInt(data.turn_number) || 0;
115
+
116
+ // Hint 0 on turn 1
117
+ if (turnNumber === 1 && data.hint0) {
118
+ addStreamMessage('New hint available:', 'system');
119
+ addStreamMessage(data.hint0, 'system');
120
+ }
121
+ // Hint 1 on turn 3
122
+ else if (turnNumber === 3 && data.hint1) {
123
+ addStreamMessage('New hint available:', 'system');
124
+ addStreamMessage(data.hint1, 'system');
125
+ }
126
+ // Hint 2 on turn 5
127
+ else if (turnNumber === 5 && data.hint2) {
128
+ addStreamMessage('New hint available:', 'system');
129
+ addStreamMessage(data.hint2, 'system');
130
+ }
131
+ // Hint 3 on turn 7
132
+ else if (turnNumber === 7 && data.hint3) {
133
+ addStreamMessage('New hint available:', 'system');
134
+ addStreamMessage(data.hint3, 'system');
135
+ }
136
+
137
+ if (data.end_timestamp) {
138
+ startCountdown(data.end_timestamp);
139
+ }
140
+ }
141
+ }
142
+
143
+ // API interactions
144
+ async function submitAdvice(adviceText) {
145
+ if (!guessText) return;
146
+
147
+ submitGuessBtn.disabled = true;
148
+ submitGuessBtn.classList.add('opacity-75');
149
+
150
+ try {
151
+ const formData = new FormData();
152
+ formData.append('guess_text', guessText);
153
+ formData.append('player_name', playerName);
154
+ formData.append('personality', document.getElementById('personality-select').value);
155
+
156
+ const response = await fetch('https://lemot.online/player/submit_guess/', {
157
+ method: 'POST',
158
+ body: formData
159
+ });
160
+
161
+ const data = await response.json();
162
+
163
+ if (data.error) {
164
+ statusEl.textContent = data.message || "Error submitting guess.";
165
+ statusEl.classList.add('text-red-600');
166
+ addStreamMessage('Error submitting guess', 'system');
167
+ } else {
168
+ feedbackSection.classList.remove('hidden');
169
+ guessedWordEl.textContent = data.guessed_word;
170
+ guessScoreEl.textContent = `${data.score} / 10`;
171
+ feedbackTextEl.textContent = data.feedback;
172
+
173
+ addStreamMessage(`${data.guessed_word} - Score: ${data.score} / 10`);
174
+ guessInput.value = '';
175
+ fetchGameState();
176
+ }
177
+ } catch (err) {
178
+ console.error(err);
179
+ statusEl.textContent = "Error submitting guess.";
180
+ statusEl.classList.add('text-red-600');
181
+ } finally {
182
+ submitGuessBtn.disabled = false;
183
+ submitGuessBtn.classList.remove('opacity-75');
184
+ }
185
+ }
186
+
187
+ function fetchGameState() {
188
+ const formData = new FormData();
189
+ formData.append('player_name', playerName);
190
+
191
+ fetch('https://lemot.online/player/play/', {
192
+ method: 'POST',
193
+ body: formData
194
+ })
195
+ .then(res => res.json())
196
+ .then(data => {
197
+ if (data.error) {
198
+ statusEl.textContent = data.message;
199
+ statusEl.classList.add('text-red-600');
200
+ addStreamMessage(data.message, 'system');
201
+ } else {
202
+ statusEl.classList.remove('text-red-600');
203
+ updateGameState(data);
204
+ }
205
+ })
206
+ .catch(err => {
207
+ console.error(err);
208
+ statusEl.textContent = "Error fetching game state.";
209
+ addStreamMessage("Error fetching game state", 'system');
210
+ });
211
+ }
212
+
213
+ // Event listeners
214
+ submitGuessBtn.addEventListener('click', () => {
215
+ const guessText = guessInput.value.trim();
216
+ submitGuessb(guessText);
217
+ });
218
+
219
+ guessInput.addEventListener('keypress', (e) => {
220
+ if (e.key === 'Enter') {
221
+ const guessText = guessInput.value.trim();
222
+ submitGuessb(guessText);
223
+ }
224
+ });
225
+
226
+ // Add square button listeners if they exist
227
+ const addSquareBtn = document.getElementById('add-square');
228
+ const removeSquareBtn = document.getElementById('remove-square');
229
+
230
+ if (addSquareBtn) {
231
+ addSquareBtn.addEventListener('click', addSquare);
232
+ }
233
+ if (removeSquareBtn) {
234
+ removeSquareBtn.addEventListener('click', removeSquare);
235
+ }
236
+
237
+ // Initialize game
238
+ window.addEventListener('load', fetchGameState);
index.html CHANGED
@@ -1,19 +1,110 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Word Game Interface</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/iconify/2.0.0/iconify.min.js"></script>
10
+ <style>
11
+ :root {
12
+ --cerulean: #227c9dff;
13
+ --light-sea-green: #17c3b2ff;
14
+ --sunset: #ffcb77ff;
15
+ --floral-white: #fef9efff;
16
+ --light-red: #fe6d73ff;
17
+ }
18
+ .cerulean { background-color: var(--cerulean); }
19
+ .light-sea-green { background-color: var(--light-sea-green); }
20
+ .sunset { background-color: var(--sunset); }
21
+ .floral-white { background-color: var(--floral-white); }
22
+ .light-red { background-color: var(--light-red); }
23
+ .pop-in { animation: pop-in 0.3s ease-out; }
24
+ .pop-out { animation: pop-out 0.3s ease-out; }
25
+ @keyframes pop-in {
26
+ from { transform: scale(0.5); opacity: 0; }
27
+ to { transform: scale(1); opacity: 1; }
28
+ }
29
+ @keyframes pop-out {
30
+ from { transform: scale(1); opacity: 1; }
31
+ to { transform: scale(0.5); opacity: 0; }
32
+ }
33
+ </style>
34
+ </head>
35
+ <body class="bg-gradient-to-br from-gray-50 to-gray-100 min-h-screen flex flex-col items-center justify-center relative">
36
+ <!-- Hint Section -->
37
+ <div id="hints-container" class="absolute top-4 left-4 flex flex-col gap-4">
38
+ <!-- Hints will be dynamically inserted here -->
39
+ </div>
40
+
41
+ <!-- Professor Section -->
42
+ <div id="large-square" class="mb-10 w-48 h-48 bg-purple-500 flex flex-col items-center justify-center rounded-lg shadow-lg text-white font-bold text-xl">
43
+ <span class="iconify" data-icon="mdi:account-tie" data-width="48" data-height="48"></span>
44
+ Professor
45
+ </div>
46
+
47
+ <!-- Dynamic Grid -->
48
+ <div id="grid-container" class="grid gap-4 p-4"></div>
49
+
50
+ <!-- Chat Section -->
51
+ <div id="chat-box" class="w-96 bg-white p-4 flex flex-col overflow-y-auto border-l border-gray-300 absolute right-4 top-4 rounded-lg shadow-md h-[calc(100vh-8rem)]">
52
+ <h2 class="text-lg font-bold text-gray-700 mb-4">Stream</h2>
53
+ <div id="chat-content" class="flex-1 flex flex-col gap-2 overflow-y-auto"></div>
54
+ </div>
55
+
56
+ <!-- Input Field -->
57
+ <div class="absolute bottom-4 left-1/2 transform -translate-x-1/2 w-full max-w-lg">
58
+ <div class="flex items-center gap-2">
59
+ <input
60
+ id="guess-input"
61
+ type="text"
62
+ placeholder="Enter your advice..."
63
+ class="flex-1 border border-gray-300 rounded px-4 py-2 focus:ring focus:ring-blue-400 focus:outline-none"
64
+ />
65
+ <button
66
+ id="submit-guess"
67
+ class="bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-600 transition"
68
+ >
69
+ Send
70
+ </button>
71
+ </div>
72
+ </div>
73
+
74
+ <!-- Dev Interface (Bottom Left) -->
75
+ <div class="fixed bottom-4 left-4 w-64 bg-white shadow-lg rounded-lg p-4 text-sm">
76
+ <div id="status" class="text-xs mb-2"></div>
77
+ <div id="timer" class="text-sm font-bold mb-2"></div>
78
+ <div id="game-info" class="text-xs"></div>
79
+
80
+ <div class="mt-2">
81
+ <select id="personality-select" class="w-full text-xs p-1 border rounded">
82
+ <option value="normal">normal</option>
83
+ <option value="genuine_friend">genuine friend</option>
84
+ <option value="sensitive_to_compliments">sensitive to compliments</option>
85
+ <option value="rebellious">rebel</option>
86
+ </select>
87
+ <input id="dev-guess-input" type="text" placeholder="Enter advice..." class="w-full text-xs p-1 mt-1 border rounded"/>
88
+ <button id="submit-guess-btn" class="w-full bg-indigo-600 text-white text-xs p-1 mt-1 rounded">
89
+ Submit
90
+ </button>
91
+ </div>
92
+
93
+ <div id="feedback-section" class="mt-2 text-xs hidden">
94
+ <div class="flex justify-between">
95
+ <span id="guessed-word"></span>
96
+ <span id="guess-score"></span>
97
+ </div>
98
+ <p id="feedback-text" class="mt-1"></p>
99
+ </div>
100
+ </div>
101
+
102
+ <script src="./game_logic.js"></script>
103
+ <script src="./music_controller.js"></script>
104
+ <script src="./eleven_labs_script.js"></script>
105
+ <script src="./personality_allocation.js"></script>
106
+ <script src="./populate_interface.js"></script>
107
+
108
+
109
+ </body>
110
+ </html>
music_controller.js ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // music_controller.js
2
+
3
+ class MusicController {
4
+ constructor() {
5
+ this.bgMusic = document.getElementById('bgMusic');
6
+ this.musicToggle = document.getElementById('musicToggle');
7
+ this.isMusicPlaying = false;
8
+ this.initializeMusic();
9
+ this.setupEventListeners();
10
+ }
11
+
12
+ initializeMusic() {
13
+ if (this.bgMusic) {
14
+ this.bgMusic.volume = 0.5;
15
+ }
16
+ }
17
+
18
+ setupEventListeners() {
19
+ // Setup toggle button click handler
20
+ this.musicToggle.addEventListener('click', () => this.toggleMusic());
21
+
22
+ // Setup initial click handler for the whole document
23
+ document.addEventListener('click', () => {
24
+ if (!this.isMusicPlaying) {
25
+ this.playMusic().catch(error => console.log('Audio playback prevented:', error));
26
+ }
27
+ }, { once: true });
28
+ }
29
+
30
+ toggleMusic() {
31
+ if (this.isMusicPlaying) {
32
+ this.pauseMusic();
33
+ } else {
34
+ this.playMusic();
35
+ }
36
+ this.updateMusicIcon();
37
+ this.isMusicPlaying = !this.isMusicPlaying;
38
+ }
39
+
40
+ playMusic() {
41
+ return this.bgMusic.play();
42
+ }
43
+
44
+ pauseMusic() {
45
+ this.bgMusic.pause();
46
+ }
47
+
48
+ updateMusicIcon() {
49
+ const icon = this.musicToggle.querySelector('.iconify');
50
+ const iconName = this.isMusicPlaying ? 'mdi:speaker-off' : 'mdi:speaker';
51
+ icon.setAttribute('data-icon', iconName);
52
+ }
53
+ }
54
+
55
+ // Initialize the music controller when the document is ready
56
+ document.addEventListener('DOMContentLoaded', () => {
57
+ const musicController = new MusicController();
58
+ });
personality_allocation.js ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // personality_allocation.js
2
+
3
+ // Personality and color mappings with additional visual properties
4
+ const personalityConfig = {
5
+ normal: {
6
+ color: '#227c9d', // cerulean
7
+ name: 'normal',
8
+ icon: 'mdi:account-circle',
9
+ backgroundColor: 'cerulean',
10
+ borderColor: 'border-blue-600'
11
+ },
12
+ genuine_friend: {
13
+ color: '#17c3b2', // light-sea-green
14
+ name: 'genuine friend',
15
+ icon: 'mdi:account-heart',
16
+ backgroundColor: 'light-sea-green',
17
+ borderColor: 'border-teal-600'
18
+ },
19
+ sensitive_to_compliments: {
20
+ color: '#ffcb77', // sunset
21
+ name: 'sensitive to compliments',
22
+ icon: 'mdi:account-star',
23
+ backgroundColor: 'sunset',
24
+ borderColor: 'border-yellow-600'
25
+ },
26
+ rebellious: {
27
+ color: '#fe6d73', // light-red
28
+ name: 'rebellious',
29
+ icon: 'mdi:account-alert',
30
+ backgroundColor: 'light-red',
31
+ borderColor: 'border-red-600'
32
+ }
33
+ };
34
+
35
+ // Get random personality
36
+ function getRandomPersonality() {
37
+ const personalities = Object.keys(personalityConfig);
38
+ const randomIndex = Math.floor(Math.random() * personalities.length);
39
+ return personalities[randomIndex];
40
+ }
41
+
42
+ // Create player square with personality
43
+ function createPlayerSquare(personality) {
44
+ const config = personalityConfig[personality];
45
+
46
+ // Create the square container
47
+ const square = document.createElement('div');
48
+ square.className = `w-16 h-16 rounded-md shadow-lg flex flex-col items-center justify-center text-white font-medium pop-in ${config.backgroundColor}`;
49
+
50
+ // Add icon and label
51
+ square.innerHTML = `
52
+ <span class="iconify mb-1" data-icon="${config.icon}" data-width="24" data-height="24"></span>
53
+ <span class="text-xs">You</span>
54
+ `;
55
+
56
+ return square;
57
+ }
58
+
59
+ // Initialize personality system
60
+ function initializePersonalitySystem() {
61
+ // Get DOM elements
62
+ const personalitySelect = document.getElementById('personality-select');
63
+ const gridContainer = document.getElementById('grid-container');
64
+ const gameInfo = document.getElementById('game-info');
65
+
66
+ // Get random personality
67
+ const selectedPersonality = getRandomPersonality();
68
+ const config = personalityConfig[selectedPersonality];
69
+
70
+ // Set the select value programmatically
71
+ personalitySelect.value = selectedPersonality;
72
+
73
+ // Create personality display
74
+ const personalityDisplay = document.createElement('div');
75
+ personalityDisplay.className = 'bg-white rounded-lg p-4 shadow-md mb-4';
76
+ personalityDisplay.innerHTML = `
77
+ <div class="text-sm text-gray-600 mb-2">Your Personality:</div>
78
+ <div class="flex items-center gap-2">
79
+ <div class="w-3 h-3 rounded-full" style="background-color: ${config.color}"></div>
80
+ <span class="font-medium capitalize">${config.name.replace(/_/g, ' ')}</span>
81
+ </div>
82
+ `;
83
+
84
+ // Add personality info to game info
85
+ if (gameInfo) {
86
+ gameInfo.insertBefore(personalityDisplay, gameInfo.firstChild);
87
+ }
88
+
89
+ // Create and add player square to grid
90
+ const playerSquare = createPlayerSquare(selectedPersonality);
91
+ if (gridContainer) {
92
+ // Clear existing squares if any
93
+ gridContainer.innerHTML = '';
94
+ gridContainer.appendChild(playerSquare);
95
+ }
96
+
97
+ // Update game state with personality
98
+ window.playerPersonality = selectedPersonality;
99
+
100
+ // Add to chat history
101
+ const chatContent = document.getElementById('chat-content');
102
+ if (chatContent) {
103
+ const personalityMessage = document.createElement('div');
104
+ personalityMessage.className = 'text-gray-600 text-sm italic mb-4';
105
+ personalityMessage.textContent = `You joined as a ${config.name.replace(/_/g, ' ')}`;
106
+ chatContent.appendChild(personalityMessage);
107
+ }
108
+ }
109
+
110
+ // Expose necessary functions and data
111
+ window.personalitySystem = {
112
+ config: personalityConfig,
113
+ getRandomPersonality,
114
+ createPlayerSquare,
115
+ initialize: initializePersonalitySystem
116
+ };
117
+
118
+ // Initialize when DOM is loaded
119
+ document.addEventListener('DOMContentLoaded', initializePersonalitySystem);
120
+
121
+ // Handle personality updates in the game logic
122
+ document.addEventListener('personalityUpdated', (event) => {
123
+ const newPersonality = event.detail.personality;
124
+ const config = personalityConfig[newPersonality];
125
+
126
+ // Update the player square
127
+ const playerSquare = document.querySelector('#grid-container > div');
128
+ if (playerSquare) {
129
+ Object.keys(personalityConfig).forEach(personality => {
130
+ const pConfig = personalityConfig[personality];
131
+ playerSquare.classList.remove(pConfig.backgroundColor);
132
+ });
133
+ playerSquare.classList.add(config.backgroundColor);
134
+ playerSquare.querySelector('.iconify').setAttribute('data-icon', config.icon);
135
+ }
136
+ });
populate_interface.js ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // populate_interface.js
2
+
3
+ class GridPopulator {
4
+ constructor(gridContainerId, initialUsers = 10) {
5
+ this.gridContainer = document.getElementById(gridContainerId);
6
+ this.users = new Map();
7
+ this.nextUserId = 1;
8
+ this.updateInterval = 2000;
9
+ this.maxUsers = 15;
10
+ this.minUsers = 5;
11
+
12
+ // Set up grid layout
13
+ this.gridContainer.className = "grid gap-4 p-4";
14
+
15
+ // Initialize with default users
16
+ for (let i = 0; i < initialUsers; i++) {
17
+ this.addUser();
18
+ }
19
+
20
+ this.updateGridLayout();
21
+ this.startUpdates();
22
+ }
23
+
24
+ generateUserName() {
25
+ return `${this.nextUserId}`;
26
+ }
27
+
28
+ updateGridLayout() {
29
+ const cols = Math.ceil(Math.sqrt(this.users.size));
30
+ this.gridContainer.style.gridTemplateColumns = `repeat(${cols}, minmax(0, 1fr))`;
31
+ }
32
+
33
+ addUser() {
34
+ const personality = window.personalitySystem.getRandomPersonality();
35
+ const square = window.personalitySystem.createPlayerSquare(personality);
36
+ const userId = this.nextUserId++;
37
+
38
+ // Update the square's text to show student instead of "You"
39
+ const nameSpan = square.querySelector('.text-xs');
40
+ nameSpan.textContent = this.generateUserName();
41
+
42
+ this.users.set(userId, {
43
+ element: square,
44
+ personality: personality
45
+ });
46
+
47
+ this.gridContainer.appendChild(square);
48
+ this.updateGridLayout();
49
+
50
+ // Add to chat content
51
+ /* const chatContent = document.getElementById('chat-content');
52
+ if (chatContent) {
53
+ const config = window.personalitySystem.config[personality];
54
+ const joinMessage = document.createElement('div');
55
+ joinMessage.className = 'text-gray-700 flex items-center gap-2';
56
+ joinMessage.innerHTML = `
57
+ <span class="font-bold">${this.users.size}.</span>
58
+ <span>Student ${userId} joined as ${config.name}</span>
59
+ `;
60
+ chatContent.appendChild(joinMessage);
61
+ chatContent.scrollTop = chatContent.scrollHeight;
62
+ } */
63
+
64
+ return userId;
65
+ }
66
+
67
+ removeUser() {
68
+ if (this.users.size <= this.minUsers) return;
69
+
70
+ const userIds = Array.from(this.users.keys());
71
+ const randomId = userIds[Math.floor(Math.random() * userIds.length)];
72
+ const user = this.users.get(randomId);
73
+
74
+ user.element.classList.add('pop-out');
75
+
76
+ // Add to chat content
77
+ /* const chatContent = document.getElementById('chat-content');
78
+ if (chatContent) {
79
+ const config = window.personalitySystem.config[user.personality];
80
+ const leaveMessage = document.createElement('div');
81
+ leaveMessage.className = 'text-gray-700 flex items-center gap-2';
82
+ leaveMessage.innerHTML = `
83
+ <span class="font-bold">${this.users.size}.</span>
84
+ <span>Student ${randomId} (${config.name}) left</span>
85
+ `;
86
+ chatContent.appendChild(leaveMessage);
87
+ chatContent.scrollTop = chatContent.scrollHeight;
88
+ } */
89
+
90
+ setTimeout(() => {
91
+ user.element.remove();
92
+ this.users.delete(randomId);
93
+ this.updateGridLayout();
94
+ }, 300);
95
+ }
96
+
97
+ startUpdates() {
98
+ setInterval(() => {
99
+ const shouldAdd = Math.random() > 0.5;
100
+
101
+ if (shouldAdd && this.users.size < this.maxUsers) {
102
+ this.addUser();
103
+ } else if (this.users.size > this.minUsers) {
104
+ this.removeUser();
105
+ }
106
+
107
+ // Randomly update some existing users' personalities
108
+ this.users.forEach((user, userId) => {
109
+ if (Math.random() < 0.1) {
110
+ const newPersonality = window.personalitySystem.getRandomPersonality();
111
+ if (newPersonality !== user.personality) {
112
+ const event = new CustomEvent('personalityUpdated', {
113
+ detail: { personality: newPersonality }
114
+ });
115
+ user.element.dispatchEvent(event);
116
+ user.personality = newPersonality;
117
+
118
+ // Update chat with personality change
119
+ /* const chatContent = document.getElementById('chat-content');
120
+ if (chatContent) {
121
+ const config = window.personalitySystem.config[newPersonality];
122
+ const changeMessage = document.createElement('div');
123
+ changeMessage.className = 'text-gray-700 flex items-center gap-2';
124
+ changeMessage.innerHTML = `
125
+ <span class="font-bold">${this.users.size}.</span>
126
+ <span>Student ${userId} changed to ${config.name}</span>
127
+ `;
128
+ chatContent.appendChild(changeMessage);
129
+ chatContent.scrollTop = chatContent.scrollHeight;
130
+ } */
131
+ }
132
+ }
133
+ });
134
+ }, this.updateInterval);
135
+ }
136
+ }
137
+
138
+ // Initialize when DOM is loaded
139
+ document.addEventListener('DOMContentLoaded', () => {
140
+ // Make sure personality system is initialized first
141
+ if (window.personalitySystem) {
142
+ window.gridPopulator = new GridPopulator('grid-container');
143
+ } else {
144
+ console.error('Personality system must be initialized before grid populator');
145
+ }
146
+ });