This page is part of the Blackjack Lab.

BlackjackUI handles interaction with the user. The game mechanics are handled by Blackjack. See Object oriented UI.

Start files:

Solutions are at the bottom of this page.

Testing BlackjackUI

Incremental testing

The only public methods in BlackjackUI are playHand and playHandsUntilQuit. Write a test class with a main method. Make an object of type BlackjackUI. Run playHand. As you complete the private methods, add appropriate calls to playHand.

This process is necessary because most of the methods can’t be tested in isolation. For example, playPlayersHand can only be tested after a valid bet has been placed and the cards dealt.

Using the real Shoe or TestShoe is up to you. If you use the TestShoe, you must specify the cards just like the other tests. The real Shoe will deal random cards.

See the Hints section below for incremental tests of getValidBet and playPlayersHand.

Manually testing the finished class

BlackjackUIManualTester provides code to manually test the completed BlackjacUI class (excluding the playHandsUntilQuit method) with various scenarios.

The existing test code requires manually entering input and manually checking the results. Comments in the test code indicate how each hand is supposed to be played and the expected result. See BlackjackUIManualTester expected interaction.

As with the other tests, the code depends on TestShoe to use predetermined cards.

Automated testing of the finished class

BlackjackUIAutomatedTester redirects the standard input (System.in) and the standard output (System.out) to allow predetermined input and check against expected output.

BlackjackUIAutomatedTester tests the same hands as BlackjackUIManualTester; however, it does so one at a time.

Passing the automated test requires precision. The output must exactly match the expected output, including spacing and line breaks. The input must be accepted exactly as specified, including line breaks.

The code depends on TestShoe.

Note: The expected output is currently harded for US dollars.

Provided code

Constant and instance variables

The constant STARTING_MONEY is self explanatory. It is used when constructing a Backjack object.

The Blackjack class is responsible for the game mechanics. BlackjackUI stores a reference to a Blackjack object.

BlackjackUI stores a reference to a Scanner to accept input from the user.

The NumberFormat object is used to produce formatted output of currency. When run on a computer configured to use US dollars, the format includes a dollar sign ($) followed by the amount rounded to 2 decimal places.

stringToNumber method

stringToNumber accepts input as a String and attempts to convert it to a double. If the conversion succeeds, the method returns the double value. If the conversion fails, the method returns -1.

This method allows easy handling of non-numeric invalid input.

Code to be written

BlackjackUI constructor

bj and fromKeyboard should be initialized normally.

NumberFormat objects are not constructed using the new keyword. The static method getCurrencyInstance is used to obtain a NumberFormat object. See the NumberFormat Java API. Also see Floating point roundoff error.

getValidBet method

getValidBet accepts and returns a valid bet. It does not attempt to actually place the bet. See Input validation as String.

playPlayersHand method

The player can hit (take another card) until the Blackjack class says they can’t or they choose to stand.

The dealer’s first card and the player’s entire hand should be displayed before each prompt to hit. If the player cannot hit initially (ex: the dealer has Blackjack) nothing is displayed.

displayResult method

displayResult displays the player’s and dealer’s complete hands and the game result ("push", "blackjack", "win", or "loss").

playHand method

playHand runs a combination of Blackjack and BlackjackUI methods to conduct the game.

playHandsUntilQuit method

playHandsUntilQuit plays at least 1 hand. After each hand, the user is asked if they want to play again. If the user wants to play again, the process repeats.

Hints

Incremental testing

Create a main method in a different class. Make an object of type BlackjackUI. Run playHand.

public static void main(String[] args)
{
    BlackjackUI bjui = new BlackjackUI();
    bjui.playHand();
}

As mentioned above, pay attention whether Blackjack is using Shoe or TestShoe. Using TestShoe requires specifying the cards. Using Shoe will result in random cards.

Testing getValidBet

To test getValidBet, temporarily implement playHand as:

public void playHand()
{
    System.out.println("Bet: " + getValidBet());
}

Invalid bets should be rejected with an error message and a new bet should be requested. This should happen as many times as necessary. A valid bet should be accepted and the amount printed.

Example interaction:

Bet: $-5
Invalid bet
Bet: $hamster
Invalid bet
Bet: $10
Bet: 10.0

Testing playPlayersHand

To test playPlayersHand, temporarily implement playHand as:

public void playHand()
{
    bj.placeBetAndDealCards(getValidBet());
    playPlayersHand();
    System.out.println(bj.getPlayersHand());
}

Run the test until you are able to hit at least once.

Note that if the player and/or the dealer have Blackjack, this test will not work well. Simply run it again.

Example interaction:

Bet: $10

Dealer: 4D
Player: 4S 7H (11)
Hit? (y/n): y

Dealer: 4D
Player: 4S 7H 8C (19)
Hit? (y/n): n
4S 7H 8C (19)

playPlayersHand method

Depending on the player’s and dealer’s hands, the player may be able to hit 0, 1, or more times. This method requires a loop that runs if it is possible for the player to hit.

Each time the loop runs, the player is asked if they want to hit. If the player wants to hit, the Blackjack method hit is run.

There are 2 conditions that prevent the player from being able to hit:

The determination of whether the player can potentially hit must be made before the first run of the loop and after each run.

playHand method

A Blackjack hand is played as follows:

This requires calling a combination of methods from Blackjack and BlackjackUI. playPlayersHand is a BlackjackUI method because it requires interaction with the user. playDealersHand is a Blackjack method because it does not require user interaction.

playHand should not call playHandsUntilQuit.

playHandsUntilQuit method

playHandsUntilQuit is responsible for allowing the user to play again. This is easily accomplished with a simple while loop based on the player’s response to "Play again (y/n): ".

playHandsUntilQuit calls playHand once for each hand the player wants to play. It is reasonable to assume that the player wants to play at least 1 hand (and does not need to be asked before the first hand if they want to play).

Solution code

BlackjackUI.java

The tested requires that Blackjack use TestShoe.

BlackjackUI constructor

public BlackjackUI()
{
    bj = new Blackjack(STARTING_MONEY);
    fromKeyboard = new Scanner(System.in);
    nf = NumberFormat.getCurrencyInstance();
}

getValidBet method

private double getValidBet()
{
    System.out.println();
    System.out.print("Bet: $");

    String input = fromKeyboard.nextLine();
    double bet = stringToNumber(input);
    
    while(bet <= 0)
    {
        System.out.println("Invalid bet");
        System.out.print("Bet: $");

        input = fromKeyboard.nextLine();
        bet = stringToNumber(input);
    }

    return bet;
}

playPlayersHand method

private void playPlayersHand()
{
    boolean canHitAgain = bj.canHit();
    
    while(canHitAgain)
    {
        System.out.println();
        System.out.println("Dealer: " + bj.getDealersHand().getCards().get(0));
        System.out.println("Player: " + bj.getPlayersHand());
       
        System.out.print("Hit? (y/n): ");
        boolean hit = fromKeyboard.nextLine().toLowerCase().startsWith("y");
        
        if(hit)
            bj.hit();

        canHitAgain = bj.canHit() && hit;
    }
}

displayResult method

private void displayResult()
{
    System.out.println();
    
    System.out.println("Dealer: " + bj.getDealersHand());
    System.out.println("Player: " + bj.getPlayersHand());
    System.out.print("Result: ");
    
    int result = bj.getResult();
    
    if(result == Blackjack.PUSH_RESULT)
        System.out.println("push");
    else if(result == Blackjack.WIN_BJ_RESULT)
        System.out.println("blackjack");
    else if(result == Blackjack.WIN_RESULT)
        System.out.println("win");
    else
        System.out.println("loss");
}

playHand method

public void playHand()
{
    bj.placeBetAndDealCards(getValidBet());
    playPlayersHand();
    bj.playDealersHand();
    displayResult();
    bj.resolveBetsAndReset();
    System.out.println("\nMoney: " + nf.format(bj.getPlayersMoney()));
}

playHandsUntilQuit method

public void playHandsUntilQuit()
{
    System.out.println("Money: " + nf.format(bj.getPlayersMoney()));
    boolean playAgain = true;
    while(playAgain)
    {
        playHand();

        System.out.print("Play again (y/n): ");
        playAgain = fromKeyboard.nextLine().toLowerCase().startsWith("y");
    }
}

Comments

Comment on Blackjack Lab