All checks were successful
Publish Docker image / Push Docker image to Docker Hub (push) Successful in 1m55s
234 lines
6.9 KiB
JavaScript
234 lines
6.9 KiB
JavaScript
const roomController = require('./room');
|
|
const youtubeService = require('../services/youtubeService');
|
|
|
|
const shuffleArray = (array) => {
|
|
const shuffled = [...array];
|
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
}
|
|
return shuffled;
|
|
};
|
|
|
|
const gameStates = {};
|
|
|
|
const initializeGameState = (roomId) => {
|
|
if (!gameStates[roomId]) {
|
|
gameStates[roomId] = {
|
|
round: 0,
|
|
phase: 'waiting',
|
|
roles: {},
|
|
selectedSong: null,
|
|
scores: {},
|
|
songOptions: [],
|
|
guessResults: {},
|
|
currentComposer: null,
|
|
roundStartTime: null,
|
|
phaseTimeLimit: {
|
|
composing: 30,
|
|
guessing: 10
|
|
},
|
|
lastFrequency: 440,
|
|
};
|
|
|
|
const users = roomController.getRoomUsers(roomId);
|
|
users.forEach(user => {
|
|
gameStates[roomId].scores[user.id] = 0;
|
|
});
|
|
}
|
|
|
|
return gameStates[roomId];
|
|
};
|
|
|
|
const startNewRound = async (roomId) => {
|
|
const gameState = gameStates[roomId];
|
|
if (!gameState) return false;
|
|
|
|
const users = roomController.getRoomUsers(roomId);
|
|
if (users.length < 2) return false;
|
|
|
|
gameState.round += 1;
|
|
gameState.phase = 'composing';
|
|
gameState.guessResults = {};
|
|
gameState.roundStartTime = Date.now();
|
|
gameState.roles = {};
|
|
|
|
gameState.currentComposer = determineNextComposer(roomId, gameState, users);
|
|
|
|
const success = await selectSongAndOptions(gameState);
|
|
if (!success) return false;
|
|
|
|
users.forEach(user => {
|
|
gameState.roles[user.id] = user.id === gameState.currentComposer ? 'composer' : 'guesser';
|
|
});
|
|
|
|
return true;
|
|
};
|
|
|
|
const determineNextComposer = (roomId, gameState, users) => {
|
|
if (gameState.round === 1 || !gameState.currentComposer) {
|
|
return users[Math.floor(Math.random() * users.length)].id;
|
|
}
|
|
|
|
const currentIndex = users.findIndex(user => user.id === gameState.currentComposer);
|
|
|
|
if (currentIndex === -1) {
|
|
return users[Math.floor(Math.random() * users.length)].id;
|
|
}
|
|
|
|
return users[(currentIndex + 1) % users.length].id;
|
|
};
|
|
|
|
// Then modify the selectSongAndOptions function to use the shuffle:
|
|
const selectSongAndOptions = async (gameState) => {
|
|
try {
|
|
const availableIds = await youtubeService.getAvailableSongIds();
|
|
|
|
if (!availableIds || availableIds.length === 0) {
|
|
console.error("No song IDs available for selection");
|
|
return false;
|
|
}
|
|
|
|
const selectedId = availableIds[Math.floor(Math.random() * availableIds.length)];
|
|
gameState.selectedSong = { id: selectedId };
|
|
|
|
const optionIds = new Set([selectedId]);
|
|
const attempts = Math.min(20, availableIds.length);
|
|
|
|
let count = 0;
|
|
while (optionIds.size < 5 && count < attempts) {
|
|
const randomId = availableIds[Math.floor(Math.random() * availableIds.length)];
|
|
optionIds.add(randomId);
|
|
count++;
|
|
}
|
|
|
|
// Shuffle the song options to randomize the position of the correct answer
|
|
gameState.songOptions = shuffleArray(Array.from(optionIds).map(id => ({ id })));
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error("Error selecting songs and options:", error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const getTimeRemaining = (roomId) => {
|
|
const gameState = gameStates[roomId];
|
|
if (!gameState || !gameState.roundStartTime) return 0;
|
|
|
|
const phaseDuration = gameState.phaseTimeLimit[gameState.phase] * 1000;
|
|
const timeElapsed = Date.now() - gameState.roundStartTime;
|
|
const timeRemaining = Math.max(0, phaseDuration - timeElapsed);
|
|
|
|
return Math.ceil(timeRemaining / 1000);
|
|
};
|
|
|
|
const advancePhase = (roomId) => {
|
|
const gameState = gameStates[roomId];
|
|
if (!gameState) {
|
|
console.error(`Cannot advance phase: no game state for room ${roomId}`);
|
|
return false;
|
|
}
|
|
|
|
const currentPhase = gameState.phase;
|
|
console.log(`Advancing phase for room ${roomId} from ${currentPhase}`);
|
|
|
|
if (currentPhase === 'composing') {
|
|
gameState.phase = 'guessing';
|
|
gameState.roundStartTime = Date.now();
|
|
console.log(`Room ${roomId} advanced to guessing phase`);
|
|
return { phase: 'guessing' };
|
|
}
|
|
else if (currentPhase === 'guessing') {
|
|
gameState.phase = 'results';
|
|
console.log(`Room ${roomId} advanced to results phase`);
|
|
return { phase: 'results' };
|
|
}
|
|
else if (currentPhase === 'results') {
|
|
console.log(`Room ${roomId} starting new round from results phase`);
|
|
return startNewRound(roomId);
|
|
}
|
|
|
|
console.warn(`Cannot advance from unknown phase "${currentPhase}" in room ${roomId}`);
|
|
return false;
|
|
};
|
|
|
|
const updateFrequency = (roomId, frequency) => {
|
|
if (!gameStates[roomId]) return false;
|
|
gameStates[roomId].lastFrequency = frequency;
|
|
return true;
|
|
};
|
|
|
|
const submitGuess = (roomId, userId, songId) => {
|
|
const gameState = gameStates[roomId];
|
|
if (!gameState || gameState.phase !== 'guessing' || gameState.roles[userId] !== 'guesser') {
|
|
return false;
|
|
}
|
|
|
|
const isCorrect = gameState.selectedSong.id === parseInt(songId);
|
|
const points = isCorrect ? 10 : 0;
|
|
|
|
gameState.guessResults[userId] = {songId, isCorrect, points};
|
|
|
|
if (isCorrect) {
|
|
gameState.scores[userId] = (gameState.scores[userId] || 0) + points;
|
|
}
|
|
|
|
return {
|
|
isCorrect,
|
|
correctSong: gameState.selectedSong,
|
|
points
|
|
};
|
|
};
|
|
|
|
const getCurrentFrequency = (roomId) => gameStates[roomId]?.lastFrequency || 440;
|
|
const getRoles = (roomId) => gameStates[roomId]?.roles || {};
|
|
const getUserRole = (roomId, userId) => gameStates[roomId]?.roles[userId] || null;
|
|
const getSongOptions = (roomId) => gameStates[roomId]?.songOptions || [];
|
|
const getSelectedSong = (roomId) => gameStates[roomId]?.selectedSong || null;
|
|
const getRoundResults = (roomId) => {
|
|
const gameState = gameStates[roomId];
|
|
if (!gameState) return {round: 0, scores: {}, guessResults: {}, selectedSong: null};
|
|
|
|
const playerNames = {};
|
|
Object.keys(gameState.scores).forEach(userId => {
|
|
playerNames[userId] = roomController.getUserName(userId) || "Player";
|
|
});
|
|
|
|
return {
|
|
round: gameState.round,
|
|
selectedSong: gameState.selectedSong,
|
|
guessResults: gameState.guessResults,
|
|
scores: gameState.scores,
|
|
playerNames: playerNames
|
|
};
|
|
};
|
|
|
|
const cleanupGameState = (roomId) => {
|
|
delete gameStates[roomId];
|
|
};
|
|
|
|
const getCurrentComposer = (roomId) => gameStates[roomId]?.currentComposer || null;
|
|
|
|
const getGameState = (roomId) => {
|
|
return gameStates[roomId] || null;
|
|
};
|
|
|
|
module.exports = {
|
|
initializeGameState,
|
|
startNewRound,
|
|
getTimeRemaining,
|
|
advancePhase,
|
|
updateFrequency,
|
|
getCurrentFrequency,
|
|
submitGuess,
|
|
getRoundResults,
|
|
getRoles,
|
|
getUserRole,
|
|
getSongOptions,
|
|
getSelectedSong,
|
|
cleanupGameState,
|
|
getCurrentComposer,
|
|
getGameState
|
|
};
|