diff --git a/.claude/worktrees/zealous-curran b/.claude/worktrees/zealous-curran new file mode 160000 index 0000000..e6982a5 --- /dev/null +++ b/.claude/worktrees/zealous-curran @@ -0,0 +1 @@ +Subproject commit e6982a57eba077af5065b606bd6c000206a9b558 diff --git a/docs/readme-uml.md b/docs/readme-uml.md new file mode 100644 index 0000000..6bf28d6 --- /dev/null +++ b/docs/readme-uml.md @@ -0,0 +1,87 @@ +# GroupCasino UML (README-Aligned) + +```mermaid +classDiagram +direction LR + +class MainApplication { + +main(args String[]) void +} + +class Casino { + -console IOConsole + +run() void + -getArcadeDashboardInput() String + -getGameSelectionInput() String + -play(gameObject Object, playerObject Object) void +} + +class CasinoAccountManager { + +getAccount(accountName String, accountPassword String) CasinoAccount + +createAccount(accountName String, accountPassword String) CasinoAccount + +registerAccount(casinoAccount CasinoAccount) void +} + +class CasinoAccount { + -username String + -password String + -balance double + +depositToBalance(amount double) void + +withdrawBalance(amount double) void + +getUsername() String + +getPassword() String + +getBalance() double +} + +class GameInterface { + <> + +add(player PlayerInterface) void + +remove(player PlayerInterface) void + +run() void +} + +class PlayerInterface { + <> + +getArcadeAccount() CasinoAccount + +play() SomeReturnType +} + +class SlotsGame +class NumberGuessGame +class BlackjackGame + +class SlotsPlayer +class NumberGuessPlayer +class BlackjackPlayer + +class Deck +class Card +class BlackjackHand + +MainApplication --> Casino : starts +Casino --> CasinoAccountManager : uses +Casino --> GameInterface : runs selected game +CasinoAccountManager --> CasinoAccount : creates/gets + +GameInterface <|.. SlotsGame +GameInterface <|.. NumberGuessGame +GameInterface <|.. BlackjackGame + +PlayerInterface <|.. SlotsPlayer +PlayerInterface <|.. NumberGuessPlayer +PlayerInterface <|.. BlackjackPlayer + +SlotsGame o--> SlotsPlayer : add/remove players +NumberGuessGame o--> NumberGuessPlayer : add/remove players +BlackjackGame o--> BlackjackPlayer : add/remove players + +SlotsPlayer --> CasinoAccount : has account +NumberGuessPlayer --> CasinoAccount : has account +BlackjackPlayer --> CasinoAccount : has account + +BlackjackGame --> Deck : uses +BlackjackGame --> BlackjackHand : dealer hand +BlackjackPlayer --> BlackjackHand : player hand +Deck o--> Card : contains +BlackjackHand o--> Card : contains +``` diff --git a/src/main/java/com/github/zipcodewilmington/Casino.java b/src/main/java/com/github/zipcodewilmington/Casino.java index 5eae9ac..961f24a 100644 --- a/src/main/java/com/github/zipcodewilmington/Casino.java +++ b/src/main/java/com/github/zipcodewilmington/Casino.java @@ -1,79 +1,152 @@ -package com.github.zipcodewilmington; - -import com.github.zipcodewilmington.casino.CasinoAccount; -import com.github.zipcodewilmington.casino.CasinoAccountManager; -import com.github.zipcodewilmington.casino.GameInterface; -import com.github.zipcodewilmington.casino.PlayerInterface; -import com.github.zipcodewilmington.casino.games.numberguess.NumberGuessGame; -import com.github.zipcodewilmington.casino.games.numberguess.NumberGuessPlayer; -import com.github.zipcodewilmington.casino.games.slots.SlotsGame; -import com.github.zipcodewilmington.casino.games.slots.SlotsPlayer; -import com.github.zipcodewilmington.utils.AnsiColor; -import com.github.zipcodewilmington.utils.IOConsole; - -/** - * Created by leon on 7/21/2020. - */ -public class Casino implements Runnable { - private final IOConsole console = new IOConsole(AnsiColor.BLUE); - - @Override - public void run() { - String arcadeDashBoardInput; - CasinoAccountManager casinoAccountManager = new CasinoAccountManager(); - do { - arcadeDashBoardInput = getArcadeDashboardInput(); - if ("select-game".equals(arcadeDashBoardInput)) { - String accountName = console.getStringInput("Enter your account name:"); - String accountPassword = console.getStringInput("Enter your account password:"); - CasinoAccount casinoAccount = casinoAccountManager.getAccount(accountName, accountPassword); - boolean isValidLogin = casinoAccount != null; - if (isValidLogin) { - String gameSelectionInput = getGameSelectionInput().toUpperCase(); - if (gameSelectionInput.equals("SLOTS")) { - play(new SlotsGame(), new SlotsPlayer()); - } else if (gameSelectionInput.equals("NUMBERGUESS")) { - play(new NumberGuessGame(), new NumberGuessPlayer()); - } else { - // TODO - implement better exception handling - String errorMessage = "[ %s ] is an invalid game selection"; - throw new RuntimeException(String.format(errorMessage, gameSelectionInput)); - } - } else { - // TODO - implement better exception handling - String errorMessage = "No account found with name of [ %s ] and password of [ %s ]"; - throw new RuntimeException(String.format(errorMessage, accountPassword, accountName)); - } - } else if ("create-account".equals(arcadeDashBoardInput)) { - console.println("Welcome to the account-creation screen."); - String accountName = console.getStringInput("Enter your account name:"); - String accountPassword = console.getStringInput("Enter your account password:"); - CasinoAccount newAccount = casinoAccountManager.createAccount(accountName, accountPassword); - casinoAccountManager.registerAccount(newAccount); - } - } while (!"logout".equals(arcadeDashBoardInput)); - } - - private String getArcadeDashboardInput() { - return console.getStringInput(new StringBuilder() - .append("Welcome to the Arcade Dashboard!") - .append("\nFrom here, you can select any of the following options:") - .append("\n\t[ create-account ], [ select-game ]") - .toString()); - } - - private String getGameSelectionInput() { - return console.getStringInput(new StringBuilder() - .append("Welcome to the Game Selection Dashboard!") - .append("\nFrom here, you can select any of the following options:") - .append("\n\t[ SLOTS ], [ NUMBERGUESS ]") - .toString()); - } - - private void play(Object gameObject, Object playerObject) { - GameInterface game = (GameInterface)gameObject; - PlayerInterface player = (PlayerInterface)playerObject; - game.add(player); - game.run(); - } -} +package com.github.zipcodewilmington; + +import com.github.zipcodewilmington.casino.CasinoAccount; +import com.github.zipcodewilmington.casino.CasinoAccountManager; +import com.github.zipcodewilmington.casino.GameInterface; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.casino.games.blackjack.BlackjackGame; +import com.github.zipcodewilmington.casino.games.blackjack.BlackjackPlayer; +import com.github.zipcodewilmington.casino.games.craps.CrapsGame; +import com.github.zipcodewilmington.casino.games.craps.CrapsPlayer; +import com.github.zipcodewilmington.casino.games.hangman.HangmanGame; +import com.github.zipcodewilmington.casino.games.hangman.HangmanPlayer; +import com.github.zipcodewilmington.casino.games.numberguess.NumberGuessGame; +import com.github.zipcodewilmington.casino.games.numberguess.NumberGuessPlayer; +import com.github.zipcodewilmington.casino.games.roulette.RouletteGame; +import com.github.zipcodewilmington.casino.games.roulette.RoulettePlayer; +import com.github.zipcodewilmington.casino.games.slots.SlotsGame; +import com.github.zipcodewilmington.casino.games.slots.SlotsPlayer; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +/** + * Created by leon on 7/21/2020. + */ +public class Casino implements Runnable { + private final IOConsole console = new IOConsole(AnsiColor.BLUE); + + @Override + public void run() { + String arcadeDashBoardInput; + CasinoAccountManager casinoAccountManager = new CasinoAccountManager(); + do { + arcadeDashBoardInput = getArcadeDashboardInput(); + if ("select-game".equals(arcadeDashBoardInput)) { + String accountName = console.getStringInput("Enter your account name:"); + String accountPassword = console.getStringInput("Enter your account password:"); + CasinoAccount casinoAccount = casinoAccountManager.getAccount(accountName, accountPassword); + boolean isValidLogin = casinoAccount != null; + if (isValidLogin) { + String gameSelectionInput; + do { + gameSelectionInput = getGameSelectionInput().toUpperCase(); + if (gameSelectionInput.equals("SLOTS")) { + play(new SlotsGame(), new SlotsPlayer(casinoAccount.getUsername(), casinoAccount)); + } else if (gameSelectionInput.equals("NUMBERGUESS")) { + play(new NumberGuessGame(), new NumberGuessPlayer(casinoAccount)); + } else if (gameSelectionInput.equals("BLACKJACK")) { + play(new BlackjackGame(), new BlackjackPlayer(casinoAccount)); + } else if (gameSelectionInput.equals("CRAPS")) { + play(new CrapsGame(), new CrapsPlayer(casinoAccount)); + } else if (gameSelectionInput.equals("ROULETTE")) { + play(new RouletteGame(), new RoulettePlayer(casinoAccount)); + } else if (gameSelectionInput.equals("HANGMAN")) { + play(new HangmanGame(), new HangmanPlayer(casinoAccount.getUsername(), casinoAccount)); + } else if (!gameSelectionInput.equals("BACK")) { + console.println("[ %s ] is an invalid game selection", gameSelectionInput); + } + } while (!gameSelectionInput.equals("BACK") && casinoAccount.getBalance() > 0); + + if (casinoAccount.getBalance() <= 0) { + console.println( + "\n" + + " _____ _____ _____ _ _ \n" + + " |_ _||_ _|| ___|| \\ | |\n" + + " | | | | | |_ | \\| |\n" + + " | | | | | _| | |\\ |\n" + + " |_| |_| |_| |_| \\_|\n" + + "\n" + + " __ __ ___ _ _ _ ____ _____ \n" + + " \\ \\/ / / _ \\| | | | / \\ | _ \\| ____|\n" + + " \\ / | | | | | | | / _ \\| |_) | _| \n" + + " / / | |_| | |_| | / ___ \\| _ <| |___\n" + + " /_/ \\___/ \\___/ /_/ \\_\\_| \\_\\_____|\n" + + "\n" + + " ____ ____ ___ _ __ _____\n" + + " | __ )| _ \\/ _ \\| |/ /| ____|\n" + + " | _ \\| |_) | | | | ' /| _| \n" + + " | |_) | _ <| |_| | . \\| |___\n" + + " |____/|_| \\_\\\\___/|_|\\_\\|_____|\n" + + "\n" + + " +------------------------------------------+\n" + + " | Ta Ta For Now... and your money too! |\n" + + " | The house ALWAYS wins! >:) |\n" + + " +------------------------------------------+\n" + ); + } + } else { + console.println("No account found with name of [ %s ] and password of [ %s ]", accountName, accountPassword); + } + } else if ("create-account".equals(arcadeDashBoardInput)) { + console.println("Welcome to the account-creation screen."); + String accountName = console.getStringInput("Enter your account name:"); + String accountPassword = console.getStringInput("Enter your account password:"); + CasinoAccount newAccount = casinoAccountManager.createAccount(accountName, accountPassword); + casinoAccountManager.registerAccount(newAccount); + console.println("Account created! You have been given $500.00 to start."); + } else if ("manage-account".equals(arcadeDashBoardInput)) { + String accountName = console.getStringInput("Enter your account name:"); + String accountPassword = console.getStringInput("Enter your account password:"); + CasinoAccount casinoAccount = casinoAccountManager.getAccount(accountName, accountPassword); + if (casinoAccount != null) { + manageAccount(casinoAccount); + } else { + String errorMessage = "No account found with name of [ %s ] and password of [ %s ]"; + console.println(String.format(errorMessage, accountName, accountPassword)); + } + } + } while (!"logout".equals(arcadeDashBoardInput)); + } + + private String getArcadeDashboardInput() { + return console.getStringInput(new StringBuilder() + .append("Welcome to the Arcade Dashboard!") + .append("\nFrom here, you can select any of the following options:") + .append("\n\t[ create-account ], [ select-game ], [ manage-account ], [ logout ]") + .toString()); + } + + private String getGameSelectionInput() { + return console.getStringInput(new StringBuilder() + .append("Welcome to the Game Selection Dashboard!") + .append("\nFrom here, you can select any of the following options:") + .append("\n\t[ SLOTS ], [ NUMBERGUESS ], [ BLACKJACK ], [ CRAPS ], [ ROULETTE ], [ HANGMAN ], [ BACK ]") + .toString()); + } + + private void manageAccount(CasinoAccount account) { + String input; + do { + input = console.getStringInput(new StringBuilder() + .append("\n=== Account: " + account.getUsername() + " ===") + .append("\n\t[ view ], [ deposit ], [ withdraw ], [ back ]") + .toString()); + + if ("view".equals(input)) { + console.println("Username: %s", account.getUsername()); + account.displayBalance(); + } else if ("deposit".equals(input)) { + double amount = console.getDoubleInput("Enter deposit amount: $"); + account.depositToBalance(amount); + } else if ("withdraw".equals(input)) { + double amount = console.getDoubleInput("Enter withdrawal amount: $"); + account.withdrawBalance(amount); + } + } while (!"back".equals(input)); + } + + private void play(GameInterface game, PlayerInterface player) { + game.add(player); + game.run(); + } +} diff --git a/src/main/java/com/github/zipcodewilmington/MainApplication.java b/src/main/java/com/github/zipcodewilmington/MainApplication.java index 508787a..5c69778 100644 --- a/src/main/java/com/github/zipcodewilmington/MainApplication.java +++ b/src/main/java/com/github/zipcodewilmington/MainApplication.java @@ -4,4 +4,4 @@ public class MainApplication { public static void main(String[] args) { new Casino().run(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/github/zipcodewilmington/casino/CasinoAccount.java b/src/main/java/com/github/zipcodewilmington/casino/CasinoAccount.java index 654c749..875fade 100644 --- a/src/main/java/com/github/zipcodewilmington/casino/CasinoAccount.java +++ b/src/main/java/com/github/zipcodewilmington/casino/CasinoAccount.java @@ -1,9 +1,80 @@ package com.github.zipcodewilmington.casino; +import java.util.ArrayList; +import java.util.List; + /** * Created by leon on 7/21/2020. * `ArcadeAccount` is registered for each user of the `Arcade`. * The `ArcadeAccount` is used to log into the system to select a `Game` to play. */ public class CasinoAccount { + private String username; + private String password; + private double balance; + private final List accounts = new ArrayList<>(); + + public CasinoAccount(String username, String password) { + this.username = username; + this.password = password; + this.balance = 0.0; + } + + public void displayBalance() { + System.out.printf("Current balance: $%.2f%n", balance); + } + + public void depositToBalance(double amount) { + if (amount <= 0) { + System.out.println("Deposit amount must be greater than zero."); + return; + } + balance += amount; + System.out.printf("Deposited $%.2f. New balance: $%.2f%n", amount, balance); + } + + public void withdrawBalance(double amount) { + if (amount <= 0) { + System.out.println("Withdrawal amount must be greater than zero."); + return; + } + if (amount > balance) { + System.out.println("Insufficient funds."); + return; + } + balance -= amount; + System.out.printf("Withdrew $%.2f. New balance: $%.2f%n", amount, balance); + } + + public String getUsername() { return username; } + public String getPassword() { return password; } + public double getBalance() { return balance; } + + public CasinoAccount createAccount(String accountName, String accountPassword) { + System.out.printf("Creating account for [ %s ]...%n", accountName); + CasinoAccount newAccount = new CasinoAccount(accountName, accountPassword); + System.out.printf("Account created for [ %s ].%n", accountName); + return newAccount; + } + + public void registerAccount(CasinoAccount casinoAccount) { + System.out.printf("Registering account [ %s ]...%n", + casinoAccount.getUsername()); + accounts.add(casinoAccount); + System.out.printf("Account [ %s ] registered successfully.%n", + casinoAccount.getUsername()); + } + + public CasinoAccount getAccount(String accountName, String accountPassword) { + System.out.printf("Looking up account for [ %s ]...%n", accountName); + for (CasinoAccount account : accounts) { + if (account.getUsername().equals(accountName) && + account.getPassword().equals(accountPassword)) { + System.out.printf("Account found for [ %s ].%n", accountName); + return account; + } + } + System.out.printf("No account found for [ %s ].%n", accountName); + return null; + } } diff --git a/src/main/java/com/github/zipcodewilmington/casino/CasinoAccountManager.java b/src/main/java/com/github/zipcodewilmington/casino/CasinoAccountManager.java index 2d09ec2..57570b5 100644 --- a/src/main/java/com/github/zipcodewilmington/casino/CasinoAccountManager.java +++ b/src/main/java/com/github/zipcodewilmington/casino/CasinoAccountManager.java @@ -1,46 +1,50 @@ package com.github.zipcodewilmington.casino; +import java.util.ArrayList; +import java.util.List; + /** * Created by leon on 7/21/2020. - * `ArcadeAccountManager` stores, manages, and retrieves `ArcadeAccount` objects - * it is advised that every instruction in this class is logged + * `CasinoAccountManager` stores, manages, and retrieves `CasinoAccount` objects. */ public class CasinoAccountManager { + + private final List accounts = new ArrayList<>(); + /** * @param accountName name of account to be returned * @param accountPassword password of account to be returned - * @return `ArcadeAccount` with specified `accountName` and `accountPassword` + * @return `CasinoAccount` with specified `accountName` and `accountPassword`, or null if not found */ public CasinoAccount getAccount(String accountName, String accountPassword) { - String currentMethodName = new Object(){}.getClass().getEnclosingMethod().getName(); - String currentClassName = getClass().getName(); - String errorMessage = "Method with name [ %s ], defined in class with name [ %s ] has not yet been implemented"; - throw new RuntimeException(String.format(errorMessage, currentMethodName, currentClassName)); + for (CasinoAccount account : accounts) { + if (account.getUsername().equals(accountName) && + account.getPassword().equals(accountPassword)) { + return account; + } + } + return null; } /** - * logs & creates a new `ArcadeAccount` + * Creates a new `CasinoAccount` with a $500 starting balance. * * @param accountName name of account to be created * @param accountPassword password of account to be created - * @return new instance of `ArcadeAccount` with specified `accountName` and `accountPassword` + * @return new instance of `CasinoAccount` */ public CasinoAccount createAccount(String accountName, String accountPassword) { - String currentMethodName = new Object(){}.getClass().getEnclosingMethod().getName(); - String currentClassName = getClass().getName(); - String errorMessage = "Method with name [ %s ], defined in class with name [ %s ] has not yet been implemented"; - throw new RuntimeException(String.format(errorMessage, currentMethodName, currentClassName)); + CasinoAccount newAccount = new CasinoAccount(accountName, accountPassword); + newAccount.depositToBalance(500.00); + return newAccount; } /** - * logs & registers a new `ArcadeAccount` to `this.getArcadeAccountList()` + * Registers a `CasinoAccount` to the managed account list. * - * @param casinoAccount the arcadeAccount to be added to `this.getArcadeAccountList()` + * @param casinoAccount the account to register */ public void registerAccount(CasinoAccount casinoAccount) { - String currentMethodName = new Object(){}.getClass().getEnclosingMethod().getName(); - String currentClassName = getClass().getName(); - String errorMessage = "Method with name [ %s ], defined in class with name [ %s ] has not yet been implemented"; - throw new RuntimeException(String.format(errorMessage, currentMethodName, currentClassName)); + accounts.add(casinoAccount); } } diff --git a/src/main/java/com/github/zipcodewilmington/casino/GameInterface.java b/src/main/java/com/github/zipcodewilmington/casino/GameInterface.java index 9873f1e..23fe7d2 100644 --- a/src/main/java/com/github/zipcodewilmington/casino/GameInterface.java +++ b/src/main/java/com/github/zipcodewilmington/casino/GameInterface.java @@ -1,23 +1,46 @@ -package com.github.zipcodewilmington.casino; - -/** - * Created by leon on 7/21/2020. - */ -public interface GameInterface extends Runnable { - /** - * adds a player to the game - * @param player the player to be removed from the game - */ - void add(PlayerInterface player); - - /** - * removes a player from the game - * @param player the player to be removed from the game - */ - void remove(PlayerInterface player); - - /** - * specifies how the game will run - */ - void run(); -} +package com.github.zipcodewilmington.casino; + +/** + * Contract for all games in the Casino. + * Methods marked `default` are optional overrides — implement them in your game class as needed. + */ +public interface GameInterface extends Runnable { + + // ── Required (must implement) ───────────────────────────────────── + + /** Add a player to the game. */ + void add(PlayerInterface player); + + /** Remove a player from the game. */ + void remove(PlayerInterface player); + + /** Main game loop — drives a full session. */ + void run(); + + // ── Optional hooks (override in your game class as needed) ──────── + + /** Load or reset any game state before a session starts. */ + default void fetch() {} + + /** Called at the start of each round. */ + default void start() {} + + /** Called at the end of each round. */ + default void end() {} + + /** Register or initialise any sub-games or game variants. */ + default void loadGames() {} + + /** + * Factory method — create and return a player for this game. + * Override to return your concrete player type (e.g. new BlackjackPlayer(account)). + */ + default PlayerInterface createPlayer(CasinoAccount account) { + return null; + } + + /** Remove and clean up a player — delegates to remove() by default. */ + default void deletePlayer(PlayerInterface player) { + remove(player); + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/PlayerInterface.java b/src/main/java/com/github/zipcodewilmington/casino/PlayerInterface.java index c50b511..3f85dac 100644 --- a/src/main/java/com/github/zipcodewilmington/casino/PlayerInterface.java +++ b/src/main/java/com/github/zipcodewilmington/casino/PlayerInterface.java @@ -1,21 +1,36 @@ -package com.github.zipcodewilmington.casino; - -/** - * Created by leon on 7/21/2020. - * All players of a game should abide by `PlayerInterface`. - * All players must have reference to the `ArcadeAccount` used to log into the `Arcade` system. - * All players are capable of `play`ing a game. - */ -public interface PlayerInterface { - /** - * @return the `ArcadeAccount` used to log into the `Arcade` system to play this game - */ - CasinoAccount getArcadeAccount(); - - /** - * Defines how a specific implementation of `PlayerInterface` plays their respective game. - * @param specify any return type you would like here - * @return whatever return value you would like - */ - SomeReturnType play(); -} +package com.github.zipcodewilmington.casino; + +/** + * Contract for all players in the Casino. + * All players must hold a CasinoAccount reference and know how to play their game. + * Methods marked `default` are provided for free — override them if you need custom behaviour. + */ +public interface PlayerInterface { + + // ── Required (must implement) ───────────────────────────────────── + + /** @return the CasinoAccount used to log into the Casino system. */ + CasinoAccount getArcadeAccount(); + + /** + * Defines how this player plays their game. + * @param any return type you need + */ + SomeReturnType play(); + + // ── Provided for free (override if needed) ──────────────────────── + + /** + * Alias for getArcadeAccount() — matches the UML name. + * Override if your player stores the account under a different field. + */ + default CasinoAccount fetchCasinoAccount() { + return getArcadeAccount(); + } + + /** @return the player's current balance, or 0.0 if no account is set. */ + default double getBalance() { + CasinoAccount account = getArcadeAccount(); + return account != null ? account.getBalance() : 0.0; + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackGame.java b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackGame.java new file mode 100644 index 0000000..682b6ea --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackGame.java @@ -0,0 +1,222 @@ +package com.github.zipcodewilmington.casino.games.blackjack; + +import java.util.ArrayList; +import java.util.List; + +import com.github.zipcodewilmington.casino.GameInterface; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +public class BlackjackGame implements GameInterface { + private static final String RED = "\u001B[31m"; + private static final String RESET = "\u001B[0m"; + private Deck deck; + private BlackjackHand dealerHand; + private List players; + private final IOConsole console = new IOConsole(AnsiColor.BLUE); + + public BlackjackGame() { + this.deck = new Deck(); + this.dealerHand = new BlackjackHand(); + this.players = new ArrayList<>(); + } + public void displayCard(String rank, String suit) { + String symbol; + switch (suit.toLowerCase()) { + case "hearts": + symbol = RED + "\u2665" + RESET; + break; + case "diamonds": + symbol = RED + "\u2666" + RESET; + break; + case "spades": + symbol = "\u2660"; + break; + case "clubs": + symbol = "\u2663"; + break; + default: + symbol = "?"; + break; + } + + System.out.println("┌─────────┐"); + System.out.printf("│ %-2s │\n", rank); + System.out.printf("│ %s │\n", symbol); + System.out.printf("│ %-2s │\n", rank); + System.out.println("└─────────┘"); + } + @Override + public void add(PlayerInterface player) { + players.add(player); + } + + @Override + public void remove(PlayerInterface player) { + players.remove(player); + } + + @Override + public void run() { + console.println("=== Welcome to Blackjack ==="); + + String playAgain = "y"; + while (playAgain.equalsIgnoreCase("y")) { + playRound(); + playAgain = console.getStringInput("\nPlay another round? (y/n): "); + } + + console.println("Thanks for playing!"); + } + + private void playRound() { + // reset everything + deck = new Deck(); + deck.shuffle(); + dealerHand.clear(); + + for (PlayerInterface p : players) { + BlackjackPlayer bp = (BlackjackPlayer) p; + bp.resetHand(); + } + + // take bets + for (PlayerInterface p : players) { + BlackjackPlayer bp = (BlackjackPlayer) p; + console.println("\n%s, your balance is $%.2f", + bp.fetchCasinoAccount().getUsername(), + bp.fetchCasinoAccount().getBalance()); + double bet = console.getDoubleInput("Place your bet: $"); + bp.placeBet(bet, 1, bp.getBalance()); + } + + dealInitialCards(); + + // show hands — dealer hides second card + console.println("\nDealer shows: %s [hidden]", + dealerHand.toString().split(",")[0]); + for (PlayerInterface p : players) { + BlackjackPlayer bp = (BlackjackPlayer) p; + console.println("%s's hand: %s", + bp.fetchCasinoAccount().getUsername(), + bp.getHand().toString()); + } + + List activePlayers = checkBlackjack(); + + if (activePlayers == null || activePlayers.isEmpty()) { + return; + } + + // each active player takes their turn + for (BlackjackPlayer bp : activePlayers) { + bp.takeTurn(deck); + + if (bp.getHand().isBust()) { + console.println("%s busted and loses their bet.", + bp.fetchCasinoAccount().getUsername()); + } + } + + dealerTurn(activePlayers); + + // determine winners + for (BlackjackPlayer bp : activePlayers) { + if (!bp.getHand().isBust()) { + settleResult(bp); + } + } + } + + private void dealInitialCards() { + for (PlayerInterface p : players) { + ((BlackjackPlayer) p).getHand().addCard(deck.deal()); + ((BlackjackPlayer) p).getHand().addCard(deck.deal()); + } + dealerHand.addCard(deck.deal()); + dealerHand.addCard(deck.deal()); + } + + private List checkBlackjack() { + boolean dealerBJ = dealerHand.isBlackjack(); + List activePlayers = new ArrayList<>(); + + if (dealerBJ) { + console.println("\nDealer reveals: %s", dealerHand.toString()); + } + + for (PlayerInterface p : players) { + BlackjackPlayer bp = (BlackjackPlayer) p; + boolean playerBJ = bp.getHand().isBlackjack(); + + if (playerBJ || dealerBJ) { + if (playerBJ && dealerBJ) { + console.println("Both have blackjack — push! Wager returned."); + bp.fetchCasinoAccount().depositToBalance(bp.getWager()); + } else if (playerBJ) { + console.println("Blackjack! %s wins!", + bp.fetchCasinoAccount().getUsername()); + bp.fetchCasinoAccount().depositToBalance(bp.getWager() * 2.5); + } else { + console.println("Dealer has blackjack. %s loses.", + bp.fetchCasinoAccount().getUsername()); + } + continue; + } + + activePlayers.add(bp); + } + + if (dealerBJ) { + return null; + } + + return activePlayers; + } + + private void dealerTurn(List activePlayers) { + boolean hasNonBustedPlayer = false; + for (BlackjackPlayer bp : activePlayers) { + if (!bp.getHand().isBust()) { + hasNonBustedPlayer = true; + break; + } + } + + if (hasNonBustedPlayer) { + console.println("\nDealer reveals: %s", dealerHand.toString()); + while (dealerHand.getTotal() < 17) { + Card drawn = deck.deal(); + dealerHand.addCard(drawn); + console.println("Dealer hits: %s", drawn.toString()); + console.println("Dealer hand: %s", dealerHand.toString()); + } + } + } + + private void settleResult(BlackjackPlayer player) { + int playerTotal = player.getHand().getTotal(); + int dealerTotal = dealerHand.getTotal(); + String name = player.fetchCasinoAccount().getUsername(); + + console.println("\n--- Result ---"); + console.println("%s: %d | Dealer: %d", name, playerTotal, dealerTotal); + + if (dealerHand.isBust()) { + console.println("Dealer busts! %s wins!", name); + player.fetchCasinoAccount().depositToBalance(player.getWager() * 2); + } else if (playerTotal > dealerTotal) { + console.println("%s wins!", name); + player.fetchCasinoAccount().depositToBalance(player.getWager() * 2); + } else if (playerTotal == dealerTotal) { + console.println("Push — tie! Wager returned."); + player.fetchCasinoAccount().depositToBalance(player.getWager()); + } else { + console.println("Dealer wins. %s loses their bet.", name); + } + + console.println("%s's new balance: $%.2f", + name, player.fetchCasinoAccount().getBalance()); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackHand.java b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackHand.java new file mode 100644 index 0000000..1d30ee0 --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackHand.java @@ -0,0 +1,117 @@ +package com.github.zipcodewilmington.casino.games.blackjack; + +import java.util.ArrayList; +import java.util.List; + +public class BlackjackHand { + private List cards; + + public BlackjackHand() { + this.cards = new ArrayList<>(); + } + + public void addCard(Card card) { + cards.add(card); + } + + public int getTotal() { + int total = 0; + int aceCount = 0; + + for (Card card : cards) { + // Assuming Card class has a getValue() and getRank() + total += card.getValue(); + if ("Ace".equalsIgnoreCase(card.getRank())) { + aceCount++; + } + } + + // Reduce Ace from 11 to 1 if we're bust + while (total > 21 && aceCount > 0) { + total -= 10; + aceCount--; + } + return total; + } + + /** + * Displays all cards in the current hand side-by-side using Unicode. + */ + public void displayHand() { + if (cards.isEmpty()) { + System.out.println("[ Empty Hand ]"); + return; + } + + // Initialize 5 rows for the ASCII/Unicode cards + String[] rows = {"", "", "", "", ""}; + + for (Card card : cards) { + String rawRank = card.getRank(); + // Convert "Ace" to "A", "King" to "K", etc., for better fit + String rank = getShortRank(rawRank); + String symbol = getSymbol(card.getSuit()); + + rows[0] += "┌─────────┐ "; + rows[1] += String.format("│ %-2s │ ", rank); // Left-aligned rank + rows[2] += String.format("│ %s │ ", symbol); // Center symbol + rows[3] += String.format("│ %2s │ ", rank); // Right-aligned rank + rows[4] += "└─────────┘ "; + } + + // Print the constructed rows + for (String row : rows) { + System.out.println(row); + } + System.out.println("Hand Total: " + getTotal()); + } + + private String getSymbol(String suit) { + switch (suit.toLowerCase()) { + case "hearts": return "\u2665"; + case "diamonds": return "\u2666"; + case "spades": return "\u2660"; + case "clubs": return "\u2663"; + default: return "?"; + } + } + + private String getShortRank(String rank) { + switch (rank.toLowerCase()) { + case "ace": return "A"; + case "king": return "K"; + case "queen": return "Q"; + case "jack": return "J"; + case "ten": return "10"; + default: return rank; // Returns numbers like "2", "3", etc. + } + } + + public boolean isBust() { + return getTotal() > 21; + } + + public boolean isBlackjack() { + return cards.size() == 2 && getTotal() == 21; + } + + public void clear() { + cards.clear(); + } + + public int size() { + return cards.size(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Card card : cards) { + sb.append(card.toString()).append(", "); + } + if (sb.length() > 2) { + sb.setLength(sb.length() - 2); + } + return sb.toString() + " (total: " + getTotal() + ")"; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackPlayer.java b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackPlayer.java new file mode 100644 index 0000000..e234793 --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/BlackjackPlayer.java @@ -0,0 +1,105 @@ +package com.github.zipcodewilmington.casino.games.blackjack; + +import com.github.zipcodewilmington.casino.CasinoAccount; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +public class BlackjackPlayer implements PlayerInterface { + private CasinoAccount casinoAccount; + private BlackjackHand hand; + private double wager; + private final IOConsole console = new IOConsole(AnsiColor.GREEN); + + public BlackjackPlayer(CasinoAccount casinoAccount) { + this.casinoAccount = casinoAccount; + this.hand = new BlackjackHand(); + this.wager = 0; + } + + public void placeBet(double amount, double min, double max) { + if (amount < min) { + console.println("Minimum bet is $%.2f.", min); + return; + } + if (amount > max) { + console.println("Maximum bet is $%.2f.", max); + return; + } + if (amount > casinoAccount.getBalance()) { + console.println("Not enough funds. Your balance is $%.2f", + casinoAccount.getBalance()); + return; + } + this.wager = amount; + casinoAccount.withdrawBalance(amount); + console.println("Bet placed: $%.2f", wager); + } + + // play() drives the player's hit-or-stand turn + // it receives a deck so it can deal cards to itself + public SomeReturnType play() { + return null; // turn logic handled in BlackjackGame + } + + public void takeTurn(Deck deck) { + while (true) { + console.println("\nYour hand: %s", hand.toString()); + String choice = console.getStringInput("Hit or stand? (h/s): "); + if (choice.equalsIgnoreCase("s")) { + stay(); + break; + } else if (choice.equalsIgnoreCase("h")) { + hit(deck); + if (hand.isBust()) { + console.println("Your hand: %s", hand.toString()); + console.println("Bust! You went over 21."); + break; + } + } else { + console.println("Please enter h or s."); + } + } + } + + public void hit(Deck deck) { + hand.addCard(deck.deal()); + } + + public void stay() { + console.println("%s stands with %s", casinoAccount.getUsername(), hand.toString()); + } + + public void doubleDown(Deck deck) { + double extra = Math.min(wager, casinoAccount.getBalance()); + casinoAccount.withdrawBalance(extra); + wager += extra; + console.println("Doubled down! New wager: $%.2f", wager); + hit(deck); + } + + public String askWhichOne() { + return console.getStringInput("Hit or stand? (h/s): "); + } + + @Override + public CasinoAccount getArcadeAccount() { + return casinoAccount; + } + + public CasinoAccount fetchCasinoAccount() { + return casinoAccount; + } + + public double getBalance() { + return casinoAccount.getBalance(); + } + + public BlackjackHand getHand() { return hand; } + public double getWager() { return wager; } + + public void resetHand() { + hand.clear(); + wager = 0; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/Card.java b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/Card.java new file mode 100644 index 0000000..5371d9c --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/Card.java @@ -0,0 +1,49 @@ +package com.github.zipcodewilmington.casino.games.blackjack; + +public class Card { + + private final String suit; + private final String rank; + private final int value; + + public Card(String suit, String rank) { + this.suit = suit; + this.rank = rank; + this.value = determineValue(rank); + } + + private int determineValue(String rank) { + if ("Ace".equalsIgnoreCase(rank)) { + return 11; + } + + if ("Jack".equalsIgnoreCase(rank) + || "Queen".equalsIgnoreCase(rank) + || "King".equalsIgnoreCase(rank)) { + return 10; + } + + try { + return Integer.parseInt(rank); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("Invalid rank: " + rank); + } + } + + public int getValue() { + return value; + } + + public String getRank() { + return rank; + } + + public String getSuit() { + return suit; + } + + @Override + public String toString() { + return rank + " of " + suit; + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/Deck.java b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/Deck.java new file mode 100644 index 0000000..5c27633 --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/blackjack/Deck.java @@ -0,0 +1,72 @@ +package com.github.zipcodewilmington.casino.games.blackjack; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Deck { + + private static final String[] SUITS = { "Hearts", "Diamonds", "Clubs", "Spades" }; + private static final String[] RANKS = { + "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King" + }; + + private final List cards; + private final int numberOfDecks; + + public Deck() { + this(1); + } + + public Deck(int numberOfDecks) { + if (numberOfDecks < 1) { + throw new IllegalArgumentException("numberOfDecks must be at least 1."); + } + this.numberOfDecks = numberOfDecks; + this.cards = new ArrayList<>(); + initializeNewShuffledDeck(); + } + + public void reset() { + initializeNewShuffledDeck(); + } + + private void initializeNewShuffledDeck() { + cards.clear(); + for (int deck = 0; deck < numberOfDecks; deck++) { + for (String suit : SUITS) { + for (String rank : RANKS) { + cards.add(new Card(suit, rank)); + } + } + } + shuffle(); + } + + public int getNumberOfDecks() { + return numberOfDecks; + } + + public void shuffle() { + Collections.shuffle(cards); + } + + public Card deal() { + if (cards.isEmpty()) { + throw new IllegalStateException("Cannot draw from an empty deck."); + } + return cards.remove(0); + } + + public Card drawCard() { + return deal(); + } + + public int size() { + return cards.size(); + } + + public boolean isEmpty() { + return cards.isEmpty(); + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/craps/CrapsGame.java b/src/main/java/com/github/zipcodewilmington/casino/games/craps/CrapsGame.java new file mode 100644 index 0000000..8fa952d --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/craps/CrapsGame.java @@ -0,0 +1,165 @@ +package com.github.zipcodewilmington.casino.games.craps; + +import java.util.ArrayList; +import java.util.List; + +import com.github.zipcodewilmington.casino.GameInterface; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +public class CrapsGame implements GameInterface { + + private Dice dice; + private List players; + private final IOConsole console = new IOConsole(AnsiColor.YELLOW); + + public CrapsGame() { + this.dice = new Dice(); + this.players = new ArrayList<>(); + } + + @Override + public void add(PlayerInterface player) { + players.add(player); + } + + @Override + public void remove(PlayerInterface player) { + players.remove(player); + } + + @Override + public void run() { + console.println("=== Welcome to Craps ==="); + + String playAgain = "y"; + while (playAgain.equalsIgnoreCase("y")) { + playRound(); + playAgain = console.getStringInput("\nPlay another round? (y/n): "); + } + + console.println("Thanks for playing Craps!"); + } + + private void playRound() { + for (PlayerInterface p : players) { + CrapsPlayer cp = (CrapsPlayer) p; + console.println("\n%s, your balance is $%.2f", + cp.fetchCasinoAccount().getUsername(), cp.getBalance()); + double bet = console.getDoubleInput("Place your bet (min $1): $"); + cp.placeBet(bet, 1, cp.getBalance()); + } + + // come-out roll + for (PlayerInterface p : players) { + CrapsPlayer cp = (CrapsPlayer) p; + cp.rollDice(); + rollDice(); + + int total = dice.getTotal(); + int point = evaluateRoll(total, 0); // 0 = no point yet + + if (point == -1) { + // natural win (7 or 11) + settleResult(cp, true); + } else if (point == 0) { + // craps (2, 3, 12) — lose + settleResult(cp, false); + } else { + // point established — keep rolling + console.println("Point is set to %d. Roll again to match it before rolling a 7!", point); + boolean roundOver = false; + while (!roundOver) { + cp.rollDice(); + rollDice(); + int roll = dice.getTotal(); + int result = evaluateRoll(roll, point); + if (result == -1) { + // matched the point + settleResult(cp, true); + roundOver = true; + } else if (result == 0) { + // rolled a 7 — lose + settleResult(cp, false); + roundOver = true; + } + // any other roll — keep going + } + } + } +} + +/** + * Rolls two dice, displays their Unicode icons, and updates the dice object. + */ +public void rollDice() { + // Roll the internal dice object (assuming it has a roll method) + this.dice.roll(); + + // Get the individual values from your dice object + // If your Dice class doesn't have these, use: (int)(Math.random() * 6) + 1 + int die1 = dice.getDie1(); + int die2 = dice.getDie2(); + + String icon1 = getDiceUnicode(die1); + String icon2 = getDiceUnicode(die2); + + console.println("\n[ The dice roll: %s %s ]", icon1, icon2); + console.println("Total: %d", (die1 + die2)); + } + + /** + * Helper to return the Unicode character for a dice face. + */ + private String getDiceUnicode(int value) { + switch (value) { + case 1: + return "\u2680"; // ⚀ + case 2: + return "\u2681"; // ⚁ + case 3: + return "\u2682"; // ⚂ + case 4: + return "\u2683"; // ⚃ + case 5: + return "\u2684"; // ⚄ + case 6: + return "\u2685"; // ⚅ + default: + return "?"; + } + } + + /** + * Evaluates the dice roll against craps rules. + * @param total the dice total + * @param point the established point (0 if come-out roll) + * @return -1 = win, 0 = lose, any other int = new/existing point + */ + public int evaluateRoll(int total, int point) { + if (point == 0) { + // come-out roll + if (total == 7 || total == 11) return -1; // natural — win + if (total == 2 || total == 3 || total == 12) return 0; // craps — lose + return total; // set the point + } else { + // point phase + if (total == point) return -1; // matched point — win + if (total == 7) return 0; // 7-out — lose + return point; // no change, keep rolling + } + } + + public void settleResult(CrapsPlayer player, boolean won) { + String name = player.fetchCasinoAccount().getUsername(); + if (won) { + double payout = player.getWager() * 2; + player.fetchCasinoAccount().depositToBalance(payout); + console.println("%s wins! Payout: $%.2f | New balance: $%.2f", + name, payout, player.getBalance()); + } else { + console.println("%s loses. Balance: $%.2f", name, player.getBalance()); + } + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/craps/CrapsPlayer.java b/src/main/java/com/github/zipcodewilmington/casino/games/craps/CrapsPlayer.java new file mode 100644 index 0000000..597e324 --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/craps/CrapsPlayer.java @@ -0,0 +1,68 @@ +package com.github.zipcodewilmington.casino.games.craps; + +import com.github.zipcodewilmington.casino.CasinoAccount; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +public class CrapsPlayer implements PlayerInterface { + + private CasinoAccount casinoAccount; + private double[] rates; + private double wager; + private final IOConsole console = new IOConsole(AnsiColor.YELLOW); + + public CrapsPlayer(CasinoAccount casinoAccount) { + this.casinoAccount = casinoAccount; + this.wager = 0; + this.rates = new double[]{ 1.0, 2.0 }; // index 0 = pass line (1:1), index 1 = come-out natural (2:1) + } + + public void placeBet(double amount, double min, double max) { + if (amount < min) { + console.println("Minimum bet is $%.2f.", min); + return; + } + if (amount > max) { + console.println("Maximum bet is $%.2f.", max); + return; + } + if (amount > casinoAccount.getBalance()) { + console.println("Not enough funds. Your balance is $%.2f", casinoAccount.getBalance()); + return; + } + this.wager = amount; + casinoAccount.withdrawBalance(amount); + console.println("Bet placed: $%.2f", wager); + } + + public void rollDice() { + console.getStringInput("Press ENTER to roll the dice..."); + } + + public double[] getRates() { + return rates; + } + + public double getBalance() { + return casinoAccount.getBalance(); + } + + public double getWager() { + return wager; + } + + public CasinoAccount fetchCasinoAccount() { + return casinoAccount; + } + + @Override + public CasinoAccount getArcadeAccount() { + return casinoAccount; + } + + @Override + public SomeReturnType play() { + return null; // turn logic handled in CrapsGame + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/craps/Dice.java b/src/main/java/com/github/zipcodewilmington/casino/games/craps/Dice.java new file mode 100644 index 0000000..cf5d6fb --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/craps/Dice.java @@ -0,0 +1,44 @@ +package com.github.zipcodewilmington.casino.games.craps; + +import java.util.Random; + +public class Dice { + + private static final Random random = new Random(); + + private int die1; + private int die2; + private final int sides; + + public Dice() { + this.sides = 6; + this.die1 = 1; + this.die2 = 1; + } + + public void roll() { + die1 = random.nextInt(sides) + 1; + die2 = random.nextInt(sides) + 1; + } + + public int getTotal() { + return die1 + die2; + } + + public int getSides() { + return sides; + } + + public int getDie1() { + return die1; + } + + public int getDie2() { + return die2; + } + + @Override + public String toString() { + return "[" + die1 + "] + [" + die2 + "] = " + getTotal(); + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/hangman/HangmanGame.java b/src/main/java/com/github/zipcodewilmington/casino/games/hangman/HangmanGame.java new file mode 100644 index 0000000..fedb3f2 --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/hangman/HangmanGame.java @@ -0,0 +1,167 @@ +package com.github.zipcodewilmington.casino.games.hangman; + +import java.util.HashSet; +import java.util.Set; +import com.github.zipcodewilmington.casino.GameInterface; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +public class HangmanGame implements GameInterface { + private HangmanPlayer player; + private String secretWord; + private Set guessedLetters = new HashSet<>(); + private int maxIncorrectGuesses = 6; + private final IOConsole console = new IOConsole(AnsiColor.GREEN); + private String getRandomWord() { + try { + java.net.URL url = new java.net.URL("https://random-word-api.herokuapp.com/word?number=1"); + String response = new String(url.openStream().readAllBytes()); + return response.replaceAll("[\\[\\]\"]", ""); // Clean up the response + } catch (Exception e) { + String[]fallback = {"casino", "hangman", "Haha"}; + return fallback[(int)(Math.random() * fallback.length)]; + } + } + + private String[] STAGES = { + // 0 wrong + " +---+\n" + + " | |\n" + + " |\n" + + " |\n" + + " |\n" + + " |\n" + + "=========", + // 1 wrong + " +---+\n" + + " | |\n" + + " O |\n" + + " |\n" + + " |\n" + + " |\n" + + "=========", + // 2 wrong + " +---+\n" + + " | |\n" + + " O |\n" + + " | |\n" + + " |\n" + + " |\n" + + "=========", + // 3 wrong + " +---+\n" + + " | |\n" + + " O |\n" + + " /| |\n" + + " |\n" + + " |\n" + + "=========", + // 4 wrong + " +---+\n" + + " | |\n" + + " O |\n" + + " /|\\ |\n" + + " |\n" + + " |\n" + + "=========", + // 5 wrong + " +---+\n" + + " | |\n" + + " O |\n" + + " /|\\ |\n" + + " / |\n" + + " |\n" + + "=========", + // 6 wrong - dead + " +---+\n" + + " | |\n" + + " O |\n" + + " /|\\ |\n" + + " / \\ |\n" + + " |\n" + + "=========" + }; + + @Override + public void add(PlayerInterface player) { + this.player = (HangmanPlayer) player; + } + + @Override + public void remove(PlayerInterface player) { + this.player = null; + } + + @Override + public void run() { + secretWord = pickWord(); + guessedLetters.clear(); + maxIncorrectGuesses = 6; + + console.println("Welcome to Hangman, " + player.getName() + "!"); + + while (!isGameOver()) { + console.println(STAGES[6 - maxIncorrectGuesses]); + displayWord(); + console.println("Remaining attempts: " + maxIncorrectGuesses); + console.println("Guessed letters: " + guessedLetters); + + char guess = player.guessLetter(); + + if (checkGuess(guess)) { + console.println("Good guess!"); + } else { + console.println("Wrong! -1 attempt"); + } + } + + console.println(STAGES[6 - maxIncorrectGuesses]); + + if (isWon()) { + console.println("You win! The word was: " + secretWord); + } else { + console.println("Game over! The word was: " + secretWord); + } + } + + private String pickWord() { + return getRandomWord(); + } + + public boolean checkGuess(char c) { + guessedLetters.add(c); + if (secretWord.indexOf(c) >= 0) { + return true; + } + maxIncorrectGuesses--; + return false; + } + + public boolean isGameOver() { + return isWon() || isLost(); + } + + public boolean isLost() { + return maxIncorrectGuesses <= 0; + } + + public boolean isWon() { + for (char c : secretWord.toCharArray()) { + if (!guessedLetters.contains(c)) return false; + } + return true; + } + + public void displayWord() { + StringBuilder sb = new StringBuilder(); + for (char c : secretWord.toCharArray()) { + if (guessedLetters.contains(c)) { + sb.append(c).append(" "); + } else { + sb.append("_ "); + } + } + console.println("Word: " + sb.toString().trim()); + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/hangman/HangmanPlayer.java b/src/main/java/com/github/zipcodewilmington/casino/games/hangman/HangmanPlayer.java new file mode 100644 index 0000000..0cb33e2 --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/hangman/HangmanPlayer.java @@ -0,0 +1,36 @@ +package com.github.zipcodewilmington.casino.games.hangman; + +import com.github.zipcodewilmington.casino.CasinoAccount; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +public class HangmanPlayer implements PlayerInterface { + private String name; + private CasinoAccount account; + private final IOConsole console = new IOConsole(AnsiColor.GREEN); + + public HangmanPlayer(String name, CasinoAccount account) { + this.name = name; + this.account = account; + } + + @Override + public CasinoAccount getArcadeAccount() { + return account; + } + + @Override + public String play() { + return name + " is playing Hangman YAYYYYY!"; + } + + public String getName() { return name; } + + public char guessLetter() { + String input = console.getStringInput("Enter a letter to guess: "); + return input.charAt(0); + } +} + + \ No newline at end of file diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/numberguess/NumberGuessGame.java b/src/main/java/com/github/zipcodewilmington/casino/games/numberguess/NumberGuessGame.java index 7957094..0c94df0 100644 --- a/src/main/java/com/github/zipcodewilmington/casino/games/numberguess/NumberGuessGame.java +++ b/src/main/java/com/github/zipcodewilmington/casino/games/numberguess/NumberGuessGame.java @@ -1,7 +1,93 @@ -package com.github.zipcodewilmington.casino.games.numberguess; - -/** - * Created by leon on 7/21/2020. - */ -public class NumberGuessGame { -} \ No newline at end of file +package com.github.zipcodewilmington.casino.games.numberguess; + +import java.util.Random; + +import com.github.zipcodewilmington.casino.GameInterface; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +public class NumberGuessGame implements GameInterface { + + private NumberGuessPlayer player; + private final IOConsole console = new IOConsole(AnsiColor.GREEN); + private final Random random = new Random(); + private int secretNumber; + + public NumberGuessGame() {} + + @Override + public void add(PlayerInterface player) { + this.player = (NumberGuessPlayer) player; + } + + @Override + public void remove(PlayerInterface player) { + this.player = null; + } + + @Override + public void run() { + console.println("=== Welcome to Number Guess Game, %s! ===", + player.getArcadeAccount().getUsername()); + + String playAgain = "yes"; + while (playAgain.equalsIgnoreCase("yes")) { + playRound(); + playAgain = console.getStringInput("\nPlay again? (yes/no): "); + } + + console.println("Thanks for playing Number Guess Game!"); + } + + private void playRound() { + generateNumber(); + int guesses = 0; + int maxGuesses = 10; + + console.println("\nI'm thinking of a number between 1 and 100. You have %d guesses.", maxGuesses); + + while (guesses < maxGuesses) { + int guess = player.makeGuess(); + guesses++; + + int result = checkGuess(guess); + + if (result == 0) { + console.println("CONGRATULATIONS! You guessed it in %d guess(es)!", guesses); + return; + } + + giveHint(guess); + + int remaining = maxGuesses - guesses; + if (remaining > 0) { + console.println("%d guess(es) remaining.", remaining); + } + } + + console.println("Out of guesses! The number was %d.", secretNumber); + } + + private void generateNumber() { + secretNumber = random.nextInt(100) + 1; + } + + /** + * Checks the guess against the secret number. + * @return -1 if too low, 0 if correct, 1 if too high + */ + public int checkGuess(int guess) { + if (guess < secretNumber) return -1; + if (guess > secretNumber) return 1; + return 0; + } + + public void giveHint(int guess) { + if (guess < secretNumber) { + console.println("Too Low!"); + } else if (guess > secretNumber) { + console.println("Too High!"); + } + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/numberguess/NumberGuessPlayer.java b/src/main/java/com/github/zipcodewilmington/casino/games/numberguess/NumberGuessPlayer.java index aa5cce2..f637c85 100644 --- a/src/main/java/com/github/zipcodewilmington/casino/games/numberguess/NumberGuessPlayer.java +++ b/src/main/java/com/github/zipcodewilmington/casino/games/numberguess/NumberGuessPlayer.java @@ -1,7 +1,30 @@ package com.github.zipcodewilmington.casino.games.numberguess; -/** - * Created by leon on 7/21/2020. - */ -public class NumberGuessPlayer { -} \ No newline at end of file +import com.github.zipcodewilmington.casino.CasinoAccount; +import com.github.zipcodewilmington.casino.PlayerInterface; +import com.github.zipcodewilmington.utils.AnsiColor; +import com.github.zipcodewilmington.utils.IOConsole; + +public class NumberGuessPlayer implements PlayerInterface { + + private final CasinoAccount account; + private final IOConsole console = new IOConsole(AnsiColor.GREEN); + + public NumberGuessPlayer(CasinoAccount account) { + this.account = account; + } + + public int makeGuess() { + return console.getIntegerInput("Enter your guess (1-100): "); + } + + @Override + public CasinoAccount getArcadeAccount() { + return account; + } + + @Override + public SomeReturnType play() { + return null; // turn logic handled in NumberGuessGame + } +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RouletteGame.java b/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RouletteGame.java new file mode 100644 index 0000000..03b2659 --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RouletteGame.java @@ -0,0 +1,160 @@ +package com.github.zipcodewilmington.casino.games.roulette; + +import com.github.zipcodewilmington.casino.CasinoAccount; +import com.github.zipcodewilmington.casino.GameInterface; +import com.github.zipcodewilmington.casino.PlayerInterface; + +import java.util.Random; +import java.util.Scanner; + +public class RouletteGame implements GameInterface { + + private static final int[] RED_NUMBERS = { + 1, 3, 5, 7, 9, 12, 14, 16, 18, 19, + 21, 23, 25, 27, 30, 32, 34, 36 + }; + + private RoulettePlayer player; + private Scanner scanner; + + public RouletteGame() { + this.scanner = new Scanner(System.in); + } + + // ── GameInterface bridge methods ────────────────────────────────────── + @Override + public void add(PlayerInterface player) { + this.player = (RoulettePlayer) player; + } + + @Override + public void remove(PlayerInterface player) { + removePlayer(player); + } + + @Override + public void run() { + play(); + } + // ───────────────────────────────────────────────────────────────────── + + public void addPlayer(CasinoAccount account) { + this.player = new RoulettePlayer(account); + } + + public void play() { + System.out.println("\n==== ROULETTE ===="); + System.out.println("Balance: $" + player.getAccount().getBalance()); + System.out.print("Enter bet amount: $"); + double betAmount = scanner.nextDouble(); + + if (betAmount <= 0) { + System.out.println("Bet must be greater than $0. Returning to lobby..."); + return; + } + + if (betAmount > player.getAccount().getBalance()) { + System.out.println("Not enough funds! Returning to lobby..."); + return; + } + + System.out.println("\nBet type:"); + System.out.println(" 1. Specific number (0-36) -- pays 35x"); + System.out.println(" 2. Red / Black -- pays 2x"); + System.out.println(" 3. Even / Odd -- pays 2x"); + System.out.print("Choose (1-3): "); + int choice = scanner.nextInt(); + + String betType = ""; + int betNumber = -1; + + switch (choice) { + case 1: + System.out.print("Pick a number (0-36): "); + betNumber = scanner.nextInt(); + if (betNumber < 0 || betNumber > 36) { + System.out.println("Invalid number! Returning to lobby..."); + return; + } + betType = "number"; + break; + case 2: + System.out.print("Type red or black: "); + betType = scanner.next().toLowerCase(); + if (!betType.equals("red") && !betType.equals("black")) { + System.out.println("Invalid choice! Returning to lobby..."); + return; + } + break; + case 3: + System.out.print("Type even or odd: "); + betType = scanner.next().toLowerCase(); + if (!betType.equals("even") && !betType.equals("odd")) { + System.out.println("Invalid choice! Returning to lobby..."); + return; + } + break; + default: + System.out.println("Invalid choice! Returning to lobby..."); + return; + } + + player.setBetAmount(betAmount); + player.setBetType(betType); + player.setBetNumber(betNumber); + + int result = spinWheel(); + String resultColor = isRed(result) ? "Red" : (result == 0 ? "Green" : "Black"); + + System.out.println("\n--- Spinning the wheel... ---"); + System.out.println("Result: " + result + " (" + resultColor + ")"); + + if (isWin(result, betType, betNumber)) { + double winnings = calculateWinnings(betAmount, betType); + player.getAccount().depositToBalance(winnings); + System.out.println("YOU WIN! + $" + winnings); + } else { + player.getAccount().withdrawBalance(betAmount); + System.out.println("You lost. - $" + betAmount); + } + + System.out.println("New balance: $" + player.getAccount().getBalance()); + } + + public int spinWheel() { + return new Random().nextInt(37); + } + + public boolean isWin(int result, String betType, int betNumber) { + switch (betType) { + case "number": return result == betNumber; + case "red": return isRed(result); + case "black": return !isRed(result) && result != 0; + case "even": return result != 0 && result % 2 == 0; + case "odd": return result % 2 == 1; + default: return false; + } + } + + public double calculateWinnings(double betAmount, String betType) { + if (betType.equals("number")) { + return betAmount * 35; + } else { + return betAmount * 2; + } + } + + public boolean isRed(int number) { + for (int red : RED_NUMBERS) { + if (number == red) return true; + } + return false; + } + + public void removePlayer(PlayerInterface player) { + if (scanner != null) { + scanner.close(); + } + this.player = null; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RoulettePlayer.java b/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RoulettePlayer.java new file mode 100644 index 0000000..5f44e71 --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RoulettePlayer.java @@ -0,0 +1,54 @@ +package com.github.zipcodewilmington.casino.games.roulette; + +import com.github.zipcodewilmington.casino.CasinoAccount; +import com.github.zipcodewilmington.casino.PlayerInterface; + +public class RoulettePlayer implements PlayerInterface { + private CasinoAccount account; + private int betNumber; + private String betType; + private double betAmount; + + public RoulettePlayer(CasinoAccount account) { + this.account = account; + } + + public CasinoAccount getAccount() { + return account; + } + + @Override + public CasinoAccount getArcadeAccount() { + return account; + } + + @Override + public SomeReturnType play() { + return null; // turn logic handled in RouletteGame + } + + public int getBetNumber() { + return betNumber; + } + + public void setBetNumber(int betNumber) { + this.betNumber = betNumber; + } + + public String getBetType() { + return betType; + } + + public void setBetType(String betType) { + this.betType = betType; + } + + public double getBetAmount() { + return betAmount; + } + + public void setBetAmount(double betAmount) { + this.betAmount = betAmount; + } + +} diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RouletteWheel.java b/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RouletteWheel.java new file mode 100644 index 0000000..2effddc --- /dev/null +++ b/src/main/java/com/github/zipcodewilmington/casino/games/roulette/RouletteWheel.java @@ -0,0 +1,63 @@ +package com.github.zipcodewilmington.casino.games.roulette; + +import java.util.Random; + +public class RouletteWheel { + private final int[] numbers; + private final String[] colors; + private int lastSpinNumber; + private String lastSpinColor; + private final Random random; + + public RouletteWheel() { + this.numbers = new int[37]; // 0 to 36 + this.colors = new String[37]; + this.random = new Random(); + initializeWheel(); + } + + private void initializeWheel() { + for (int i = 0; i <= 36; i++) { + numbers[i] = i; + if (i == 0) { + colors[i] = "Green"; + } else if (isRedNumber(i)) { + colors[i] = "Red"; + } else { + colors[i] = "Black"; + } + } + } + + // Official Roulette logic for Red numbers + private boolean isRedNumber(int n) { + if ((n >= 1 && n <= 10) || (n >= 19 && n <= 28)) { + return n % 2 != 0; // Odd is Red + } else { + return n % 2 == 0; // Even is Red + } + } + + public void spin() { + int index = random.nextInt(37); + this.lastSpinNumber = numbers[index]; + this.lastSpinColor = colors[index]; + } + + public int getLastSpinNumber() { + return lastSpinNumber; + } + + public String getLastSpinColor() { + return lastSpinColor; + } + + // Logic for your Game teammate to use for payouts + public boolean lastSpinWasEven() { + return lastSpinNumber != 0 && lastSpinNumber % 2 == 0; + } + + public boolean lastSpinWasOdd() { + return lastSpinNumber != 0 && lastSpinNumber % 2 != 0; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/slots/SlotsGame.java b/src/main/java/com/github/zipcodewilmington/casino/games/slots/SlotsGame.java index 8cb20c7..58eecee 100644 --- a/src/main/java/com/github/zipcodewilmington/casino/games/slots/SlotsGame.java +++ b/src/main/java/com/github/zipcodewilmington/casino/games/slots/SlotsGame.java @@ -1,7 +1,65 @@ package com.github.zipcodewilmington.casino.games.slots; +import java.util.Random; +import java.util.Scanner; -/** - * Created by leon on 7/21/2020. - */ -public class SlotsGame { -} +import com.github.zipcodewilmington.casino.GameInterface; +import com.github.zipcodewilmington.casino.PlayerInterface; + + +public class SlotsGame implements GameInterface{ + private SlotsPlayer player; + private Random random = new Random(); + private String[] symbols = {"🍒", "🍋", "⭐", "🔔", "💎"}; + + @Override + public void add(PlayerInterface player) { + this.player = (SlotsPlayer)player; + } + + @Override + public void remove(PlayerInterface player) { + this.player = null; + } + + + @Override + public void run() { + Scanner scanner = new Scanner(System.in); + System.out.println("Welcome to Slots, " + player.getName() + "!"); + + while (player.getBalance() > 0 ) { + System.out.println("Balance: $" + player.getBalance()); + System.out.print("Enter bet amount (or 0 to quit): "); + double bet = scanner.nextDouble(); + if (bet == 0) { + break; + } + if (bet > player.getBalance()) { + System.out.println("Insufficient balance!"); + continue; + } + + String result = spin(); + System.out.println("Spinning..."); + + String[] reels = result.split(" \\| "); + if (reels[0].equals(reels[1]) && reels[1].equals(reels[2])) { + double winnings = bet * 3; + player.setBalance(player.getBalance() + winnings); + System.out.println("Jackpot! You win $" + winnings); + } else { + player.setBalance(player.getBalance() - bet); + System.out.println("You lose $" + bet); + } + } + System.out.println("Game over! Final balance: $" + player.getBalance()); + } + + public String spin() { + String reel1 = symbols[random.nextInt(symbols.length)]; + String reel2 = symbols[random.nextInt(symbols.length)]; + String reel3 = symbols[random.nextInt(symbols.length)]; + return reel1 + " | " + reel2 + " | " + reel3; + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/zipcodewilmington/casino/games/slots/SlotsPlayer.java b/src/main/java/com/github/zipcodewilmington/casino/games/slots/SlotsPlayer.java index f89ebd7..3d11ee5 100644 --- a/src/main/java/com/github/zipcodewilmington/casino/games/slots/SlotsPlayer.java +++ b/src/main/java/com/github/zipcodewilmington/casino/games/slots/SlotsPlayer.java @@ -1,7 +1,39 @@ package com.github.zipcodewilmington.casino.games.slots; +import com.github.zipcodewilmington.casino.CasinoAccount; +import com.github.zipcodewilmington.casino.PlayerInterface; + /** * Created by leon on 7/21/2020. */ -public class SlotsPlayer { -} \ No newline at end of file +public class SlotsPlayer implements PlayerInterface { + private String name; + private CasinoAccount account; + + public SlotsPlayer(String name, CasinoAccount account) { + this.name = name; + this.account = account; + } + + @Override + public CasinoAccount getArcadeAccount() { + return account; + } + @Override + public String play() { + return name + " is playing Slots YAYYYYY!"; + } + + public String getName() { return name; } + + public double getBalance() { return account.getBalance(); } + + public void setBalance(double newBalance) { + double current = account.getBalance(); + if (newBalance > current) { + account.depositToBalance(newBalance - current); + } else if (newBalance < current) { + account.withdrawBalance(current - newBalance); + } + } +} diff --git a/src/main/java/com/github/zipcodewilmington/utils/IOConsole.java b/src/main/java/com/github/zipcodewilmington/utils/IOConsole.java index c7afc01..049bd3e 100644 --- a/src/main/java/com/github/zipcodewilmington/utils/IOConsole.java +++ b/src/main/java/com/github/zipcodewilmington/utils/IOConsole.java @@ -68,4 +68,9 @@ public Long getLongInput(String prompt, Object... args) { public Integer getIntegerInput(String prompt, Object... args) { return getLongInput(prompt, args).intValue(); } + + public int getIntInput(String string) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getIntInput'"); + } } \ No newline at end of file diff --git a/src/test/java/com/github/zipcodewilmington/CasinoAccountTest.java b/src/test/java/com/github/zipcodewilmington/CasinoAccountTest.java new file mode 100644 index 0000000..95546e9 --- /dev/null +++ b/src/test/java/com/github/zipcodewilmington/CasinoAccountTest.java @@ -0,0 +1,43 @@ +package com.github.zipcodewilmington; + +import org.junit.Assert; +import org.junit.Test; + +import com.github.zipcodewilmington.casino.CasinoAccount; + +public class CasinoAccountTest { + @Test + public void testDepositIncreasesBalance() { + CasinoAccount account = new CasinoAccount("alice", "pass"); + account.depositToBalance(100.0); + + Assert.assertEquals(100.0, account.getBalance(), 0.001); + } + + @Test + public void testWithdrawDecreasesBalance() { + CasinoAccount account = new CasinoAccount("alice", "pass"); + account.depositToBalance(100.0); + account.withdrawBalance(40.0); + + Assert.assertEquals(60.0, account.getBalance(), 0.001); + } + + @Test + public void testWithdrawInsufficientFunds() { + CasinoAccount account = new CasinoAccount("alice", "pass"); + account.depositToBalance(50.0); + account.withdrawBalance(100.0); + + Assert.assertEquals(50.0, account.getBalance(), 0.001); + } + + @Test + public void testGetAccountReturnsNullIfNotFound() { + CasinoAccount accountRegistry = new CasinoAccount("registry", "registry-pass"); + + CasinoAccount result = accountRegistry.getAccount("nobody", "wrong"); + + Assert.assertNull(result); + } +} \ No newline at end of file