Ayuda con algoritmo en JavaScript

SimP

Thalion
Ex-Staff
#1
¡Hola! Para un final de la facultad que entrego el miércoles, tengo que desarrollar un juego basado en web utilizando:

Obligatoriamente: HTML, CSS, JavaScript vanilla y jQuery.
Opcional: PHP, librerías JS a elección.

El juego consiste en adivinar un número aleatorio de X cantidad de cifras. El juego está subido y lo pueden jugar acá: www.juegodescifralo.com

Si lo juegan, notarán que hay tres tipos de "cifras" dentro del número que ingrese el usuario:
Buenas: son aquellos dígitos que coinciden con la posición del dígito del número a adivinar. Por ejemplo:

Si el número a adivinar es 8512
Y el número ingresado es 8365... El 8 es una cifra "buena"

Regulares: supongo que van entendiendo... En el ejemplo anterior, el 5 es una cifra regular, ya que está dentro del aleatorio pero no en la posición correcta.

Malas: son las cifras que no están dentro del número aleatorio.

Me cuesta explicar el inconveniente que tengo. Paso a dejar mi código para chequear cada cifra y ver si es buena, regular o mala.
Código:
function checkNumeros(randomArray, miArray) {
        for (var x = 0; x < randomArray.length; x++) {
            var posRepetido = randomArray.indexOf(miArray[x]); //El número que recorro ahora está dentro del random??

            if (posRepetido == -1) {
                console.log("el numero" + miArray[x] + "no está dentro del conjunto");
                malos++;

            } else { //Está dentro del conjunto
                regulares++;

                if (miArray[x] == randomArray[x]) { //El actual es igual al aleatorio en la misma posicion
                    console.log("el numero" + miArray[x] + "está en la misma posición");

                    buenos++;
                    regulares--;
                } else { //No es igual en la misma

                    if (randomArray[posRepetido] != miArray[posRepetido]) {
                       console.log("el numero" + miArray[x] + "está dentro del conjunto pero no en la misma posición");
                      

                    } else {
                        regulares--;
                        malos++;
                        console.log("el numero" + miArray[x] + "no está dentro del conjunto");

                    }
                }
            }
        }

        var obj = {
            buenos: buenos,
            regulares: regulares,
            malos: malos
        };

        return obj;
    }
En su momento tuve otra versión de este código, pero lo que ocurría era lo siguiente:

Supongan que el número aleatorio es 8544
Y yo arriesgo el número 8455

Me va a decir: 8 bueno, 4 regular, 5 regular, 5 regular. Lo cual es incorrecto e ilógico. La falla está en que el código busca el número 5, y como lo encuentra dentro del número aleatorio, dice que el 5 es regular, sin importar la cantidad de veces que esté en el aleatorio.

Como dije, eso ocurría en una versión previa del código que puse arriba. Esta versión tiene algo de ese bug corregido, pero en ocasiones se sigue bugueando de formas básicamente impredecibles.

Todo empeora cuando en el juego se puede elegir la dificultad "personalizada", donde el usuario elige la cantidad de cifras que tenga el número a adivinar. Entonces puede elegir adivinar un número de 20 cifras, donde sí o sí habrá repetición de números, y no habrá números malos sino sólo regulares y buenos.

Mi pregunta se resume a: ¿de qué manera podría realizar este algoritmo de una manera más inteligente, y funcional sean cuantas seas las cifras del número a adivinar?

Sé que es extraño y difícil de entender. Mi recomendación es que jueguen (www.juegodescifralo.com), seguramente lo entiendan con mayor claridad.

¡Desde ya, muchas gracias por la ayuda!
 

Feer~

Oráculo Lvl 1
#2
Es
¡Hola! Para un final de la facultad que entrego el miércoles, tengo que desarrollar un juego basado en web utilizando:

Obligatoriamente: HTML, CSS, JavaScript vanilla y jQuery.
Opcional: PHP, librerías JS a elección.

El juego consiste en adivinar un número aleatorio de X cantidad de cifras. El juego está subido y lo pueden jugar acá: www.juegodescifralo.com

Si lo juegan, notarán que hay tres tipos de "cifras" dentro del número que ingrese el usuario:
Buenas: son aquellos dígitos que coinciden con la posición del dígito del número a adivinar. Por ejemplo:

Si el número a adivinar es 8512
Y el número ingresado es 8365... El 8 es una cifra "buena"

Regulares: supongo que van entendiendo... En el ejemplo anterior, el 5 es una cifra regular, ya que está dentro del aleatorio pero no en la posición correcta.

Malas: son las cifras que no están dentro del número aleatorio.

Me cuesta explicar el inconveniente que tengo. Paso a dejar mi código para chequear cada cifra y ver si es buena, regular o mala.
Código:
function checkNumeros(randomArray, miArray) {
        for (var x = 0; x < randomArray.length; x++) {
            var posRepetido = randomArray.indexOf(miArray[x]); //El número que recorro ahora está dentro del random??

            if (posRepetido == -1) {
                console.log("el numero" + miArray[x] + "no está dentro del conjunto");
                malos++;

            } else { //Está dentro del conjunto
                regulares++;

                if (miArray[x] == randomArray[x]) { //El actual es igual al aleatorio en la misma posicion
                    console.log("el numero" + miArray[x] + "está en la misma posición");

                    buenos++;
                    regulares--;
                } else { //No es igual en la misma

                    if (randomArray[posRepetido] != miArray[posRepetido]) {
                       console.log("el numero" + miArray[x] + "está dentro del conjunto pero no en la misma posición");
                     

                    } else {
                        regulares--;
                        malos++;
                        console.log("el numero" + miArray[x] + "no está dentro del conjunto");

                    }
                }
            }
        }

        var obj = {
            buenos: buenos,
            regulares: regulares,
            malos: malos
        };

        return obj;
    }
En su momento tuve otra versión de este código, pero lo que ocurría era lo siguiente:

Supongan que el número aleatorio es 8544
Y yo arriesgo el número 8455

Me va a decir: 8 bueno, 4 regular, 5 regular, 5 regular. Lo cual es incorrecto e ilógico. La falla está en que el código busca el número 5, y como lo encuentra dentro del número aleatorio, dice que el 5 es regular, sin importar la cantidad de veces que esté en el aleatorio.

Como dije, eso ocurría en una versión previa del código que puse arriba. Esta versión tiene algo de ese bug corregido, pero en ocasiones se sigue bugueando de formas básicamente impredecibles.

Todo empeora cuando en el juego se puede elegir la dificultad "personalizada", donde el usuario elige la cantidad de cifras que tenga el número a adivinar. Entonces puede elegir adivinar un número de 20 cifras, donde sí o sí habrá repetición de números, y no habrá números malos sino sólo regulares y buenos.

Mi pregunta se resume a: ¿de qué manera podría realizar este algoritmo de una manera más inteligente, y funcional sean cuantas seas las cifras del número a adivinar?

Sé que es extraño y difícil de entender. Mi recomendación es que jueguen (www.juegodescifralo.com), seguramente lo entiendan con mayor claridad.

¡Desde ya, muchas gracias por la ayuda!
Estuve probando el juego ese que dejaste y pareciera que te dice que el número es regular únicamente si está cerca de la posición original. En caso contrario te dice que es malo..
 

SimP

Thalion
Ex-Staff
#3
Es


Estuve probando el juego ese que dejaste y pareciera que te dice que el número es regular únicamente si está cerca de la posición original. En caso contrario te dice que es malo..
Yo lo que había notado es que cuando tenés 2 números buenos y uno de ellos se repite dentro de los no-buenos, te lo puede tomar malo aunque sea regular... No sé, la verdad las circunstancias de bug me parece que varían y son bastante impredecibles. Por eso me está dando dolor de cabeza. ¡Gracias!

Saludos!
 

MrMan

IT Team Lead @ Booking.com
Miembro del equipo
Administrador
#4
Esto debería funcionar.

Asumo que tenes jQuery 1.2 al menos que soporte inArray() o sino reemplaza esa parte con lodash o similar.

Código:
function adivinar (numUser, numRandom) {
    if (typeof numUser == 'number') {
        numUser = numUser.toString().split('');
    }
    if (typeof numRandom == 'number') {
        numRandom = numRandom.toString().split('');
    }

    if (typeof numRandom != 'object' || typeof numUser != 'object') {
        return false;
    }

    if (numRandom == numUser) {
        return true;
    }

    var numRegular = {},
        numBuenos = {},
        numMalos = {},
    numRepeat = {};


    for(var i  = 0; i < numRandom.length; i++) {
      if (!numRepeat[numRandom[i]]) {
        numRepeat[numRandom[i]] = 0;
    }
      numRegular[numRandom[i]] = 0;
      numRepeat[numRandom[i]]++;
  }
 

    for (var i  = 0; i < numUser.length; i++) {
        if (numUser[i] == numRandom[i]) {
            numBuenos[numUser[i]] = numUser[i];
        }else if ($.inArray(numUser[i], numRandom)) {
      if (!numRegular[numUser[i]]) {
        numRegular[numUser[i]] = 0;
      }
      if (numRegular[numUser[i]] < numRepeat[numUser[i]]) {
          numRegular[numUser[i]]++;
      } else {
                numMalos[numUser[i]] = numUser[i];
      }
        } else {
            numMalos[numUser[i]] = numUser[i];
        }
    }


    return {
        regular: Object.values(numRegular).reduce((a, b) => a + b),
        buenos: Object.keys(numBuenos).length,
        malos: Object.keys(numMalos).length
    };

}

console.log(adivinar(8365, 8512));
[DOUBLEPOST=1532938598,1532904330][/DOUBLEPOST]Para los buenos: Chequeo que el número actual del loop coincida en la posición del Array de random. Si coincide lo agrego al objeto de números buenos.
Para los regulares: Esto es un poco tricky porque un número puede estar más de una vez repetido en el random y en el input del user entonces primero lo que hago es poner en un Array todos los números que hay en el número random y cuantas veces se repiten.
Desues cuando estoy loopeando el input del usuario me fijo si el número ingresado está dentro del array random, si está y la cantidad de veces que lo agregué a numRegulares es menor que la cantidad de veces que está ese numero en el array de randoms sumo 1 a la cantidad de regulares para ese número.
Para los malos: Si ninguna de las condiciones anteriores se cumplen agrego al número como malo.

Lo que termino devolviendo es el tamaño del object de buenos y malos. Pero para los regulares como en realidad es una suma lo que hago es reduce donde sumo la cantidad de veces que encontré cada uno de los números dentro del objeto.

Creo que funciona incluso mejor que la página que pasaste.
 

SimP

Thalion
Ex-Staff
#7
Esto debería funcionar.

Asumo que tenes jQuery 1.2 al menos que soporte inArray() o sino reemplaza esa parte con lodash o similar.

Código:
function adivinar (numUser, numRandom) {
    if (typeof numUser == 'number') {
        numUser = numUser.toString().split('');
    }
    if (typeof numRandom == 'number') {
        numRandom = numRandom.toString().split('');
    }

    if (typeof numRandom != 'object' || typeof numUser != 'object') {
        return false;
    }

    if (numRandom == numUser) {
        return true;
    }

    var numRegular = {},
        numBuenos = {},
        numMalos = {},
    numRepeat = {};


    for(var i  = 0; i < numRandom.length; i++) {
      if (!numRepeat[numRandom[i]]) {
        numRepeat[numRandom[i]] = 0;
    }
      numRegular[numRandom[i]] = 0;
      numRepeat[numRandom[i]]++;
  }
 

    for (var i  = 0; i < numUser.length; i++) {
        if (numUser[i] == numRandom[i]) {
            numBuenos[numUser[i]] = numUser[i];
        }else if ($.inArray(numUser[i], numRandom)) {
      if (!numRegular[numUser[i]]) {
        numRegular[numUser[i]] = 0;
      }
      if (numRegular[numUser[i]] < numRepeat[numUser[i]]) {
          numRegular[numUser[i]]++;
      } else {
                numMalos[numUser[i]] = numUser[i];
      }
        } else {
            numMalos[numUser[i]] = numUser[i];
        }
    }


    return {
        regular: Object.values(numRegular).reduce((a, b) => a + b),
        buenos: Object.keys(numBuenos).length,
        malos: Object.keys(numMalos).length
    };

}

console.log(adivinar(8365, 8512));
[DOUBLEPOST=1532938598,1532904330][/DOUBLEPOST]Para los buenos: Chequeo que el número actual del loop coincida en la posición del Array de random. Si coincide lo agrego al objeto de números buenos.
Para los regulares: Esto es un poco tricky porque un número puede estar más de una vez repetido en el random y en el input del user entonces primero lo que hago es poner en un Array todos los números que hay en el número random y cuantas veces se repiten.
Desues cuando estoy loopeando el input del usuario me fijo si el número ingresado está dentro del array random, si está y la cantidad de veces que lo agregué a numRegulares es menor que la cantidad de veces que está ese numero en el array de randoms sumo 1 a la cantidad de regulares para ese número.
Para los malos: Si ninguna de las condiciones anteriores se cumplen agrego al número como malo.

Lo que termino devolviendo es el tamaño del object de buenos y malos. Pero para los regulares como en realidad es una suma lo que hago es reduce donde sumo la cantidad de veces que encontré cada uno de los números dentro del objeto.

Creo que funciona incluso mejor que la página que pasaste.
Te agradezco enormemente la ayuda. Ahora no tengo tiempo para probar bien el código, pero veo que es una posible solución muy interesante. En cuanto lo testee edito este post o te aviso. De nuevo, muchas gracias por tu tiempo.

¡Saludos!
 
Arriba