diff --git a/game/qgservice/generatorLogic/queries.js b/game/qgservice/generatorLogic/queries.js index 8f355a6..7dfd197 100644 --- a/game/qgservice/generatorLogic/queries.js +++ b/game/qgservice/generatorLogic/queries.js @@ -47,12 +47,14 @@ const chemicalElementQuery = `SELECT ?element ?elementLabel ?symbol WHERE { } ` -const monumentQuery = `SELECT ?monument ?monumentLabel ?country ?countryLabel WHERE { - ?monument wdt:P31 wd:Q4989906; # Instance of historical monument - wdt:P17 ?country. # Country of the monument +const monumentQuery = `SELECT ?monument ?monumentLabel ?country ?countryLabel ?followers WHERE { + ?monument wdt:P31 wd:Q570116; # Instance of historical monument + wdt:P8687 ?followers; # Social media followers count + wdt:P17 ?country. # Country of the monument SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". } } -LIMIT 1000 +ORDER BY DESC(?followers) +LIMIT 100 ` diff --git a/game/qgservice/generatorLogic/questiongenerator.js b/game/qgservice/generatorLogic/questiongenerator.js index e259aa5..780ed34 100644 --- a/game/qgservice/generatorLogic/questiongenerator.js +++ b/game/qgservice/generatorLogic/questiongenerator.js @@ -95,7 +95,7 @@ function generateQuestionCapital(countryCapitalMap) { // Create the question object const question = { uuid: uuid.v4(), - question: `What is the symbol of ${chemical}?`, + question: `What is the chemical symbol of ${chemical}?`, correctAnswer: symbol, incorrectAnswer1: incorrectAnswers[0], incorrectAnswer2: incorrectAnswers[1], @@ -125,7 +125,7 @@ function generateQuestionMonument(monumentMap) { while (incorrectAnswers.length < 3) { const randomMonument = monumentArray[Math.floor(Math.random() * monumentArray.length)]; const [randomMonumentLabel, randomCountry] = randomMonument; - if (randomMonumentLabel !== monumentLabel && !incorrectAnswers.includes(randomCountry)) { + if (randomMonumentLabel !== monumentLabel && !incorrectAnswers.includes(randomCountry) && randomCountry!= countryLabel) { incorrectAnswers.push(randomCountry); } } diff --git a/multiplayerservice/server.js b/multiplayerservice/server.js index b3a6f56..cb7aa3d 100644 --- a/multiplayerservice/server.js +++ b/multiplayerservice/server.js @@ -89,13 +89,11 @@ io.on('connection', socket => { }); socket.on('updateQuestions', (partyCode, questions) => { - console.log('here') // Broadcast questions to all users in the specified party broadcastQuestions(partyCode, questions); }); socket.on('exitParty', (partyCode) => { - console.log("aqui") if (lobby[partyCode]) { // Remove the user from the lobby delete lobby[partyCode][socket.id]; diff --git a/webapp/src/components/game/LobbyGame.scss b/webapp/src/components/game/LobbyGame.scss index 4f607f1..973e5a4 100644 --- a/webapp/src/components/game/LobbyGame.scss +++ b/webapp/src/components/game/LobbyGame.scss @@ -32,6 +32,13 @@ table { padding: 10px; } + .lobby-title-container { + display: flex; + align-items: center; + justify-content: space-between; + margin: 0px 2em 0px 2em; + } + img { max-width: 100px; // Cambia el tamaño máximo de la imagen según sea necesario height: auto; diff --git a/webapp/src/components/game/QuestionsGame.scss b/webapp/src/components/game/QuestionsGame.scss new file mode 100644 index 0000000..29a81b7 --- /dev/null +++ b/webapp/src/components/game/QuestionsGame.scss @@ -0,0 +1,30 @@ +.question-container { + text-align: center; + } + + .question-title { + font-size: 24px; + margin-bottom: 20px; + } + + .answer-grid { + margin-top: 3em; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.5em; + } + + .answer-grid button { + width: 100%; + height: 50px; + border-radius: 10px; /* Rounded borders */ + font-size: 18px; /* Big text */ + font-weight: bold; /* Bold font */ + transition: transform 0.2s; /* Transition for scale animation */ + cursor:pointer; + } + + .answer-grid button:active { + transform: scale(0.95); /* Scale down on active state */ + } + \ No newline at end of file diff --git a/webapp/src/components/game/ScoreboardGame.css b/webapp/src/components/game/ScoreboardGame.css index 8194be0..872e07e 100644 --- a/webapp/src/components/game/ScoreboardGame.css +++ b/webapp/src/components/game/ScoreboardGame.css @@ -1,22 +1,30 @@ table { width: 100%; border-collapse: collapse; + border-radius: 50%; + margin-top: 5rem; } table caption { - background-color: black; + background-color: #1E1E1E; color: white; - padding: 10px; + padding: 20px; text-align: center; } table th, table td { - border: 1px solid black; + border: 1px solid #1E1E1E; width: 33.33%; text-align: center; padding: 10px; } table th { - color: blue; + font-weight: bold; + color: #007bff; + } + + .selected { + background-color: #D9D9D9; + font-weight: bold; } \ No newline at end of file diff --git a/webapp/src/components/game/ScoreboardGame.tsx b/webapp/src/components/game/ScoreboardGame.tsx index 28eb8f9..9f9ebfc 100644 --- a/webapp/src/components/game/ScoreboardGame.tsx +++ b/webapp/src/components/game/ScoreboardGame.tsx @@ -11,26 +11,33 @@ interface ScoreboardGameProps { const ScoreboardGame:FC = ({userScoresSinglePlayer, userScoresMultiPlayer}) => { let sorted; + const username = localStorage.getItem('username'); if(userScoresSinglePlayer){ sorted = userScoresSinglePlayer.sort((a, b) => b.points - a.points); } else if (userScoresMultiPlayer){ sorted = userScoresMultiPlayer.sort((a, b) => b.points - a.points); } return ( -
- + - + {sorted.map((score, index) => { + if(score.username === username) return( + + + + + + ); return ( - + @@ -39,7 +46,6 @@ const ScoreboardGame:FC = ({userScoresSinglePlayer, userSco })}
ScoreboardGame Scoreboard
UsernamePosition Username Points
{index+1}{score.username}{score.points}
{index+1} {score.username} {score.points}
-
) } diff --git a/webapp/src/components/game/multiplayer/GameMultiPlayer.tsx b/webapp/src/components/game/multiplayer/GameMultiPlayer.tsx index 7644e54..f7d974f 100644 --- a/webapp/src/components/game/multiplayer/GameMultiPlayer.tsx +++ b/webapp/src/components/game/multiplayer/GameMultiPlayer.tsx @@ -31,7 +31,6 @@ export interface PlayerWithPoints { const GameMultiPlayer: FC = () => { const SERVER_URL = 'http://localhost:8006'; - const username = localStorage.getItem("username") const [socket, setSocket] = useState(null); const [stage, setStage] = useState(1) @@ -40,6 +39,26 @@ const GameMultiPlayer: FC = () => { const [questions, setQuestions] = useState([]); const [sortedUsersByPoints, setSortedUsersByPoints] = useState([]) + const shuffleQuestions = (questions: Question4Answers[]): Question4Answers[] => { + return questions.map((question) => { + const { correctAnswer, incorrectAnswer1, incorrectAnswer2, incorrectAnswer3 } = question; + const answers = [correctAnswer, incorrectAnswer1, incorrectAnswer2, incorrectAnswer3]; + + // Shuffle the order of incorrect answers (answers excluding correctAnswer) + for (let i = answers.length - 1; i > 1; i--) { + const j = Math.floor(Math.random() * (i - 1) + 1); // Exclude correctAnswer from shuffling + [answers[i], answers[j]] = [answers[j], answers[i]]; + } + + return { + ...question, + incorrectAnswer1: answers[1], + incorrectAnswer2: answers[2], + incorrectAnswer3: answers[3], + }; + }); + }; + useEffect(() => { const newSocket = io(SERVER_URL); setSocket(newSocket); @@ -61,13 +80,19 @@ const GameMultiPlayer: FC = () => { console.log('Party not found'); }); - newSocket.on('allPlayersFinished', (playersWithPoints:PlayerWithPoints[]) => { + newSocket.on('allPlayersFinished', async (playersWithPoints:PlayerWithPoints[]) => { + await new Promise((resolve) => { // Specify void as the type argument + setTimeout(() => { + resolve(); // Resolve the promise without any arguments + }, 1000); + }); setSortedUsersByPoints(playersWithPoints); setStage(4); }) newSocket.on('questionsUpdated', (questions: Question4Answers[]) => { - setQuestions(questions); + const shuffledquestions = shuffleQuestions(questions); + setQuestions(shuffledquestions); setStage(3); }) diff --git a/webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx b/webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx index d21bd1a..1f4e79e 100644 --- a/webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx +++ b/webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx @@ -44,8 +44,10 @@ const LobbyMultiPlayer: FC = ({socket, handleCurrentStage return (
-

Lobby - Multiplayer

-

Party code: {partyCode}

+
+

Lobby - Multiplayer

+

Party code: {partyCode}

+
{users.map((player) => (
{player.uuid} diff --git a/webapp/src/components/game/multiplayer/MenuMultiplayer.css b/webapp/src/components/game/multiplayer/MenuMultiplayer.css index 941263f..9aa4d12 100644 --- a/webapp/src/components/game/multiplayer/MenuMultiplayer.css +++ b/webapp/src/components/game/multiplayer/MenuMultiplayer.css @@ -2,11 +2,25 @@ display: flex; flex-direction: column; align-items: center; + justify-content: center; + height: 70vh; + } + .container > * { + margin-bottom: 60px; /* Adjust the margin as needed */ } .create-party-button { - margin-top: 20px; - } + font-size: 1.2em; + padding: 10px 20px; + margin: 10px; + text-decoration: none; + background-color: #007bff; + color: #ffffff; + border: none; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.3s ease; + } .join-party-container { margin-top: 20px; @@ -14,14 +28,28 @@ align-items: center; } - .join-party-label { - margin-right: 10px; - } - .join-party-input { + width: 250px; + height: 35px; + font-size: 16px; + padding: 10px; margin-right: 10px; } .join-party-button { + font-size: 1.2em; /* Adjust as needed */ + padding: 10px 20px; /* Adjust as needed */ + margin: 10px; /* Adjust as needed */ + text-decoration: none; + background-color: #007bff; + color: #ffffff; /* White text color */ + border: none; + border-radius: 8px; /* Make the button slightly round */ cursor: pointer; + transition: background-color 0.3s ease; + } + + h1{ + margin-bottom: 20px; + margin-top: 20px; } \ No newline at end of file diff --git a/webapp/src/components/game/multiplayer/MenuMultiplayer.tsx b/webapp/src/components/game/multiplayer/MenuMultiplayer.tsx index 9512f84..dd1067c 100644 --- a/webapp/src/components/game/multiplayer/MenuMultiplayer.tsx +++ b/webapp/src/components/game/multiplayer/MenuMultiplayer.tsx @@ -38,15 +38,16 @@ const MenuMultiplayer: FC = ({socket, handleCurrentStage, } return ( -
- -
- setTypedCode(e.target.value)}> - +
+

Create a party or join one!

+ +
+ setTypedCode(e.target.value)}> + +
-
) } diff --git a/webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx b/webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx index 90a44b0..58b0bda 100644 --- a/webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx +++ b/webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx @@ -1,7 +1,8 @@ -import { FC, useState } from 'react' +import { FC, useMemo, useState } from 'react' import { SocketProps } from './GameMultiPlayer'; import { Question4Answers } from '../singleplayer/GameSinglePlayer'; import axios from 'axios'; +import '../QuestionsGame.scss'; interface QuestionsMultiPlayerProps { socket: SocketProps; @@ -10,19 +11,46 @@ interface QuestionsMultiPlayerProps { partyCode: string } + const QuestionsMultiPlayer: FC = ({socket, questions, partyCode}) => { + + const answersShuffled = useMemo(() => { + return questions.map((question) => { + const answers = [question.correctAnswer, question.incorrectAnswer1, question.incorrectAnswer2, question.incorrectAnswer3]; + for (let i = answers.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [answers[i], answers[j]] = [answers[j], answers[i]]; + } + return answers; + }); + }, [questions]); + const uuid = localStorage.getItem("userUUID"); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; const [currentQuestion, setCurrentQuestion] = useState(0); const [correctAnswers, setCorrectAnswers] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); + + const [isWaiting, setIsWaiting] = useState(false); - const handleAnswerClick = async (answer: string) => { - if(questions[currentQuestion].correctAnswer === answer){ - setCorrectAnswers(correctAnswers + 1); + + const handleAnswerClick = async (answer: string, isCorrect:boolean) => { + + if(!isWaiting){ + setIsWaiting(true); + setSelectedAnswer(answer); + setTimeout(() => { + if (isCorrect) { + setCorrectAnswers(correctAnswers + 1); + } + setCurrentQuestion(currentQuestion + 1); + setSelectedAnswer(null); + }, 1000); + setIsWaiting(false); } - setCurrentQuestion(currentQuestion + 1); + if(currentQuestion+2 === questions.length){ const totalPoints = calculatePoints(correctAnswers, questions.length); // the player has finished the game @@ -34,6 +62,11 @@ const QuestionsMultiPlayer: FC = ({socket, questions, "totalScore": totalPoints, "isWinner": false }]} + + // update score in localstorage + const previousScore = parseInt(localStorage.getItem("score")) + localStorage.setItem("score", (previousScore + totalPoints).toString()) + await axios.post(`${apiEndpoint}/updateStats`, requestData); // pass the points obtained of each player to the socket socket.emit('playerFinished', partyCode, totalPoints) @@ -46,38 +79,36 @@ const QuestionsMultiPlayer: FC = ({socket, questions, return points; } - const getShuffledAnswers = () => { - const answers = [ - questions[currentQuestion].correctAnswer, - questions[currentQuestion].incorrectAnswer1, - questions[currentQuestion].incorrectAnswer2, - questions[currentQuestion].incorrectAnswer3, - ]; - - for (let i = answers.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [answers[i], answers[j]] = [answers[j], answers[i]]; + const getAnswers = () => { + const answers = answersShuffled[currentQuestion]; + if (answers.length > 4) { + console.log(answers) + const removeCount = answers.length - 4; + answers.splice(0, removeCount); } - - return answers; + return answersShuffled[currentQuestion]; }; return (
{(currentQuestion+1) < questions.length && ( <> -

Question {currentQuestion + 1}

-

{questions[currentQuestion].question}

-
- {getShuffledAnswers().map((answer) => ( - - ))} -
+
+

Question {currentQuestion + 1} / {questions.length}

+

{questions[currentQuestion].question}

+
+
+ {getAnswers().map((answer) => { + const isCorrect = questions[currentQuestion].correctAnswer === answer; + const buttonColor = (selectedAnswer === answer && !isWaiting) ? (isCorrect ? '#66ff66' : '#ff6666') : '#89c3ff'; + return ( + + )} + )} +
)} {currentQuestion+1 === questions.length && ( diff --git a/webapp/src/components/game/singleplayer/GameSinglePlayer.tsx b/webapp/src/components/game/singleplayer/GameSinglePlayer.tsx index 6d064cc..930d4ee 100644 --- a/webapp/src/components/game/singleplayer/GameSinglePlayer.tsx +++ b/webapp/src/components/game/singleplayer/GameSinglePlayer.tsx @@ -51,8 +51,6 @@ const GameSinglePlayer = () => { const response = await axios.post(`${apiEndpoint}/createGame`, requestData); setQuestions(response.data); - console.log("Juego creado") - console.log(response.data) setCurrentStage(1); setFetched(true); } catch (error) { diff --git a/webapp/src/components/game/singleplayer/PlayingGameSinglePlayer.tsx b/webapp/src/components/game/singleplayer/PlayingGameSinglePlayer.tsx index 729f311..d0455fe 100644 --- a/webapp/src/components/game/singleplayer/PlayingGameSinglePlayer.tsx +++ b/webapp/src/components/game/singleplayer/PlayingGameSinglePlayer.tsx @@ -1,4 +1,4 @@ -import { FC, useState } from 'react' +import { FC, useMemo, useState } from 'react' import { Player, Question4Answers } from './GameSinglePlayer' import axios from 'axios'; @@ -16,23 +16,45 @@ const PlayingGame: FC = ({questions, setCurrentStage, setPlaye const [currentQuestion, setCurrentQuestion] = useState(0); const [correctAnswers, setCorrectAnswers] = useState(0); + const [selectedAnswer, setSelectedAnswer] = useState(null); - const handleAnswerClick = (answer: string) => { - if(questions[currentQuestion].correctAnswer === answer){ - setCorrectAnswers(correctAnswers + 1); + const [isWaiting, setIsWaiting] = useState(false); + + const answersShuffled = useMemo(() => { + return questions.map((question) => { + const answers = [question.correctAnswer, question.incorrectAnswer1, question.incorrectAnswer2, question.incorrectAnswer3]; + for (let i = answers.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [answers[i], answers[j]] = [answers[j], answers[i]]; + } + return answers; + }); + }, [questions]); + + const handleAnswerClick = async (answer: string, isCorrect:boolean) => { + if(!isWaiting){ + setIsWaiting(true); + setSelectedAnswer(answer); + setTimeout(() => { + if (isCorrect) { + setCorrectAnswers(correctAnswers + 1); + } + setCurrentQuestion(currentQuestion + 1); + setSelectedAnswer(null); + }, 1000); + setIsWaiting(false); } - setCurrentQuestion(currentQuestion + 1); + + // if(currentQuestion+2 === questions.length){ + // finishGame(); + // } }; - const calculatePoints = (correctAnswers: number, totalQuestions: number) => { - const incorrectQuestions = totalQuestions - correctAnswers; - const points = correctAnswers * 100 - incorrectQuestions * 25; - return points; - } - - const handleNext = async () => { - + const finishGame = async() => { + const totalPoints = calculatePoints(correctAnswers, questions.length); + // the player has finished the game + // update stats for each player players.map(player => { const randomPoints = Math.floor(Math.random() * (1000 - 100 + 1) / 50) * 50 + 100; if(player.isBot){ @@ -43,33 +65,35 @@ const PlayingGame: FC = ({questions, setCurrentStage, setPlaye return player; }) setPlayers(players); - setCurrentStage(3); - const sorted = players.sort((a, b) => b.points - a.points); + const requestData ={ "players": [{ "uuid": uuid, "nCorrectAnswers": correctAnswers, "nWrongAnswers": questions.length - correctAnswers, - "totalScore": calculatePoints(correctAnswers, questions.length), - "isWinner": !sorted[0].isBot + "totalScore": totalPoints, + "isWinner": false }]} + // update score in localstorage + const previousScore = parseInt(localStorage.getItem("score")) + localStorage.setItem("score", (previousScore + totalPoints).toString()) + setCurrentStage(3); await axios.post(`${apiEndpoint}/updateStats`, requestData); } - const getShuffledAnswers = () => { - const answers = [ - questions[currentQuestion].correctAnswer, - questions[currentQuestion].incorrectAnswer1, - questions[currentQuestion].incorrectAnswer2, - questions[currentQuestion].incorrectAnswer3, - ]; - - // Fisher-Yates Shuffle for randomizing answer order - for (let i = answers.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [answers[i], answers[j]] = [answers[j], answers[i]]; + const calculatePoints = (correctAnswers: number, totalQuestions: number) => { + const incorrectQuestions = totalQuestions - correctAnswers; + const points = correctAnswers * 100 - incorrectQuestions * 25; + return points; + } + + const getAnswers = () => { + const answers = answersShuffled[currentQuestion]; + if (answers.length > 4) { + console.log(answers) + const removeCount = answers.length - 4; + answers.splice(0, removeCount); } - return answers; }; @@ -80,14 +104,18 @@ const PlayingGame: FC = ({questions, setCurrentStage, setPlaye

Question {currentQuestion + 1}

{questions[currentQuestion].question}

- {getShuffledAnswers().map((answer) => ( + {getAnswers().map((answer) => { + const isCorrect = questions[currentQuestion].correctAnswer === answer; + const buttonColor = (selectedAnswer === answer && !isWaiting) ? (isCorrect ? '#66ff66' : '#ff6666') : '#89c3ff'; + return ( - ))} + )})}
)} @@ -95,7 +123,7 @@ const PlayingGame: FC = ({questions, setCurrentStage, setPlaye <>

You answered {correctAnswers} out of {questions.length} questions correctly.

You earned {calculatePoints(correctAnswers, questions.length)} points.

- + )}