# Tipp bei HangMan geben



## Feanix (20. Feb 2021)

Hallo. 

Ich hoffe ich bin nicht im falschen Forum, bin neu hier xD. 

Ich arbeite gerade an meiner Facharbeit und programmiere dazu HangMan als Spiel. Funktioniert auch alles gut, nur mein einziges Problem ist, dass wenn man "Tipp" eingibt (um einen Tipp zu bekommen), trotzdem der Buchstabe "T" als Versuch genommen wird. Ich hätte gerne, dass in diesem Fall kein Buchstabe als Versuch genommen wird, aber ich kriege es einfach nicht hin. Ich hoffe ihr könnt mir helfen. 

In Zeile 154 bis 156 seht ihr den auszugebenen Tipp. 

(Mir ist klar, dass das vllt. nicht die schönste Lösung ist, bin aber noch nicht super fortgeschritten.)


```
import java.io.*;
import sum.kern.*;
public class HangMan
{
    // Bezugsobjekte
    
    String word,clue;
    int option;
    PrintWriter p = new PrintWriter(System.out,true);
    
    // Zustandsvariablen
      
    
    private void clearScreen()
    {
        System.out.print('\u000C');
    }   
    
    HangManStufen HangManStufen;
    Kategorien Kategorien;
    public static void main(String args[])throws IOException, InterruptedException
    {
        System.out.print('\u000C');
        HangMan obj = new HangMan();
        HangManStufen lStufe = new HangManStufen(1);
        Kategorien lKategorie = new Kategorien(1);
        InputStreamReader isr = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(isr);
        PrintWriter p = new PrintWriter(System.out,true);
        p.println("Willkommen bei Hangman!");
        p.println();
        
        
        Thread.sleep(1500);
        p.println("\nFolgende Regeln:\n");
        Thread.sleep(1500);
        p.println("1. Du kannst alle Buchstaben von A-Z und alle Umlaute eingeben.\n");
        Thread.sleep(5000);
        p.println("2. Du kannst dir Zeit lassen, es gibt kein Zeitlimit.\n");
        Thread.sleep(5000);
        p.println("3. Falls du einen Tipp brauchst, kannst du unten einfach 'Tipp' eingeben.\n");
        Thread.sleep(5000);
        p.println("Das Spiel beginnt in 5 Sekunden automatisch. Viel Spaß!");
        Thread.sleep(5000);
        obj.clearScreen();
        
        
        p.println("Gib unten deine Wahl passend zu den folgenden Möglichkeiten ein. \nHinweis: Die zu erratenden Wörter hängen mit dem gewählten Themen zusammen.\n");
        p.println();
        p.println("1. Informatik");
        p.println("2. Computer");
        p.println("3. Filme");
        p.println("4. Essen");
        p.println("5. Allgemein");
        p.println("6. Lange Wörter");

        int choice = Integer.parseInt(br.readLine());
        obj.clearScreen();
        String bothwordandclue[] = new String[2];
        if(choice == 1)
            bothwordandclue = lKategorie.Informatik();
        else if(choice == 2)
            bothwordandclue = lKategorie.Computer();
        else if(choice == 3)
            bothwordandclue = lKategorie.Filme();
        else if(choice == 4)
            bothwordandclue = lKategorie.Essen();
        else if(choice == 5)
            bothwordandclue = lKategorie.Allgemein();
        else if(choice == 6)   
            bothwordandclue = lKategorie.LangeWoerter();
        else
            p.println("Falsche Wahl. Bitte wähle eine verfügbare Zahl.");
        int counter = 6;
        String guessword = bothwordandclue[0];
        String wordclue = bothwordandclue[1];
        int lengthofword = (int)(Math.round(((double)guessword.length()/2)));
        int checkguess = 0;
        String a;
        String b;
        String guesses[] = new String [50];
        StringBuffer guessit = new StringBuffer();
        for ( int i = 0; i<lengthofword; i++)
            guessit = guessit.append("_ ");
        guessit.delete((2 * lengthofword)-1,(2 * lengthofword));
        int index = guessword.indexOf("|");
        while(index >=0)
        {
            guessit.setCharAt(index, '|');
            index = guessword.indexOf("|", index+1);
        }
        
        do
        {
            p.println();
            p.println();
            p.println("Gib deinen Buchstaben ein.");
            
            if(counter == 6)
                lStufe.Hang1();
            else if(counter == 5)
                lStufe.Hang2();
            else if(counter == 4)
                lStufe.Hang3();
            else if(counter == 3)
                lStufe.Hang4();
            else if(counter == 2)
                lStufe.Hang5();
            else if(counter == 1)
            {   
                lStufe.Hang6();
            }   
                
            
            
            String guessletter2 = br.readLine();
            String guessletter = guessletter2.toUpperCase();
            obj.clearScreen();
            
            
            for(int i = 0; i<lengthofword; i++)
            {
                if(guessletter.charAt(0) == guessword.charAt(2*i))
                {
                    guessit.setCharAt(2*i,guessletter.charAt(0));
                    checkguess=1;
                }   
            }
            
            if(checkguess == 1)
            {
                p.println("Richtige Wahl. Du hast " + counter + " falsche Wahlen übrig. \n");
                p.println(guessit);
            }
            else
            {
                
                counter--;
                
                if(counter == 0)
                {
                    p.println("Falsche Wahl. Du hast den Mann erhängt :(\n");
                    p.println(guessit);
                    lStufe.Hang7();
                }
                else
                {
                    p.println("Falsche Wahl. Du hast " + counter + " falsche Wahlen übrig. \n");
                    p.println(guessit);
                }
                
            }
            
            b = guessletter2;
            if(b.equals("Tipp"))
                p.println("\nHier ein kleiner Tipp:\n" + lKategorie.clue);
            
            checkguess = 0;
            if(counter == 0)
                p.println("\nDu hast das Spiel leider verloren\n\nDas gesuchte Wort war " + lKategorie.word);
            a = guessit.toString();
            if(a.equals(guessword))
            {
                p.println("\nDu hast das Wort erraten!");
                counter = 0;
            }   
        }while(counter>0);
        
        
        Thread.sleep(5000);
        System.out.print('\u000C');
        p.println("Das Spiel beginnt in 5 Sekunden von Vorne. \n\nWenn du aufhören möchtest, drücke oben links auf 'Optionen' und dann auf 'Beenden'. \n\nVielen Dank fürs Spielen! ");
        Thread.sleep(8000);
        
        
        System.out.print('\u000C');
        
        HangMan.main(null);
    }
    
    
    
        
    

}
```


----------



## Robert Zenz (20. Feb 2021)

Na deine Pruefung ob es der String "Tipp" ist ist viel zu spaet. Du musst die Eingabe einfach pruefen ob es "Tipp" ist, und wenn nicht dann es als Eingabe akzeptieren.


----------



## Feanix (20. Feb 2021)

Robert Zenz hat gesagt.:


> Na deine Pruefung ob es der String "Tipp" ist ist viel zu spaet. Du musst die Eingabe einfach pruefen ob es "Tipp" ist, und wenn nicht dann es als Eingabe akzeptieren.


Gut das kann wahrscheinlich einfach behoben werden, aber wenn ich es eher mache, ist es an der falschen Stelle. Das Spiel soll ja schließlich auch gut aussehen. Und was meinst du mit die Eingabe überprüfen? Das mach ich doch genau mit dem Befehl?


----------



## Robert Zenz (20. Feb 2021)

Deine Pruefung muss relativ direkt nach "String guessletter2 = br.readLine();" erfolgen, nur das du halt pruefen musst ob "Tipp" eingeben wurde, und wenn nicht dann als Rateversuch werten. Im Moment machst du ja den Rateversuch und pruefst danach ob ein "Tipp" gefragt war. In Pseudo-Code:


```
String eingabe = br.readLine();

if (einageb.equals("Tipp")) {
    // TODO Tipp ausgeben.
} else {
    // TODO Als Rateversuch werten.
}
```


----------



## Feanix (20. Feb 2021)

Robert Zenz hat gesagt.:


> Deine Pruefung muss relativ direkt nach "String guessletter2 = br.readLine();" erfolgen, nur das du halt pruefen musst ob "Tipp" eingeben wurde, und wenn nicht dann als Rateversuch werten. Im Moment machst du ja den Rateversuch und pruefst danach ob ein "Tipp" gefragt war. In Pseudo-Code:
> 
> 
> ```
> ...


Jawoll, es hat funktioniert. Danke! Es wertet den Tipp nicht mehr als Versuch. Danke!!!


----------



## Feanix (20. Feb 2021)

Robert Zenz hat gesagt.:


> Deine Pruefung muss relativ direkt nach "String guessletter2 = br.readLine();" erfolgen, nur das du halt pruefen musst ob "Tipp" eingeben wurde, und wenn nicht dann als Rateversuch werten. Im Moment machst du ja den Rateversuch und pruefst danach ob ein "Tipp" gefragt war. In Pseudo-Code:
> 
> 
> ```
> ...


Noch eine Sache. Gibt es ne Möglichkeit, per Befehl das Terminal zu schließen? Also wenn ich "Beenden" eingebe, dass das Programm dann beendet wird?


----------



## Robert Zenz (20. Feb 2021)

Entweder aus deiner Hauptschleife springen damit das Ende erreicht ist, oder "System.exit(0)" aufrufen. Bitte beachte, dass es in diesem Fall egal ist, aber "System.exit" ist kein "JVM herunterfahren" sondern "JVM Prozess toeten". Wenn "System.exit" aufgerufen wird, ist nichtmal garantiert dass "final" Bloecke ausgefuehrt werden.

Soll ich dir noch ein Code-Review ueber ein paar Kleinigkeiten machen die mir gerade auffallen?


----------



## Feanix (20. Feb 2021)

Robert Zenz hat gesagt.:


> Entweder aus deiner Hauptschleife springen damit das Ende erreicht ist, oder "System.exit(0)" aufrufen. Bitte beachte, dass es in diesem Fall egal ist, aber "System.exit" ist kein "JVM herunterfahren" sondern "JVM Prozess toeten". Wenn "System.exit" aufgerufen wird, ist nichtmal garantiert dass "final" Bloecke ausgefuehrt werden.
> 
> Soll ich dir noch ein Code-Review ueber ein paar Kleinigkeiten machen die mir gerade auffallen?


Alles klar. 

Klar gerne.


----------



## Feanix (20. Feb 2021)

Robert Zenz hat gesagt.:


> Entweder aus deiner Hauptschleife springen damit das Ende erreicht ist, oder "System.exit(0)" aufrufen. Bitte beachte, dass es in diesem Fall egal ist, aber "System.exit" ist kein "JVM herunterfahren" sondern "JVM Prozess toeten". Wenn "System.exit" aufgerufen wird, ist nichtmal garantiert dass "final" Bloecke ausgefuehrt werden.
> 
> Soll ich dir noch ein Code-Review ueber ein paar Kleinigkeiten machen die mir gerade auffallen?


Ja das Programm wird beendet, das Fenster bleibt aber leider offen.


----------



## Robert Zenz (20. Feb 2021)

Achso, wenn es vorher schon offen war, kannst du es nicht schlieszen, da hast du von deinem Prozess aus keien Kontrolle darueber.


Du folgst nicht den Java conventions, ist das Absicht? Wenn ja, alles gut, wenn nicht, dann waere es gut wenn du dich an diese haeltst. Der Vorteil von diesen globalen Konventionen ist, dass Code halt immer gleich aussieht, egal von wo er stammt. Dass macht es fuer andere Entwickler sehr viel einfacher in deinen Code einzusteigen.

Auszerdem solltest du einen automatischen Formatter fuer deinen Code verwenden, deine IDE hat sicher einen dabei. Dann musst du nie wieder ueber die Formatierung nachdenken.



```
String word,clue;
    int option;
    PrintWriter p = new PrintWriter(System.out,true);
```

Wenn du keinen modifier angibst (private, protected, public) dann sind Klassen und Member immer package-private. package-private ist meiner Meinung nach ein Konzept welches vermieden werden sollte, weil es die Variablen fuer die aktuelle Klasse, alle Klassen im selben Paket aber nicht ableitenden Klassen zugaenglich macht. Damit kann man sehr leicht Klassen im selben Paket aus Versehen so verkoppeln dass sie von Auszen nicht mehr erweiterbar sind. Im Standardfall macht es jeder "private".



```
private void clearScreen()
```

Du hast dir Method angelegt, was gut ist, verwendest sie aber nicht immer.



```
HangManStufen HangManStufen;
```

Du bist sehr, sehr wankelmuetig was deine Namen angeht, manche sind echt gut, andere unbrauchbar. Kuerze niemals Namen ab nur weil du kannst, es macht deinen Code nur schwerer zu verstehen und zu warten. Bestes Beispiel hier ist "a"...schnell, was macht "b"?

Achte immer darauf deine Sachen gut und verstaendlich zu benennen.



```
if(choice == 1)
            bothwordandclue = lKategorie.Informatik();
        else if(choice == 2)
```

Hier bietet sich ein switch an.



```
int lengthofword = (int)(Math.round(((double)guessword.length()/2)));
```

Wenn du einen int mit einem int dividierst, bekommst du automatisch einen abgerundeten int. Wenn du jetzt einen aufgerundeten Wert willst, kannst du in diesem Fall "schummeln", naemlich mit "(length + 1) / 2". Dann ersparst du die das konvertieren vom Wert.



```
StringBuffer guessit = new StringBuffer();
        for ( int i = 0; i<lengthofword; i++)
            guessit = guessit.append("_ ");
        guessit.delete((2 * lengthofword)-1,(2 * lengthofword));
```

StringBuffer ist synchronisiert, StringBuilder ist es nicht. Es spielt in dem Fall keine Rolle, aber fuer gewoehnlich verwendet man einen StringBuilder auszer man braucht die Thread-Sicherheit von einem StringBuffer.

Leichter zu lesen waere auch wenn du die Laenge vom Buffer/Builder verwendest, dann sieht man sofort dass das letzte Zeichen geloescht wird:


```
guessit.delete(guessit.length() - 1, guessit.length());
```

Wenn ich das richtig lese, dann kannst du das mit Java 11 einfacher gestalten, naemlich mit "String.repeat("_ ", lengthOfWord).trim()". Ist etwas einfacher zu lesen. Solche kleinen Logikeinheiten sind auch tolle Kandidaten um diese in eine Funktion zu packen.



```
HangMan.main(null);
```

Du rufst das Spiel erneut auf per Rekursion, eventuell waere hier eine "normale" Schleife angebrachter. Im momentanen Fall verwendest du naemlich mit jedem Durchlauf mehr und mehr Speicher, da nichts weggeraeumt werden kann, unter anderem.


Du kannst auch noch versuchen die einzelnen Abschnitte in Funktionen auszulagern, das wuerde dann den Hauptteil deiner Logik uebrsichtlicher machen.


----------



## Feanix (20. Feb 2021)

Robert Zenz hat gesagt.:


> Achso, wenn es vorher schon offen war, kannst du es nicht schlieszen, da hast du von deinem Prozess aus keien Kontrolle darueber.


Ok schade. 



Robert Zenz hat gesagt.:


> Du folgst nicht den Java conventions, ist das Absicht?


Tatsächlich kannte ich den Java conventions bis grade nicht, da wir nur unsere Kleinigkeiten in der Schule programmieren und der für mich damit nicht wichtig ist.



Robert Zenz hat gesagt.:


> Du hast dir Method angelegt, was gut ist, verwendest sie aber nicht immer.


Ich hab einfach noch nicht aufgeräumt 



Robert Zenz hat gesagt.:


> Du bist sehr, sehr wankelmuetig was deine Namen angeht, manche sind echt gut, andere unbrauchbar. Kuerze niemals Namen ab nur weil du kannst, es macht deinen Code nur schwerer zu verstehen und zu warten. Bestes Beispiel hier ist "a"...schnell, was macht "b"?
> 
> Achte immer darauf deine Sachen gut und verstaendlich zu benennen.


Ich wollte nur schnell voran kommen und hab in dem Moment nicht drüber nachgedacht. Aber werde ich ab jetzt machen. 



Robert Zenz hat gesagt.:


> Wenn du einen int mit einem int dividierst, bekommst du automatisch einen abgerundeten int. Wenn du jetzt einen aufgerundeten Wert willst, kannst du in diesem Fall "schummeln", naemlich mit "(length + 1) / 2". Dann ersparst du die das konvertieren vom Wert.


Alles klar.



Robert Zenz hat gesagt.:


> Du rufst das Spiel erneut auf per Rekursion, eventuell waere hier eine "normale" Schleife angebrachter. Im momentanen Fall verwendest du naemlich mit jedem Durchlauf mehr und mehr Speicher, da nichts weggeraeumt werden kann, unter anderem.


Was meinst du mit einer "normalen" Schleife? Ich kenne das nur so mittels Rekursion.



Robert Zenz hat gesagt.:


> Du kannst auch noch versuchen die einzelnen Abschnitte in Funktionen auszulagern, das wuerde dann den Hauptteil deiner Logik uebrsichtlicher machen.


Jo, habe bereits die Kategorien und einzelnen HangMan Stufen ausgelagert, aber das kann ich natürlich noch weiter machen. Wollte erstmal grob fertig werden. 


Insgesamt Vielen Dank für die Arbeit! Hast mir sehr geholfen.


----------



## Robert Zenz (20. Feb 2021)

Wegen der Schleife, in Pseudo-Code geht es glaube ich am einfachsten zu erklaeren:


```
boolean gameRunning = true;

while (gameRunning) {
    // TODO Spiele Logik
    String userInput = getUserInput();
    
    if (userInput.equals("quit")) {
        gameRunning = false;
    }
}
```

Alternativ kannst du natuerlich auch einfach ein "return" einbauen an der Stelle an welcher du dann wirklich abbrechen willst.


----------

