/////////////////////////////// // File: ByteMe.java // // Programmer: Marc Douet // // Date: 06/28/02 // /////////////////////////////// import java.util.*; import java.io.*; ////////////////////////////////////////////////////////////////////////// // Class Name: ByteMe // // // // Description: Main driver class of the Binary Black Jack Simulator. // // It is responsible for reading in input, playing the // // game of binary black jack, and printing the results. // ////////////////////////////////////////////////////////////////////////// public class ByteMe { //////////////////// // Data Members // //////////////////// private Dealer cardDealer; // Dealer of the card game. private Players cardPlayers; // Players of the card game. private String tag; // String to read in the START/END tag. private String numPlayersString; // String to read in the number of players. private String dealerHand; // String to hold the dealer's hand. private String playerHands; // String to hold all of the player's hands. private String byteString; // String for all cards in the byte deck. private String nibbleString; // String for all cards in the nibble deck. private String line; // String to hold a line from input. private StringTokenizer tokenBuffer; // Tokenizer to grab each data element. private StringTokenizer byteDeck; // Tokenizer to grab each byte in the deck. private StringTokenizer nibbleDeck; // Tokenizer to grab each nibble in the deck. private BufferedReader in; // Buffered Reader to read in input. private int numPlayers; // The number of players in this game. private final int WIN_SCORE = 510; // The score needed to win the card game. private final int BYTE_LIMIT = 382; // The highest score to take a byte hit on. private final int NIBBLE_LIMIT = 500; // The highest score to take a nibble hit. private final int BYTE = 1; // Flag for taking a byte-hit. private final int NIBBLE = 0; // Flag for taking a nibble-hit. //////////////////////// // Member Functions // //////////////////////// ////////////////////////////////////////////////////////////////// // Function: Constructor // // // // Synopsis: public ByteMe(void) // // // // Description: Initializes all data members of the Binary // // Black Jack Simulator. // // // // Return Value: None. // ////////////////////////////////////////////////////////////////// public ByteMe() { // Initialize all data members. tag = null; numPlayersString = null; dealerHand = null; playerHands = null; byteString = null; nibbleString = null; line = null; tokenBuffer = null; byteDeck = null; nibbleDeck = null; numPlayers = 0; in = new BufferedReader(new InputStreamReader(System.in)); } ////////////////////////////////////////////////////////////////// // Function: start // // // // Synopsis: public void start(void) // // // // Description: Starts the simulation by reading the data set, // // dealing the cards to the dealer and players, // // playing the card game, and printing the outcome.// // // // Return Value: None. // ////////////////////////////////////////////////////////////////// public void start() { // Read in the first line of input. try { line = in.readLine(); tokenBuffer = new StringTokenizer(line); tag = tokenBuffer.nextToken(); } catch(IOException ioError) { System.out.println( "Error occurred while reading in the first line of input."); ioError.printStackTrace(); System.exit(1); } // While we have found a START tag... while((tag != null) && (tag.equals("START"))) { // Get the number of players from the token buffer. numPlayersString = tokenBuffer.nextToken(); // If there are too few, or too many players, print an error and exit 1. numPlayers = Integer.parseInt(numPlayersString); if(numPlayers < 1 || numPlayers > 10) { System.out.println("Error: " + numPlayers + " is an invalid number of players."); System.exit(1); } // Read in the dealer's hand. try { dealerHand = in.readLine(); } catch(IOException ioError) { System.out.println( "Error occurred while reading in the dealer's hand."); ioError.printStackTrace(); System.exit(1); } // Create a Dealer by populating his hand with the cards drawn. cardDealer = new Dealer(dealerHand); // Read in all of the players' hands. try { playerHands = in.readLine(); } catch(IOException ioError) { System.out.println( "Error occurred while reading in the players' hands."); ioError.printStackTrace(); System.exit(1); } // Create the Players by populating all of the players' hands with // the cards drawn. cardPlayers = new Players(playerHands, numPlayers); // Read in the byte deck (all byte cards availiable for hitting). try { byteString = in.readLine(); } catch(IOException ioError) { System.out.println( "Error occurred while reading in the byte deck."); ioError.printStackTrace(); System.exit(1); } // Read in the nibble deck (all nibble cards availiable for hitting). try { nibbleString = in.readLine(); } catch(IOException ioError) { System.out.println( "Error occurred while reading in the nibble deck."); ioError.printStackTrace(); System.exit(1); } // Read in the END Tag. try { tag = null; line = null; line = in.readLine(); tokenBuffer = new StringTokenizer(line); tag = tokenBuffer.nextToken(); } catch(IOException ioError) { System.out.println( "Error occurred while reading in the End Tag."); ioError.printStackTrace(); System.exit(1); } // IF the END Tag was not found, print an error and exit 1. if(!tag.equals("END")) { System.out.println( "Error: No END tag was found after the data set."); System.exit(1); } // Play the card game and print out the results. playGame(); // Try to read the next START Tag/Player Count pair. try { line = null; tag = null; line = in.readLine(); } catch(IOException ioError) { System.out.println( "Error occurred while reading in the start of the next data set."); ioError.printStackTrace(); System.exit(1); } // If we were able to read another data line, tokenize and get the tag. try { if(line.startsWith("START")) { tokenBuffer = new StringTokenizer(line); tag = tokenBuffer.nextToken(); } } catch(NullPointerException ptrError) {} // Re-initialize all strings used to read in input. numPlayersString = null; dealerHand = null; playerHands = null; byteString = null; nibbleString = null; } } ////////////////////////////////////////////////////////////////// // Function: playGame // // // // Synopsis: private void playGame(void) // // // // Desciption: Plays the card game, printing out the outcome. // // // // Return Value: None. // ////////////////////////////////////////////////////////////////// private void playGame() { String gameResult = null; // String to hold the final print message. // Initialize the tokenizers for the byte and nibble decks. byteDeck = new StringTokenizer(byteString); nibbleDeck = new StringTokenizer(nibbleString); // IF the byte deck doesn't have 4 cards, throw an error. if(byteDeck.countTokens() != 4) { System.out.println("Error: There are not 4 byte cards in the deck."); System.exit(1); } // IF the nibble deck doesn't have 4 cards, throw an error. if(nibbleDeck.countTokens() != 4) { System.out.println("Error: There are not 4 nibble cards in the deck."); System.exit(1); } // Print out the game label. System.out.println("HAND " + numPlayers); // WHILE the dealer has not won, the dealer is not out of hits, // the dealer's score is less than the best player's score, the // dealer's score is less than or equal to the nibble-hit limit, // and none of the other players have won... while((cardDealer.getScore() < WIN_SCORE) && (cardDealer.getRemainingHits() > 0) && (cardDealer.getScore() < cardPlayers.getBestScore()) && (cardDealer.getScore() <= NIBBLE_LIMIT) && (cardPlayers.getBestScore() <= WIN_SCORE)) { // IF the dealer's score is less than the byte-hit limit, take a // byte hit! if(cardDealer.getScore() < BYTE_LIMIT) { // Grab the next byte card, and get its size in bits. String byteCard = new String(byteDeck.nextToken()); int numBits = byteCard.length(); // Insure that the card is a byte. if(numBits != 8) { System.out.println( "Error: Found a byte card that consists of " + numBits + " bits."); System.exit(1); } // Add the byte card to the dealer's hand. cardDealer.addCardToHand(byteCard.toCharArray(), BYTE); // Print a message saying that we are taking a byte hit. System.out.println("Byte me!"); } // ELSE take a hit from the nibble deck! else { // Grab the next nibble card, and get its size in bits. String nibbleCard = new String(nibbleDeck.nextToken()); int numBits = nibbleCard.length(); // Insure that the card is a nibble if(numBits != 4) { System.out.println( "Error: Found a nibble card that consists of " + numBits + " bits."); System.exit(1); } // Add the nibble card to the dealer's hand. cardDealer.addCardToHand(nibbleCard.toCharArray(), NIBBLE); // Print a message saying that we are taking a nibble hit. System.out.println("Nibble me!"); } } // IF the dealer's score is greater than the win score, set the result // to 'Bust'. if(cardDealer.getScore() > WIN_SCORE) gameResult = "Bust!"; // IF the dealer's score is less than the best player's score, set the result // to 'Lose'. else if(cardDealer.getScore() < cardPlayers.getBestScore()) gameResult = "Lose!"; // ELSE the dealer has won the game, so set the result to 'Win'. else gameResult = "Win!"; // Print the final result. System.out.println(gameResult); } ////////////////////////////////////////////////////////////////// // Function: main // // // // Synopsis: public static void main(String args[]) // // args [IN] Not used. // // // // Description: The main driver of the Binary Black Jack Sim. // // Creates an instance of the simulator, and then // // starts it up. // // // // Return Value: None. // ////////////////////////////////////////////////////////////////// public static void main(String args[]) { // Create an instance of the Binary Black Jack Simulator. ByteMe binaryBlackJack = new ByteMe(); // Start the Binary Black Jack Simulator. binaryBlackJack.start(); // We are done, so exit without errors. System.exit(0); } } ////////////////////////////////////////////////////////////////////////// // Class Name: Dealer // // // // Description: Object to represent the state of the dealer. // ////////////////////////////////////////////////////////////////////////// class Dealer { //////////////////// // Data Members // //////////////////// private BinaryConverter cardConverter; // Converts the each of the dealer's cards. private int[][] dealerHand; // The value of each of the dealer's cards. private int numCards; // The number of cards the dealer has. private int remainingHits; // The number of times the dealer can hit. private int totalPoints; // The total integer value of the dealer's hand. private final int BYTE = 1; // Flag for when a byte hit is being performed. private final int NIBBLE = 0; // Flag for when a nibble hit is being performed. //////////////////////// // Member Functions // //////////////////////// ////////////////////////////////////////////////////////////////// // Function: Constructor // // // // Synopsis: public Dealer(String cardList) // // cardList [IN] List of 2 cards drawn. // // // // Description: Create an instance of the Dealer object by // // adding cards to the dealer's hand, determine // // the value of his hand, and initialize the number// // of remaining hits. // // // // Return Value: None. // ////////////////////////////////////////////////////////////////// public Dealer(String cardList) { // Initialize all data members. dealerHand = new int[6][8]; // Dealer can have up to 6 byte cards. cardConverter = new BinaryConverter(); // The converter used convert each card. totalPoints = 0; // The dealer's score starts at 0. remainingHits = 4; // The dealer is allowed 4 hits. numCards = 0; // The dealer starts out with 0 cards. // Tokenize the list of dealer cards. StringTokenizer dealerCards = new StringTokenizer(cardList); // IF the dealer wasn't dealt 2 cards, throw an error. if(dealerCards.countTokens() != 2) { System.out.println("Error: The dealer was not dealt 2 cards."); System.exit(1); } // Get the cards to be dealt to the dealer. String cardA = new String(dealerCards.nextToken()); String cardB = new String(dealerCards.nextToken()); // Ensure that the cards are bytes. if(cardA.length() != 8 || cardB.length() != 8) { System.out.println( "Error: The cards dealt to the dealer are the wrong size."); System.exit(1); } // Add two cards the has dealer drawn to his hand and update the point total. // Note: BYTE is passed as the hit type, since all cards will be bytes. addCardToHand(cardA.toCharArray(), BYTE); addCardToHand(cardB.toCharArray(), BYTE); } ////////////////////////////////////////////////////////////////// // Function: addCardToHand // // // // Synopsis: public void addCardToHand( // // char[] cardDrawn // // ,int hitType) // // // // cardDrawn [IN] Binary value of the card to add // // to the dealer's hand. // // hitType [IN] 1 if doing a byte hit and 0 if // // doing a nibble hit. // // // // Description: Add the card to the dealer's hand, and update // // the dealer's point total. // // // // Return Value: None. // ////////////////////////////////////////////////////////////////// public void addCardToHand(char[] cardDrawn, int hitType) { int bitCount; // Number of bits in the card. // IF the dealer has no more hits remaining, throw an error. if(remainingHits == 0 || numCards >= 6) { System.out.println("Error: Cannot hit, no remaining hits."); System.exit(1); } // Initalize the card bit count depending on the type of hit (byte or nibble). if(hitType == BYTE) bitCount = 8; else bitCount = 4; // Add the card drawn to the dealer's hand, converting the value to an integer. for(int cardIndex = 0; cardIndex < bitCount; cardIndex++) { dealerHand[numCards][cardIndex] = (cardDrawn[cardIndex] - 48); } // Increment the number of cards the dealer has in his hand. numCards++; // IF we are hitting, then decrement the number of hits remaining. if(numCards > 2) remainingHits--; // Use the card converter to update the dealer's point total. totalPoints += cardConverter.getCardValue(dealerHand[numCards-1], bitCount); } ////////////////////////////////////////////////////////////////// // Function: getRemainingHits // // // // Synopsis: public int getRemainingHits(void) // // // // Description: Get the number of hits the dealer has left. // // // // Return Value: Returns the number of hits the dealer has left.// ////////////////////////////////////////////////////////////////// public int getRemainingHits() { return remainingHits; } ////////////////////////////////////////////////////////////////// // Function: getScore // // // // Synopsis: public int getScore(void) // // // // Description: Get the dealer's total score. // // // // Return Value: Return the dealer's total score. // ////////////////////////////////////////////////////////////////// public int getScore() { return totalPoints; } } ////////////////////////////////////////////////////////////////////////// // Class Name: Players // // // // Description: Object to represent the state of all players. // ////////////////////////////////////////////////////////////////////////// class Players { //////////////////// // Data Members // //////////////////// private BinaryConverter cardConverter; // Converts of each of the player's cards. private int[][] playerHands; // List of cards each player has showing. private int[] hiddenCard; // Value of each player's hidden hand. private int playerCount; // Number of players in the game. private int bestPlayerScore; // The total value of the best player's hand. //////////////////////// // Member Functions // //////////////////////// ////////////////////////////////////////////////////////////////// // Function: Constructor // // // // Synopsis: public Players(String cardList, int numPlayers) // // cardList [IN] List of all cards dealt to all // // players. // // numPlayers [IN] The number of players playing in // // the game. // // // // Description: Create an instance of the Players object by // // setting the number of players in the game, // // adding all dealt cards to the list of player // // cards, determine the best score out of all of // // the players, and set the value of the hidden // // card (same for all players). // // // // Return Value: None. // ////////////////////////////////////////////////////////////////// public Players(String cardList, int numPlayers) { // Initialize all member variables. playerHands = new int[10][8]; // Can have up to 10 players. hiddenCard = new int[8]; // The hidden card is a byte card. cardConverter = new BinaryConverter(); // The converter used to convert cards. bestPlayerScore = 0; // The best starting score. playerCount = numPlayers; // Number of players in this game. // Tokenize the list of player cards. StringTokenizer playerCards = new StringTokenizer(cardList); // IF there isn't a card for every player, throw an error. if(playerCards.countTokens() != numPlayers) { System.out.println( "Error: There must be one card dealt to every player."); System.exit(1); } // Deal the cards out to all players, determining the best score along the way. for(int player = 0; player < playerCount; player++) { // Grad the card to be dealt to the player. String card = new String(playerCards.nextToken()); // Ensure that the card is a byte. if(card.length() != 8) { System.out.println( "Error: The cards dealt to the players are the wrong size."); System.exit(1); } // Add the card to the player's hand. addCardToHand(card.toCharArray(), player); } // Set the value of the hidden card (a byte consisting of all 1's). for(int cardIndex = 0; cardIndex < 8; cardIndex++) hiddenCard[cardIndex] = 1; } ////////////////////////////////////////////////////////////////// // Function: addCardToHand // // // // Synopsis: private void addCardToHand( // // char[] cardDealt // // ,int playerId) // // cardDealt [IN] Binary value of the card to add // // to the player's hand. // // playerId [IN] ID of the player we are giving a // // card to. // // // // Description: Add the card to the player's hand, update that // // player's score, and determine whether that score// // is the best seen so far, and update the best // // score total accordingly. // // // // Return Value: None. // ////////////////////////////////////////////////////////////////// private void addCardToHand(char[] cardDealt, int playerId) { // IF there are more players than expected, throw an exception. if((playerId+1) > playerCount) { System.out.println("Error, this is an invalid player ID."); System.exit(1); } // Add the card to the player's hand, converting it to an integer. for(int cardIndex = 0; cardIndex < 8; cardIndex++) { playerHands[playerId][cardIndex] = (cardDealt[cardIndex] - 48); // Get that player's score, passing 8 since this is a byte card. int playerScore = cardConverter.getCardValue(playerHands[playerId], 8); // IF this is the best score so far, update the best score total. if(playerScore > bestPlayerScore) bestPlayerScore = playerScore; } } ////////////////////////////////////////////////////////////////// // Function: getBestScore // // // // Synopsis: public int getBestScore(void) // // // // Description: Get the highest score amoung all of the players,// // which is the score the dealer has to beat. // // // // Return Value: Returns the value of the showing card of the // // best player + the value of the hidden card. // ////////////////////////////////////////////////////////////////// public int getBestScore() { return bestPlayerScore + cardConverter.getCardValue(hiddenCard, 8); } } ////////////////////////////////////////////////////////////////////////// // Class Name: BinaryConverter // // // // Description: Object to perform binary to integer conversions on // // integer strings which represent binary numbers. This // // is not really an object in the sense that it doesn't // // have any state variables, but was created as a seperate// // class to avoid having to define binary to integer // // conversion functions in both the Dealer and Players // // classes. // ////////////////////////////////////////////////////////////////////////// class BinaryConverter { //////////////////////// // Member Functions // //////////////////////// ////////////////////////////////////////////////////////////////// // Function: Constructor // // // // Synopsis: public BinaryConverter(void) // // // // Description: Creates an instance of the BinaryConverter // // object. Nothing to do here since there are no // // member variables to initialize. // // // // Return Value: None. // ////////////////////////////////////////////////////////////////// public BinaryConverter() { } ////////////////////////////////////////////////////////////////// // Function: binaryToInt // // // // Synopsis: private int binaryToInt( // // int[] binaryNum // // ,int numBits) // // // // binaryNum [IN] This is the integer string that // // represents the binary number to // // be converted. // // numBits [IN] The number of bits in the binary // // string. // // // // Description: Convert the binary number to its integer equiv. // // by stepping through the integer array and using // // the position of each integer element in the // // array to calculate the integer equivalent. The // // following formula is applied to each element in // // the integer array: // // 1) IF the integer is a 0, do nothing. // // 2) IF the integer is 1, use: // // value += (2^((numBits-1)-position)) // // // // Return Value: Return the integer equivalent of the binary // // number. // ////////////////////////////////////////////////////////////////// private int binaryToInt(int[] binaryNum, int numBits) { int intValue = 0; // Integer equivalent of the binary number. // Cumulatively calculate the integer value using each element in the array. for(int index = 0; index < numBits; index++) { // While we're here, let's check the integrity of the data... if((binaryNum[index] != 0) && (binaryNum[index] != 1)) { System.out.println( "Error: The card data must only consist of 0's and 1's."); System.exit(1); } // If we've found a 1, accumulate the total for that position. if(binaryNum[index] == 1) intValue += (int)Math.pow(2.0, (double)((numBits-1)-index)); } // Return the integer equivalent of the binary number. return intValue; } ////////////////////////////////////////////////////////////////// // Function: getCardValue // // // // Synopsis: public int getCardValue(int[] card, int numBits) // // // // card [IN] This is the integer string that // // represents the binary value of the // // card to be converted. // // numBits [IN] The number of bits in the binary // // to be converted. // // // // Description: Call binaryToInt() to get the integer value of // // the binary representation of the card and return// // that value to the caller. // // // // Return Value: Return the integer equivalent of the binary // // representation of the card. // ////////////////////////////////////////////////////////////////// public int getCardValue(int[] card, int numBits) { return binaryToInt(card, numBits); } }