This code is part of my Hangman game. One of my minor issues is how to properly print out the hangman.
Could you please give me hints on how to make the code less hardcoded? This code is so redundant, shoot me...
Any comments are welcome.
class HangmanState
static void show(int count, PrintStream out) {
switch(count){
case 1: {
showOne(out);
break;
}
case 2: {
showTwo(out);
break;
}
case 3: {
showThree(out);
break;
}
case 4: {
showFour(out);
break;
}
case 5: {
showFive(out);
break;
}case 6: {
showSix(out);
break;
}
case 7: {
showSeven(out);
break;
}
case 8: {
showEight(out);
break;
}
case 9: {
showNine(out);
break;
}case 10: {
showTen(out);
break;
}default: {
showZero(out);
break;
}
}
}
private static void showZero(PrintStream out) {
out.println(" ");
out.println(" ");
out.println(" ");
out.println(" ");
out.println(" ");
out.println(" ");
out.println(" _______________");
out.println(" | 0/10 |");
out.println(" | |");
}
private static void showOne(PrintStream out) {
out.println(" ");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 1/10 |");
out.println(" | |");
}
private static void showTwo(PrintStream out) {
out.println(" ________");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 2/10 |");
out.println(" | |");
}
private static void showThree(PrintStream out) {
out.println(" ________");
out.println(" \\|");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 3/10 |");
out.println(" | |");
}
private static void showFour(PrintStream out) {
out.println(" ________");
out.println(" | \\|");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 4/10 |");
out.println(" | |");
}
private static void showFive(PrintStream out) {
out.println(" ________");
out.println(" | \\|");
out.println(" o |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 5/10 |");
out.println(" | |");
}
private static void showSix(PrintStream out) {
out.println(" ________");
out.println(" | \\|");
out.println(" o |");
out.println(" | |");
out.println(" | |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 6/10 |");
out.println(" | |");
}
private static void showSeven(PrintStream out) {
out.println(" ________");
out.println(" | \\|");
out.println(" o |");
out.println(" |\\ |");
out.println(" | |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 7/10 |");
out.println(" | |");
}
private static void showEight(PrintStream out) {
out.println(" ________");
out.println(" | \\|");
out.println(" o |");
out.println(" /|\\ |");
out.println(" | |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 8/10 |");
out.println(" | |");
}
private static void showNine(PrintStream out) {
out.println(" ________");
out.println(" | \\|");
out.println(" o |");
out.println(" /|\\ |");
out.println(" | |");
out.println(" / |");
out.println(" ___________|___");
out.println(" | 9/10 |");
out.println(" | |");
}
private static void showTen(PrintStream out) {
out.println(" ________");
out.println(" | \\|");
out.println(" o |");
out.println(" /|\\ |");
out.println(" | |");
out.println(" / \\ |");
out.println(" ___________|___");
out.println(" | 10/10 |");
out.println(" | R.I.P |");
}
3 Answers 3
You can effectively compress the data by defining two strings, representing a format and a mask.
private static final String IMG_FMT =
" ________%n" +
" | \\|%n" +
" o |%n" +
" /|\\ |%n" +
" | |%n" +
" / \\ |%n" +
" ___________|___%n" +
" | %2d/10 |%n" +
" | R.I.P |%n";
private static final String IMG_FMT_MASK =
" 2222222200" +
" 4 3100" +
" 5 100" +
" 867 100" +
" 6 100" +
" 9 a 100" +
" 00000000000100000" +
" 0 000000 000" +
" 0 aaaaa 000";
static { assert(IMG_FMT.length() == IMG_FMT_MASK.length()); }
public static void show(int stage, PrintStream out) {
char m = Character.forDigit(stage, 36);
StringBuilder s = new StringBuilder(IMG_FMT.length());
for (int i = 0; i < IMG_FMT.length(); i++) {
s.append((IMG_FMT_MASK.charAt(i) <= m) ? IMG_FMT.charAt(i) : ' ');
}
out.printf(s.toString(), stage);
}
A caveat, though, is that the animation must be additive. Specifically, you want the base of the vertical post to change from -
initially to a |
character, and that cannot be accomplished using this technique without a nasty hack.
-
\$\begingroup\$ using a mask - thats a nice technique!!! i never would have had such an idea!!! great! \$\endgroup\$Martin Frank– Martin Frank2019年03月01日 09:12:36 +00:00Commented Mar 1, 2019 at 9:12
The drawings may be arbitrarily complex. As you grow the program, you might want to add color, animation, who knows what.
Thus, your drawings are resources, and should be treated as such.
This answer provides code, links, and explanations. In short:
create one or more (your call) text files
store the drawings in some format (your call) in the text files
open a reader on the appropriate resource:
InputStream in = getClass().getResourceAsStream("/file.txt"); BufferedReader reader = new BufferedReader(new InputStreamReader(in));
read in the data, parse the format, and render the artwork
PROFIT!!
Note: to start with, the right format is probably "here are my chars" and you just read them and echo to the screen. If you create one file per image, you can generate the file name as "imageNN.txt" and your rendering code becomes 5 or 6 lines.
Update:
Now that I'm on a computer, not a phone, consider this:
static final String GALLOWS_FORMAT = "images/gallows%02d.txt";
// ... other code ...
static void show(int count, PrintStream out) {
final String filespec = String.format(GALLOWS_FORMAT, count);
InputStream in = getClass().getResourceAsStream(filespec);
BufferedReader gallows = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = gallows.readLine()) != null) {
out.println(line)
}
gallows.close();
}
-
\$\begingroup\$ i would have not got the idea to move this part into an ressource... that's quite obvious once you read it, but i was not able to do it... i think that would be the preffered way, especially when i take your hint about 'growing' (colors new shapes etc...) thanks! \$\endgroup\$Martin Frank– Martin Frank2019年03月18日 12:17:11 +00:00Commented Mar 18, 2019 at 12:17
Use a Map
Since Java 8 it is possible to treat methods as higher order functions, what makes it possible to store them as a value inside a Map
Map<Integer, Consume<OutputStream> countByConsumer = new HashMap<>();
countByConsumer.put(1, BaseTest::showOne);
countByConsumer.put(2, BaseTest::showTwo);
Than you can simply use get
inside show
static void show(int count, PrintStream out) {
Consumer<PrintStream> printStreamConsumer = countByConsumer.get(count);
printStreamConsumer.accept(out);
}
The advantage of this method is that you get ride of the huge switch
Use OOP
Use the State-Pattern
If you want a oop solution, i think the state-pattern would be the way to go. There for you would have the class Hangman
which have multiple HealthState
s
class Hangman {
private HealthState healthState;
// constructor
void setHealthState(HealthState healthState) {
this.healthState = healthState;
}
void display(HealthState healthState, PrintStream out) {
healthState.display(out)
}
}
class OneHealth implements HealthState {
private Hangman hangman;
// constructor
@Override
public void display(PrintStream out) {
out.println(" ");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" |");
out.println(" ___________|___");
out.println(" | 1/10 |");
out.println(" | |");
hangman.setHealthState(new TwoHealth());
}
}
class TwoHealth implements HealthState {
private Hangman hangman;
// constructor
@Override
public void display(PrintStream out) {
/*...*/
hangman.setHealthState(new ThreeHealth());
}
}
-
\$\begingroup\$ i don't think it's a good idea to set the state during
display
... but really: using methods as higher order functions is a real bumper - thats a very good hint on how to handle that (since this is not the first time i ran into a problem like this) \$\endgroup\$Martin Frank– Martin Frank2019年03月01日 05:23:00 +00:00Commented Mar 1, 2019 at 5:23 -
\$\begingroup\$ yes, you are right. Maybe it would be better to return a
List
or a custom object \$\endgroup\$Roman– Roman2019年03月01日 06:12:59 +00:00Commented Mar 1, 2019 at 6:12