Código fuente de la Calculadora

HTML


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Juego Piedra, Papel o Tijera</title>
    <link rel="icon" href="./IMG/logoPag.png">
    <link rel="stylesheet" href="./styles.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" 
    integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>

    <main id="main">
        <div id="mensaje">
           <p></p>
           <div class="peq">(para usar una oportunidad debes seleccionarla antes de cerrar éste mensaje)</div>
           <button id="cerrar">CERRAR</button>
        </div>
        <section class="pantalla">
            <div class="arriba">
                <div class="caja_opor">
                    <button id="usar_opor">Usar oportunidad</button>
                    <div class="oportunidades">
                        <p>OPORTUNIDADES</p>
                        <i class="fa-solid fa-heart"></i>
                        <i class="fa-solid fa-heart"></i>
                        <i class="fa-solid fa-heart"></i>  
                    </div>
                </div>
                <div class="rounds">
                    <p>ROUNDS</p>
                    <p>
                        <span id="round">1</span>/10
                    </p>
                </div>
            </div>
            <div class="bajo">
                <div class=" icono tu">
                    <p class="puntuacion">0</p>
                    <img src="./IMG/icono_persona.png" alt="">
                    <p>Tú</p>
                    <p class="seleccion">Has seleccionado: <span id="selecc_tu"></span></p>
                </div>
                <p class="vs">v/s</p>
                <div class="icono PC">
                    <p class="puntuacion">0</p>
                    <img src="./IMG/pc.png" alt="">
                    <p>PC</p>
                    <p class="seleccion">Has seleccionado: <span id="selecc_pc"></span></p>
                </div>
            </div>
        </section>
        <div class="footer">
            <div class="btns">
                <button id="piedra" class="btn piedra" data-name="PIEDRA"></button>
                <button id="papel" class="btn papel" data-name="PAPEL"></button>
                <button id="tijera" class="btn tijeras" data-name="TIJERAS"></button>
            </div>
            <button id="jugar">JUGAR</button>
        </div>
    </main>
    <script src="app.js"></script>
</body>
</html>
  

CSS


* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    font-family: Arial, Helvetica, sans-serif;
}

main {
    width: 100%;
    /*     width: fit-content; */

    max-width: 1200px;
    border-radius: 10px;
    box-shadow: 3px 0px 10px 3px rgb(14, 13, 13);
    background: url(IMG/tablero.png);
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;

}

#mensaje {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    transform-origin: center;
    width: 30%;
    height: 20%;
    background-color: rgba(251, 253, 252, 0.856);
    font-weight: bold;
    z-index: 3;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    border-radius: 15px;
    gap: 15px;
    visibility: hidden;
    text-align: center;
    transition: .5s;
}

#mensaje p {
    letter-spacing: 2px;
    font-size: 3em;
}

.peq {
    display: none;
    font-size: 15px;
    color: green;
}

#cerrar {
    padding: 10px 20px;
    background-color: rgb(82, 47, 8);
    color: aliceblue;
    letter-spacing: 1px;
    cursor: pointer;
}

.pantalla .arriba {
    display: flex;
    justify-content: space-around;
    margin-top: 30px;
}

.caja_opor {
    display: flex;
    align-items: center;
    justify-content: space-around;
    gap: 30px;
    
}

#usar_opor {
    color: rgb(198, 212, 198);
    text-transform: uppercase;
    font-weight: bold;
    padding: 10px;
    border-radius: 7px;
    cursor: pointer;
    background-color: rgba(0, 128, 0, 0.397);
    transition: 1s;
}

.oportunidades,
.rounds {
    max-width: 200px;
    text-align: center;
    font-weight: bold;
    backdrop-filter: blur(5px);
    padding: 10px 30px;
    border-radius: 7px;
    box-shadow: 0px 2px 2px 2px rgb(85, 45, 7);
}

.oportunidades i {
    text-align: center;
    color: rgb(223, 13, 13);
    font-size: 25px;
    text-shadow: 3px 3px 3px rgb(77, 29, 29);
}



.rounds p:nth-of-type(2) {
    font-size: 2em;
}

.pantalla .bajo {
    width: 100%;
    max-width: 700px;
    display: flex;
    justify-content: space-around;
    display: flex;
    align-items: center;
    margin:10px auto;
    font-size: 1.5em;
    font-weight: bold;
}

#empate {
    display: none;
}

.seleccion {
    font-size: 13px;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.seleccion span {
    text-transform: uppercase;
    font-size: 1.5em;
    color: rgb(8, 235, 148);
}

.bajo .icono {
    width: 50%;
    max-width: 160px;
    backdrop-filter: blur(5px);
    text-align: center;
    padding: 5px 10px;
    border-radius: 7px;
}

.icono img {
    width: 50%;
}

.vs {
    font-size: 2.5em;
}

.footer {
    width: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;   
}

.btns {
    width: 100%;
    max-width: 800px;
    display: flex;
    justify-content: space-around;
}

.piedra {
    background: url(./IMG/manoPiedra.png);
}

.papel {
    background: url(./IMG/manoPapel.png);
}

.tijeras {
    background: url(./IMG/manoTijera.png);
}

.btn {
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
    width: 150px;
    height: 150px;
    border-radius: 100%;
    cursor: pointer;
    transition: .3s;
    position: relative;
}

.btn::after {
    content: attr(data-name);
    position: absolute;
    width: 100%;
    padding: 10px 0px;
    bottom: -40px;
    background: white;
    font-weight: bold;
    border-radius: 10px;
    transform: translate(-50%, -50%);
}


.btns .btn:hover {
    scale: 1.1;
    box-shadow: 0px 0px 4px 3px rgb(8, 235, 148);
}


#jugar {
   /*  margin: 50px 0px 10px; */
    width: 100%;
    max-width: 300px;
    padding: 20px 0;
    border-radius: 10px;
    font-weight: bold;
    font-size: 1.5em;
    margin-top: 40px;

    cursor: pointer;
    border: none;
    box-shadow: 0px 0px 3px 3px rgb(44, 42, 42);
    transition: .3s;
}

#jugar:hover {
    background-color: rgb(173, 117, 65);
    color: rgb(252, 251, 251);
    box-shadow: 0px 0px 3px 3px rgb(63, 36, 11);
    letter-spacing: 6px;
}

@media (max-height: 1000px) {
    .icono {
        width: 100px;
    }

    .btn {
        width: 100px;
        height: 100px;
    }

    .pantalla .bajo {
        margin-top: 6px;
    }
}

@media (max-width:1000px) {
    * {
        font-size: calc(100% - 1px);
    }
     .arriba{
        width: 100%;
        display: flex;
        gap: 4px;
        justify-content: space-between;
    }

    #usar_opor {
        width: 30%;
        min-width: 100px;
    }
    .oportunidades i{
        font-size: 13px;
    }
    .rounds {
        padding:7px;
    }
    .caja_opor{
        gap: 4px;
    }
    .footer{
        max-width: 500px;
        margin: auto;
    }
    .btn {
        width: 65px;
        height: 65px;
    }




}
  

JavaScript



const btJugar = document.getElementById('jugar');
let resptServidor 
const seleccTu = document.getElementById('selecc_tu')
const seleccPc = document.getElementById('selecc_pc')
const mensaje = document.getElementById('mensaje')
let round = document.getElementById('round')
let cerrar = document.getElementById('cerrar')
let punt = document.querySelector('.tu .puntuacion')
let puntPc = document.querySelector('.PC .puntuacion')
const mensajeOpor = document.querySelector('.peq')
const usarOpor = document.getElementById('usar_opor')
const nodeListCorazon = document.querySelectorAll('.oportunidades i')
let oportunidades = 3
let setInterval1
let boolean = false

//Solicitar información al servidor, pero en este caso no lo haré con él, haré un Mat.random()
function llamadaAlServidor() {
    let http_request

    //comprobar compatibilidad de navegadores 
    if (window.XMLHttpRequest) {//compruebo si existe éste método moderno
        http_request = new XMLHttpRequest()//creo una instancia de la clase
        if (http_request.overrideMimeType) {//verifico si tiene el método "overrideMimeType", método utilizado en navegadores antiguos como Firefox para cambiar el  tipo MIME(tipo de dato a recibir)
            http_request.overrideMimeType('text/xml')//mirar en google tipos de MIME   
        }
    } else if (window.ActiveXObject) {//para navegadores viejos como Internet Explorer
        try {
            http_request = new ActiveXObject("Msxml2.XMLHTTP");//Intenta crear una instancia de ActiveXObject usando el progID "Msxml2.XMLHTTP"  que es una versión más reciente del componente XMLHTTP de Microsoft
        } catch (e) {//Si la línea anterior falla (por ejemplo, porque el navegador no reconoce "Msxml2.XMLHTTP"), se captura el error con catch(e) y se intenta otra alternativa.
            try {
                http_request = new ActiveXObject("Microsoft.XMLHTTP");//Esta es la versión más antigua del objeto XMLHTTP de Microsoft, usada en versiones incluso anteriores del navegador. Se intenta esta opción como respaldo si "Msxml2.XMLHTTP" no está disponible.
            } catch (e) { }//Si incluso esta versión falla, se captura el error, pero no se hace nada (bloque catch vacío).Esto significa que si ninguna de las dos versiones funciona, no se lanza una excepción ni se da un mensaje de error. Simplemente http_request queda sin valor.
        }

    }//termino de comprobar compatibilidad

    //compruebo si la petición se hizo con éxito
    http_request.onreadystatechange = () => {
        if (http_request.readyState == 4) {//revisa el estado de la petición , mirar abajo los valores de la propiedad "readyState",el estado 4 es que la respuesta completa del servidor ha sido recibida y es posible continuar procesándola.
            if (http_request.status == 200) {//verificar si la petición fue exitosa, ".status" es una propiedad que devuelve el código de estado HTTP de la respuesta, y 200 es el código HTTP que significa OK.(mirar abajo NOTAS)
                resptServidor = http_request.responseText//guardo en una variable global la respuesta
                console.log(resptServidor)
            } else {
                console.error('Petición fallida ' + http_request.status)
            }
        }
    }

    http_request.open('GET', 'http://10.10.1.94/curso_web_2025/ppt.php', true);//Configuro la información antes de enviar al servidor, el primer parámetro es el método en que enviará u obtendrá la información(GET, POST, HEAD, en mayúsculas para más compatiblidad con Firefox), luego la url con el dominio exacto, y el último parámetro "true"(indica asincronía, por lo que el código después de esa línea aunque no haya recibido respuesta seguirá ejecutándose) o "false"(lo contrario ,sincrono). 
    http_request.send()/*Envío de la solicitud u petición .El parámetro en el método send() puede ser cualquier información que se quiera enviar al servidor si se usa POST para la petición; la información se debe enviar en forma de cadena, por ejemplo: name=value&anothername=othervalue&so=on
    Si se quiere enviar información de esta forma, es necesario cambiar el tipo MIME de la petición usando la siguiente línea:
    http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    De otro modo el servidor descartará la información. 
    Ejemplo:
    http_request.setRequestHeader("Content-Type", "application/json");
    http_request.send(JSON.stringify({ nombre: "Ana", edad: 30 }));
    */

}










function estilosMensajeYoport(){
    mensaje.style.visibility = 'hidden'
    usarOpor.style.backgroundColor = 'rgba(0, 128, 0, 0.397)'
    usarOpor.style.color = 'rgb(198, 212, 198)'
    clearInterval(setInterval1)
}
function opor(){

    if(oportunidades == 0){
        usarOpor.removeEventListener('click',opor)
        return
    }
    oportunidades -= 1
    nodeListCorazon[oportunidades].style.color = 'rgba(217, 235, 217, 0.100)'
    estilosMensajeYoport()  
    console.log(oportunidades) 
}
function hasGanado() {
    punt.textContent = Number(punt.textContent) + 1
    setTimeout(() => {
        seleccTu.innerText = ''
        seleccPc.innerText = ''
        mensajeOpor.style.display = 'none'
        mensaje.querySelector('p').innerText = 'HAS GANADO'
        mensaje.style.visibility = 'visible'
        mensaje.style.width = '80%'
        mensaje.style.height = '70%'
    }, 900)
}

function hasPerdido() {
    puntPc.textContent = Number(puntPc.textContent) + 1

    setTimeout(() => {
        seleccTu.innerText = ''
        seleccPc.innerText = ''
        mensaje.querySelector('p').innerText = 'HAS PERDIDO'
        mensajeOpor.style.display = 'block'
        mensaje.style.visibility = 'visible'
        mensaje.style.width = '80%'
        mensaje.style.height = '70%'
    }, 900)


    let escalado = false;
    setInterval1 = setInterval(() => {
        usarOpor.style.backgroundColor = '#66ff00'
        usarOpor.style.color = '#000000'
       usarOpor.style.transform = escalado ? 'scale(1)' : 'scale(1.2)';
        escalado = !escalado;
    }, 900);

    usarOpor.addEventListener('click',opor)
}


function compararResultados() {
    let seleccion = seleccTu.innerText.toLocaleLowerCase()
    if (resptServidor == seleccion) {
        mensajeOpor.style.display = 'none'
        seleccTu.innerText = ''
        mensaje.querySelector('p').innerText = 'EMPATE (repetir round)'
        mensaje.style.visibility = 'visible'
        mensaje.style.width = '80%'
        mensaje.style.height = '70%'
        return
    }
    const ganaJugador =
        (seleccion === 'piedra' && resptServidor === 'tijeras') ||
        (seleccion === 'tijeras' && resptServidor === 'papel') ||
        (seleccion === 'papel' && resptServidor === 'piedra')

    if (ganaJugador) {
        hasGanado()
    } else {
        hasPerdido()
    }



}


btJugar.addEventListener('click', () => {
    let numero = Math.floor(Math.random() * 3)
    switch (numero) {
        case 0:
            resptServidor = 'piedra'
            break;
        case 1:
            resptServidor = 'papel'
            break;
        
        default:
            resptServidor = 'tijeras'
            break;
    }
    if (seleccTu.innerText == '') {
        mensaje.querySelector('p').innerText = 'DEBES SELECCIONAR PIEDRA, PAPEL O TIJERAS.'
        mensaje.style.visibility = 'visible'
        mensaje.style.width = '80%'
        mensaje.style.height = '70%'
    } else {

        /*   llamadaAlServidor() */
        seleccPc.innerText = resptServidor
        compararResultados()
    }
})



/* 
   ++++++++++++++++
         NOTAS
   ++++++++++++++++    
   
   Lista de valores de la propiedad .readyState:
    (dice en qué etapa está la solicitud)

   0 => (no inicializada)
   1 => (leyendo)
   2 => (leído)
   3 => (interactiva)
   4 => (completo)


    Código de estado HTTP que devuelve el servidor:
    (dice cómo respondió el servidor cuando la solicitud ya está completa)

    200 => OK (éxito)
    404 => No encontrado
    500 => Error intento del servidor
 */

function manos(target) {
    const dataName = target.dataset.name
    seleccTu.innerText = dataName

}

const main = document.getElementById('main')
main.addEventListener('click', (e) => {
    const target = e.target
    const targetId = target.id

    switch (targetId) {
        case 'usar_opor':

            break;
        case 'piedra':
            manos(target)
            break;
        case 'papel':
            manos(target)
            break;
        case 'tijera':
            manos(target)
            break;
        case 'cerrar':
            if(!mensaje.querySelector('p').innerText.includes('EMPATE')){
                round.textContent = Number(round.textContent) + 1
            }
            estilosMensajeYoport()
            break;

        default:
            break;
    }
})