From 89b5f6d81d4387d23118f9e4497184889e4e04ef Mon Sep 17 00:00:00 2001 From: Aldrin Misquitta Date: Thu, 31 Oct 2024 14:53:36 +0400 Subject: [PATCH] Java implementation of Bullfight --- 17_Bullfight/java/Bullfight.java | 466 +++++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 17_Bullfight/java/Bullfight.java diff --git a/17_Bullfight/java/Bullfight.java b/17_Bullfight/java/Bullfight.java new file mode 100644 index 000000000..16fe299c0 --- /dev/null +++ b/17_Bullfight/java/Bullfight.java @@ -0,0 +1,466 @@ +import java.util.Random; +import java.util.Scanner; + +/** + * BULLFIGHT + *

+ * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) + */ +public class Bullfight { + + private static final int MAX_PASSES_BEFORE_CHARGE = 3; + + public static void main(String[] args) { + printHeader(); + + Scanner scanner = new Scanner(System.in); + System.out.print("\n\n"); + + var instructionsChoice = readInstructionsChoice(scanner); + if (instructionsChoice) { + printInstructions(); + } + + Random random = new Random(); + + //initialize the game with default values + GameState gameState = new GameState(); + + //Randomly select a bull grade + int randomGrade = (int) (random.nextFloat() * BullGrade.values().length + 1); + var bullGrade = BullGrade.fromValue(randomGrade); + + printBullGradeInfo(bullGrade); + + System.out.println(); + + //D[1] in the original + gameState.picadoresDamage = firstStage("PICADO", "RES", bullGrade, random); + + //D[2] in the original + gameState.toreadoresDamage = firstStage("TOREAD", "ORES", bullGrade, random); + + boolean done = false; //controls the main game loop + + while (!done) { + + gameState.passNumber++; + System.out.printf("\n\nPASS NUMBER %d \n", gameState.passNumber); + + if (gameState.passNumber < MAX_PASSES_BEFORE_CHARGE) { + System.out.println("THE BULL IS CHARGING AT YOU! YOU ARE THE MATADOR--"); + System.out.print("DO YOU WANT TO KILL THE BULL? "); + } else { + System.out.print("HERE COMES THE BULL. TRY FOR A KILL? "); + } + + BooleanAnswer yesOrNo = readYesOrNo(scanner); + if (yesOrNo.equals(BooleanAnswer.YES)) { + gameState = attemptKillBull(bullGrade, random, gameState, scanner); + done = true; + } else { + int capeMove; + if (gameState.passNumber < MAX_PASSES_BEFORE_CHARGE) { + capeMove = readCapeMove("WHAT MOVE DO YOU MAKE WITH THE CAPE", scanner); + } else { + capeMove = readCapeMove("CAPE MOVE", scanner); + } + + //handle cape move + gameState = handleCapeMove(capeMove, random, scanner, bullGrade, gameState); + + if (gameState.matadorStatus.equals(MatadorStatus.DEFEATED) || gameState.matadorStatus.equals(MatadorStatus.DEAD)) { + done = true; + } + } + + } + + crowdReaction(gameState, bullGrade, random); + + System.out.println("\nADIOS\n\n"); + } + + private static void printBullGradeInfo(BullGrade bullGrade) { + System.out.println("\n\nYOU HAVE DRAWN A " + bullGrade.name() + " BULL."); + if (bullGrade.equals(BullGrade.AWFUL)) { + System.out.println("YOU'RE LUCKY."); + } else if (bullGrade.equals(BullGrade.SUPERB)) { + System.out.println("GOOD LUCK. YOU'LL NEED IT."); + } + } + + private static void crowdReaction(GameState gameState, BullGrade bullGrade, Random random) { + System.out.println("\n\n"); + if (!gameState.matadorStatus.equals(MatadorStatus.DEFEATED)) { + if (!gameState.matadorStatus.equals(MatadorStatus.INJURED)) { + if (gameState.bullStatus.equals(BullStatus.DEAD)) { + System.out.println("THE CROWD CHEERS!"); + } + } else { + System.out.println("THE CROWD CHEERS WILDLY!"); + } + + System.out.println("\nTHE CROWD AWARDS YOU"); + var crowdReactionScore = calculateCrowdReactionScore(gameState, bullGrade, random); + if (crowdReactionScore < 2.4) { + System.out.println("NOTHING AT ALL."); + } else if (crowdReactionScore < 4.9) { + System.out.println("ONE EAR OF THE BULL."); + } else if (crowdReactionScore < 7.4) { + System.out.println("BOTH EARS OF THE BULL!"); + System.out.println("OLE!"); + } else { + System.out.println("OLE! YOU ARE 'MUY HOMBRE'!! OLE! OLE!"); + } + + } else { + System.out.println("THE CROWD BOOS FOR TEN MINUTES. IF YOU EVER DARE TO SHOW"); + System.out.println("YOUR FACE IN A RING AGAIN, THEY SWEAR THEY WILL KILL YOU--"); + System.out.println("UNLESS THE BULL DOES FIRST."); + } + } + + private static GameState handleCapeMove(int capeMove, Random random, Scanner scanner, BullGrade bullGrade, GameState gameState) { + double m; + if (capeMove == 0) { + m = 3; + } else if (capeMove == 1) { + m = 2; + } else { + //capeMove == 2 + m = 0.5; + } + gameState.capeMovesCumulative += m; + + double f = (6 - bullGrade.getValue() + m / 10) * random.nextFloat() / ((gameState.picadoresDamage + gameState.toreadoresDamage + gameState.passNumber / 10d) * 5); + if (f >= 0.51) { + System.out.println("THE BULL HAS GORED YOU!"); + gameState = stateAfterGoring(random, scanner, gameState, bullGrade); + } + return gameState; + } + + private static GameState stateAfterGoring(Random random, Scanner scanner, GameState gameState, BullGrade bullGrade) { + GameState newGameState = gameState.newCopy(); + if (random.nextBoolean()) { + System.out.println("YOU ARE DEAD."); + newGameState.matadorStatus = MatadorStatus.DEAD; + } else { + System.out.println("YOU ARE STILL ALIVE."); + newGameState = readAndHandleForfeitDecision(random, scanner, newGameState, bullGrade); + } + return newGameState; + } + + /** + * Calculate the crowd's reaction score based on the game state plus some randomness. + * (FNC in the original code on line 1390) + */ + private static double calculateCrowdReactionScore(GameState gameState, BullGrade bullGrade, Random random) { + return calculateGameScore(gameState, bullGrade) * random.nextFloat(); + } + + /** + * Calculates the ame score based on the current state and the selected bull grade. + * (FND in the original code on line 1395) + */ + private static double calculateGameScore(GameState gameState, BullGrade bullGrade) { + return (4.5 + gameState.capeMovesCumulative / 6 - (gameState.picadoresDamage + gameState.toreadoresDamage) * 2.5 + 4 * gameState.matadorStatus.getValue() + 2 * gameState.bullStatus.getValue() - Math.pow(gameState.passNumber, 2) / 120f - bullGrade.getValue()); + } + + private static int readInt(String prompt, Scanner scanner) { + System.out.print(prompt); + while (true) { + System.out.print("? "); + String input = scanner.nextLine(); + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); + } + } + } + + private static int readCapeMove(String initialPrompt, Scanner scanner) { + String prompt = initialPrompt; + while (true) { + int capeMove = readInt(prompt, scanner); + if (capeMove <= 0 || capeMove > 3) { + System.out.println("DON'T PANIC, YOU IDIOT! PUT DOWN A CORRECT NUMBER"); + prompt = ""; + } else { + return capeMove; + } + } + } + + private static BooleanAnswer readYesOrNo(Scanner scanner) { + while (true) { + String answer = scanner.nextLine(); + if (answer.equalsIgnoreCase("YES")) { + return BooleanAnswer.YES; + } else if (answer.equalsIgnoreCase("NO")) { + return BooleanAnswer.NO; + } else { + System.out.println("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'."); + } + } + } + + private static void printInstructions() { + System.out.print("HELLO, ALL YOU BLOODLOVERS AND AFICIONADOS.\n"); + System.out.print("HERE IS YOUR BIG CHANCE TO KILL A BULL.\n\n"); + System.out.print("ON EACH PASS OF THE BULL, YOU MAY TRY\n"); + System.out.print("0 - VERONICA (DANGEROUS INSIDE MOVE OF THE CAPE)\n"); + System.out.print("1 - LESS DANGEROUS OUTSIDE MOVE OF THE CAPE\n"); + System.out.print("2 - ORDINARY SWIRL OF THE CAPE.\n\n"); + System.out.print("INSTEAD OF THE ABOVE, YOU MAY TRY TO KILL THE BULL\n"); + System.out.print("ON ANY TURN: 4 (OVER THE HORNS), 5 (IN THE CHEST).\n"); + System.out.print("BUT IF I WERE YOU,\n"); + System.out.print("I WOULDN'T TRY IT BEFORE THE SEVENTH PASS.\n\n"); + System.out.print("THE CROWD WILL DETERMINE WHAT AWARD YOU DESERVE\n"); + System.out.print("(POSTHUMOUSLY IF NECESSARY).\n"); + System.out.print("THE BRAVER YOU ARE, THE BETTER THE AWARD YOU RECEIVE.\n\n"); + System.out.print("THE BETTER THE JOB THE PICADORES AND TOREADORES DO,\n"); + System.out.print("THE BETTER YOUR CHANCES ARE.\n\n"); + } + + private static void printHeader() { + System.out.println(" BULL"); + System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); + } + + /** + * Random number 1 or 2 + * FNA in the original + */ + private static int randomNumber1or2(Random random) { + return (int) (random.nextFloat() * 2 + 1); + } + + private static double firstStage(String horseType, String suffix, BullGrade bullGrade, Random random) { + double b = ((3d / bullGrade.getValue()) * random.nextFloat()); + double c; + + if (b < 0.37) { + c = 0.5; + } else if (b < 0.5) { + c = 0.4; + } else if (b < 0.63) { + c = 0.3; + } else if (b < 0.87) { + c = 0.2; + } else { + c = 0.1; + } + + int t = (int) (10 * c + 0.2); + + System.out.println("THE " + horseType + suffix + " DID A " + getPerformanceRating(t) + " JOB."); + + if (t >= 4) { + if (t == 5) { + if (!horseType.equals("TOREAD")) { + System.out.println(" " + randomNumber1or2(random) + " OF THE HORSES OF THE " + horseType + suffix + " KILLED."); + } + System.out.println(" " + randomNumber1or2(random) + " OF THE " + horseType + suffix + " KILLED."); + + } else { + if (random.nextBoolean()) { + System.out.println("ONE OF THE " + horseType + " " + suffix + " WAS KILLED."); + } else { + System.out.println("NO " + horseType + " " + suffix + " WERE KILLED."); + } + } + } + + System.out.println(); + return c; + } + + public static String getPerformanceRating(int t) { + switch (t) { + case 1: + return "SUPERB"; + case 2: + return "GOOD"; + case 3: + return "FAIR"; + case 4: + return "POOR"; + default: + return "AWFUL"; + } + } + + private static GameState attemptKillBull(BullGrade bullGrade, Random random, GameState gameState, Scanner scanner) { + GameState newGameState = gameState.newCopy(); + System.out.println("\nIT IS THE MOMENT OF TRUTH.\n"); + + int h = readInt("HOW DO YOU TRY TO KILL THE BULL", scanner); + if (h == 4 || h == 5) { + var K = (6 - bullGrade.getValue()) * 10 * random.nextFloat() / ((gameState.picadoresDamage + gameState.toreadoresDamage) * 5 * gameState.passNumber); + if (h == 4) { + if (K > 0.8) { + System.out.println("THE BULL HAS GORED YOU!"); + newGameState = stateAfterGoring(random, scanner, newGameState, bullGrade); + } else { + System.out.println("YOU KILLED THE BULL!"); + newGameState.bullStatus = BullStatus.DEAD; + return newGameState;//game over + } + } else { + if (K > 0.2) { + System.out.println("THE BULL HAS GORED YOU!"); + newGameState = stateAfterGoring(random, scanner, newGameState, bullGrade); + } else { + System.out.println("YOU KILLED THE BULL!"); + newGameState.bullStatus = BullStatus.DEAD; + return newGameState;//game over + } + } + + } else { + System.out.println("YOU PANICKED. THE BULL GORED YOU."); + if (random.nextBoolean()) { + System.out.println("YOU ARE DEAD."); + newGameState.matadorStatus = MatadorStatus.DEAD; + return newGameState; + } else { + return readAndHandleForfeitDecision(random, scanner, newGameState, bullGrade); + } + + } + + return newGameState; + } + + private static GameState readAndHandleForfeitDecision(Random random, Scanner scanner, GameState gameState, BullGrade bullGrade) { + GameState newGameState = gameState.newCopy(); + + System.out.print("\nDO YOU RUN FROM THE RING? "); + BooleanAnswer yesOrNo = readYesOrNo(scanner); + if (yesOrNo == BooleanAnswer.NO) { + System.out.println("\n\nYOU ARE BRAVE. STUPID, BUT BRAVE."); + if (random.nextBoolean()) { + newGameState.matadorStatus = MatadorStatus.INJURED; + return newGameState; + } else { + System.out.println("YOU ARE GORED AGAIN!"); + return stateAfterGoring(random, scanner, newGameState, bullGrade); + } + } else { + System.out.println("COWARD"); + newGameState.matadorStatus = MatadorStatus.DEFEATED; + return newGameState; + + } + } + + private static boolean readInstructionsChoice(Scanner scan) { + System.out.print("DO YOU WANT INSTRUCTIONS? "); + + String choice = scan.nextLine(); + return !choice.equalsIgnoreCase("NO"); + } + + + private enum BooleanAnswer { + YES, NO + } + + private enum BullGrade { + SUPERB(1), + GOOD(2), + FAIR(3), + POOR(4), + AWFUL(5); + + private final int value; + + BullGrade(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static BullGrade fromValue(int value) { + for (BullGrade grade : BullGrade.values()) { + if (grade.getValue() == value) { + return grade; + } + } + throw new IllegalArgumentException("Invalid value: " + value); + } + + } + + /** + * Represents the game state. + */ + private static class GameState { + private MatadorStatus matadorStatus; //D[4] in the original + private BullStatus bullStatus; //D[5] in the original + private double picadoresDamage; //D[1] in the original + private double toreadoresDamage; //D[2] in the original + private int passNumber; //D[3] in the original + private double capeMovesCumulative; //L in the original + + public GameState() { + picadoresDamage = 0; + toreadoresDamage = 0; + passNumber = 0; + matadorStatus = MatadorStatus.ALIVE; + bullStatus = BullStatus.ALIVE; + capeMovesCumulative = 1; + } + + public GameState newCopy() { + GameState newState = new GameState(); + newState.matadorStatus = this.matadorStatus; + newState.bullStatus = this.bullStatus; + newState.picadoresDamage = this.picadoresDamage; + newState.toreadoresDamage = this.toreadoresDamage; + newState.passNumber = this.passNumber; + newState.capeMovesCumulative = this.capeMovesCumulative; + return newState; + } + } + + private enum MatadorStatus { + ALIVE(1), + INJURED(2), + DEAD(1.5), + DEFEATED(0); + + private final double value; + + MatadorStatus(double value) { + this.value = value; + } + + public double getValue() { + return value; + } + } + + private enum BullStatus { + ALIVE(1), + DEAD(2); + + private final double value; + + BullStatus(double value) { + this.value = value; + } + + public double getValue() { + return value; + } + } + +} + diff --git a/README.md b/README.md index f714bc89b..17b828ff0 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ NOTE: per [the official blog post announcement](https://blog.codinghorror.com/up | 14_Bowling | x | x | x | | | x | x | | | x | | 15_Boxing | x | x | x | | | x | x | | | x | | 16_Bug | x | x | x | | | | x | x | | x | -| 17_Bullfight | x | | x | x | | | x | | | x | +| 17_Bullfight | x | x | x | x | | | x | | | x | | 18_Bullseye | x | x | x | | | x | x | | x | x | | 19_Bunny | x | x | x | | | x | x | x | | x | | 20_Buzzword | x | x | x | | x | x | x | x | x | x |