Master Mind 2.0

di il
21 risposte

Master Mind 2.0

Ciao a tutti!
Un po’ di tempo fa ho proposto un forum riguardante il gioco del Mister Mind.
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Riassumo in breve per chi non sa di cosa stia parlando:
Master mind è un gioco di logica, nel quale il giocatore deve essere in grado di capire la combinazione segreta. Per farlo il programma, a partire da una sequenza che il giocatore ritiene che possa essere la combinazione segreta, risponde con uno strike 'X' se LA LETTERA E' GIUSTA E STA AL POSTO GIUSTO, mentre con un bal 'O' SE C'E' LA LETTERA GIUSTA MA E' AL POSTO SBAGLIATO. Si possono utilizzare lettere dalla A alla F, con una lunghezza massima di 4 caratteri.
****ESEMPIO****: SOLUZIONE: FAAD
***tentativi del giocatore****
1° BCFD XO (1 strikes and 1 bal)
2° FFDD XX (2 strikes and 0 bal)
3° BDBB X (1 strikes and 0 bal)
4° BCFD XOO (1 strikes and 2 bals)
5° FAAD XXXX ***SOLUTION!***
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Ora voglio fare il contrario, ovvero il player diventa il computer e l’utente conosce la combinazione secreta dando le due indicazioni X,O ad ogni tentativo del computer.
Il pc, dunque, dovrà essere in grado di arrivare alla soluzione pensata dall’ utente.
Io ho agito in questo modo: il primo tentativo viene generato totalmente random, mentre per i successivi tentativi ho voluto farli in funzione del tentativo precedente e delle indicazioni che ha dato l’utente e generare alcune lettere in modo random.
ESEMPIO:
soluzione: DABF
tent 1 CBCD OO
ten2 FCEC O --> questo tentativo è stato generato per metà random, tenendo presente delle indicazioni (OO) del tentativo precedente.
ten3 DEBA XXO
tent4 DDBE XX
E così via.. Finché non arriva alla soluzione, dopo molti tentativi, questo si traduce in poca efficienza.
Perché ho fatto in questo modo?
Sarebbe stata una grossa perdita di tempo andare a scambiare le posizioni oppure cercare di capire quali scambiare e quali lasciare ferme.
Cosa non funziona?
Il codice che ho scritto arriva fino al secondo tentativo, fa inserire le indicazioni dell’ utente, ma poi nel momento in cui deve generare il terzo tentativo è come se entrasse in un loop infinito.
Inoltre, mi rendo conto che non è molto efficiente questo algoritmo, perché genera delle lettere random che in realtà non dovrebbero esserci, e questo lo si evince in base agli altri tentativi, quindi bisognerebbe ragionare sull’ insiemistica per poter togliere le lettere che molto probabilmente non sono nella soluzione.
Io ho scritto il seguente codice, ammetto di aver messo molta carne al fuoco, ma proposte e suggerimenti sono ben accetti:

#include <iostream>
#include <time.h>
#include <windows.h>
#include <conio.h>
#include <string>
using namespace std;
char car;

int main(){
	int n_tentativi,i,j,c,x;
	string tentativo1,tentativo2,indicazioni1,strike,bal,temporaneo,concatena,temporaneo1;
	bool valido;
	char lettere[6];
	n_tentativi=1,c=0,valido=false;
do{
	for( i=0;i<6;i++) lettere[i]=65+i; //riempe l'array lettere dalla A alla F
	if(n_tentativi==1){
		srand(time(NULL));
		for(j=0;j<4;j++) tentativo1+=rand()%6+65; //generazione del primo tentativo completamente random 
		n_tentativi=n_tentativi+1; 
		cout<<tentativo1; cout<<"    ";
	}else{
		do{
			for(j=0;j<4;j++) tentativo2+=rand()%6+65;//generazione randomica del secondo tentativo in poi.
			for(i=0;i<4;i++) temporaneo+=tentativo2[i];  temporaneo1+=tentativo1[i];
			//controllo della validità del tentativo successivo, in funzione di quello precedente
			for ( i=0 ; i<4; i++){
        			if (temporaneo1[i] == temporaneo[i]){
            		strike +='X';
            		temporaneo1[i]='0'; // avoiding count like a bal
            		temporaneo[i]='1';///avoiding reuse the char
					} 
        	}	
			for ( x=0 ; x<4; x++){
        		for ( j=0 ; j<4; j++){
            		if (temporaneo1[x] == temporaneo[j]){
                		bal+='O'; 
                		temporaneo1[x]='0'; //avoiding double count
                		temporaneo[j]='1';} ///avoiding reuse the char 
            	}
    		}
    		concatena=strike+bal;
			if(indicazioni1==concatena) {  cout<<endl; cout<<tentativo2; valido=true;} 
		}while(!valido);
		for(x=0;x<4;x++) tentativo1[x]=tentativo2[x]; //tentativo2 diventa il vecchio tentativo, cioè tentqativo1
		valido=false;}
		do{
			car=getch();
			if((car==88 or car==79) and c!=4 ){ putch(car);indicazioni1[c]=car;c=c+1;}	
			if((car==120 or car==111) and c!=4 ){car=car-32;putch(car); indicazioni1[c]=car; c=c+1;}//If the user hasn't activeted the block maiusc 
			if (car==8 and c>0 ) {putch(8);putch(32);putch(8);c=c-1;}
		}while((car!=13 and c!=4)and(car!=27));c=0;
		
	}while(car!=27 and car==13); //27==ESC 13==INVIO
	
	return 0;
}
Vi ringrazio!

21 Risposte

  • Re: Master Mind 2.0

    Consigli

    1) formatta bene il codice
    2) rimuovi l'interfaccia utente, quella magari la aggiungi alla fine: scegli la stringa da testare direttamente nel codice
    3) creati un array con le 1296 soluzioni possibili e un altro array di 1296 valori booleani dove tieni traccia delle soluzioni da scartare
    4) scegli una soluzione di prova tra quelle non scartate
    5) se non è la soluzione giusta, scarta quella e tutte le soluzioni che non darebbero lo stesso output come strike e bal (ad esempio se tenti "ABCA" e ottieni uno strike e zero bal, la soluzione non può essere "AAAA" perché in quel caso avresti avuto due strike e zero bal)
    6) ritenta tra le soluzioni rimaste (magari puoi fare un sort per ordinare le soluzioni non scartate e semplificare il codice)
  • Re: Master Mind 2.0

    Buonasera,
    @Weierstrass ho ragionato un po' sull'algoritmo che mi hai suggerito e penso che ci siano 2 problemi, correggimi se sbaglio.
    Secondo me non è molto efficiente in quanto, quando genero una possibile combinazione random(che andremo poi ad utilizzare come nuovo tentativo), devo vedere se quest'ultima è gia stata utilizzata nel tentativo precedente.
    Vale a dire che, si eseguirà un ciclo di controllo che scorrerà tutto il vettore di 1296 elementi e quando si accorge che, la nuova combinazione generata, è uguale a quella utilizzata nel tentativo precedente, il ciclo viene interrotto e ne verrà generata un altra, completamente random con la possibilità che si ripresenti lo stesso problema (ossia che la nuova combinazione generata sia uguale alla precedente) e continuerà ad eseguire dunque il ciclo di controllo senza mai terminarlo, bloccandosi sempre quando si verificherà ciò, e per assurdo si potrebbe creare una situazione di stallo e si rischia che le altri parti vuote del vettore non vengano riempite.
    Es. possibile combinazione già presente nell' array= ABCC .
    Es. Nuova combinazione generata per la quale il ciclo si bloccherà = ABCC (uguale ad una già inserita nell' array).
    Poi un altro problema è che la SORT non credo di poterla fare, in quanto, nel mio programma i tentativi generati sono array di stringhe e non di caratteri, dunque non posso interpretarli singolarmente come dei caratteri Ascii. ES: A=65 B=66 se voglio metterli in ordine decrescente: B-A
  • Re: Master Mind 2.0

    Il random lo devi fare per un numero da zero a n - 1, dove n sono le soluzioni rimaste. Poi scorri l'array saltando le soluzioni già scartate per vedere a quale stringa corrisponde il numero che hai sorteggiato; sicuramente la stringa sarà una di quelle non scartate se hai scritto bene il codice

    Oppure lascia perdere il random: parti direttamente da "AAAA" e scegli sempre la prima soluzione non scartata ad ogni giro. Per iniziare va bene anche così
  • Re: Master Mind 2.0

    Mi spiego meglio: ho in mente di generare le stringhe in modo che ogni lettera sia generata random...
    Della serie:
    string poss_tent;
    poss_tent += rand()%6+65;

    e ripetere questa operazione per 4 volte, e poi passare alla generazione della stringa successiva.


    ho capito cosa intendi tu ma il problema non è tanto cercare la stringa, cioè la possibile configurazione, nell'array, ma nel caricare tali stringhe nell'array:
    potrebbe presentarsi il caso di stringhe uguali, per cui la stringa appena generata deve essere cambiata, altrimenti è inutile riempire l'array con stringhe uguali... E fino qui ok... Ma poi se nel caso più assurdo, si venga a generare una stringa uguale ad un'altra già presente nell'array questo implica che bisognerebbe ripetere l'operazione per la seconda volta, e magari per una terza e così via.
    Noi non possiamo permetterci che questo avvenga altrimenti il computer potrebbe entrare in una sorta di loop, influendo sulle prestazioni e l'efficienza dell'algoritmo stesso.
  • Re: Master Mind 2.0

    Secondo me dovresti sforzarti di capire quello che ti suggeriscono gli altri. Anche nell'altro thread avevi bollato come soluzione con numerosi errori una soluzione che ti aveva dato un altro utente, che era invece corretta

    Sorteggiare i caratteri è una strada errata: l'insieme su cui ragionare è quello delle stringhe, non quello dei caratteri. Tra l'altro qui hai l'ulteriore vantaggio che l'insieme delle soluzioni possibili è limitato (6^4 = 1296), quindi tanto vale scriverle tutte direttamente. Come non sorteggiare i doppioni ti è stato descritto, puoi anche non sorteggiare.

    Tra l'altro il suggerimento dato è il primo passo verso la soluzione ottimale (gli altri due sono la scelta iniziale e la strategia di sorteggio nei passaggi successivi)
  • Re: Master Mind 2.0

    Questo quanto suggerito, prendendo a prestito il codice di Nippolo
    
    #include <iostream>
    using namespace std;
    
    typedef struct {
        int strikes;
        int bals;
    } outcome;
    
    outcome round(char * sol, char * ten){
        outcome ret = {0,0};
        int i, occ[6] = {0};
      
        for(i = 0; i < 4; i++){
            ret.strikes += sol[i] == ten[i];
            ++occ[sol[i] - 'A'];
            --occ[ten[i] - 'A'];
        }
        ret.bals = 4 - ret.strikes;
        for(i = 0; i < 6; i++)
            if(occ[i] > 0)
                ret.bals -= occ[i]; 
        
        return ret;
    }
    
    int main() {
        char * s = (char *)"DCEC";
        cout <<  "Soluzione : " << s << endl;
        
        char soluzione[1296][4];
        int i, j, k, l;
        
        for(i = 0; i < 6; i++)
            for(j = 0; j < 6; j++)
                for(k = 0; k < 6; k++)
                    for(l = 0; l < 6; l++){
                        soluzione[i + j * 6 + k * 36 + l * 216][0] = l + 'A';
                        soluzione[i + j * 6 + k * 36 + l * 216][1] = k + 'A';
                        soluzione[i + j * 6 + k * 36 + l * 216][2] = j + 'A';
                        soluzione[i + j * 6 + k * 36 + l * 216][3] = i + 'A';
                    }
                  
        bool scartata[1296] = {0};
        outcome n, o = {0,0};
        
        i = -1; k = 1;
        while(o.strikes != 4){
            while(scartata[++i]);
            o = round(s, &soluzione[i][0]);
            cout <<  "Tentativo " << k << " - " 
                 << soluzione[i][0] << soluzione[i][1] << soluzione[i][2] << soluzione[i][3]
                 << ": " << o.strikes  << "strikes e " << o.bals << "bals" << endl;
            if(o.strikes != 4){
                scartata[i] = true;
                for(j = 0; j < 1296; j++)
                    if(!scartata[j]){
                        n = round(&soluzione[j][0], &soluzione[i][0]);
                        if(n.strikes != o.strikes || n.bals != o.bals)
                            scartata[j] = true;
                }
                k++;
            }
        }
        
        cout << "Soluzione trovata!" << endl;
        return 0;
    }
    
  • Re: Master Mind 2.0

    Salve @Weirestrass, ho riflettuto su quanto mi hai detto però ho un problema. Vorrei utilizzare un array al posto della matrice, in quanto mi resta più comodo per procedere al controllo del possibile tentativo successivo, perchè ho intenzione di utilizzare il codice consigliato da Rubik nel precedente forum sul Mister Mind 1.
    Un problema che sorge è riuscire a fare il calcolo per posizionare le lettere come hai fatto tu nella matrice, in quanto nel caso in cui io utilizzassi l' array le lettere che compongono la possibile configurazione , per così dire, non verrebbero "compatte", come invece avviene nella matrice.
    Mi spiego meglio:
    Se ho un array char all' indice zero posso inserire una sola lettera,invece io ho bisogno di scriverne 4 allo stesso indice.
    Stavo pensando di utilizzare un array di tipo stringa in modo da ovviare questo problema, ma purtroppo non riesco ad addattare il calcolo da te suggerito in questo caso.
  • Re: Master Mind 2.0

    Non ho capito bene cosa vorresti fare, comunque un array di stringhe lo potresti generare così
    
    #include <iostream>
    #include <iomanip>
    #include <string>
    
    using namespace std;
    
    int main () {
        stringstream stream;
        string s[1296];
        int i, j, k, l, n = 0;
        
        for(i = 10; i < 16; i++)
            for(j = 10; j < 16; j++)
                for(k = 10; k < 16; k++)
                    for(l = 10; l < 16; l++){
        stream << uppercase << hex <<  l + (k<<4) + (j<<8) + (i<<12);
        s[n] = stream.str();
        stream = stringstream();
        cout << s[n++] << endl;
                    }   
      return 0;
    }
    
    
  • Re: Master Mind 2.0

    @Weierstrass ho compilato il tuo codice e inizialmente mi ha dato questi errori (vedi error1.png)
    Ho cercato l'errore e visto che probabilmente era dovuto alla mancanza di tale libreria #include <sstream> però pur inserendola, quando vado a compilare mi mostra l'errore non sul codice ma sulla libreria stessa . (vedi error2.png)
    Allegati:
    29909_cbe808196c3256aa31a40b06e837ed2a.png
    29909_cbe808196c3256aa31a40b06e837ed2a.png

    29909_02bf0dc5821af10990bef43de6eb5117.png
    29909_02bf0dc5821af10990bef43de6eb5117.png
  • Re: Master Mind 2.0

    Ci sono tanti modi per farlo, prova così che dovrebbe andare anche con i compilatori di trenta anni fa
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main () {
        string s[1296];
        int i, j, k, l, n = 0;
        
        for(i = 0; i < 6; i++)
            for(j = 0; j < 6; j++)
                for(k = 0; k < 6; k++)
                    for(l = 0; l < 6; l++){
                        s[n]  = i + 'A';
                        s[n] += j + 'A';
                        s[n] += k + 'A';
                        s[n] += l + 'A';    
                        cout << s[n++] << endl;
                    }   
        return 0;
    }
    
  • Re: Master Mind 2.0

    Ciao @Weirestrass,
    sono arrivato quasi alla fine del programma, ma mi sono imbattuto nell' ultimo problema che poi in realtà è il cuore del mio programma:
    ossia generare il tentativo n°1 ad un indice random e marcare successivamente il tentativo effettuato e gli altri che non producono lo stesso output per strikes e bals (forniti dall' utente). Il problema dunque è controllare ogni stringa nell'array s e vedere se è coerente con le informazioni che ha dato l'utente.
    Tent. 1 --> ABBA XX
    controllo con tutte le possibili soluzioni nell' array s...
    Tent. 2 --> ABCD ecc.. (il tentativo generato deve avere almeno due strike, per essere coerente con quello precedente) e cosi via per gli altri a seguire, dovranno essere coerenti con quello precedente finchè non si trova la soluzione: ossia quando il tentativo generato ha come indicazioni "XXXX".
    Come anche tu hai suggerito, io ho provato a scrivere il codice che secondo me dovrebbe funzionare, ma al momento che inserisco le indicazioni e premo invio, la finestra del MS DOS ritorna il valore: 3221225477, non generando il tentativo successivo.
    Allego qui il mio codice dove riscontro il problema:
    
    if(n_attempts==0){
    		indice=rand()%1296;
    		marca[indice]=true;
    		gotoxy(36,5);
    		cout<<" MISTER MIND **PLAYER** by Daniele ";
    		gotoxy(10,8);
    		cout<<"  "<<c<<" Try N. "<<n_attempts+1<<" ==> ";
    		cout<<s[indice]<<" ? ";
    		n_attempts++;
    		/*richiesta input dell' utente (input controllato), indication rappresenta la stringa con i caratteri Strikes e bals, 
    		da confrontare più tardi*/
    		do{ 
    		car=getch();
    		if((car==88 or car==79)and j!=4){ putch(car);indication+=car;j++;}	
    		if((car==120 or car==111) and j!=4  ){car=car-32;putch(car); indication+=car; j++;}
    		if (car==8 and j>0 ) {putch(8);putch(32);putch(8);j--;}
    		}while((car!=13 and car!=27));
    		n=0; 
    		do{
    			strcpy(temporary,s[indice].c_str()); //utilizzo la funzione strcpy per copiare il tentativo tentato nell' array temporary(c_str() da array di stringa ad array di char)
    			strcpy(s_found,s[n].c_str()); //utilizzo la funzione strcpy per copiare il possibile  tentativo nell' array s_found.
    			//da qui in poi viene utilizzato il codice di Rubik presente nel forum Master Mind(per verificare gli strikes e i bals)
    			for ( i=0 ; i<4; i++){
            		if (temporary[i] == s_found[i]){ 
                		s_found[i]='0'; // avoiding count like a bal
                		temporary[i]='1';///avoiding reuse the char
    					indication_comp += "X";}  //il confronto tra il tentativo provato e quello nell'array s, ha prodotto uno Strike.
            	}	
    			for ( int x=0 ; x<4; x++){
            		for ( j=0 ; j<4; j++){
                		if (s_found[x] == temporary[j]){ 
                    		s_found[x]='0'; //avoiding double count
                    		temporary[j]='1';///avoiding reuse the char 
    						indication_comp += "O";} //il confronto tra il tentativo provato e quello nell'array s, ha prodotto uno Strike.
                	}
        		}
        		//controllo se le indicazioni dell' utente sono uguali a quelle del computer
        		if((indication==indication_comp ) and (indication!="       " and indication_comp!="      ")){ 
        			found=true; //ho trovato il tentativo successivo
        			marca[n]=true;//marco alla posizione n, nell' array parallelo a s[1296]
    			}
    			else{marca[n]=true;}//altrimento marco perchè vuol dire che il possibile tentativo non è sicuramente la soluzione
    			indication="       ";indication_comp="      "; n=n+1; //svuoto le stringhe e incremento n
    		}while(n<=1296 and !found);
    		gotoxy(10,9);
    		cout<<"  "<<c<<" Try N. "<<n_attempts+1<<" ==> ";
    		cout<<s[n-1]<<" ? "; /*stampo n-1 
    		perchè n viene incrementato anche se ho trovato il tentativo successivo(quindi prendo quello alla posizione precedente)*/
    
    CODICE COMPLETO:
    
    #include <conio.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <time.h>
    #include <iostream>
    #include <string>
    #include <cstring>
    using namespace std;
    char car;
    bool marca[1296];
    int n_attempts=0,c=1296;
    float t_dif;
    time_t ini,fin; 
    
    void gotoxy( short column, short line );
    
    int esc_function();
    
    void show_solution();
    
    void check_solution(string array_s[]);
    
    
    int main(){
        int y;
        do{
        	
        	for(y=0;y<1296;y++){
        		marca[y]=false;
    		}
        	n_attempts=0; //set  number of attempts
    		system("cls");
    		gotoxy(33,5);
    		cout<<" MISTER MIND **PLAYER** by Daniele   ";
    		gotoxy(41,25);
    		cout<<"Click enter to continue--> ";
    		do{
    			car=getch();
    		}while(car==0 and car!=27);
        	if(car==27 ) esc_function();
        	system("cls");
        	do{
        	show_solution();
        	}while(car==13 and car!=27);
    	}while(car!=27 and car==13);
        
        
    	return 0;	
    }
    void gotoxy( short column, short line ){ //position of the text on screen 25 lines and 80 columns
      COORD pos={column,line};
      SetConsoleCursorPosition(GetStdHandle( STD_OUTPUT_HANDLE ),pos);}
      
    int esc_function(){if(car==27){{system("cls");gotoxy(40,15);cout<<"----Thanks to played! bye!---- ";ini=time(NULL);do{ fin=time(NULL); t_dif=difftime(fin,ini);} while(t_dif<3);exit(true);return (0);}}}
    
    void show_solution(){
    	string s[1296],indication,indication_comp;
    	char temporary[4],s_found[4];
    	bool found=false;
    	int i, j, k, l, n = 0,indice;
        
        for(i = 0; i < 6; i++)
            for(j = 0; j < 6; j++)
                for(k = 0; k < 6; k++)
                    for(l = 0; l < 6; l++){
                        s[n]  = i + 'A';
                        s[n] += j + 'A';
                        s[n] += k + 'A';
                        s[n] += l + 'A';
                        s[n++];
                    } 
        srand(time(NULL));j=0;
    	if(n_attempts==0){
    		indice=rand()%1296;
    		marca[indice]=true;
    		gotoxy(36,5);
    		cout<<" MISTER MIND **PLAYER** by Daniele   ";
    		gotoxy(10,8);
    		cout<<"  "<<c<<" Try N. "<<n_attempts+1<<" ==> ";
    		cout<<s[indice]<<" ? ";
    		n_attempts++;
    		do{
    		car=getch();
    		if((car==88 or car==79)and j!=4){ putch(car);indication+=car;j++;}	
    		if((car==120 or car==111) and j!=4  ){car=car-32;putch(car); indication+=car; j++;}
    		if (car==8 and j>0 ) {putch(8);putch(32);putch(8);j--;}
    		}while((car!=13 and car!=27));
    		n=0; 
    		do{
    			strcpy(temporary,s[indice].c_str());
    			strcpy(s_found,s[n].c_str());
    			for ( i=0 ; i<4; i++){
            		if (temporary[i] == s_found[i]){ 
                		s_found[i]='0'; 
                		temporary[i]='1';
    					indication_comp += "X";} 
            	}	
    			for ( int x=0 ; x<4; x++){
            		for ( j=0 ; j<4; j++){
                		if (s_found[x] == temporary[j]){ 
                    		s_found[x]='0'; 
                    		temporary[j]='1';
    						indication_comp += "O";} 
                	}
        		}
        		if((indication==indication_comp ) and (indication!="       " and indication_comp!="      ")){
        			found=true;
        			marca[n]=true;
    			}
    			else{marca[n]=true;}
    			indication="       ";indication_comp="      "; n=n+1;
    		}while(n<=1296 and !found);
    		gotoxy(10,9);
    		cout<<"  "<<c<<" Try N. "<<n_attempts+1<<" ==> ";
    		cout<<s[n-1]<<" ? ";
    	}
    }
    
  • Re: Master Mind 2.0

    Quell'errore è una violazione di accesso alla memoria. Avrai usato un puntatore in maniera sbagliata: esegui in modalità debug, procedi passo per passo e vedi dove è il problema.

    Ma poi come fa a compilare il tuo codice? and in C++ si scrive &&
    Il codice corretto non te lo compilava e invece sul codice sbagliato non fa una piega e crea l'eseguibile? Io cambierei IDE e compilatore
  • Re: Master Mind 2.0

    Si, anche io penso che sia un problema dovuto ad un puntatore all'interno del mio programma. Ho già provato a fare un file di debug per individuare il problema ma non ne sono venuto a capo, è da 2gg che provo.
  • Re: Master Mind 2.0

    Ma non devi fare un file, devi proprio eseguire in debug. Metti un breakpoint alla riga dopo a quella dove acquisici le istruzioni, poi procedi riga per riga e sicuramente il debug terminerà alla riga dove hai sbagliato a scrivere.
Devi accedere o registrarti per scrivere nel forum
21 risposte