Code viewer for World: TriviaGPT

// Created by Jamie Kavanagh on 23 Nov 2023 

// This world is a game show where the player tries to rack up the highest score using questions supplied by chatGPT


const token = "";
const url = "https://api.openai.com/v1/chat/completions";

function getQuestion() {
    const userScore = document.getElementById('userScore');
    const score = parseInt(userScore.textContent);
    const chaserScore = document.getElementById('chaser');
    const Cscore = parseInt(chaserScore.textContent);
    
    if (score === Cscore) {
           document.getElementById('category-div').style.display = 'none'
           document.getElementById('gameIntro').style.display = 'none'
           document.getElementById('gameOver').style.display = 'block'
           return
        }
      // Simulated backend response with question and answers
      document.getElementById('category-div').style.display = 'none'; // Hide the selection box
      document.getElementById('loadingbox').style.display = 'block'; // Show the loading screen
      
      value = document.getElementById("categorySelect").value
      // fetch method adapted from https://www.freecodecamp.org/news/javascript-post-request-how-to-send-an-http-post-request-in-js/
      
      // MH edit
        console.log (  "I need you to give me a random trivia question based on " + value + ", i want you to return a json of the question a string and a list called answers with 4 possible answers, the head of the list will always be the correct answer" );

    fetch(url, {
        method: 'POST',
        // found in https://ancientbrain.com/world.php?world=2850716357
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + token,
        },
        body: JSON.stringify({
            'model': 'gpt-3.5-turbo',
            'messages': [{"role": "user", "content": "I need you to give me a random trivia question based on " + value + ", i want you to return a json of the question a string and a list called answers with 4 possible answers, the head of the list will always be the correct answer"}]
         })
    })
    .then(response => {
        return response.json(); // Return JSON from the response
    })
    .then(data => {
        console.log("data:", data);
    
        // https://rollbar.com/blog/chatgpt-api-with-javascript/
        let apiResponse = data["choices"][0].message.content;
        console.log("test:", apiResponse);
    
        // Find the JSON string within the API response
          
        // https://stackoverflow.com/questions/58564271/looking-for-the-easiest-way-to-extract-an-unknown-substring-from-within-a-string
        let jsonStartIndex = apiResponse.indexOf('{');
        let jsonEndIndex = apiResponse.lastIndexOf('}') + 1; // Get indexes
    
        if (jsonStartIndex !== -1 && jsonEndIndex !== -1) { // Check there is json to parse
            let jsonString = apiResponse.slice(jsonStartIndex, jsonEndIndex); //Extract the json from the message 
            let triviaJSON = JSON.parse(jsonString); // parse the json
            console.log("Extracted JSON object:", triviaJSON);
    
            const question = triviaJSON.question;
            const answers = triviaJSON.answers;
            //const randomNum =triviaJSON.rand;
        
    
            // Check if the json worked
            console.log("Question:", question);
            console.log("Answers:", answers);
            //console.log("Num:", randomNum);
            displayAnswer(question, answers); // pass to the next function

        } else {
            console.log("JSON data not found in the API response.");
        }
    })
    
    .catch(error => {
        console.error('Error:', error);
    });

}

function displayAnswer(question, answers) {
    console.log("Recieved", question);
    window.headWord = answers[0]; // Make the first element of a list a global variable
    console.log("Head", headWord);
    
    //https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
    for (let i = 0; i < answers.length; i++) {  // Mix up the list
        const j = Math.floor(Math.random() * (i + 1));
        [answers[i], answers[j]] = [answers[j], answers[i]];
    }
    
    console.log("Swapped Array", answers);
    
    document.getElementById('question').textContent = question;
    const answerElements = document.getElementsByClassName('answer'); // Display each answer in html 
    for (let i = 0; i < answerElements.length; i++) {
        answerElements[i].textContent = answers[i]; 
    }

    document.getElementById('loadingbox').style.display = 'none'; //Hide the load screen
    document.getElementById('questiondiv').style.display = 'block'; // Show the answer card
}

function checkAnswer(selectedIndex) {
    // Simulated correct answer text for testing 
    //const correctAnswerText = "Correct Answer";

    const answerElements = document.getElementsByClassName('answer'); // Get all the answers
    for (let i = 0; i < answerElements.length; i++) {
        const answerText = answerElements[i].textContent.trim(); // Check what answer is correct 
        if (answerText === headWord) {
            answerElements[i].style.backgroundColor = 'green'; // Highlight the correct one green 
        } else {
            answerElements[i].style.backgroundColor = 'red'; // Highlight the wrong ones red
        }
        answerElements[i].style.pointerEvents = 'none'; // Disable clicks on answers
    }

    const userScore = document.getElementById('userScore');
    const score = parseInt(userScore.textContent);
    const isCorrect = answerElements[selectedIndex].textContent.trim() === headWord;
    userScore.textContent = isCorrect ? score + 1 : score; // Increase users score if correct 
    
    const rand = Math.floor(Math.random() * 4); // Generate a random number 
    console.log("Chaser", rand)
    
    const chaserScore = document.getElementById('chaser');
    const Cscore = parseInt(chaserScore.textContent);
    const check = answerElements[rand].textContent.trim() === headWord; // if the number is the index where the correct answer is
    chaserScore.textContent = check ? Cscore + 1 : Cscore; // Increase chasers score if correct 
}


    function nextQuestion() {
      document.getElementById('category-div').style.display = 'block'; // Display the category boxes
      document.getElementById('questiondiv').style.display = 'none';

      const answerElements = document.getElementsByClassName('answer');
      for (let i = 0; i < answerElements.length; i++) {
        answerElements[i].style.backgroundColor = ''; // Reset answer box colors
        answerElements[i].style.pointerEvents = 'auto'; // Enable answer selection
      }
    }
    
    
document.write ( `
<!DOCTYPE html>
<html>
<head>
  <title>Trivia Game</title>
  <style>
   /*All Animated css is influenced from https://uiverse.io/ */
  body {
  margin: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #f0f0f0; /* Background color of the body */
  color: blue;
}

.center-screen {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
}

.matte-black-card {
  background-color: #000;
  color: #fff;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
   width: calc(43.75vw - 40px); /* 7/16 of viewport width minus padding */
  height: calc(43.75vh - 40px); /* One-third of viewport height minus padding */
  max-width: 900px; /* Adjust the maximum width as needed */
  max-height: 900px; /* Adjust the maximum height as needed */
}


  
  .wrapper {
  width: 200px;
  height: 60px;
  position: relative;
  z-index: 1;
}

.circle {
  width: 20px;
  height: 20px;
  position: absolute;
  border-radius: 50%;
  background-color: #fff;
  left: 15%;
  transform-origin: 50%;
  animation: circle7124 .5s alternate infinite ease;
}

@keyframes circle7124 {
  0% {
    top: 60px;
    height: 5px;
    border-radius: 50px 50px 25px 25px;
    transform: scaleX(1.7);
  }

  40% {
    height: 20px;
    border-radius: 50%;
    transform: scaleX(1);
  }

  100% {
    top: 0%;
  }
}

.circle:nth-child(2) {
  left: 45%;
  animation-delay: .2s;
}

.circle:nth-child(3) {
  left: auto;
  right: 15%;
  animation-delay: .3s;
}

.shadow {
  width: 20px;
  height: 4px;
  border-radius: 50%;
  background-color: rgba(0,0,0,0.9);
  position: absolute;
  top: 62px;
  transform-origin: 50%;
  z-index: -1;
  left: 15%;
  filter: blur(1px);
  animation: shadow046 .5s alternate infinite ease;
}

@keyframes shadow046 {
  0% {
    transform: scaleX(1.5);
  }

  40% {
    transform: scaleX(1);
    opacity: .7;
  }

  100% {
    transform: scaleX(.2);
    opacity: .4;
  }
}

.shadow:nth-child(4) {
  left: 45%;
  animation-delay: .2s
}

.shadow:nth-child(5) {
  left: auto;
  right: 15%;
  animation-delay: .3s;
}

.centered-div {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.title {
  position: absolute;
  top: 20px; /* Adjust the distance from the top as needed */
  left: 50%; /* Align the element horizontally */
  transform: translateX(-50%); /* Center the element horizontally */
  color: blue;
}

#score {
  position: absolute;
  top: 50px; /* Adjust the distance from the top as needed */
  left: 20%; /* Align the element horizontally */
  transform: translateX(-50%); /* Center the element horizontally */
  color: blue;
}

.container {
  height: 294px;
  width: 240px;
  color: red;
  perspective: 800px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

.card {
  width: 100%;
  height: 100%;
  background: black;
  border-radius: 2rem;
  position: relative;
  transition: transform 1500ms;
  transform-style: preserve-3d;
}

.card-top {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 10%;
  position: absolute;
  width: 50%;
  background-color: transparent;
  border: 2px solid black;
  top: 0;
  border-top: none;
  border-radius: 0 0 1rem 1rem;
  box-shadow: 0px 0px 10px 5px rgba(255, 0, 0, 0.7);
}

.card-top-para {
  font-size: 16px;
  font-weight: bold;
}

.container:hover > .card {
  cursor: pointer;
  transform: rotateX(180deg) rotateZ(-180deg);
}

.front,
.back {
  height: 100%;
  width: 100%;
  border-radius: 2rem;
  box-shadow: 0px 0px 10px 5px rgba(255, 0, 0, 0.7);
  position: absolute;
  backface-visibility: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
}

.back {
  background-color: black;
  transform: rotateX(180deg) rotateZ(-180deg);
}

.heading {
  font-size: 22px;
  font-weight: bold;
}

.follow {
  font-size: 16px;
  font-weight: 500;
}

#category-div {
  position: absolute;
  top: 130px; /* Adjust the distance from the top as needed */
  left: 50%; /* Align the element horizontally */
  transform: translateX(-50%); /* Center the element horizontally */
  color: blue;
}

#categoryBox {
  display: flex;
  flex-direction: column;
  align-items: center;
}

#categorySelect {
  margin-bottom: 30px; /* Adjust as needed for space between select and button */
}

select {
   -webkit-appearance:none;
   -moz-appearance:none;
   -ms-appearance:none;
   appearance:none;
   outline:0;
   box-shadow:none;
   border:0!important;
   background: #5c6664;
   background-image: none;
   flex: 1;
   padding: 0 .5em;
   color:black;
   cursor:pointer;
   font-size: 1em;
   font-family: 'Open Sans', sans-serif;
}
select::-ms-expand {
   display: none;
}
.select {
   position: relative;
   display: flex;
   width: 20em;
   height: 3em;
   line-height: 3;
   background: #5c6664;
   overflow: hidden;
   border-radius: .25em;
}


/*
MH edit
original below looked like:
  content: '(backslash)25BC';
but this caused console error:
  Uncaught SyntaxError: Octal escape sequences are not allowed in template strings.
  
think this is downward pointing triangle
so can I use
  content: '&#9660;';
needs some debug   
*/

.select::after {
 /*  content: '25BC';   */       
   position: absolute;
   top: 0;
   right: 0;
   padding: 0 1em;
   background: #2b2e2e;
   cursor:pointer;
   pointer-events:none;
   transition:.25s all ease;
}
.select:hover::after {
   color: #23b499;
}

button {
  height: 50px;
  margin: 5px;
  width: 120px;
  background: #333;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  font-family: Consolas, Courier New, monospace;
  border: solid #404C5D 1px;
  font-size: 16px;
  color: #4d5354;
  -webkit-transition: 500ms;
  transition: 500ms;
  border-radius: 5px;
  background: linear-gradient(
#292929, black, #292929);
  -webkit-box-shadow: -1px -5px 15px #41465B, 
               5px  5px 15px #41465B, 
    inset      5px  5px 10px #212121, 
    inset     -5px -5px 10px #212121;
  box-shadow: -1px -5px 15px red, 
               5px  5px 15px red, 
    inset      5px  5px 10px black, 
    inset     -5px -5px 10px black;
}

button:hover {
  -webkit-box-shadow: 1px 1px 13px blue,
                    -1px -1px 13px grey;
  box-shadow: 1px 1px 13px #20232e,
             -1px -1px 13px #545b78;
  color: green;
  -webkit-transition: 500ms;
  transition: 500ms;
}

button:active {
  -webkit-box-shadow: 1px 1px 13px green,
                     -1px -1px 33px green;
  box-shadow: 1px 1px 13px green,
             -1px -1px 33px green;
  color: black;
  -webkit-transition: 100ms;
  transition: 100ms;
}

</style>
</head>
<body style="background-image: url('/uploads/jayk49/vanishing-stripes.png');">
<div class="title">
    <h1 id="gameIntro">Welcome to TriviaGPT</h1>
    <div id="gameOver" style="display: none;">
        <h1>Game Over</h1>
        <p>You've Been Caught!!</p>
    </div>
</div>

<div id="score">
    <h2>Score: <span id="userScore">0</span></h1>
    <h2>Chaser: <span id="chaser">-1</span></h1>
</div>
        <div id="category-div">
            <div id="categoryBox">
                <div class"select">
                    <select id="categorySelect">
                      <option value="science">Science</option>
                      <option value="history">History</option>
                      <option value="sports">Sports</option>
                      <option value="geography">Geography</option>
                      <option value="music">Music</option>
                      <option value="movies">Movies</option>
                      <option value="general knowledge">General Knowledge</option>
                      <option value="entertainment">Entertainment</option>
                    </select>
                </div>
                <button onclick="getQuestion()">Get Question</button>
            </div>
        </div>
        
            <div class="container">
              <div class="card" id="questiondiv" style="display: none;">
                <div class="front">
                  <div class="card-top">
                    <p class="card-top-para">Question</p>
                  </div>
                  <center><p class="heading"></p></center>
                  <center><p class="follow" id="question"></p></center>
                  <p id="question"></p>
                </div>
                <div class="back">
                  <div class="card-top">
                    <p class="card-top-para">Answers</p>
                  </div>
                  <div id="questionBox"
                    <div id="answers">
                      <div class="answer" onclick="checkAnswer(0)"></div>
                      <div class="answer" onclick="checkAnswer(1)"></div>
                      <div class="answer" onclick="checkAnswer(2)"></div>
                      <div class="answer" onclick="checkAnswer(3)"></div>
                    </div>
                    <button onclick="nextQuestion()">Next Question</button>
                  </div>
                </div>
              </div>
            </div>

          
          <div class="wrapper centered-div" id="loadingbox" style="display: none;">
            <div class="circle"></div>
            <div class="circle"></div>
            <div class="circle"></div>
            <div class="shadow"></div>
            <div class="shadow"></div>
            <div class="shadow"></div>
          </div>
</body>
</html>
`);