Mastermind en C

0buS

Hola,
Tengo que hacer una practica sobre el juego Mastermind en C, y pff ando perdidisimo, mas que nada porque acabo de dar los arrays, y a mi profesora no se le ocurre otra cosa que mandarnos estas practicas.
La practica en cuestión es la siguiente:

Realiza un programa en C para jugar al juego del MASTER MIND.

El programa genera una combinación de 4 caracteres aleatorios (6 letras de la A – F sin repetición) que deberá ser acertada por el jugador en un número limitado de propuestas.

Cada vez que el jugador realice una propuesta de combinación, la máquina le informará en los siguientes términos.

  COLOCADAS = X  DESCOLOCADAS = Y

Se consideran colocadas aquellas letras tanto en valor como en posición exacta que ocupan dentro de la secuencia.

Se consideran descolocadas aquellas letras que aparecen en la secuencia pero no en la posición que el jugador ha propuesto.

Se deben mantener en pantalla las jugadas realizadas y los resultados de las mismas mientras dure la partida, ya que, en buena lógica, cada combinación propuesta se basará en los resultados obtenidos en las combinaciones anteriores.

Finalizada la partida el programa preguntará:

OTRA PARTIDA? (S/N)

Hasta ahora lo que llevo hecho es esto:

/*MASTERMIND*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    
time_t t; int i,j,pos; char color[6]={'A','B','C','D','E','F'}; char combi[4]={' ',' ',' ',' '}; pos=0; srand((unsigned)time(&t)); for(i=0;i<4;i++) { do { pos=rand()%6; }while (combi[pos]==color[i] && combi[pos]==color[i] && combi[pos]==color[i] && combi[pos]==color[i] && combi[pos]==color[i]); combi[i]=color[pos]; } printf("\nCombinacion: "); for(j=0;j<4;j++) printf("%c ",combi[j]); printf("\n\n"); system("pause");


return 0; }

Para lo poco que llevo, me sigue generando letras repetidas, cuando me las deberia de comprobar y no las comprueba :S.

RaymaN

Yo también soy novato en C, pero el do que tienes dentro del for no debería tener una condición para que termine?

No uso nunca los do, solo for y while, pero supongo que tendrás que ponerle

do
{
} while (x < y);

Edit: vale, no había visto el while entre tanta morralla de código xD

maRc

Enfoca de otra manera el problema, porque no tengo ni idea de que estás haciendo aquí, pero te vas a salir de rango del vector combi:

do
{
    pos=rand()%6;
}
while (combi[pos]==color[i] && combi[pos]==color[i] && combi[pos]==color[i] && combi[pos]==color[i] && combi[pos]==color[i]);

Pos puede valer hasta 5 y combi va de 0 a 3.

cabron

No entiendo como lo pretendes hacer.

Te pongo la lógica que uso yo para estas cosas. Puede nos er la más eficiente, pero para cosas como lo quieres hacer vale de sobra:

INICIO crear combinacion

DESDE posicionActual = 0 hasta 4

HACER

letra aleatoria = Generar letra aleatoria

MIENTRAS letra ya usada (letra aleatoria, combinacion, posicionActual)

combinación [posicionActual] = letra aleatoria

FIN DESDE

FIN

Y para comprobar si ya la has usado:

INICIO letra ya usada (letra, combinacion, posicionesUsadas)

letra usada = falso

PARA i = 0, MIENTRAS i < posicionesUsadas

SI combinacion = letra

letra usada = verdadero

FIN SI


FIN PARA

devolver letraUsada

FIN

Te lo he puesto del tirón sin comprobarlo y a lo mejor he metido la gamba en algo, pero vamos, que sería algún detalle, he creado unas cuantas veces combinaciones aleatorias así en distintos lenguajes.

0buS

gracias cabron, lo reflexionaré a ver si me surge algo macho, porque que lio llevo xd.

NeB1

habeis studiado arrays multidimensionales? (o matrices, como quieras) porque para guardar todas las jugadas yo creo que lo vas a necesitar...
además

ese do while que has hecho con esa condición de parada....

do{

}while(condicion AND lamismacondición AND lamismacondición AND lamismacondición)

no tiene mucho sentido, le pone en 4 ANDS la misma condición xDD. de todas formas no afecta al comportamiento del programa.

además en ese mismo sitio, a "pos" le asignas un valor random entre 1 y 6, después haces:

"combi[pos]==color" y si pos vale 6 estás haciendo combi[6] lo cual no existe porque lo has declarado como un vector de 4 elementos.
Supongo que querías hacer "combi==color[pos]".

NeB1

ah coño! tu querías que no se repitiera ninguna letra!!! xDD ahora entiendo lo que querías hacer y porque has puesto eso de 4 condiciones de parada iguales. Esta sería lo que creo que era tu forma de hacerlo:

 /*MASTERMIND*/

 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>

 int main()
 {

 time_t t;

 int i,j,pos;
 char color[6]={'A','B','C','D','E','F'};
 char combi[4]={' ',' ',' ',' '};

 pos=0;

 srand((unsigned)time(&t));

 for(i=0;i<4;i++)
 {
    do
    {
       pos=rand()%6;
    }while (combi[0]!=color[pos] && combi[1]!=color[pos] && combi[2]!=color[pos] && combi[3]!=color[pos]);
    
combi[i]=color[pos]; } printf("\nCombinacion: "); for(j=0;j<4;j++) printf("%c ",combi[j]); printf("\n\n"); system("pause"); return 0; }

de todas formas no es la forma más correcta de hacerlo.

Además mira a ver que valores coge pos=rand()%6. *¿Del 0 al 6? ¿Del 1 al 6? ¿Del 0 al 5? piensa que las posiciones de los vectores empiezan por 0, así que color[] tiene desde la posición 0 hasta la 5, no existe color[6].

  • ¿Del 0 al 6? ¿Del 1 al 6? ¿Del 0 al 5? = si es la primera respuesta, tienes que cambiar la linea por "pos=rand()%5". Si es la respuesta segunda (lo más probable) tienes que añadir justo despues "pos=pos-1" y si es la última respuesta irá bien.
0buS

Bueno edito el post xD.

He conseguido hacerlo casi entero, solo que me queda, hacer una función para que no se repitan las letras aleatorias que genera el rand.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define CIFRAS 4

int comprobarLetra(char letra[],char combinacion[],int azar);

int main()
{
    
time_t t; int i,j,azar,intentos,colocadas,descolocadas,repetida,jugar; char letra[6]={'A','B','C','D','E','F'}; char combinacion[CIFRAS]={' ',' ',' ',' '}; // 5 6 4 7 char jugada[CIFRAS]={' ',' ',' ',' '}; char volver_jugar; jugar=intentos=repetida=azar=0; srand((unsigned)time(&t)); for(i=0;i<4;i++) { do { azar=rand()%6; }while (FUNCION); combinacion[i]=letra[azar]; comprobarLetra(letra,combinacion,azar); } printf("\nCombinacion: "); for(j=0;j<CIFRAS;j++) printf("%c ",combinacion[j]); printf("\n\nIntroduce una letra por posicion, de la A a la F"); do { for(j=0;j<CIFRAS;j++) { printf("\nLetra %d: ",j+1); scanf("%c",&jugada[j]); fflush(stdin); } intentos++; colocadas=0; descolocadas=0; for (i=0;i<CIFRAS;i++) { for (j=0;j<CIFRAS;j++) { if (combinacion[i]==jugada[j]) if (i==j) colocadas++; else descolocadas++; } } printf(">>>>>>> Jugada: "); for(i=0;i<CIFRAS;i++) printf("%c ",jugada[i]); printf("|||| [COLOCADAS: %d] [DESCOLOCADAS: %d] >>>>>>>",colocadas,descolocadas); if(colocadas==4) printf("\n\nENHORABUENA! HAS GANADO!(%d intentos)\n",intentos); else { printf("\n\nOTRA PARTIDA<S/N>?"); scanf("%c",&volver_jugar); fflush(stdin); if (volver_jugar=='S') jugar=1; else jugar=0; } }while (colocadas<CIFRAS && jugar==1); system("pause");
return 0; }

En el do while primero:

#  do
# {
# azar=rand()%6;
#
# }while (FUNCION); 

donde pone FUNCION, necesito implementar una funcion para que revise si, en el caso de repetirse una letra, volver a generar el rand.
Hasta ahora tengo esto, pero no me funciona:

int comprobarLetra(char letra[],char combinacion[],int azar)
{
     int i,repetida=0;
     
for(i=1;i<4;i++) { if(combinacion[i]==combinacion[i-1]) { repetida=1; } } return (repetida); }

Con esta funcion, cambiando el main asi, no me funciona:

do
           {
              azar=rand()%6;
              
}while (repetida==1); combinacion[i]=letra[azar]; comprobarLetra(letra,combinacion,azar); }
NeB1

la función deberias llamarla dentro del do while:

    do
    {
        azar=rand()%6;
        /*metemos la letra en el array*/
        combinacion[i]=letra[azar];
        /*comprobamos que no se repita, y en caso de repetirse, sobreescribiremos esa letra, por eso no salimos del do...while, para que la "i" siga valiendo lo mismo y estemos sobreescribiendo en la misma posición*/
        comprobarLetra(letra,combinacion,azar);
    }while (repetida==1);
    

además la función que has creado solo comprueba letras repetidas CONSECUTIVAS, sería así para comprobar que no se repita ninguna letra dentro del array:

    int comprobarLetra(char letra[],char combinacion[],int azar)
    {
        int i,j,repetida=0;
   
for(i=0;i<4;i++) //Recorremos cada elemento del array (EMPIEZAN POR 0!) { for(j=0;j<4;j++){ /*Para cada elemento del array lo comprobamos con TODOS los elementos del propio array, menos el mismo*/ if(combinacion[i]==combinacion[j]) { if( i != j) /*si no estamos comprobando un elemento del vector con él mismo, es que está repetido*/ repetida=1; } } } return (repetida); }

Así estaría bien, a la función le pasas dos parametros innecesarios (letra y azar) ya que al pasarle "combinación" después de introducirle el último valor "combinación=letra[azar]" ya tienes todo lo que necesitas.

0buS

joder pues me sigue generando letras repetidas :S. Tengo la función y el do-while como lo has puesto.

Nada tio, no soy capaz de que no repita las letras. Estoy ralladisimo ya xDD. Esto es lo que tengo:

for(i=0;i<CARACT;i++) 
        {
              do
              {
              repetida=0;
              azar=rand()%6; 
              combinacion[i]=letra[azar];
              comprobarLetra(combinacion);
              }while(repetida==1);
        }
int comprobarLetra(char combinacion[])
{
     int i,j,repetida=0;
     
for(i=0;i<CARACT;i++) { for(j=0;j<CARACT;j++) { if(combinacion[i]==combinacion[j]) { if(i!=j) repetida=1; } } } return (repetida); }
JuAn4k4

vector de posibles letras

int idx( char letra ){
// Dev la posicion en el vector de una letra
return (int)letra - (int)'A';
}

char generarLetra(int* letrasgeneradas)
{
int generada = 0;

while ( !generada )
{
// genero letra al azar
generada = vec[ idx(letra) ];
}
return letra;

}

int main (...)
{int vec[30];
//inicializo vec a 0.
char letra;

letra = generarLetra( vec );
}

Problema? Que si hay muchas letras generadas, que saque al azar la que falta es muy jodido.

Otra solución:
Tener un conjunto de letras, y sacar una al azar, eliminandola del conjunto.
Te creas un TAD (tipo abstracto de datos) conjuntoAbecedario, con regenerar (empieza con todas las letras), sacar una al azar (elimina del conjunto) y ya lo tienes !

Seguro que tienes el TAD conjunto por ahi, solo tendras que hacer lo de sacar una aleatoriamente, pero eliminarla eliminara y tal.. incluso tendras mas ops si quieres.

0buS

joder no entinedo nada de lo que me pones juanaka xDD. Mi nivel de c es pésimo... xd.

NeB1

te está vacilando xDDD aunque es una posible solución.

oye, tienes el código fuente entero del programa? postealo así le hago una traza a a ver.

NeB1
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
   
#define CIFRAS 4 int comprobarLetra(char a,char combinacion[]); int main() {
time_t t;
int i,j,azar,intentos,colocadas,descolocadas,repetida,jugar,ola; char letra[6]={'A','B','C','D','E','F'}; char combinacion[CIFRAS]={' ',' ',' ',' '}; // 5 6 4 7 char jugada[CIFRAS]={' ',' ',' ',' '}; char volver_jugar;
jugar=intentos=repetida=azar=0;
srand((unsigned)time(&t));
for(i=0;i<4;i++) { do { azar=rand()%5; /*metemos la letra en el array*/ /*comprobamos que no se repita, y en caso de repetirse, sobreescribiremos esa letra, por eso no salimos del do...while, para que la "i" siga valiendo lo mismo y estemos sobreescribiendo en la misma posición*/ repetida=comprobarLetra(letra[azar],combinacion); combinacion[i]=letra[azar]; }while (repetida==1);
}
printf("\nCombinacion: "); for(j=0;j<CIFRAS;j++) printf("%c ",combinacion[j]); printf("\n\nIntroduce una letra por posicion, de la A a la F");
do { for(j=0;j<CIFRAS;j++) { printf("\nLetra %d: ",j+1); scanf("%c",&jugada[j]); fflush(stdin); } intentos++;
colocadas=0; descolocadas=0; for (i=0;i<CIFRAS;i++) { for (j=0;j<CIFRAS;j++) { if (combinacion[i]==jugada[j]) if (i==j) colocadas++; else descolocadas++; } }
printf(">>>>>>> Jugada: "); for(i=0;i<CIFRAS;i++) printf("%c ",jugada[i]); printf("|||| [COLOCADAS: %d] [DESCOLOCADAS: %d] >>>>>>>",colocadas,descolocadas); if(colocadas==4) printf("\n\nENHORABUENA! HAS GANADO!(%d intentos)\n",intentos); else { printf("\n\nOTRA PARTIDA<S/N>?"); scanf("%c",&volver_jugar); fflush(stdin); if (volver_jugar=='S') jugar=1; else jugar=0; } }while (colocadas<CIFRAS && jugar==1);

system("pause");

return 0; } int comprobarLetra(char a,char combinacion[]){ int i,j,repetida=0; for(i=0;i<4;i++) //Recorremos cada elemento del array (EMPIEZAN POR 0!) {
if(combinacion[i]==a) { repetida=1; } } return (repetida); }

ahí lo tienes, creo q nos habíamos complicado demasiado xDDD ahora me he puesto a mirarlo y he visto que era más fácil.

elkaoD

http://pastebin.com/f919a56

A ver si te vale, lo he intentado explicar y hacer todo lo mejor y más sencillo que he podido. Me gusta mucho el método que he usado para generar la combinación aleatoria. Fácil, sencillo y elegante (Para mi gusto) sobre todo porque no se tira haciendo rands hasta que sale una nueva letra sino que simplemente descarta las usadas. Encima me ha quedado un ejercicio cortito y bastante limpio (Creo) para la cantidad de funcionalidad extra que tiene xD

Si no entiendes cómo genera la cadena, aquí hay una forma visual de entenderlo con valores de ejemplo:
                  cadLetras - cadHecha
Estado inicial: "ABCDEF" - "" (Ahora se genera elegLetra aleatorio de 0 a 5)
elegLetra = 4: "ABCDF" - "E" (elegLetra de 0 a 4)
elegLetra = 1: "ACDF"­­ - "EB" (elegLetra de 0 a 3)
elegLetra = 3: "ACD" - "EBF" (elegLetra de 0 a 2)
elegLetra = 0: "CD" - "EBFA" (Se acabó)

Además cumple con todos los requisitos que pide el ejercicio. Fíjate que no es lo mismo descolocado que el hecho de que no coincidan, descolocado es que está en la cadena pero en otro lugar, con lo que si la cadena es ABCD y pones ABDC colocadas hay 2 y descolocadas 2, pero si pones ABEF colocadas hay 2 y descolocadas 0, ¿Me explico?

Lo pongo en pastebin porque con los comentarios en MV hay demasiado salto de linea.

0buS

uf gracias a los dos. Tu codigo keaod es comprensible, pero se nota que vas bastante avanzado en c xdd. Aun no he llegado a usar la libreria strcpy ni algunas cosas que utilizas. Me resulta dificil pero eficaz.

En cuanto a ti neb1, no estoy en casa, pero el cambio que has exo a sido pasar el valor del array letras por la variable 'a' no? para comprobarlo en la funcion. Creo que era eso lo que fallaba.

Y la funcion el valor que devolvia la llamada a la funcion guardarla en 'repetida'. Eso lo intenté hacer yo tambien pero me daba error de no se que xd. Ponia char letras[azar] en lugar de char a, en la declaracion de la función. No se me ocurrió en pasarla por una simple variable char.

Espero que sea esto lo que falle xD.

Gracias a todos, hay buenos programadores por aquí xd.

NeB1

#16 de nada hombre, he intentado hacerlo a tu manera, porque nuse, porque si hago yo mi propio programa no entenderás nada, así yo creo que le buscarás mejor la lógica. Además parece que se te dará bien la programación.

P.D: strcpy(cadena1,cadena2) copia una cadena encima de la otra ^^

elkaoD

Lo del strcpy(cadena2, cadena1) lo único que hace es simplificar la copia de cadena1 encima de cadena2. strlen(cadena) devuelve la longitud de la cadena. Para que entiendas cómo funciona, se podría hacer a mano así:

char *copiarCadena (char *destino, const char *origen)
{
    int i = 0;
    while (origen[i] != '\0') // Mientras que no se llegue al fin de cadena
    {
        destino[i] = origen[i];
        i++;
    }
    destino[i] = '\0'; // Hay que asegurarse de copiar el 0 de fin de cadena

return destino;
}

size_t longitudCadena (const char *cadena)
{
    // size_t no es mas que un entero sin signo
    size_t tamanyo = 0;
    while (cadena[tamanyo] != '\0')
        tamanyo++;

return tamanyo;
}
NeB1

yo creo que no sabe nada de cadenas todavía, ni de carácteres centinela ni nada de eso

elkaoD

Ostia, no sé, aunque ahora que lo dices viendo cómo abordó el ejercicio inicial tiene sentido. Es que yo aprendí C por mi cuenta y no sé si se dan antes las cadenas, los arrays o qué.

Para que entiendas mejor mi ejemplo 0buS, las cadenas no son más que un array de tipo char.

char cadena[5] = "Hola";

Es lo mismo que poner

char cadena[5] = ('H', 'o', 'l', 'a', '\0');

De hecho se llaman cadenas porque no es más que una cadena (Entendiendo 'cadena' como lista de cosas que van una detrás de otra) de caracteres (Es decir, en C, tipo 'char'.)

Se usa strcpy en lugar de simplementa "cadena1 = cadena2;" porque no puedes copiar un array a otro de esa forma.

Fíjate en el '\0' del final. El \0 es un carácter de control, como '\n' para hacer los saltos de linea o '\t' para las tabulaciones. En realidad sólo representan un valor ASCII numérico que no puede ser escrito con el teclado porque no hay una tecla/letra que les corresponda. C acaba las cadenas (Porque si no, no sabe dónde está el final) con el caracter '\0', que es la representación del valor ASCII cero, al igual que 'a' es el ASCII (En decimal) 98, 'b' es el ASCII 99, etc. aunque 'a' y 'b' sí que pueden ser representados por teclado, por lo que no existe un carácter de control que los genere.

0buS

si elkeaod, eso lo estoy dando ahora. Solo he dado arrays con numeros enteros, o flotantes. y algo de cadenas. Eso que me explicas es lo ultimo que hemos dado, pero claro es la primera vez que lo oigo xD.

Bueno en realidad lo ultimo que hemos "practicado" han sido los arrays enteros. Ahora estoy empezando con cadenas de caracteres, pero eso, que solo ha explicado varias cosas, no como para practicarlo.

NeB1

#20 si, por eso de declarar un vector de "chars" decía que no habían dado cadenas xD. bueno, ya nos dirás que tal llevas el programa

kolka

#21 Un consejo, no te limites por lo que te enseñen, intenta aprender cosas por ti mismo :)

Usuarios habituales

  • kolka
  • NeB1
  • 0buS
  • elkaoD
  • JuAn4k4
  • maRc
  • RaymaN