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
Blackjack
methodcanHit
returnsfalse
. This can happen before the first hit or after any hit. - The player chooses to stand. This can only happen after the player is asked 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:
- A valid bet is placed and the initial hands are dealt.
- The player plays their hand.
- The dealer plays their hand.
- The result is displayed.
- Bets are resolved adn the game is reset.
- The player’s money is displayed.
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
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");
}
}