Zahlenratespiel/ Ständige Probleme mit der Inkrementierung



  • Solltest du dir bei Gelegenheit mal ansehen, wie man mit Debuggern umgeht. Aber vorerst reichen auch Debugausgaben.

    Jo, das werde ich auf jeden Fall machen; soweit ich weiß kommt in meinem Lehrbuch auch ein Kapitel vor, wo die "Debugging-Geschichte" ausführlich angesprochen wird. Ich kämpfe mich ehrgeizig durch! 😃
    Jetzt wäre mein Zahlenratespiel fast fertig, nur gibt es da noch ein Problem: Ich möchte einen kleinen Algorithmus erstellen, der die Anzahl der "Kühe" korrekt herausfiltert, d.h. wenn eine Zahl richtig erraten wurde, jedoch NICHT an der richtigen Position der vierstelligen Zahl steht. Mein vorläufiger Code sieht momentan so aus:

    // Einser
    if(vergleich[0] != zerlegen_einser(users_eingabe) && 
    vergleich[0] == zerlegen_zehner(users_eingabe) || 
    vergleich[0] == zerlegen_hunderter(users_eingabe) || 
    vergleich[0] == zerlegen_tausender(users_eingabe)) {
    				++count_cows;
    }
    
    // Zehner
    if(vergleich[1] != zerlegen_zehner(users_eingabe) && 
    vergleich[1] == zerlegen_einser(users_eingabe) || 
    vergleich[1] == zerlegen_hunderter(users_eingabe) || 
    vergleich[1] == zerlegen_tausender(users_eingabe)) {
    				++count_cows;
    }
    
    // Hunderter
    if(vergleich[2] != zerlegen_hunderter(users_eingabe) && 
    vergleich[2] == zerlegen_einser(users_eingabe) || 
    vergleich[2] == zerlegen_zehner(users_eingabe) || 
    vergleich[2] == zerlegen_tausender(users_eingabe)) {
    				++count_cows;
    }
    
    // Tausender
    if(vergleich[3] != zerlegen_tausender(users_eingabe) && 
    vergleich[3] == zerlegen_einser(users_eingabe) || 
    vergleich[3] == zerlegen_zehner(users_eingabe) || 
    vergleich[3] == zerlegen_hunderter(users_eingabe)) {
                    ++count_cows;
    }
    

    Das Ganze ist noch nicht ganz ausgereift: Wenn die zu erratende Zahl = 5448 wäre und man beim Raten "4444" eingibt, dann lautet die Ausgabe mit folgender Ergänzung im Quellcode

    // Gibt die Anzahl an "Kuehen" aus
    			cout << " " << count_cows << " Ku(e)h(e).\n";
    			count_cows = 0;
    			cout << endl;
    

    (mit restlichem Quellcode, siehe oben) "2 Bullen. 2 Kuehe.". Das wäre natürlich Unsinn, da in diesem Fall nur 2 "Bullen" vorkommen. Habe schon einiges ausprobiert...
    Wie könnte ich das vermeiden und NUR die "Cows" -einmalig- über

    ++count_cows;
    

    mitzählen lassen, falls eine davon in der 4-stelligen Zahl vorkommt?
    Bin für jede Hilfestellung sehr dankbar!



  • An der Aufgabe habe ich auch mal gesessen.
    Was Du brauchst ist ein Konstrukt* wo Du Dir merkst, ob das Ergebnis schon gezählt wurde. Da bietet sich natürlich sowas wie vector<bool> an, der zu dem vector<int> vergleich zugeordnet wird.
    Dann wird zusätzlich zu der Abfrage auf Gleichheit gecheckt, ob Du diese Zahl schon gezählt hast.

    Zu Deinem neuen Programmteil:
    Du solltest Dir außerdem mal überlegen, ob Du Dir die ganze Tipperei nicht sparen kannst, wenn Du die Vergleiche in eine Schleife packst und dann auf vergleich[i] prüfst. Ist dann auch übersichtlicher...

    * Eigentlich brauchst Du sogar zwei, einmal für die zu ratende Zahl und einmal für die Eingabe.



  • vector<bool> ist böse, finde leider den Link nicht zu 😉

    Falls man es aber benötigt, kann man diese nicht dereferenzieren.

    Wie wäre es da mit

    struct cow{
        int pos;  //position
        int value; // der Wert
    }
    
    std::vector<cow> cows;
    

    und dann einfach über

    cows.size()
    

    ?



  • Meiner bescheidenen Meinung nach solltest Du Dich freimachen von dem Gedanken, dass Du es mit Zahlen zu tun hast.
    Es ist ähnlich wie beim Sudoku - dort benutzt man zwar auch Zahlen, aber das hat nix mit Mathe oder so zu tun. Du rechnest nicht mit diesen Zahlen. Die einzelnen Ziffern sind nur Symbole.

    So könntest Du z.B. den Code als einen string von 4 Zeichen auffassen.
    Du wirst sehen, dass Dein Programm erheblich an lesbarkeit gewinnt.

    Hier z.B. nur mal das einlesen:

    #include <algorithm>  // std::count
    #include <iostream>
    #include <string>
    
    typedef char digit;
    typedef std::string code;
    
    bool is_valid(digit c){
      const digit digits[] = "0123456789";
      // das digit muss in digits enthalten sein
      return std::count(digits, digits+10, c)==1;
    }
    
    code read_code(){
      code c;
      digit d;
      // lies digits und fuege diese ein, bis code 4 valide digits enthaelt
      while(c.size()!=4 && std::cin >> d)
        if(is_valid(d))
          c.push_back(d);
      return c;
    }
    
    int main(){
      code c = read_code();
      std::cout << c << '\n';
    }
    


  • Zunächst einmal vielen Dank für eure Ratschläge und Anregungen!

    Du solltest Dir außerdem mal überlegen, ob Du Dir die ganze Tipperei nicht sparen kannst, wenn Du die Vergleiche in eine Schleife packst und dann auf vergleich[i] prüfst. Ist dann auch übersichtlicher...

    Das ist korrekt, hatte ich auch schon mal so

    for(unsigned int n = 0; n < vergleich.size(); ++n) {
    				if(vergleich[n] == user_guess[0] && 
    					user_guess[0] != vergleich[0])
    						++count_cows;
    				if(vergleich[n] == user_guess[1] && 
    					user_guess[1] != vergleich[1])
    						++count_cows;
    				if(vergleich[n] == user_guess[2] && 
    					user_guess[2] != vergleich[2])
    						++count_cows;
    				if(vergleich[n] == user_guess[3] &&
    					user_guess[3] != vergleich[3]) 
    						++count_cows;
    			}
    

    strukturiert, aber wie gesagt, es läuft noch nicht ganz.

    Wie wäre es da mit

    struct cow{
        int pos;  //position
        int value; // der Wert
    }
     
     
    std::vector<cow> cows;
    

    und dann einfach über

    cows.size()
    

    ?

    Das klingt interessant/vielversprechend: Obwohl ich vom Schlüsselwort "struct" schon mal irgendwo etwas "mitbekommen" habe, weiß ich leider noch nicht so ganz genau, wie ich mit dieser Technik - oder besser, mit Deinem Vorschlag - genau arbeiten soll 😕
    Könntest Du mir diese Idee etwas genauer erläutern?



  • Voltobal schrieb:

    Das ist korrekt, hatte ich auch schon mal so

    for(unsigned int n = 0; n < vergleich.size(); ++n) {
    				if(vergleich[n] == user_guess[0] && 
    					user_guess[0] != vergleich[0])
    						++count_cows;
    				if(vergleich[n] == user_guess[1] && 
    					user_guess[1] != vergleich[1])
    						++count_cows;
    				if(vergleich[n] == user_guess[2] && 
    					user_guess[2] != vergleich[2])
    						++count_cows;
    				if(vergleich[n] == user_guess[3] &&
    					user_guess[3] != vergleich[3]) 
    						++count_cows;
    			}
    

    strukturiert, aber wie gesagt, es läuft noch nicht ganz.

    Das sieht schon viel besser aus als die Version mit den zerlege_*() Funktionen.
    Was heißt "es läuft noch nicht ganz"?



  • Die Schleife, die ich erstellt habe, zählt zwar (falls vorhanden) "Cows", jedoch habe ich hier das Problem, dass nicht korrekt gezählt wird: Wenn eine Zahl beispielsweise doppelt in der vom Spieler erratenen 4-stelligen Zahl vorkommt (Bsp.: 4616, hier also die '6'), dann zählt die Schleife doppelt und führt zu unsinnigen Ergebnissen - siehe Zitat von mir:

    Das Ganze ist noch nicht ganz ausgereift: Wenn die zu erratende Zahl = 5448 wäre und man beim Raten "4444" eingibt, dann lautet die Ausgabe mit folgender Ergänzung im Quellcode

    // Gibt die Anzahl an "Kuehen" aus
                cout << " " << count_cows << " Ku(e)h(e).\n";
                count_cows = 0;
                cout << endl;
    

    (mit restlichem Quellcode, siehe oben) "2 Bullen. 2 Kuehe.". Das wäre natürlich Unsinn, da in diesem Fall nur 2 "Bullen" vorkommen. Habe schon einiges ausprobiert...
    Wie könnte ich das vermeiden und NUR die "Cows" -einmalig- über

    ++count_cows;
    

    mitzählen lassen, falls eine davon in der 4-stelligen Zahl vorkommt?
    Bin für jede Hilfestellung sehr dankbar!

    Ziel wäre hier, dass sich das Programm merkt, ob ein Ergebnis schon gezählt wurde oder nicht. Die Frage ist nur, WIE ich das anstellen kann - für mich als fortgeschrittener (na ja) Anfänger eine echte Herausforderung... 😞

    [Übrigens: Mit Klassen (class, struct), Zeigern und den ganzen Algorithmen aus der Standard Template Library habe ich bisher nocht nicht gearbeitet - steht noch alles an -, falls euch das weiterhilft...]



  • Okay.
    Ich gehe also recht in der Annahme, dass die zerlege_* Funktionen Schnee von gestern sind, und ich die nie wieder sehe? 🙂

    Vielleicht hilft es Dir, wenn Du Dein Bulls and Cows so implementierst, dass jede Zahl nur einmal vorkommt - so steht es auch in der engl. Wikipedia.

    Egal, ob Du Die obige Einschränkung noch implementierst oder nicht:
    Wenn Du beim Kuehe zaehlen einfach die Bullen mitzaehlst und anschließend wieder abziehst wird es einfacher.
    Siehe z.B. Z. 13:

    typedef std::vector<int> code;
    
    int bulls(const code& vergleich, const code& user_guess){
      int result = 0;
      // ... Dein Algorithmus
      return result;
    }
    
    int cows(const code& vergleich, const code& user_guess){
      int result = 0;
      // .. Dein Algorithmus
      result-=bulls(vergleich, user_guess);
      return result;
    }
    


  • Hab ich da etwas falsch verstanden? fuer abba als Zahlen und bbbb als Eingabe ist es doch richtig "2 Bullen und 2 Kuehe" zu haben, oder 😕 wie soll das den sonst sein?


  • Mod

    Hae schrieb:

    Hab ich da etwas falsch verstanden?

    Dies. Hast du nie Mastermind gespielt?

    fuer abba als Zahlen und bbbb als Eingabe ist es doch richtig "2 Bullen und 2 Kuehe" zu haben, oder 😕 wie soll das den sonst sein?

    2 Bullen, 0 Kühe.



  • Vielleicht hilft es Dir, wenn Du Dein Bulls and Cows so implementierst, dass jede Zahl nur einmal vorkommt - so steht es auch in der engl. Wikipedia.

    Das würde die ganze Sache tatsächlich angenehmer gestalten! 😃
    Und genau da sind wir an diesem Knackpunkt: Wenn der Spieler jetzt aber eine Zahl in seinem "Guess" zwei- oder dreimal eingibt (Bsp. user_guess: 4383; hier die '3') und im "Lösungsvector" die 3 min. einmal vorkommt, habe ich spätestens ab diesem Zeitpunkt ein ärgerliches Problem: Die Zahl wird über die for-Schleife automatisch doppelt gezählt, genau DAS möchte ich unbedingt verhindern. Das erweist sich viel schwieriger, als ich mir anfangs dachte, oh ja...

    Ich gehe also recht in der Annahme, dass die zerlege_* Funktionen Schnee von gestern sind, und ich die nie wieder sehe?
    

    Hoffentlich rennst Du jetzt nicht weg ( 🙄 )... Ich hatte es der Übersichtlichkeit halber so geregelt:

    // Vector mit User-Eingaben
    		    vector<int> user_guess(4);
    		    user_guess [0] = zerlegen_einser(users_eingabe);
    		    user_guess [1] = zerlegen_zehner(users_eingabe);
    		    user_guess [2] = zerlegen_hunderter(users_eingabe);
    		    user_guess [3] = zerlegen_tausender(users_eingabe);
    

    Mit dieser Alternative werden die "Cows" alle brav gezählt, nur eben doppelt oder dreifach - die "Bulls" werden immerhin ignoriert:

    for(int i = 0; i < user_guess.size(); ++i)
    				for(int j = 0; j < vergleich.size(); ++j)
    					if(user_guess[i] == vergleich[j])
    						if(i != j)
    							++count_cows;
    


  • Voltobal schrieb:

    Mit dieser Alternative werden die "Cows" alle brav gezählt, nur eben doppelt oder dreifach - die "Bulls" werden immerhin ignoriert:

    for(int i = 0; i < user_guess.size(); ++i)
    				for(int j = 0; j < vergleich.size(); ++j)
    					if(user_guess[i] == vergleich[j])
    						if(i != j)
    							++count_cows;
    

    Ich hab's jetzt auch implementiert und hab mir noch ein Hilfskonstrukt bool is_cow_at[4]; gegönnt. Dort speichere ich, ob eine Stelle im Tipp schon als cow gewertet wurde. Ausserdem breche ich die innere Schleife ab, sobald ich eine Kuh gefunden habe - damit ich für eine Zahl im "secret" nicht mehrere cows habe.

    Ich gebe zu, dass sieht erstmal leichter aus, als es ist. Aber sicher schaffbar, wenn Du Dir über dein händisches Vorgehen im klaren bist.

    Du liest Deine Zahl also immer noch über einen int ein und zerteilst die?
    Was ist denn, wenn meine Eingabe 0123 ist, oder 0000? IMHO eignet sich der int hier nur bedingt...



  • Irgendwie finde ich, dass du das Ganze viel zu kompliziert angehst.
    Mit 53 Zeilen Code war die Aufgabe für mich nach 10 Minuten gelöst, ohne Dinge wie vector oder dem Zerlegen von Zahlen auch nur in Erwgägung zu ziehen.

    Also entweder habe ich die Aufgabenstellung missverstanden oder du musst deinen Kopf ein wenig frei machen und ganz simpel überlegen, wie man das am einfachsten lösen kann 😉



  • Du liest Deine Zahl also immer noch über einen int ein und zerteilst die?
    Was ist denn, wenn meine Eingabe 0123 ist, oder 0000? IMHO eignet sich der int hier nur bedingt...

    Gut, ich glaube Du hast Recht und mich noch ganz nebenbei überzeugt - der Integer scheint für das Lösen dieses Problems offensichtlich nicht das Gelbe vom Ei zu sein. 🙂
    Es ist wirklich wie beim Sudoku; vermutlich ist man hier am besten mit einem String oder Char gut bedient.
    Ich fahre demnächst in den Urlaub; werde mein Laptop dabei haben und Dir versprechen, das ganze Codefragment nochmal bei Gelegenheit komplett neu zu überarbeiten (und ohne Integer oder gar diese zerlege-Funktionen, Ehrenwort!)
    Irgendwann Mitte September poste ich das Ganze dann nochmal hier hin (zu diesem Thread). Würde mich freuen, wenn Du bzw. ihr dann mal wieder vorbeischau(s)t! 😃

    Viele Grüße und bis dann!



  • Voltobal schrieb:

    Ich fahre demnächst in den Urlaub; werde mein Laptop dabei haben und Dir versprechen, das ganze Codefragment nochmal bei Gelegenheit komplett neu zu überarbeiten (und ohne Integer oder gar diese zerlege-Funktionen, Ehrenwort!)
    Irgendwann Mitte September poste ich das Ganze dann nochmal hier hin (zu diesem Thread). Würde mich freuen, wenn Du bzw. ihr dann mal wieder vorbeischau(s)t! 😃

    Word!

    Schönen Urlaub 🙂


Anmelden zum Antworten