Code viewer for World: AI API Comparison
var pgn = "";
var fen = "";
var pngUrl = "";
var APIKEY1 = "";
var APIKEY2 = "";
var solution = "";
var mateValue = 1;
var tmp = 0.7;

function changeTemp(){//slider to change GPTs temperature
    tmp = jQuery("input#myRange").val();
    tmp = parseFloat(tmp);//change from string to float
}

function checkKeys(){
    console.log(APIKEY1, APIKEY2)
    if (APIKEY1 && APIKEY2){
        start();
    }
    else{
        $("#start").html ( "<b> Need API KEYS!" );
        setTimeout(function() {//wait 1 second then reload page.
            location.reload();
        }, 1000);
    }
}

function handleAPIKEY1(){
    APIKEY2 = jQuery("input#apikey1").val();
}
function handleAPIKEY2(){
    APIKEY1 = jQuery("input#apikey2").val();
}

function changeMateValue(){
    
    mateValue = jQuery("input#myRange2").val();
    mateValue = parseInt(mateValue);//change from string to float
}


function handleApi(event){
    event.preventDefault();
    // close the modal
    const lockModal = bootstrap.Modal.getInstance(document.getElementById('lockModal'));
    lockModal.hide();
    sessionStorage.setItem("APIKEY1", APIKEY1);
}

function convert2FEN(pgn){
    
    const chess = new Chess();
    chess.load_pgn(pgn);
        
    const fen = chess.fen();
    return fen;
}
function initLockModal() {
    const lockModal = new bootstrap.Modal(document.getElementById('lockModal'));
    // Check if sessionStorage has a flag
    if (sessionStorage.getItem('modalClosed') === "true") {
        lockModal.show(); // show modal on page load
    }
    // Close button behavior
    document.getElementById('closeModalBtn').addEventListener('click', () => {
      lockModal.hide();
      sessionStorage.setItem('modalClosed', 'true');
    });
}
async function fenToPngUrl(fen) {
    const encoded = encodeURIComponent(fen);
    return `https://fen2png.com/api/?fen=${encoded}&raw=true`;
}
async function getRandomPuzzlePGN() {
    const response = await fetch(`https://lichess.org/api/puzzle/next?angle=mateIn${mateValue}&difficulty=normal`, {headers: {}}
    );

    if (!response.ok) {
        throw new Error("Invalid puzzle ID or Lichess error");
    }

    const data = await response.json();
    console.log(data)
    const pgn = data.game?.pgn || data.pgn;
    
    solution = data.puzzle.solution;
    
    if (!pgn || !solution) {
        throw new Error("Puzzle has no PGN or SOLUTION");
    }
    return pgn;
}

async function fetchPuzzleImage() {
    try {
        document.getElementById("result1").innerHTML = "Loading answer...";
        document.getElementById("result2").innerHTML = "Loading answer...";
        document.querySelector("main p:nth-of-type(2)").innerHTML = "Loading puzzle...";

        // 1. Get puzzle PGN
        pgn = await getRandomPuzzlePGN();
        console.log("PGN:", pgn);

        // 2. Convert PGN → FEN
        fen = convert2FEN(pgn);
        console.log("FEN:", fen);

        // 3. Convert FEN → PNG URL
        pngUrl = await fenToPngUrl(fen);
        console.log("Image URL:", pngUrl);
        console.log("SOLUTION:", solution);

        // 4. Display puzzle image
        setTimeout(function() {
            document.querySelector("main p:nth-of-type(2)").innerHTML = `
                Mate in: <strong>${mateValue}</strong><br><br>
                <img src="${pngUrl}" style="max-width:100%;border:1px solid #ccc;border-radius:8px;height:80%;width:80%;" />
                <br><br><strong>EXPECTED SOLUTION:</strong> ${solution}
            `;
        }, 2000);
        
        send2APIs();

    } catch (err) {
        console.error(err);
        document.querySelector("main p:nth-of-type(2)").innerHTML = "Error: " + err.message;
    }
}
async function callAIAPI(apiOptions, targetElementId) {
  var finalText = "";
  if (targetElementId === "result1"){
  try {
    const response = await fetch("https://api.groq.com/openai/v1/responses", apiOptions);
    const data = await response.json();
    
    finalText = data.output[1].content[0].text;

    console.log("Groq Data Aquired!");
    console.log(data);

    // Insert into the page
    document.getElementById(targetElementId).innerHTML = finalText;

  } catch (error) {
    console.error("Groq API Error:", error);
    document.getElementById(targetElementId).innerHTML = "Error fetching response";
  }
      
  }
  if (targetElementId === "result2"){
    try{
        const response = await fetch("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent", apiOptions);
        const data = await response.json();
        console.log("Gemini Data Aquired!");
        console.log(data);
        
        finalText = data.candidates[0].content.parts[0].text;
        
        document.getElementById(targetElementId).innerHTML = finalText;
        
    }catch (error) {
        console.error("Gemini API Error:", error);
        document.getElementById(targetElementId).innerHTML = "Error fetching response";
    }
      
  }
}

async function send2APIs(){
    const input = `Mate in: ${mateValue}, give the solution for the given FEN string: ${fen},give the moves required in an array showing all moves including mate. Also give a brief 20 word explaination for your decision. Do not use markdown, just text will do.`;
    console.log(input);
    const API1Options = {//POST to API
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${APIKEY1}`,//checking API key
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({//what to send
            model: "openai/gpt-oss-120b",
            temperature: tmp,
            input: input, 
        })
    };
    const API2Options = {
          method: "POST",
          headers: {
            'x-goog-api-key': `${APIKEY2}`,//checking API key
            "Content-Type": "application/json"
          },
          body: JSON.stringify({
            contents: [
              { 
                  role: "user", parts: [{ 
                      text: input 
                      
                  }] }
            ],
            generationConfig: {
                temperature: tmp,
            }
          })
        }
    try{
        await callAIAPI(API1Options, "result1");
        await callAIAPI(API2Options, "result2");
    }
    catch(err){
        console.error(err);
    }
}

function start(){
    document.write(`
    <head>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
        <style>
            
        
            html, body { height: 100%; }
            body {
            padding: 0.5rem;
              display: flex;
              flex-direction: column;
              font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
              color: #111;
              background: #f7f7f8;
              line-height: 1.4;
            }
            button {
              padding: 0.6rem 1rem;
              border-radius: 6px;
              border: 1px solid #1f6feb;
              background: #1f6feb;
              color: white;
              font-weight: 600;
              cursor: pointer;
              font-size: 1rem;
            }
        
            header {
              display: flex;
              align-items: center;
              margin-left: auto;
              margin-right: auto;
              padding: 1rem;
              background: white;
              border: 1px solid #e6e6e9;
            }
        
            .wrap {
              display: grid;
              grid-template-columns: 220px 1fr 220px; 
              gap: 1rem;
              padding: 1rem;
              flex: 1 1 auto;
              align-items: start;
            }
        
            aside {
              background: white;
              border: 5px solid #e6e6e9;
              padding: 1rem;
              border-radius: 8px;
              min-height: 200px;
            }
            set{
               background: black; 
            }
            main {
              background: white;
              border: 5px solid #e6e6e9;
              padding: 1rem;
              border-radius: 8px;
              min-height: 200px;
              text-align: center;
              display: flex;
              flex-direction: column;
              align-items: center;
            }
        
            .bottom-bar {
              position: sticky; 
              bottom: 0;
              background: #fff;
              border-top: 1px solid #e6e6e9;
              padding: 0.75rem 1rem;
              display: flex;
              gap: 0.5rem;
              align-items: center;
              justify-content: center;
            }
        
            .bottom-bar-inner {
              width: 100%;
              max-width: 1100px;
              display: flex;
              gap: 0.5rem;
              align-items: center;
              justify-content: center;
            }
        
            .bottom-bar button {
              padding: 0.6rem 1rem;
              border-radius: 6px;
              border: 1px solid #1f6feb;
              background: #1f6feb;
              color: white;
              font-weight: 600;
              cursor: pointer;
              font-size: 1rem;
            }
        
            .bottom-bar button:active { transform: translateY(1px); }
        </style>
    </head>
    <body>
        <script  src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
        <script>
            window.addEventListener('DOMContentLoaded', ${initLockModal});
        </script>
        <!-- Modal -->
        <div class="modal fade" id="lockModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
          <div class="modal-dialog modal-dialog-centered" style="width:fit-content;">
            <div class="modal-content">
              <div class="modal-header" style="display:flex; align-items: center; justify-content:center;">
                <h5 class="modal-title">Enter Api Key</h5>
              </div>
              <div class="modal-body" style="display:flex; flex-direction: column; align-items: center; gap: 1rem;">
                <input type="password" placeholder="Api Key" onChange="APIKEY1=this.value;" onkeydown="if(event.key === 'Enter'){ APIKEY1=this.value;handleApi(event); }"/>
                <button type="button" class="btn btn-primary" id="closeModalBtn" onClick=handleApi(event)>Done</button>
              </div>
            </div>
          </div>
        </div>
        <header>
            <h1 style="font-size:2.1rem;">Chess Move Comparison</h1>
        </header>
        <div class="wrap">
            <aside class="left" aria-label="Left column" style="max-height: 400px; overflow-y: auto;">
                <h2 style="font-size:0.95rem; margin-bottom:0.5rem; text-align:center;">API 1 (ChatGPT)</h2>
                <div id="result1"></div>
            </aside>
            <main role="main" aria-label="Main content">
                <h2 style="font-size:0.95rem; text-align:center; margin-bottom:0.5rem;">Chess Puzzle</h2>
                <p>
                <p style="margin-top:1rem;"></p>
                <!-- Add more content to demonstrate scrolling -->
            </main>
            <aside class="right" aria-label="Right column" style="max-height: 400px; overflow-y: auto;">
                <h2 style="font-size:0.95rem; margin-bottom:0.5rem; text-align:center;">API 2 (Gemini)</h2>
                <div id="result2"></div>
            </aside>
        </div>
        <form class="bottom-bar">
            <div class="bottom-bar-inner">
                <p><b>Temperature Control</b></p>
                <input style="accent-color: #1f6feb;" type="range" min="0" max="1" value="0.7" step="0.1" class="slider" id="myRange" onChange="rangeValue.innerText = this.value; changeTemp();"> , <p id="rangeValue">0.7</p>
            </div>
            <div class="bottom-bar-inner">
                <button id="sendMessage" type="button" onClick=fetchPuzzleImage()>Send</button>
            </div>
            <div class="bottom-bar-inner">
                <p><b>Mate in N (Choose Between 1 and 5)</b></p>
            <input style="accent-color: #1f6feb;" type="range" min="1" max="5" value="1" step="1" class="slider" id="myRange2" onChange="rangeValue2.innerText = this.value; changeMateValue();"> , <p id="rangeValue2">1</p>
            </div>
        </form>
    </body>
    `);
}


document.write(
    `   <style>
            #default {
                  border: 5px solid #e6e6e9;
                  position: absolute;
                  top: 50%;
                  left: 50%;
                  transform: translate(-50%, -50%);
                  padding: 10px;
                }
            input.apikey1{
                background:"red"
            }
            </style>
        <div>
            <h1 style="text-align:center;padding:20px;"> AI API Chess Move Comparison</h1>
        </div>
        <div id=default>
            Enter your API KEY: <br><br>
        	<input NAME="apikey1" id="apikey1" oninput=handleAPIKEY1() VALUE='' placeholder="Gemini API KEY"><br>
        	<input NAME="apikey2" id="apikey2" oninput=handleAPIKEY2() VALUE='' placeholder="Groq API KEY"><br>
        	<button id="set" type="button" onclick='checkKeys();'>SET KEY TO START!
`);