3
\$\begingroup\$

Below is a long messy one method code for a craps game. The user can play the game multiple times and has the option to quit when ever they want.

  • How could you break this code down into different types of methods?
  • How many methods do you think the code can be broken down to?
  • What types of methods should be used?
import java.util.Random;
import java.util.Scanner;

public class craps {

        public static void main(String args[]) {
        System.out.print("How much money do you have total? ");
        Scanner keyboard4 = new Scanner(System.in);
        double total = keyboard4.nextDouble();


        boolean stop = false;
        while (stop == false) {
            int n = 0; 
            System.out.print("Place your bets! $");
            Scanner keyboard = new Scanner(System.in);
            double bet = keyboard.nextDouble();
            double bet1 = 0; 
            Random generator = new Random();
            int dies = generator.nextInt(6) + 1;
            int dies2 = generator.nextInt(6) + 1;
            int dietotal = dies + dies2;
            System.out.println("Press 'Enter' to roll");
            Scanner keyboard3 = new Scanner(System.in);
            String q2 = keyboard3.nextLine();
            System.out.println("Your first roll was " + dietotal);
            if (dietotal == 7 || dietotal == 11) {
                System.out.println("You win!");
                System.out.println("Amount won: $" + bet);
                total = total + bet;
                System.out.println("Your new total is: " + total);
                System.out.print("\nDo you want to play again?: ");
                Scanner keyboard2 = new Scanner(System.in);
                String cont = keyboard2.nextLine();
                if (cont.equals("") || cont.equals("Y") || cont.equals("y") || cont.equals("yes") || cont.equals("YES") || cont.equals("Yes")) {
                    stop = false;
                } else {
                    stop = true;
                }
            } else if (dietotal == 2 || dietotal == 3 || dietotal == 12) {
                System.out.println("You Crapped out!");
                System.out.println("Amount lost: $" + bet);
                total = total - bet;
                System.out.println("Your new total is: " + total);
                System.out.print("\nDo you want to play again?: ");
                Scanner keyboard2 = new Scanner(System.in);
                String cont = keyboard2.nextLine();
                if (cont.equals("Y") || cont.equals("y") || cont.equals("yes") || cont.equals("YES") || cont.equals("Yes")) {
                    stop = false;
                } else {
                    stop = true;
                }
            } else {
                if (dietotal == 4 || dietotal == 10) {
                    bet1 = bet * 2;
                }
                if (dietotal == 5 || dietotal == 9) {
                    bet1 = bet / 2 * 3;
                }
                if (dietotal == 6 || dietotal == 8) {
                    bet1 = bet / 5 * 6;
                }
                int diet = 0;
                while (!(diet == 7 || diet == dietotal)) {
                    int die = generator.nextInt(6) + 1;
                    int die2 = generator.nextInt(6) + 1;
                    diet = die + die2;
                    n = n + 1;
                    System.out.println("Press 'Enter' to roll again");
                    Scanner keyboard2 = new Scanner(System.in);
                    String q = keyboard2.nextLine();
                    System.out.println("Roll " + n + ": " + diet);
                }
                if (diet == 7) {
                    System.out.println("You lose");
                    System.out.println("You lost your bet of: $" + bet);
                    total = total - bet;
                }
                if (diet == dietotal) {
                    System.out.println("You win!");
                    System.out.println("Amount won: $" + bet1);
                    total = total + bet1;
                }
                System.out.println("Your new total is: " + total);

                System.out.print("\nDo you want to play again?: ");
                Scanner keyboard2 = new Scanner(System.in);
                String cont = keyboard2.nextLine();
                if (cont.equals("Y") || cont.equals("y") || cont.equals("yes") || cont.equals("YES") || cont.equals("Yes")) {
                    stop = false;
                } else {
                    stop = true;
                }
            }
        }
    }
}
\$\endgroup\$
4
\$\begingroup\$

How could you break this code down into different types of methods?

Never use Copy&Paste for code. (You can use Cut&Paste.) If you want to Copy&Paste a chunk of code, instead put that chunk in a method and call it from multiple locations. Do this to already Copy&Pasted code also.

System.out.print("\nDo you want to play again?: ");
Scanner keyboard2 = new Scanner(System.in);
String cont = keyboard2.nextLine();
if (cont.equals("") || cont.equals("Y") || cont.equals("y") || cont.equals("yes") || cont.equals("YES") || cont.equals("Yes")) {
    stop = false;
} else {
    stop = true;
}

chunk is an obvious target for converting to a method. It could be:

private static final Set<String> affirmativeAnswers = new HashSet<String>(
        Arrays.asList("Y", "y", "yes", "YES", "Yes")); 

private static boolean userWantsToPlayAgain(Scanner scanner) {
    System.out.print("Do you want to play again? [y/n]: ");
    return affirmativeAnswers.contains(scanner.nextLine());
}

Another such candidate is

int dies = generator.nextInt(6) + 1;
int dies2 = generator.nextInt(6) + 1;
int dietotal = dies + dies2;

Also note generator.nextInt(6) + 1 chunks are repeated within the repeated chunk.

So these chunks become:

private static int getDieTotal(Random random) {
    return rollDie(random) + rollDie(random);
}

private static int rollDie(Random random) {
    return random.nextInt(6) + 1;
}

All repeated chunks MUST be extracted to methods. Any chunk of code that does something SHOULD be extracted to method, even if it is not repeated.

Consider:

if (dietotal == 4 || dietotal == 10) {
    bet1 = bet * 2;
}
if (dietotal == 5 || dietotal == 9) {
    bet1 = bet / 2 * 3;
}
if (dietotal == 6 || dietotal == 8) {
    bet1 = bet / 5 * 6;
}

This should be extracted to:

private static double getBet1(double bet, int dietotal) {
    return bet * getScale(dietotal);
}

private static double getScale(int dietotal) {
    if (dietotal == 4 || dietotal == 10) return 2.0;
    if (dietotal == 5 || dietotal == 9) return 1.5;
    if (dietotal == 6 || dietotal == 8) return 1.2;
    return 0;
}

How many methods do you think the code can be broken down to?

The ideal is Extract till you Drop

\$\endgroup\$
3
\$\begingroup\$

I think it's easiest if I start by stepping through your code, line by line and then see what happens when I get to the stuff that's bugging me:

public class craps {

Classes in Java are supposed to be named in PascalCase (or CapitalizedCamelCase) and this is where it starts ;)

public class Craps {
public static void main(String[] args) {
System.out.print("How much money do you have total? ");
Scanner keyboard4 = new Scanner(System.in);
double total = keyboard4.nextDouble();

I value indentation as one of the easiest tools to improve readability of code... You should really indent the code inside your main-method...

Additionally I personally prefer System.out.println over System.out.print since it's less strain on yourself to get the linebreaks right.

and now we get to the fun part.... keyboard4. First off: A Scanner is not a Keyboard! Next off: Don't number your variables! Do not number your variables. (that is if you can't help it in any way)

oh by the way, where's keyboard 1 through 3?
Additionally... you do know everything breaks and dies when I don't input a double into the next line?


boolean stop = false;
while (stop == false) {

stop right there (~badum, tss). You can have that easier already:

boolean stop = false;
while (!stop) {

This code does exactly the same, has one comparison less, even.


int n = 0;
System.out.print("Place your bets! $");
Scanner keyboard = new Scanner(System.in);
double bet = keyboard.nextDouble();
double bet1 = 0;

hmm... what is n?? you declare some variable n here, and at least in this "logical block" don't use it. Don't do that it's confusing and trips people up. Declare variables as close as possible to their usage.

Skipping that System.out, and going to keyboard. Oh right, this is number one?... Tell me: What is the difference between keyboard and keyboard4?

None, right? Don't declare stuff you already declared again! Instead reuse your already declared keyboard4... and name it something different. How about: input?

And again you have the same problem when I input things like... twelve ;)

And here's where the real fun starts. Remember what we did earlier.. the "same" three lines of code here and at the start of main. This is our first candidate to extract into a method.

So... What are we doing? We are prompting user input and expect a double out of it. We need a string to ask the user for something. Well then our method head (and also body) is clear:

private static double promptUser(String prompt) {
    Scanner input = new Scanner(System.in);
    System.out.println(prompt);
    double result = input.nextDouble();
    return result;
}

We can further optimize this. How about taking that new Scanner(System.in); somewhere we can always get it from and declare it a static memeber of Craps? This makes the first line unneeded... and then there's result = ...; return result;. Either we get in a validation for the input right there or we skip the assignment:

private static double promptUser(String prompt) {
  System.out.println(prompt);
  return input.nextDouble();
}

Let's look at your class after applying what I mentioned until now:

public class Craps {

    private static Scanner input = new Scanner(System.in);

    private static double promptUser(String prompt) {
        System.out.println(prompt);
        return input.nextDouble();
    }

    public static void main(String[] args) {
        double total = promptUser("How much money do you have total?");

        boolean stop = false;
        while (!stop) {
            double bet = promptUser("Place your bets! $");
            //stuff goes on here...

It's not really much shorter, but I think you now can read over it and understand it faster now. The point is, you can drastically change your code by extracting repetitve blocks of logic into methods and elements common to these logic steps into class-level fields.

I hope to see a version of your code where you applied a few of the tricks I gave to you here throughout the whole code ;)

\$\endgroup\$
2
\$\begingroup\$

I have used enum for game results representation (GameResult), here should be cases for all game results, so for example WIN case are related to 7 and 11 according to given logic.

Was implemented Command pattern for sequence of actions for different GameResults , that returns GameStatus enity that shows new bet and should game be stopped or not.

Using this way of implementing this game gives you opportunity to easy extend , change and support it ;)

I almost finished all code , but left you small part of it ;)

Code:

GameResult:

package com.stackoverflow.game;

public enum GameResult {
    WIN(7, 11), LOST(2, 3, 12), DOUBLE_BET(4, 10), HALF_INCREASE(5, 9), SMALL_WIN(6, 8), UNDEFINED_RESULT;

    private int gameResultsNumbers[];

    private GameResult(int... gameResultsNumbers) {
        this.gameResultsNumbers = gameResultsNumbers;
    }

    public static GameResult getRusltByRandomNumber(int inNumber) {
        for (GameResult result : GameResult.values()) {
            for (int gameResultNumber : result.getGameResultsNumbers()) {
                if (gameResultNumber == inNumber) {
                    return result;
                }
            }
        }
        return UNDEFINED_RESULT;
    }

    public int[] getGameResultsNumbers() {
        return gameResultsNumbers;
    }

}

GameStatus:

package com.stackoverflow.game;

public class GameStatus {
    private double bet;
    private boolean stop;

    public double getBet() {
        return bet;
    }

    public void setBet(double bet) {
        this.bet = bet;
    }

    public boolean isStop() {
        return stop;
    }

    public void setStop(boolean stop) {
        this.stop = stop;
    }

}

GameCommand:

package com.stackoverflow.game;

import java.util.Scanner;

/**
 * Provides command pattern implementation for different game outputs
 *
 */
public interface GameCommand {

    GameStatus execute(Scanner keyboard, double bet, double total);
}

GameDoubleBetCommand:

package com.stackoverflow.game;

import java.util.Scanner;

public class GameDoubleBetCommand implements GameCommand {

    @Override
    public GameStatus execute(Scanner keyboard, double bet, double total) {
        GameStatus result = new GameStatus();
        result.setBet(bet * 2);
        return result;
    }
}

GameHalfIncreaseCommand:

package com.stackoverflow.game;

import java.util.Scanner;

public class GameHalfIncreaseCommand implements GameCommand {

    @Override
    public GameStatus execute(Scanner keyboard, double bet, double total) {
        GameStatus result = new GameStatus();
        result.setBet(bet / 2 * 3);
        return result;
    }
}

GameLostCommand:

package com.stackoverflow.game;

import java.util.Scanner;

public class GameLostCommand implements GameCommand {

    @Override
    public GameStatus execute(Scanner keyboard, double bet, double total) {
        GameStatus gameStatus = new GameStatus();
        gameStatus.setBet(bet);
        System.out.println("You Crapped out!");
        System.out.println("Amount lost: $" + bet);
        total = total - bet;
        System.out.println("Your new total is: " + total);
        System.out.print("\nDo you want to play again?: ");
        String cont = keyboard.nextLine();
        if (cont.equals("Y") || cont.equals("y") || cont.equals("yes") || cont.equals("YES") || cont.equals("Yes")) {
            gameStatus.setStop(false);
        } else {
            gameStatus.setStop(true);
        }
        return gameStatus;
    }
}

GameSmallWinCommand:

package com.stackoverflow.game;

import java.util.Scanner;

public class GameSmallWinCommand implements GameCommand {

    @Override
    public GameStatus execute(Scanner keyboard, double bet, double total) {
        GameStatus result = new GameStatus();
        result.setBet(bet / 5 * 6);
        return result;
    }
}

GameWinCommand:

package com.stackoverflow.game;

import java.util.Scanner;

public class GameWinCommand implements GameCommand {

    @Override
    public GameStatus execute(Scanner keyboard, double bet, double total) {
        GameStatus gameStatus = new GameStatus();
        gameStatus.setBet(bet);
        System.out.println("You win!");
        System.out.println("Amount won: $" + bet);
        total = total + bet;
        System.out.println("Your new total is: " + total);
        System.out.print("\nDo you want to play again?: ");
        String cont = keyboard.nextLine();
        if (cont.equals("") || cont.equals("Y") || cont.equals("y") || cont.equals("yes") || cont.equals("YES") || cont.equals("Yes")) {
            gameStatus.setStop(false);
        } else {
            gameStatus.setStop(true);
        }
        return gameStatus;
    }
}

GameLauncher:

package com.stackoverflow.game;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;

public class GameLauncher {
    private static final Map<GameResult, GameCommand> gameContext;
    static {
        gameContext = new HashMap<GameResult, GameCommand>();
        gameContext.put(GameResult.WIN, new GameWinCommand());
        gameContext.put(GameResult.LOST, new GameLostCommand());
        gameContext.put(GameResult.DOUBLE_BET, new GameDoubleBetCommand());
        gameContext.put(GameResult.HALF_INCREASE, new GameHalfIncreaseCommand());
        gameContext.put(GameResult.SMALL_WIN, new GameSmallWinCommand());
    }

    public static void main(String args[]) {
        System.out.print("How much money do you have total? ");
        Scanner keyboard = new Scanner(System.in);
        double total = keyboard.nextDouble();

        boolean stop = false;
        while (stop == false) {
            int n = 0;
            System.out.print("Place your bets! $");
            double bet = keyboard.nextDouble();
            Random generator = new Random();
            int rolledNumber = generator.nextInt(6) + generator.nextInt(6) + 2;
            GameResult gameResult = GameResult.getRusltByRandomNumber(rolledNumber);
            System.out.println("Press 'Enter' to roll");
            keyboard.nextLine();
            System.out.println("Your first roll was " + rolledNumber);
            GameStatus gameStatus = gameContext.get(gameResult).execute(keyboard, bet, total);
            stop = gameStatus.isStop();
            double resultedBet = gameStatus.getBet();
            int diet = 0;
            while (!(diet == 7 || diet == rolledNumber)) {
                int die = generator.nextInt(6) + 1;
                int die2 = generator.nextInt(6) + 1;
                diet = die + die2;
                n = n + 1;
                System.out.println("Press 'Enter' to roll again");
                System.out.println("Roll " + n + ": " + diet);
            }
            if (diet == 7) {
                System.out.println("You lose");
                System.out.println("You lost your bet of: $" + bet);
                total = total - bet;
            }
            if (diet == rolledNumber) {
                System.out.println("You win!");
                System.out.println("Amount won: $" + resultedBet);
                total = total + resultedBet;
            }
            System.out.println("Your new total is: " + total);

            System.out.print("\nDo you want to play again?: ");
            String cont = keyboard.nextLine();
            if (cont.equals("Y") || cont.equals("y") || cont.equals("yes") || cont.equals("YES") || cont.equals("Yes")) {
                stop = false;
            } else {
                stop = true;
            }
        }
    }
}
\$\endgroup\$
  • 2
    \$\begingroup\$ The creed of the stackxchange network is teaching people to fish not giving away free (and IMO slightly smelly) fish. I dislike your answer because you didn't review OP's code but instead rewrote it .. \$\endgroup\$ – Vogel612 Nov 12 '14 at 22:58
  • \$\begingroup\$ Vogel612, I separated main points , and showed how code is going to look like , I didn't finish it completely, there is still some part of code that wasn't rewrote and to rewrite it creator should comprehend main ideas of logic. \$\endgroup\$ – Maksym Nov 12 '14 at 23:09
  • \$\begingroup\$ I agree it is still my old code he did not change it, showed me how the code is going to look @Vogel612 \$\endgroup\$ – daman Nov 13 '14 at 3:46
  • \$\begingroup\$ When the player has a balance of 0 how would I print out this: @Maksym Sadly, you are no longer eligible to play Your ending account balance is: $ 0.00 Your starting balance has decreased by $200.00 (-100%) Of the 10 games played, you have won 3 games (30.0 %) and lost 7 games (70.0 %) \$\endgroup\$ – daman Nov 19 '14 at 5:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.