Proyecto Bonoloto y problemas con puerto serial

Me alegro de saludar a la comunidad, acabo de llegar tanto al foro como al mundillo Arduino.

Gracias por vuestras aportaciones que ayudan a entender y aprender este hobby (para mí, al menos).

Voy al grano: mi proyecto consiste en programar Arduino UNO para que me devuelva 6 números aleatorios en series de dos (un par de lineas). Tras investigar por la red, hice un sckecht con números aleatorios comprendidos entre 1 y 49; pero me encuentro con tres problemas que, por mucho que he buscado en este foro y en Internet, no consigo resolver.

Se trata, en primer lugar, de conseguir que esos números no se repitan en las secuencias; segundo que se ordenen de menor a mayor (aunque esto no es imprescindible), y por último, como dije más arriba, conseguir que sean tan solo seis números en cada fila y no una secuencia indefinida, como tengo ahora.

Agradecería que alguien me echara una mano y si es necesario subir el código, con paciencia, lo haría, no sin esfuerzo pues es la primera vez. No obstante he adjuntado un archivo con el código. ¿Es suficiente?

Gracias por anticipado.

/*programarfacil.com
   blog/random
*/
long randomNumber;

void setup() {

  Serial.begin(9600);
  Serial.println("Bonoloto - Apuesta");
  randomSeed(analogRead(A0));
  int randomNumber;
}
void loop(){
  randomNumber = random(1, 49);
    Serial.print("Numero:   ");
    Serial.println(randomNumber);
    delay(1000);
}

Numeros_aleatorios.ino (322 Bytes)

jvp:
Se trata, en primer lugar, de conseguir que esos números no se repitan en las secuencias; segundo que se ordenen de menor a mayor (aunque esto no es imprescindible), y por último, como dije más arriba, conseguir que sean tan solo seis números en cada fila y no una secuencia indefinida, como tengo ahora.

  • Debes inicializar la "semilla" con alguna fuente de valores aleatorios (ejemplo: pin analógico Ax sin conectar a nada). Si aún así no estás satisfecho con esto, será que busques "Arduino true RNG" para encontrar alternativas diferentes de aleatoriedad verdadera.
  • Ordenar se hace con vectores (datos almacenados); y el algoritmo a utilizar depende del tamaño de este.
  • Mediante un ciclo for repites la obtención de números (pseudo) aleatorios solo las veces necesarias.

PD: ¿pueden los números repetirse en una misma secuencia? Este detalle lo debes considerar a la hora de crear el programa, ya que es completamente posible que suceda.

Ah y por cierto, puede que este hilo esté en la sección equivocada. Por favor no hagas otro con el mismo tema, deja que algún moderador mueva este donde corresponda :wink:

Hilo movido a Software

Lucario, gracias por tu interés, pero no estoy a la altura de esas explicaciones. ¡¡¡ No sé ni por dónde empezar!!!

Esta es la semilla de la que Lucario te habla

randomSeed(analogRead(A0));

Quieres ordenar: busca como indican las normas en google: Arduino ordenar

Seis numeros en cada fila es un simple manejo de como quieres mostrar los números

Simplemente parece una tarea escolar y estas pidiendo que te la hagan.

Un ciclo

for (int i=0; i<6; i++) {
              randomNumber = random(1, 49);
              Serial.print(randomNumber);
              Serial.print(" ");
          }
          Serial.println();

te permitirá controlar que imprimes, cuando el ciclo termina, cambias de linea con un Serial.println();
Y estas listo para la siguiente linea.

Ahora esto no ordena nada. Te lo dejo a ti.

Buenos días. Mil gracias por vuestra actitud, seguro que con esos consejos resuelvo mi "jueguecillo".

ArduMith, me refiero a mostrarlo en consola. Gracias.

Surbyte, inestimable tu ayuda, gracias. En el poco tiempo que llevo por este foro, he leído bastantes posts con temas interesantes y he visto tus comentarios en muchos, donde aprecio tu carácter formativo y una constante, aconsejas siempre que la gente se lo curre. Bien por ti.

Leí con atención las normas del foro y entendí perfectamente la que dice "Buscar antes de postear". Cosa que hice, aunque no fuera una norma del foro. En mi primer mensaje podrás leer "Tras investigar por la red...", y "...por mucho que he buscado en este foro y en Internet...".

Ahora bien, me interesa que sepas algo de mí: paso ya de los 60 y comencé a trabajar apenas con 14. Trabajando estudié mi carrera y gracias a mi esfuerzo saqué adelante, con sobresaliente, a mi familia y ayudé a progresar a muchísima gente. Jamás, ni he pedido que alguien haga alguna tarea por mí ni se me han caído los anillos cuando de dedicar horas a investigar algo se trataba.

El lenguaje escrito se da a innumerables interpretaciones, por lo que te ruego no te formes una mala opinión de este humilde "programador novato y torpe" y, por supuesto, te ruego que me perdones si yo no he sido capaz de interpretar tu escrito. Aquí tienes un amigo, con muchas ganas de aprender y dejarse enseñar.

Gracias, nuevamente. Un saludo.

Ojala el sistema permitiera poner edades, muchas malas interpretaciones de un texto escrito se evitarían con solo leerla. Tampoco sería 100% seguro pero... 99%.
Cuando digo como lo dicen las reglas me refiero a que es curioso pero todos buscan en internet pero cuando le agregas a la busqueda de lo que quieres Arduino, todo aparece y si no lo pones, todo se mezcla y se confunde.
A eso me refería.

jvp:
Lucario, gracias por tu interés, pero no estoy a la altura de esas explicaciones.

Admito que a veces se me va la mano y hablo como si la persona que pregunta estuviera más o menos a mi nivel (poco o nada comprensible para el principiante); así que iré más despacio.

Ya que te dieron una explicación más fácil de entender, solo diré que me había confundido en esta parte:

jvp:
conseguir que esos números no se repitan en las secuencias

En un principio lo entendí como que por varios reinicios te generaba exactamente la misma secuencia de números (esto es posible si la "semilla" se inicializa con el mismo valor en ambas ejecuciones); en vez de ver que era simplemente evitar que un mismo número se repitiera en la secuencia.

Para solucionarlo tienes dos opciones:

  • Almacenar los números generados en vector (que de por sí es necesario para ordenarlos), si el siguiente ya está ahí (verificarlo), solicitar otro hasta hallar uno que nunca haya estado.
  • Preguntarle a un conjunto de boolean (variable tipo "verdadero-falso") si dicho número ya existe; de ser así, se procede de la misma manera antes mencionada. Este método consume más memoria que el anterior, pero se ejecuta mucho más rápido (para saber por qué, busca la diferencia entre "acceso secuencial" y "acceso aleatorio").

Espero que me hayas captado la idea, sino tendré que ejemplificarla.

Surbyte y Lucario, todo correcto. Me pongo a ello. Simplemente, se me ocurrió esta idea y como estoy practicando con mi nuevo kit, me quedé sin recursos. Eso pasa por no ir algo más despacio adquiriendo experiencia.

Gracias. Saludos.

Cuidado, la función random(min, max) es "un poco engañosa" con su parámetro max. El valor máximo que puede llegar a generar no es max, sino uno menos (max - 1), con lo que:

  randomNumber = random(1, 49);

... genera números aleatorios entre 1 y 48. Para generar números aleatorios entre 1 y 49 debería de ser:

  randomNumber = random(1, 50); // Genera números aleatorios entre 1 y 49

Documentación (en inglés) de la función random(): random() - Arduino Reference

Si quieres, jvp, puedo poner una posoble solución al problema que has planteado. Pero tal vez prefieras intentar dar con la solución por tu cuenta, quizás con pequeñas ayudas, antes que te lo den todo resuelto.

Yo he seguido la primera opción de Lucario448, usar un array (vector) con los números generados, pero manteniendo el array ordenado en todo momento en lugar de ordenarlo una vez obtenidos todos los resultados.

Creo que a ArduMyth se le han escapado un par de "detalles" en su propuesta, por despiste o por prisas. Entre ellos el "problema" del máximo de la función random() que ya he comentado.

Aquí posibles correcciones con un pequeño comentario en cada una de ellas:

#define SIZE(x) (sizeof(x) /sizeof(x[0])) // <-- Faltaba cerrar un paréntesis

void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0));
}

void loop() {
  generar();
  delay(3000); //odio delay, es para simplificar el ejemplo.
}

void generar() {
  Serial.println("_____________________");
  Serial.println("Bonoloto:");
  byte resultados[7], indice = 0; // <-- Es recomendable inicializar 'indice' con el cero
  while( indice < SIZE(resultados) ) {
    bool add = true;
    byte rnd = random(1,50); // <-- Para genererar un número aleatorio del 1 al 49
    for( byte i = 0; i < SIZE(resultados); i++) {
      if( rnd == resultados[i]) {
        add = false;
        break;
      }
    }
    if(add){
      resultados[indice] = rnd;
      indice++;
      Serial.print("("+dosCifras(rnd)+")");
    }
  }
  byte complementario = resultados[6];
  byte reintegro = random(0,10); // <-- Para generera un número aleatorio del 0 al 9
  // Serial.print("("+dosCifras(complementario)+")*"); // <-- El "complementario" se repetía...
  Serial.print("*"); // <-- ... basta con imprimir sólo el *, el complementario es el último generado el el while
  Serial.println("("+String(reintegro)+")*"); // <-- El reintegro siempre es de un dígito (del 0 al 9), "sobra" 'dosCifras()'
  Serial.println("Complementario: "+String(complementario)); // <-- Faltaba cerrar un paréntesis
  Serial.println("Reintegro: " + String(reintegro));
}

String dosCifras(byte n) {
  return ( (n<=9) ? "0" : "" ) +String(n); // <-- Si es menor o igual (no estrictamete menor)
}

Hola, amigos.

Esto es divertido!!

ArduMyth, te agradezco tu solución. Yo tampoco quiero que me lo den hecho, pero es la primera vez que me enfrento a esto sin tener ni idea. He sido un poco osado. En tu scketch debe haber un error en la linea 17 (quizá la anterior) del tipo "se espera ')' antes de '}' que no resuelvo. Aún así me sirve de mucho para estudiar la lógica de las secuencias y tomar notas para cuando sea "Senior".

Por cierto, en la bonoloto tan solo se marcan los números de las apuestas, complementario y reintegro te lo dan de forma automática. Toda esa zona la he eliminado (creo).

IgnoranteAbsoluto (de ignorante "absolutamente" nada), muchas gracias. Si no es molestia si me gustaría, también, conocer tu solución. Es un punto de vista más y me sirve de estudio.

Como comprenderéis, el proyecto es un tanto infantil, pero soy de los que se lanzan "a hacer", que es como se aprende.

Un saludo.

Mi propuesta:

const int VALOR_MINIMO = 1;          // El mínimo valor del rango de números aleatorios a obtener
const int VALOR_MAXIMO = 49;         // El máximo valor del rango de números aleatorios a obtener
const int CANTIDAD_RESULTADOS = 6;   // Total de números a obtener (sin repetición)

void setup() {
    Serial.begin(9600);
    Serial.println("Bonoloto - Apuesta");
    randomSeed(analogRead(A0));
}

void loop() {
    int resultados[CANTIDAD_RESULTADOS]; // Array que almacena los números obtenidos
    int obtenidos = 0; // Cantidad de resultados obtenidos, sin repetición

    while (obtenidos < CANTIDAD_RESULTADOS) { // Bucle que controla cuántos resultados, sin repetición, llevan obtenidos
        int candidato = random(1, VALOR_MAXIMO + 1); // Obtenemos un posible resultado. Hay que sumar uno al máximo, mirar la documentación de random()
        int posicion = 0; // Controla la posición del elemento del array de resultados que estamos comparando con el resultado actual
        while ((posicion < obtenidos) && (candidato > resultados[posicion])) { // Mientras no se sobrepase el último elemento y el "candidato" sea mayor que el elemento comparado...
            posicion++; // ... pasamos al siguiente elemento obtenido, ya que el actual es menor que el candidato
        }
        // Si hemos llegado hasta aquí es porque el "candidato" es mayor que todos los encontrados hasta ahora o porque es mayor o igual al que está en "posicion"
        if (posicion < obtenidos) { // Esta condición no se cumple si el candidato es mayor que todos los encontrados hasta ahora...
            // ... así que si entra aquí es que no se ha de poner al final de la lista de los encontrados
            if (candidato == resultados[posicion]) { // Si es igual al que ya se ha encontrado...
                continue; // ... salta directamente al principio del 'while (obtenidos < CANTIDAD_RESULTADOS)' y lo evalúa. Este resultado no sirve, por lo que lo descartamos y buscamos otro
            }
            for (int i = obtenidos; i > posicion; i--) { // Este bucle es para mantener el array ordenado, "moviendo" una posición todos los elementos desde el último 'obtenido' hasta el que se encuentra en la 'posicion' en que ha de ir "el nuevo")
                resultados[i] = resultados[i - 1]; // "Movemos" el elemento, de una posición a otra del array
            }
        }
        resultados[posicion] = candidato; // Colocamos al "candidato" en la posición que le corresponde del array
        obtenidos++; // Actualizamos la cantiad de resultados obtenidos
    }
    // Llegado a este punto ya tenemos todos los resultados en el array
    for (int i = 0; i < CANTIDAD_RESULTADOS; i++) { // Recorremos todos los resultados del array
        if (i > 0) { // Si no se trata del primer resultado...
            Serial.print(" "); // ... ponemos un separador entre resultados
        }
        if (resultados[i] < 10) { // Si tiene menos de dos cifras...
            Serial.print(' '); // ... "rellenamos" con un espacio
        }
        Serial.print(resultados[i]); // Mostramos el resultado
    }
    Serial.println(); // Saldo de línea para separar cada grupo de resultados
    delay(1000);
}

Señores, genial. he probado con las correcciones y funciona divinamente.

Tan solo modificar lo de complementario y reintegro.

Pero hay otra cosa que yo no resolví y era una de las preguntas en este hilo: la secuencia se repite indefinidamente. ¿Cómo consigo que pare tan solo con dos? Y ya no pido más. Solo preguntaré si algo no entiendo y tras haber buscado en Google, como me sugirieron.

Gracias.

Hay muchas maneras de hacer que solo lo muestre dos veces. Una de ellas es "entrar en un bucle infinito" que no haga nada, una vez que lo ha hecho dos veces. Para ello basta con cambiar la línea:

void loop() {

Por:

int veces = 0;
void loop() {
    if (++veces > 2) for (); // una vez hecho dos veces, se queda en el for "para siempre"

Ardu e ignorante, en ambos casos perfecto. Gracias por vuestra ayuda. Ahora a estudiar linea por linea.

Saludos. Buen domingo!!!