testjs / index.html
zshashz's picture
aa
4a9fc22
raw
history blame
15.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shy Guy Simulator - Grid Edition</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 20px auto;
padding: 20px;
background-color: #1a1a1a;
color: #fff;
}
.game-layout {
display: grid;
grid-template-columns: 1fr 400px;
gap: 20px;
}
#game-container {
background-color: #2a2a2a;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
.grid-container {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 2px;
background-color: #333;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
}
.grid-cell {
aspect-ratio: 1;
background-color: #4a4a4a;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
}
.chat-container {
height: 400px;
overflow-y: auto;
padding: 10px;
background-color: #333;
border-radius: 4px;
}
.message {
margin: 10px 0;
padding: 10px;
border-radius: 4px;
word-wrap: break-word;
}
.wingman { background-color: #2c5282; margin-right: 20%; }
.shyguy { background-color: #4a5568; margin-left: 20%; }
.error { background-color: #c53030; text-align: center; }
#input-container {
display: flex;
gap: 10px;
margin-top: 20px;
}
#user-input {
flex-grow: 1;
padding: 10px;
border: none;
border-radius: 4px;
background-color: #4a4a4a;
color: white;
}
button {
padding: 10px 20px;
background-color: #4299e1;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
#stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 20px;
}
.stat-box {
background-color: #333;
padding: 10px;
border-radius: 4px;
text-align: center;
}
#api-key-container {
margin-bottom: 20px;
}
</style>
</head>
<body>
<div id="game-container">
<h1>Shy Guy Simulator - Grid Edition</h1>
<div id="api-key-container">
<input type="password" id="api-key" placeholder="Enter your Mistral API key" style="width: 100%; padding: 10px; margin-bottom: 10px;">
<button onclick="initializeGame()" id="start-button">Start Game</button>
</div>
<div id="game-content" style="display: none;">
<div id="stats">
<div class="stat-box">Confidence: <span id="confidence">0</span>%</div>
<div class="stat-box">Drinks: <span id="drinks">0</span></div>
<div class="stat-box">Time: <span id="time">8:00 PM</span></div>
</div>
<div class="game-layout">
<div class="grid-container" id="grid"></div>
<div class="chat-section">
<div class="chat-container" id="chat-container"></div>
<div id="input-container">
<input type="text" id="user-input" placeholder="Type your encouragement as wingman...">
<button onclick="handleUserInput()">Send</button>
</div>
</div>
</div>
</div>
</div>
<script>
class ShyGuySimulator {
constructor(apiKey) {
this.apiKey = apiKey;
this.state = {
confidence: 0,
drinks: 0,
time: new Date(2024, 0, 1, 20, 0),
position: { x: 0, y: 0 }, // Start at top-left
isProcessing: false
};
this.targetPosition = { x: 7, y: 7 }; // Girl's position (bottom-right)
this.gridSize = 8;
// System prompt that instructs the model to return structured responses
this.context = [{
role: 'system',
content: `You are roleplaying as a shy guy at a party, providing both dialogue and movement decisions.
The party is on an 8x8 grid. You start at (0,0), and the girl you like is at (7,7).
ALWAYS structure your responses in this exact format:
{
"dialogue": "Your spoken response here",
"movement": {
"x": number (-1, 0, or 1 for movement),
"y": number (-1, 0, or 1 for movement)
},
"emotion": "anxious|nervous|slightly_confident|confident"
}
Rules:
1. When drinks > 2, be more likely to move toward the girl
2. When confidence < 30, prefer to move away or stay still
3. Keep dialogue natural and brief (1-2 sentences)
4. Movement should reflect emotional state`
}];
this.initialize();
}
initialize() {
this.setupGrid();
this.addMessage("Let's help you talk to her! I'll be your wingman tonight.", 'wingman');
this.addMessage("I... I don't know about this. Maybe I should just go home...", 'shyguy');
this.updateStats();
this.updateGrid();
}
setupGrid() {
const grid = document.getElementById('grid');
grid.innerHTML = '';
for (let y = 0; y < this.gridSize; y++) {
for (let x = 0; x < this.gridSize; x++) {
const cell = document.createElement('div');
cell.className = 'grid-cell';
cell.id = `cell-${x}-${y}`;
grid.appendChild(cell);
}
}
}
updateGrid() {
// Clear all cells
document.querySelectorAll('.grid-cell').forEach(cell => {
cell.textContent = '';
});
// Place shy guy
const shyGuyCell = document.getElementById(`cell-${this.state.position.x}-${this.state.position.y}`);
if (shyGuyCell) shyGuyCell.textContent = '😳';
// Place girl
const girlCell = document.getElementById(`cell-${this.targetPosition.x}-${this.targetPosition.y}`);
if (girlCell) girlCell.textContent = '👧';
}
async handleInput(userInput) {
if (this.state.isProcessing) return;
this.state.isProcessing = true;
try {
if (!userInput.trim()) throw new Error("Please enter some text");
this.addMessage(userInput, 'wingman');
this.addLoadingMessage();
const currentState = `Current state:
- Confidence: ${this.state.confidence}%
- Drinks: ${this.state.drinks}
- Position: (${this.state.position.x}, ${this.state.position.y})
- Distance to girl: ${this.calculateDistance()}`;
this.context.push({
role: 'user',
content: `${userInput}\n\n${currentState}`
});
const response = await this.callMistralAPI();
this.removeLoadingMessage();
this.processAIResponse(response);
this.context.push({
role: 'assistant',
content: response
});
if (this.context.length > 10) {
this.context = [
this.context[0],
...this.context.slice(-4)
];
}
} catch (error) {
this.removeLoadingMessage();
this.addMessage(`Error: ${error.message}`, 'error');
console.error('Error:', error);
} finally {
this.state.isProcessing = false;
}
}
calculateDistance() {
const dx = this.targetPosition.x - this.state.position.x;
const dy = this.targetPosition.y - this.state.position.y;
return Math.sqrt(dx * dx + dy * dy);
}
processAIResponse(responseText) {
try {
// Extract JSON from response if it's wrapped in text
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
const responseData = jsonMatch ? JSON.parse(jsonMatch[0]) : JSON.parse(responseText);
// Add the dialogue to chat
this.addMessage(responseData.dialogue, 'shyguy');
// Update position
const newX = Math.max(0, Math.min(7, this.state.position.x + responseData.movement.x));
const newY = Math.max(0, Math.min(7, this.state.position.y + responseData.movement.y));
this.state.position = { x: newX, y: newY };
// Update confidence based on emotion
const emotionConfidenceMap = {
'anxious': -5,
'nervous': 0,
'slightly_confident': 5,
'confident': 10
};
this.state.confidence = Math.max(0, Math.min(100,
this.state.confidence + (emotionConfidenceMap[responseData.emotion] || 0)
));
// Update the grid and stats
this.updateGrid();
this.updateStats();
// Check win condition
if (this.state.position.x === this.targetPosition.x &&
this.state.position.y === this.targetPosition.y) {
this.handleWin();
}
} catch (error) {
console.error('Error processing AI response:', error);
this.addMessage("I... uh... *mumbles something incoherent*", 'shyguy');
}
}
handleWin() {
this.addMessage("Oh my god, I actually made it! Hi... I've been wanting to talk to you...", 'shyguy');
this.addMessage("Congratulations! You've helped Shy Guy reach his goal!", 'wingman');
// Disable input
document.getElementById('user-input').disabled = true;
}
async callMistralAPI() {
try {
const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({
model: 'mistral-large-latest',
messages: this.context,
max_tokens: 150,
temperature: 0.7
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'API request failed');
}
const data = await response.json();
return data.choices[0].message.content;
} catch (error) {
if (error.message.includes('API key')) {
throw new Error('Invalid API key. Please check your API key and try again.');
}
throw new Error('Failed to get response from AI. Please try again.');
}
}
addMessage(text, type) {
const chat = document.getElementById('chat-container');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
messageDiv.textContent = text;
chat.appendChild(messageDiv);
chat.scrollTop = chat.scrollHeight;
}
addLoadingMessage() {
const chat = document.getElementById('chat-container');
const loadingDiv = document.createElement('div');
loadingDiv.className = 'message shyguy typing';
loadingDiv.id = 'loading-message';
loadingDiv.textContent = 'Thinking...';
chat.appendChild(loadingDiv);
chat.scrollTop = chat.scrollHeight;
}
removeLoadingMessage() {
const loadingMessage = document.getElementById('loading-message');
if (loadingMessage) {
loadingMessage.remove();
}
}
updateStats() {
document.getElementById('confidence').textContent = this.state.confidence;
document.getElementById('drinks').textContent = this.state.drinks;
document.getElementById('time').textContent =
this.state.time.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
}
}
let game;
function initializeGame() {
const apiKey = document.getElementById('api-key').value.trim();
if (!apiKey) {
alert('Please enter your Mistral API key');
return;
}
document.getElementById('api-key-container').style.display = 'none';
document.getElementById('game-content').style.display = 'block';
game = new ShyGuySimulator(apiKey);
}
async function handleUserInput() {
if (!game) return;
const input = document.getElementById('user-input');
const text = input.value.trim();
if (text) {
await game.handleInput(text);
input.value = '';
}
}
document.getElementById('user-input')?.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
handleUserInput();
}
});
</script>
</body>
</html>