testjs / index.html
zshashz's picture
ssss
d0d2794
raw
history blame
25.3 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 - Complete 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: 2fr 3fr;
gap: 20px;
}
#game-container {
background-color: #2a2a2a;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}
.chat-container {
height: 300px;
overflow-y: auto;
margin: 20px 0;
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;
}
.grid-container {
display: grid;
grid-template-columns: repeat(10, 1fr);
gap: 2px;
background-color: #1a1a1a;
padding: 10px;
margin: 20px 0;
border-radius: 4px;
aspect-ratio: 1;
}
.grid-cell {
aspect-ratio: 1;
background-color: #2d3748;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
cursor: pointer;
}
#player {
background-color: #4299e1;
width: 80%;
height: 80%;
border-radius: 50%;
position: absolute;
transition: all 0.3s ease;
}
.bar { background-color: #744210 !important; }
.dj { background-color: #2c5282 !important; }
.girl { background-color: #d53f8c !important; }
.sister { background-color: #805ad5 !important; }
.obstacle { background-color: #4a5568 !important; }
.legend {
display: flex;
gap: 10px;
margin-top: 10px;
flex-wrap: wrap;
}
.legend-item {
display: flex;
align-items: center;
gap: 5px;
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 4px;
}
#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;
}
button:hover {
background-color: #3182ce;
}
#stats {
margin-top: 20px;
padding: 10px;
background-color: #333;
border-radius: 4px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
}
.movement-controls {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 5px;
max-width: 200px;
margin: 20px auto;
}
.drunk-effect {
animation: wobble 1s infinite;
}
@keyframes wobble {
0% { transform: translate(0, 0) rotate(0deg); }
15% { transform: translate(-5%, 0) rotate(-5deg); }
30% { transform: translate(5%, 0) rotate(5deg); }
45% { transform: translate(-5%, 0) rotate(-3deg); }
60% { transform: translate(5%, 0) rotate(3deg); }
75% { transform: translate(-5%, 0) rotate(-1deg); }
100% { transform: translate(0, 0) rotate(0deg); }
}
#api-key-container {
margin-bottom: 20px;
}
.tooltip {
position: absolute;
background: rgba(0, 0, 0, 0.8);
padding: 5px;
border-radius: 4px;
font-size: 12px;
pointer-events: none;
z-index: 100;
display: none;
}
.win-screen {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.9);
padding: 20px;
border-radius: 10px;
text-align: center;
z-index: 1000;
}
</style>
</head>
<body>
<div id="game-container">
<h1>Shy Guy Simulator - Complete 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">
<span>Confidence: <span id="confidence">0</span>%</span>
<span>Anxiety: <span id="anxiety">100</span>%</span>
<span>Drinks: <span id="drinks">0</span></span>
<span>Time: <span id="time">8:00 PM</span></span>
</div>
<div class="game-layout">
<div class="chat-side">
<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 class="game-side">
<div class="grid-container" id="party-grid"></div>
<div class="legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #4299e1;"></div>
<span>You</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #744210;"></div>
<span>Bar</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #2c5282;"></div>
<span>DJ</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #d53f8c;"></div>
<span>Girl</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #805ad5;"></div>
<span>Sister</span>
</div>
</div>
<div class="movement-controls">
<button onclick="move(0, -1)"></button>
<button onclick="move(-1, 0)"></button>
<button onclick="move(1, 0)"></button>
<button onclick="move(0, 1)"></button>
</div>
</div>
</div>
</div>
</div>
<div class="tooltip" id="tooltip"></div>
<script>
class ShyGuySimulator {
constructor(apiKey) {
this.apiKey = apiKey;
this.state = {
confidence: 0,
anxiety: 100,
drinks: 0,
time: new Date(2024, 0, 1, 20, 0),
playerPos: { x: 0, y: 9 },
moveSpeed: 1,
isProcessing: false,
hasSpokenToGirl: false,
locations: {
bar: [{ x: 8, y: 1 }, { x: 9, y: 1 }],
dj: [{ x: 4, y: 0 }, { x: 5, y: 0 }],
girl: [{ x: 9, y: 8 }],
sister: [{ x: 2, y: 5 }],
obstacles: [
{ x: 3, y: 3 }, { x: 4, y: 3 },
{ x: 6, y: 6 }, { x: 7, y: 6 }
]
}
};
this.context = [
{
role: 'system',
content: `You are roleplaying as a shy and anxious guy at a homecoming party.
You're standing near the entrance, and the girl you like is across the room.
Your responses should reflect your social anxiety and reluctance to approach her.
Keep responses concise (max 2-3 sentences) and natural.
Express hesitation, worry, and self-doubt while reacting to the wingman's encouragement.
Current state: Confidence: ${this.state.confidence}%, Anxiety: ${this.state.anxiety}%`
}
];
this.initialize();
this.initializeGrid();
}
initialize() {
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');
this.addMessage("I... I don't know about this. Maybe I should just go home...", 'shyguy');
this.updateStats();
}
initializeGrid() {
const grid = document.getElementById('party-grid');
grid.innerHTML = '';
for (let y = 0; y < 10; y++) {
for (let x = 0; x < 10; x++) {
const cell = document.createElement('div');
cell.className = 'grid-cell';
cell.dataset.x = x;
cell.dataset.y = y;
if (this.isLocation(x, y, 'bar')) {
cell.classList.add('bar');
cell.dataset.tooltip = "Bar - Get liquid courage";
}
if (this.isLocation(x, y, 'dj')) {
cell.classList.add('dj');
cell.dataset.tooltip = "DJ - Vibe to the music";
}
if (this.isLocation(x, y, 'girl')) {
cell.classList.add('girl');
cell.dataset.tooltip = "The girl you like";
}
if (this.isLocation(x, y, 'sister')) {
cell.classList.add('sister');
cell.dataset.tooltip = "Your sister - Get some encouragement";
}
if (this.isLocation(x, y, 'obstacles')) {
cell.classList.add('obstacle');
cell.dataset.tooltip = "Can't walk here";
}
if (x === this.state.playerPos.x && y === this.state.playerPos.y) {
const player = document.createElement('div');
player.id = 'player';
if (this.state.drinks >= 3) player.classList.add('drunk-effect');
cell.appendChild(player);
}
cell.addEventListener('mouseover', this.showTooltip);
cell.addEventListener('mouseout', this.hideTooltip);
grid.appendChild(cell);
}
}
}
showTooltip(e) {
const tooltip = document.getElementById('tooltip');
const tooltipText = e.target.dataset.tooltip;
if (tooltipText) {
tooltip.textContent = tooltipText;
tooltip.style.display = 'block';
tooltip.style.left = e.pageX + 10 + 'px';
tooltip.style.top = e.pageY + 10 + 'px';
}
}
hideTooltip() {
const tooltip = document.getElementById('tooltip');
tooltip.style.display = 'none';
}
isLocation(x, y, type) {
return this.state.locations[type].some(pos => pos.x === x && pos.y === y);
}
async movePlayer(dx, dy) {
if (this.state.isProcessing) return;
let newX = this.state.playerPos.x + dx * this.state.moveSpeed;
let newY = this.state.playerPos.y + dy * this.state.moveSpeed;
// Check boundaries
newX = Math.max(0, Math.min(9, newX));
newY = Math.max(0, Math.min(9, newY));
// Check obstacles
if (this.isLocation(newX, newY, 'obstacles')) return;
// Random stumble when drunk
if (this.state.drinks >= 3) {
const stumbleChance = (this.state.drinks - 2) * 0.1;
if (Math.random() < stumbleChance) {
const randomDir = Math.random() < 0.5 ? 1 : -1;
if (Math.random() < 0.5) {
newX += randomDir;
} else {
newY += randomDir;
}
newX = Math.max(0, Math.min(9, newX));
newY = Math.max(0, Math.min(9, newY));
}
}
// Update position
this.state.playerPos = { x: newX, y: newY };
// Check for interactions
if (this.isLocation(newX, newY, 'bar')) {
this.state.drinks++;
this.state.confidence = Math.min(100, this.state.confidence + 15);
this.state.anxiety = Math.max(0, this.state.anxiety - 10);
this.state.moveSpeed = Math.min(2, 1 + this.state.drinks * 0.2);
await this.handleInput("*Takes another drink from the bar*");
// Add extra stumbling when too drunk
if (this.state.drinks > 5) {
await this.handleInput("*Starting to feel really dizzy...*");
this.state.confidence = Math.max(0, this.state.confidence - 5);
}
}
if (this.isLocation(newX, newY, 'sister')) {
this.state.confidence = Math.min(100, this.state.confidence + 20);
this.state.anxiety = Math.max(0, this.state.anxiety - 15);
await this.handleInput("*Talks to sister for encouragement*");
}
if (this.isLocation(newX, newY, 'dj')) {
this.state.confidence = Math.min(100, this.state.confidence + 10);
this.state.anxiety = Math.max(0, this.state.anxiety - 5);
await this.handleInput("*Vibing to the music near the DJ*");
}
if (this.isLocation(newX, newY, 'girl')) {
if (this.state.confidence >= 70 && this.state.anxiety <= 50) {
await this.handleInput("*Finally gathered the courage to talk to her!*");
this.gameWon();
} else {
await this.handleInput("*Gets too nervous and quickly walks away*");
this.state.playerPos = {
x: Math.max(0, newX - 2),
y: Math.max(0, newY - 2)
};
this.state.anxiety += 15;
this.state.confidence = Math.max(0, this.state.confidence - 10);
}
}
// Advance time
this.state.time = new Date(this.state.time.getTime() + 2 * 60000); // 2 minutes per move
this.updateStats();
this.initializeGrid();
}
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}%, Anxiety: ${this.state.anxiety}%, Drinks: ${this.state.drinks}, Location: ${this.getLocationDescription()}`;
this.context.push({
role: 'user',
content: `${userInput}\n\n${currentState}`
});
const response = await this.callMistralAPI();
this.removeLoadingMessage();
this.addMessage(response, 'shyguy');
this.context.push({
role: 'assistant',
content: response
});
// Keep context manageable
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;
}
}
getLocationDescription() {
const { x, y } = this.state.playerPos;
if (this.isLocation(x, y, 'bar')) return 'at the bar';
if (this.isLocation(x, y, 'dj')) return 'near the DJ';
if (this.isLocation(x, y, 'sister')) return 'with sister';
if (this.isLocation(x, y, 'girl')) return 'near the girl';
return 'in the room';
}
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.');
}
}
gameWon() {
this.addMessage("Congratulations! You successfully talked to her, and she seems interested! The party wasn't so scary after all.", 'wingman');
this.state.hasSpokenToGirl = true;
const winScreen = document.createElement('div');
winScreen.className = 'win-screen';
winScreen.innerHTML = `
<h2>You did it!</h2>
<p>Final Stats:</p>
<p>Confidence: ${this.state.confidence}%</p>
<p>Anxiety: ${this.state.anxiety}%</p>
<p>Drinks: ${this.state.drinks}</p>
<p>Time taken: ${this.getTimeDifference()}</p>
<button onclick="location.reload()">Play Again</button>
`;
document.body.appendChild(winScreen);
}
getTimeDifference() {
const startTime = new Date(2024, 0, 1, 20, 0);
const timeDiff = this.state.time - startTime;
const minutes = Math.floor(timeDiff / 60000);
return `${minutes} minutes`;
}
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('anxiety').textContent = this.state.anxiety;
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 = '';
}
}
async function move(dx, dy) {
if (game) {
await game.movePlayer(dx, dy);
}
}
// Keyboard controls
document.addEventListener('keydown', async (e) => {
if (!game) return;
switch(e.key) {
case 'ArrowUp':
await move(0, -1);
break;
case 'ArrowDown':
await move(0, 1);
break;
case 'ArrowLeft':
await move(-1, 0);
break;
case 'ArrowRight':
await move(1, 0);
break;
}
});
// Enter key for input
document.getElementById('user-input')?.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
handleUserInput();
}
});
</script>
</body>
</html>