zshashz commited on
Commit
f75003b
·
1 Parent(s): a16d218
Files changed (1) hide show
  1. index.html +232 -222
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Shy Guy Simulator</title>
7
  <style>
8
  body {
9
  font-family: Arial, sans-serif;
@@ -21,30 +21,63 @@
21
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
22
  }
23
 
24
- #story-text {
25
- margin-bottom: 20px;
26
- line-height: 1.6;
27
- min-height: 100px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
29
 
30
- #choices {
31
  display: flex;
32
- flex-direction: column;
33
  gap: 10px;
 
 
 
 
 
 
 
 
 
 
34
  }
35
 
36
  button {
37
  padding: 10px 20px;
38
- background-color: #4a4a4a;
39
  color: white;
40
  border: none;
41
  border-radius: 4px;
42
  cursor: pointer;
43
- transition: background-color 0.3s;
44
  }
45
 
46
  button:hover {
47
- background-color: #666;
48
  }
49
 
50
  #stats {
@@ -52,257 +85,234 @@
52
  padding: 10px;
53
  background-color: #333;
54
  border-radius: 4px;
 
 
55
  }
56
 
57
- .thought-bubble {
58
  font-style: italic;
59
- color: #aaa;
60
- margin: 10px 0;
 
 
 
61
  }
62
  </style>
63
  </head>
64
  <body>
65
  <div id="game-container">
66
- <h1>Shy Guy Simulator</h1>
67
- <div id="stats">
68
- Confidence: <span id="confidence">0</span>% |
69
- Drinks: <span id="drinks">0</span> |
70
- Time: <span id="time">8:00 PM</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  </div>
72
- <div id="story-text"></div>
73
- <div id="choices"></div>
74
  </div>
75
 
76
  <script>
77
- class ShyGuyGame {
78
- constructor() {
79
- this.stats = {
 
 
 
80
  confidence: 0,
81
- drinks: 0,
82
- time: new Date(2024, 0, 1, 20, 0) // 8:00 PM
 
 
 
83
  };
84
-
85
- this.currentScene = 'start';
86
- this.thoughts = [
87
- "Maybe she won't like me...",
88
- "What if I say something stupid?",
89
- "Everyone's watching...",
90
- "I should probably just go home...",
91
- "She looks way out of my league..."
 
 
92
  ];
93
 
94
  this.initialize();
95
  }
96
 
97
  initialize() {
 
 
98
  this.updateStats();
99
- this.loadScene(this.currentScene);
100
- setInterval(() => this.showRandomThought(), 15000);
101
  }
102
 
103
- showRandomThought() {
104
- if (this.stats.confidence < 70) {
105
- const thoughtText = this.thoughts[Math.floor(Math.random() * this.thoughts.length)];
106
- const thoughtBubble = document.createElement('div');
107
- thoughtBubble.className = 'thought-bubble';
108
- thoughtBubble.textContent = `Inner voice: "${thoughtText}"`;
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- const storyText = document.getElementById('story-text');
111
- storyText.insertBefore(thoughtBubble, storyText.firstChild);
112
 
113
- setTimeout(() => thoughtBubble.remove(), 5000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  }
115
  }
116
 
117
- updateStats() {
118
- document.getElementById('confidence').textContent = this.stats.confidence;
119
- document.getElementById('drinks').textContent = this.stats.drinks;
120
- document.getElementById('time').textContent =
121
- this.stats.time.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
 
124
- advanceTime(minutes) {
125
- this.stats.time = new Date(this.stats.time.getTime() + minutes * 60000);
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  this.updateStats();
127
  }
128
 
129
- loadScene(sceneId) {
130
- const scene = this.scenes[sceneId];
131
- if (!scene) return;
 
 
 
132
 
133
- document.getElementById('story-text').innerHTML = scene.text;
134
-
135
- const choicesContainer = document.getElementById('choices');
136
- choicesContainer.innerHTML = '';
137
-
138
- scene.choices.forEach(choice => {
139
- if (!choice.condition || choice.condition(this.stats)) {
140
- const button = document.createElement('button');
141
- button.textContent = choice.text;
142
- button.onclick = () => {
143
- if (choice.effect) {
144
- choice.effect(this.stats);
145
- this.updateStats();
146
- }
147
- this.loadScene(choice.next);
148
- };
149
- choicesContainer.appendChild(button);
150
- }
151
- });
152
  }
153
 
154
- scenes = {
155
- start: {
156
- text: "You're at the homecoming party, standing awkwardly by the snack table. Across the room, you see her - the pretty girl you've been too nervous to talk to all semester. What do you do?",
157
- choices: [
158
- {
159
- text: "Get a drink to calm your nerves",
160
- effect: stats => {
161
- stats.drinks++;
162
- stats.confidence += 10;
163
- this.advanceTime(15);
164
- },
165
- next: 'after_drink'
166
- },
167
- {
168
- text: "Try to move closer to the dance floor",
169
- effect: stats => {
170
- stats.confidence += 5;
171
- this.advanceTime(10);
172
- },
173
- next: 'dance_floor'
174
- },
175
- {
176
- text: "Stay by the snacks and observe",
177
- effect: stats => {
178
- this.advanceTime(20);
179
- },
180
- next: 'observe'
181
- }
182
- ]
183
- },
184
- after_drink: {
185
- text: "The drink starts to take effect, making you feel a bit warmer and more relaxed. The music seems more inviting now.",
186
- choices: [
187
- {
188
- text: "Get another drink",
189
- effect: stats => {
190
- stats.drinks++;
191
- stats.confidence += 15;
192
- this.advanceTime(15);
193
- },
194
- next: 'more_drinks'
195
- },
196
- {
197
- text: "Start swaying to the music",
198
- effect: stats => {
199
- stats.confidence += 10;
200
- this.advanceTime(10);
201
- },
202
- next: 'dancing'
203
- }
204
- ]
205
- },
206
- more_drinks: {
207
- text: "You're definitely feeling the drinks now. Your anxiety is lower, but you're also a bit wobbly.",
208
- choices: [
209
- {
210
- text: "Try to approach her",
211
- condition: stats => stats.confidence >= 40,
212
- effect: stats => this.advanceTime(5),
213
- next: 'approach'
214
- },
215
- {
216
- text: "Take a break outside",
217
- effect: stats => {
218
- stats.confidence -= 10;
219
- this.advanceTime(20);
220
- },
221
- next: 'outside'
222
- }
223
- ]
224
- },
225
- dance_floor: {
226
- text: "You're closer to where she's standing now. The music is louder, and people are dancing around you.",
227
- choices: [
228
- {
229
- text: "Start dancing near her group",
230
- effect: stats => {
231
- stats.confidence += 15;
232
- this.advanceTime(15);
233
- },
234
- next: 'dancing'
235
- },
236
- {
237
- text: "Retreat to get a drink",
238
- effect: stats => {
239
- stats.drinks++;
240
- stats.confidence += 5;
241
- this.advanceTime(10);
242
- },
243
- next: 'after_drink'
244
- }
245
- ]
246
- },
247
- dancing: {
248
- text: "You're dancing! Not too badly either. You notice she's glancing in your direction occasionally.",
249
- choices: [
250
- {
251
- text: "Make eye contact and smile",
252
- effect: stats => {
253
- stats.confidence += 20;
254
- this.advanceTime(5);
255
- },
256
- next: 'eye_contact'
257
- },
258
- {
259
- text: "Keep dancing but avoid eye contact",
260
- effect: stats => {
261
- stats.confidence += 5;
262
- this.advanceTime(15);
263
- },
264
- next: 'continue_dancing'
265
- }
266
- ]
267
- },
268
- eye_contact: {
269
- text: "She smiles back! Your heart is racing, but in a good way.",
270
- choices: [
271
- {
272
- text: "Walk over and say hi",
273
- condition: stats => stats.confidence >= 60,
274
- next: 'success'
275
- },
276
- {
277
- text: "Panic and get another drink",
278
- effect: stats => {
279
- stats.drinks++;
280
- stats.confidence += 10;
281
- this.advanceTime(10);
282
- },
283
- next: 'more_drinks'
284
- }
285
- ]
286
- },
287
- success: {
288
- text: "Congratulations! You managed to overcome your shyness and talk to her. Turns out, she's been hoping you'd come say hi all evening. Sometimes the hardest part is just taking that first step.",
289
- choices: [
290
- {
291
- text: "Play Again",
292
- effect: stats => {
293
- stats.confidence = 0;
294
- stats.drinks = 0;
295
- stats.time = new Date(2024, 0, 1, 20, 0);
296
- },
297
- next: 'start'
298
- }
299
- ]
300
  }
301
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  }
303
 
304
- // Start the game
305
- const game = new ShyGuyGame();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  </script>
307
  </body>
308
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Shy Guy Simulator - AI Edition</title>
7
  <style>
8
  body {
9
  font-family: Arial, sans-serif;
 
21
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
22
  }
23
 
24
+ .chat-container {
25
+ height: 400px;
26
+ overflow-y: auto;
27
+ margin: 20px 0;
28
+ padding: 10px;
29
+ background-color: #333;
30
+ border-radius: 4px;
31
+ }
32
+
33
+ .message {
34
+ margin: 10px 0;
35
+ padding: 10px;
36
+ border-radius: 4px;
37
+ word-wrap: break-word;
38
+ }
39
+
40
+ .wingman {
41
+ background-color: #2c5282;
42
+ margin-right: 20%;
43
+ }
44
+
45
+ .shyguy {
46
+ background-color: #4a5568;
47
+ margin-left: 20%;
48
+ }
49
+
50
+ .error {
51
+ background-color: #c53030;
52
+ text-align: center;
53
  }
54
 
55
+ #input-container {
56
  display: flex;
 
57
  gap: 10px;
58
+ margin-top: 20px;
59
+ }
60
+
61
+ #user-input {
62
+ flex-grow: 1;
63
+ padding: 10px;
64
+ border: none;
65
+ border-radius: 4px;
66
+ background-color: #4a4a4a;
67
+ color: white;
68
  }
69
 
70
  button {
71
  padding: 10px 20px;
72
+ background-color: #4299e1;
73
  color: white;
74
  border: none;
75
  border-radius: 4px;
76
  cursor: pointer;
 
77
  }
78
 
79
  button:hover {
80
+ background-color: #3182ce;
81
  }
82
 
83
  #stats {
 
85
  padding: 10px;
86
  background-color: #333;
87
  border-radius: 4px;
88
+ display: flex;
89
+ justify-content: space-between;
90
  }
91
 
92
+ .typing {
93
  font-style: italic;
94
+ color: #718096;
95
+ }
96
+
97
+ #api-key-container {
98
+ margin-bottom: 20px;
99
  }
100
  </style>
101
  </head>
102
  <body>
103
  <div id="game-container">
104
+ <h1>Shy Guy Simulator - AI Edition</h1>
105
+
106
+ <div id="api-key-container">
107
+ <input type="password" id="api-key" placeholder="Enter your Mistral API key" style="width: 100%; padding: 10px; margin-bottom: 10px;">
108
+ <button onclick="initializeGame()" id="start-button">Start Game</button>
109
+ </div>
110
+
111
+ <div id="game-content" style="display: none;">
112
+ <div id="stats">
113
+ <span>Confidence: <span id="confidence">0</span>%</span>
114
+ <span>Anxiety: <span id="anxiety">100</span>%</span>
115
+ <span>Time: <span id="time">8:00 PM</span></span>
116
+ </div>
117
+ <div class="chat-container" id="chat-container"></div>
118
+ <div id="input-container">
119
+ <input type="text" id="user-input" placeholder="Type your encouragement as wingman...">
120
+ <button onclick="handleUserInput()">Send</button>
121
+ </div>
122
  </div>
 
 
123
  </div>
124
 
125
  <script>
126
+ let game;
127
+
128
+ class ShyGuySimulator {
129
+ constructor(apiKey) {
130
+ this.apiKey = apiKey;
131
+ this.state = {
132
  confidence: 0,
133
+ anxiety: 100,
134
+ time: new Date(2024, 0, 1, 20, 0),
135
+ location: 'entrance',
136
+ hasSpokenToGirl: false,
137
+ isProcessing: false
138
  };
139
+
140
+ this.context = [
141
+ {
142
+ role: 'system',
143
+ content: `You are roleplaying as a shy and anxious guy at a homecoming party.
144
+ You're standing near the entrance, and the girl you like is across the room.
145
+ Your responses should reflect your social anxiety and reluctance to approach her.
146
+ Keep responses concise (max 2-3 sentences) and natural.
147
+ Express hesitation, worry, and self-doubt while reacting to the wingman's encouragement.`
148
+ }
149
  ];
150
 
151
  this.initialize();
152
  }
153
 
154
  initialize() {
155
+ this.addMessage("Hey! I'll be your wingman tonight. I see that girl you like over there - let's help you talk to her!", 'wingman');
156
+ this.addMessage("I... I don't know about this. Maybe I should just go home...", 'shyguy');
157
  this.updateStats();
 
 
158
  }
159
 
160
+ async handleInput(userInput) {
161
+ if (this.state.isProcessing) return;
162
+ this.state.isProcessing = true;
163
+
164
+ try {
165
+ if (!userInput.trim()) throw new Error("Please enter some text");
166
+
167
+ this.addMessage(userInput, 'wingman');
168
+ this.addLoadingMessage();
169
+
170
+ const currentState = `Current confidence: ${this.state.confidence}%, anxiety: ${this.state.anxiety}%, location: ${this.state.location}`;
171
+
172
+ this.context.push({
173
+ role: 'user',
174
+ content: `${userInput}\n\n${currentState}`
175
+ });
176
+
177
+ const response = await this.callMistralAPI();
178
 
179
+ this.removeLoadingMessage();
180
+ this.addMessage(response, 'shyguy');
181
 
182
+ this.updateGameState(response);
183
+
184
+ this.context.push({
185
+ role: 'assistant',
186
+ content: response
187
+ });
188
+
189
+ if (this.context.length > 10) {
190
+ this.context = [
191
+ this.context[0],
192
+ ...this.context.slice(-4)
193
+ ];
194
+ }
195
+ } catch (error) {
196
+ this.removeLoadingMessage();
197
+ this.addMessage(`Error: ${error.message}`, 'error');
198
+ console.error('Error:', error);
199
+ } finally {
200
+ this.state.isProcessing = false;
201
  }
202
  }
203
 
204
+ async callMistralAPI() {
205
+ try {
206
+ const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
207
+ method: 'POST',
208
+ headers: {
209
+ 'Content-Type': 'application/json',
210
+ 'Authorization': `Bearer ${this.apiKey}`
211
+ },
212
+ body: JSON.stringify({
213
+ model: 'mistral-large-latest',
214
+ messages: this.context,
215
+ max_tokens: 150,
216
+ temperature: 0.7
217
+ })
218
+ });
219
+
220
+ if (!response.ok) {
221
+ const error = await response.json();
222
+ throw new Error(error.error?.message || 'API request failed');
223
+ }
224
+
225
+ const data = await response.json();
226
+ return data.choices[0].message.content;
227
+ } catch (error) {
228
+ if (error.message.includes('API key')) {
229
+ throw new Error('Invalid API key. Please check your API key and try again.');
230
+ }
231
+ throw new Error('Failed to get response from AI. Please try again.');
232
+ }
233
  }
234
 
235
+ updateGameState(response) {
236
+ const lowerResponse = response.toLowerCase();
237
+
238
+ // Update confidence
239
+ if (lowerResponse.includes('okay') || lowerResponse.includes('maybe') || lowerResponse.includes('right')) {
240
+ this.state.confidence = Math.min(100, this.state.confidence + 10);
241
+ this.state.anxiety = Math.max(0, this.state.anxiety - 5);
242
+ } else if (lowerResponse.includes('no') || lowerResponse.includes('can\'t') || lowerResponse.includes('scared')) {
243
+ this.state.confidence = Math.max(0, this.state.confidence - 5);
244
+ this.state.anxiety = Math.min(100, this.state.anxiety + 10);
245
+ }
246
+
247
+ // Advance time
248
+ this.state.time = new Date(this.state.time.getTime() + 5 * 60000);
249
+
250
  this.updateStats();
251
  }
252
 
253
+ updateStats() {
254
+ document.getElementById('confidence').textContent = this.state.confidence;
255
+ document.getElementById('anxiety').textContent = this.state.anxiety;
256
+ document.getElementById('time').textContent =
257
+ this.state.time.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
258
+ }
259
 
260
+ addMessage(text, type) {
261
+ const chat = document.getElementById('chat-container');
262
+ const messageDiv = document.createElement('div');
263
+ messageDiv.className = `message ${type}`;
264
+ messageDiv.textContent = text;
265
+ chat.appendChild(messageDiv);
266
+ chat.scrollTop = chat.scrollHeight;
 
 
 
 
 
 
 
 
 
 
 
 
267
  }
268
 
269
+ addLoadingMessage() {
270
+ const chat = document.getElementById('chat-container');
271
+ const loadingDiv = document.createElement('div');
272
+ loadingDiv.className = 'message shyguy typing';
273
+ loadingDiv.id = 'loading-message';
274
+ loadingDiv.textContent = 'Thinking...';
275
+ chat.appendChild(loadingDiv);
276
+ chat.scrollTop = chat.scrollHeight;
277
+ }
278
+
279
+ removeLoadingMessage() {
280
+ const loadingMessage = document.getElementById('loading-message');
281
+ if (loadingMessage) {
282
+ loadingMessage.remove();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
284
+ }
285
+ }
286
+
287
+ function initializeGame() {
288
+ const apiKey = document.getElementById('api-key').value.trim();
289
+ if (!apiKey) {
290
+ alert('Please enter your Mistral API key');
291
+ return;
292
+ }
293
+
294
+ document.getElementById('api-key-container').style.display = 'none';
295
+ document.getElementById('game-content').style.display = 'block';
296
+
297
+ game = new ShyGuySimulator(apiKey);
298
  }
299
 
300
+ async function handleUserInput() {
301
+ if (!game) return;
302
+
303
+ const input = document.getElementById('user-input');
304
+ const text = input.value.trim();
305
+ if (text) {
306
+ await game.handleInput(text);
307
+ input.value = '';
308
+ }
309
+ }
310
+
311
+ document.getElementById('user-input')?.addEventListener('keypress', function(e) {
312
+ if (e.key === 'Enter') {
313
+ handleUserInput();
314
+ }
315
+ });
316
  </script>
317
  </body>
318
  </html>