5
\$\begingroup\$

I am doing exercises for the Art and Science of Java textbook. I had an exercise that required me to program the simulation of flipping a coin until 3 consecutive "Heads" result appeared.

I did it, but I'm not sure if my code is simple enough - since I used an instance variable to count the heads and a function that not only flips the coins but counts the consecutive heads as well.

I avoided placing too much lines in the run method (the main method since im using acm libraries) but i'm not sure if this is the most efficient coding.

Here's the code:

import acm.program.*;
import acm.util.*;

public class ConsecutiveHeads  extends ConsoleProgram {

    private static final int CONSECUTIVE_HEADS_LIM = 3;

    public void run () {
        int nFlips = 0;
        while (nConsecutiveHeads < CONSECUTIVE_HEADS_LIM) {
            flipCoin();
            nFlips++;
        }
        println ("You needed " + nFlips + " flips to get " + CONSECUTIVE_HEADS_LIM + " Consecutive Heads.");
    }

    private void flipCoin () {
        String coinFace = rgen.nextBoolean() ? "Heads" : "Tails";
        if (coinFace.equals("Heads")) {
            nConsecutiveHeads++;
        } else if (coinFace.equals("Tails")){
            nConsecutiveHeads = 0;
        }
        println (coinFace);
    }

    private int nConsecutiveHeads = 0;

    private RandomGenerator rgen = RandomGenerator.getInstance();
}
\$\endgroup\$

2 Answers 2

7
\$\begingroup\$

The one thing that really stands out to me: you don't need to convert the boolean result of nextBoolean() to a human-readable string, then test the string. Just test the result directly, then convert it to a human-readable string.

private void flipCoin () {
    boolean result = rgen.nextBoolean();
    String coinFace;
    if (result) {
        nConsecutiveHeads++;
        coinFace = "Heads";
    } else {
        nConsecutiveHeads = 0;
        coinFace = "Tails";
    }
    System.out.println(coinFace);
}

Although you can do this for brevity:

private void flipCoin () {
    boolean result = rgen.nextBoolean();
    nConsecutiveHeads = result ? nConsecutiveHeads + 1 : 0;
    System.out.println(result ? "Heads" : "Tails");
}

You should probably get into the habit of using format strings rather than string concatenation. It's generally better practice.

This next bit is more a matter of style, but if you don't need to keep nConsecutiveHeads as a state variable, I wouldn't keep it as a class member. You can define it as a variable within the run method and pass it in to flipCoin instead, like this:

public void run () {
    int nFlips = 0;
    int nHeads = 0;
    while (nHeads < CONSECUTIVE_HEADS_LIM) {
        nHeads = flipCoin(nHeads);
        nFlips++;
    }

    System.out.printf("You needed %d flips to get %d consecutive heads.", nFlips, nHeads);
}

private int flipCoin (int nHeads) {
    boolean result = rgen.nextBoolean();
    System.out.println(result ? "Heads" : "Tails");
    return result ? nHeads + 1 : 0;
}
\$\endgroup\$
1
  • \$\begingroup\$ Uau! I wasn't aware you could insert boolean results with a ? : conditional statement inside the println itself! That was a eye opener! Indeed that code like this is simpler and more clear! Ty, I learned something nice today! ps. - I still didn't learn how to format Strings - i'm doing the Stanford University Course, that's why I only use Concatenation yet. Im sure to use it once I learn more :) \$\endgroup\$ Commented Apr 14, 2013 at 11:18
5
\$\begingroup\$

Building upon @p.s.w.g's excellent answer, you can similarly use the ? : conditional operator within the while-loop's condition to manipulate the value of nHeads.

public class HavingFun {
    private static final int CONSECUTIVE_HEADS_LIM = 3;
    private static java.util.Random rgen = new Random(System.currentTimeMillis());
    public static void main(String[] args) {
        int nFlips = 1;
        int nHeads = 0;
        while ((nHeads = (rgen.nextBoolean() ? ++nHeads : 0)) < CONSECUTIVE_HEADS_LIM) {
            System.out.println(nHeads == 0 ? "Tails" : "Heads");
            nFlips++;
        }
        System.out.printf("Heads\nYou needed %d flips to get %d consecutive heads.",
                            nFlips, nHeads);
    }
}

The condition reads: if rgen.nextBoolean is true, increment nHeads and use the new value (hence the ++ preceding the variable, otherwise known as a pre-increment operator) for nHeads, else set nHeads to 0. Within the loop, we check if nHeads was reset to 0 or not, printing Tails or Heads accordingly.

You can also see that the loop exits upon the last throw of Heads, and that explains why the final output statement has an extra Heads line. For the same reason, nFlips is initialized as 1, since it will need to be incremented at the end anyways.

I think @p.s.w.g's answer is a good start for introductory Java programming, whereas my solution is just a shorter solution, not necessary better (given the extra explanations above). Hope you'll have a better idea of nifty tips-and-tricks in Java programming!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ +1 for the insight that your code is harder to read :) I think in general if there are no other requirements you should always optimize for readability and nothing else. Any further optimization should be addressed if you run in trouble with your current code. \$\endgroup\$ Commented Apr 15, 2013 at 12:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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