From a7182ed402f665bbbafa3c6addb055b880f2e803 Mon Sep 17 00:00:00 2001 From: journich <70119791+journich@users.noreply.github.com> Date: Wed, 24 Feb 2021 11:48:12 +1030 Subject: [PATCH] Java version of the Bug Basic game --- 16 Bug/java/src/Bug.java | 254 +++++++++++++++++++++ 16 Bug/java/src/BugGame.java | 10 + 16 Bug/java/src/ComputerBug.java | 15 ++ 16 Bug/java/src/Insect.java | 363 +++++++++++++++++++++++++++++++ 16 Bug/java/src/PlayerBug.java | 18 ++ 5 files changed, 660 insertions(+) create mode 100644 16 Bug/java/src/Bug.java create mode 100644 16 Bug/java/src/BugGame.java create mode 100644 16 Bug/java/src/ComputerBug.java create mode 100644 16 Bug/java/src/Insect.java create mode 100644 16 Bug/java/src/PlayerBug.java diff --git a/16 Bug/java/src/Bug.java b/16 Bug/java/src/Bug.java new file mode 100644 index 00000000..626615de --- /dev/null +++ b/16 Bug/java/src/Bug.java @@ -0,0 +1,254 @@ +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Game of Bug + *

+ * Based on the Basic game of Bug here + * https://github.com/coding-horror/basic-computer-games/blob/main/16%20Bug/bug.bas + *

+ * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing + * new features - no additional text, error checking, etc has been added. + */ +public class Bug { + + // Dice roll + public static final int SIX = 6; + + private enum GAME_STATE { + START, + PLAYER_TURN, + COMPUTER_TURN, + CHECK_FOR_WINNER, + GAME_OVER + } + + // Used for keyboard input + private final Scanner kbScanner; + + // Current game state + private GAME_STATE gameState; + + + private final Insect playersBug; + + private final Insect computersBug; + + // Used to show the result of dice roll. + private final String[] ROLLS = new String[]{"BODY", "NECK", "HEAD", "FEELERS", "TAIL", "LEGS"}; + + public Bug() { + + playersBug = new PlayerBug(); + computersBug = new ComputerBug(); + + gameState = GAME_STATE.START; + + // Initialise kb scanner + kbScanner = new Scanner(System.in); + } + + /** + * Main game loop + */ + public void play() { + + do { + switch (gameState) { + + // Show an introduction the first time the game is played. + // And optionally instructions. + case START: + intro(); + if (!noEntered(displayTextAndGetInput("DO YOU WANT INSTRUCTIONS? "))) { + instructions(); + } + + gameState = GAME_STATE.PLAYER_TURN; + break; + + case PLAYER_TURN: + int playersRoll = randomNumber(); + System.out.println("YOU ROLLED A " + playersRoll + "=" + ROLLS[playersRoll - 1]); + switch (playersRoll) { + case 1: + System.out.println(playersBug.addBody()); + break; + case 2: + System.out.println(playersBug.addNeck()); + break; + case 3: + System.out.println(playersBug.addHead()); + break; + case 4: + System.out.println(playersBug.addFeelers()); + break; + case 5: + System.out.println(playersBug.addTail()); + break; + case 6: + System.out.println(playersBug.addLeg()); + break; + } + + gameState = GAME_STATE.COMPUTER_TURN; + break; + + case COMPUTER_TURN: + int computersRoll = randomNumber(); + System.out.println("I ROLLED A " + computersRoll + "=" + ROLLS[computersRoll - 1]); + switch (computersRoll) { + case 1: + System.out.println(computersBug.addBody()); + break; + case 2: + System.out.println(computersBug.addNeck()); + break; + case 3: + System.out.println(computersBug.addHead()); + break; + case 4: + System.out.println(computersBug.addFeelers()); + break; + case 5: + System.out.println(computersBug.addTail()); + break; + case 6: + System.out.println(computersBug.addLeg()); + break; + } + + gameState = GAME_STATE.CHECK_FOR_WINNER; + break; + + case CHECK_FOR_WINNER: + boolean gameOver = false; + + if (playersBug.complete()) { + System.out.println("YOUR BUG IS FINISHED."); + gameOver = true; + } else if (computersBug.complete()) { + System.out.println("MY BUG IS FINISHED."); + gameOver = true; + } + + if (noEntered(displayTextAndGetInput("DO YOU WANT THE PICTURES? "))) { + gameState = GAME_STATE.PLAYER_TURN; + } else { + System.out.println("*****YOUR BUG*****"); + System.out.println(); + draw(playersBug); + + System.out.println(); + System.out.println("*****MY BUG*****"); + System.out.println(); + draw(computersBug); + gameState = GAME_STATE.PLAYER_TURN; + + if (gameOver) { + System.out.println("I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!"); + gameState = GAME_STATE.GAME_OVER; + } + } + } + } while (gameState != GAME_STATE.GAME_OVER); + } + + /** + * Draw the bug (player or computer) based on what has + * already been added to it. + * + * @param bug The bug to be drawn. + */ + private void draw(Insect bug) { + ArrayList insectOutput = bug.draw(); + for (String s : insectOutput) { + System.out.println(s); + } + } + + /** + * Display an intro + */ + private void intro() { + System.out.println("BUG"); + System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); + System.out.println(); + System.out.println("THE GAME BUG"); + System.out.println("I HOPE YOU ENJOY THIS GAME."); + } + + private void instructions() { + System.out.println("THE OBJECT OF BUG IS TO FINISH YOUR BUG BEFORE I FINISH"); + System.out.println("MINE. EACH NUMBER STANDS FOR A PART OF THE BUG BODY."); + System.out.println("I WILL ROLL THE DIE FOR YOU, TELL YOU WHAT I ROLLED FOR YOU"); + System.out.println("WHAT THE NUMBER STANDS FOR, AND IF YOU CAN GET THE PART."); + System.out.println("IF YOU CAN GET THE PART I WILL GIVE IT TO YOU."); + System.out.println("THE SAME WILL HAPPEN ON MY TURN."); + System.out.println("IF THERE IS A CHANGE IN EITHER BUG I WILL GIVE YOU THE"); + System.out.println("OPTION OF SEEING THE PICTURES OF THE BUGS."); + System.out.println("THE NUMBERS STAND FOR PARTS AS FOLLOWS:"); + System.out.println("NUMBER\tPART\tNUMBER OF PART NEEDED"); + System.out.println("1\tBODY\t1"); + System.out.println("2\tNECK\t1"); + System.out.println("3\tHEAD\t1"); + System.out.println("4\tFEELERS\t2"); + System.out.println("5\tTAIL\t1"); + System.out.println("6\tLEGS\t6"); + System.out.println(); + + } + + /** + * Checks whether player entered N or NO to a question. + * + * @param text player string from kb + * @return true if N or NO was entered, otherwise false + */ + private boolean noEntered(String text) { + return stringIsAnyValue(text, "N", "NO"); + } + + /** + * Check whether a string equals one of a variable number of values + * Useful to check for Y or YES for example + * Comparison is case insensitive. + * + * @param text source string + * @param values a range of values to compare against the source string + * @return true if a comparison was found in one of the variable number of strings passed + */ + private boolean stringIsAnyValue(String text, String... values) { + + // Cycle through the variable number of values and test each + for (String val : values) { + if (text.equalsIgnoreCase(val)) { + return true; + } + } + + // no matches + return false; + } + + /* + * Print a message on the screen, then accept input from Keyboard. + * + * @param text message to be displayed on screen. + * @return what was typed by the player. + */ + private String displayTextAndGetInput(String text) { + System.out.print(text); + return kbScanner.next(); + } + + /** + * Generate random number + * + * @return random number + */ + private int randomNumber() { + return (int) (Math.random() + * (SIX) + 1); + } +} \ No newline at end of file diff --git a/16 Bug/java/src/BugGame.java b/16 Bug/java/src/BugGame.java new file mode 100644 index 00000000..074ff45e --- /dev/null +++ b/16 Bug/java/src/BugGame.java @@ -0,0 +1,10 @@ + + +public class BugGame { + + public static void main(String[] args) { + + Bug bug = new Bug(); + bug.play(); + } +} diff --git a/16 Bug/java/src/ComputerBug.java b/16 Bug/java/src/ComputerBug.java new file mode 100644 index 00000000..fe232364 --- /dev/null +++ b/16 Bug/java/src/ComputerBug.java @@ -0,0 +1,15 @@ +public class ComputerBug extends Insect { + + // Create messages specific to the computer player. + + public ComputerBug() { + // Call superclass constructor for initialization. + super(); + addMessages(new String[]{"I GET A FEELER.", "I HAVE " + MAX_FEELERS + " FEELERS ALREADY.", "I DO NOT HAVE A HEAD."}, PARTS.FEELERS); + addMessages(new String[]{"I NEEDED A HEAD.", "I DO NOT NEED A HEAD.", "I DO NOT HAVE A NECK."}, PARTS.HEAD); + addMessages(new String[]{"I NOW HAVE A NECK.", "I DO NOT NEED A NECK.", "I DO NOT HAVE A BODY."}, PARTS.NECK); + addMessages(new String[]{"I NOW HAVE A BODY.", "I DO NOT NEED A BODY."}, PARTS.BODY); + addMessages(new String[]{"I NOW HAVE A TAIL.", "I DO NOT NEED A TAIL.", "I DO NOT HAVE A BODY."}, PARTS.TAIL); + addMessages(new String[]{"I NOW HAVE ^^^" + " LEG", "I HAVE " + MAX_LEGS + " FEET.", "I DO NOT HAVE A BODY."}, PARTS.LEGS); + } +} diff --git a/16 Bug/java/src/Insect.java b/16 Bug/java/src/Insect.java new file mode 100644 index 00000000..08e9414f --- /dev/null +++ b/16 Bug/java/src/Insect.java @@ -0,0 +1,363 @@ +import java.util.ArrayList; +import java.util.Arrays; + +/** + * This tracks the insect (bug) and has methods to + * add body parts, create an array of output so it + * can be drawn and to determine if a bug is complete. + * N.B. This is a super class for ComputerBug and PlayerBug + */ +public class Insect { + + public static final int MAX_FEELERS = 2; + public static final int MAX_LEGS = 6; + + public static final int ADDED = 0; + public static final int NOT_ADDED = 1; + public static final int MISSING = 2; + + // Various parts of the bug + public enum PARTS { + FEELERS, + HEAD, + NECK, + BODY, + TAIL, + LEGS + } + + // Tracks what parts of the bug have been added + private boolean body; + private boolean neck; + private boolean head; + private int feelers; + private boolean tail; + private int legs; + + // Messages about for various body parts + // These are set in the subclass ComputerBug or PlayerBug + private String[] bodyMessages; + private String[] neckMessages; + private String[] headMessages; + private String[] feelerMessages; + private String[] tailMessages; + private String[] legMessages; + + public Insect() { + init(); + } + + /** + * Add a body to the bug if there is not one already added. + * + * @return return an appropriate message about the status of the operation. + */ + public String addBody() { + + boolean currentState = false; + + if (!body) { + body = true; + currentState = true; + } + + return addBodyMessage(currentState); + } + + /** + * Create output based on adding the body or it being already added previously + * + * @return contains the output message + */ + + private String addBodyMessage(boolean wasAdded) { + + // Return the appropriate message depending on whether the + // body was added or not. + if (wasAdded) { + return bodyMessages[ADDED]; + } else { + return bodyMessages[NOT_ADDED]; + } + } + + /** + * Add a neck if a) a body has previously been added and + * b) a neck has not previously been added. + * + * @return text containing the status of the operation + */ + public String addNeck() { + + int status = NOT_ADDED; // Default is not added + + if (!body) { + // No body, cannot add a neck + status = MISSING; + } else if (!neck) { + neck = true; + status = ADDED; + } + + return neckMessages[status]; + } + + /** + * Add a head to the bug if a) there already exists a neck and + * b) a head has not previously been added + * + * @return text outlining the success of the operation + */ + public String addHead() { + + int status = NOT_ADDED; // Default is not added + + if (!neck) { + // No neck, cannot add a head + status = MISSING; + } else if (!head) { + head = true; + status = ADDED; + } + + return headMessages[status]; + } + + /** + * Add a feeler to the head if a) there has been a head added to + * the bug previously, and b) there are not already 2 (MAX_FEELERS) + * feelers previously added to the bug. + * + * @return text outlining the status of the operation + */ + public String addFeelers() { + + int status = NOT_ADDED; // Default is not added + + if (!head) { + // No head, cannot add a feeler + status = MISSING; + } else if (feelers < MAX_FEELERS) { + feelers++; + status = ADDED; + } + + return feelerMessages[status]; + } + + /** + * Add a tail to the bug if a) there is already a body previously added + * to the bug and b) there is not already a tail added. + * + * @return text outlining the status of the operation. + */ + public String addTail() { + + int status = NOT_ADDED; // Default is not added + + if (!body) { + // No body, cannot add a tail + status = MISSING; + } else if (!tail) { + tail = true; + status = ADDED; + } + + return tailMessages[status]; + } + + /** + * Add a leg to the bug if a) there is already a body previously added + * b) there are less than 6 (MAX_LEGS) previously added. + * + * @return text outlining status of the operation. + */ + public String addLeg() { + + int status = NOT_ADDED; // Default is not added + + if (!body) { + // No body, cannot add a leg + status = MISSING; + } else if (legs < MAX_LEGS) { + legs++; + status = ADDED; + } + + String message = ""; + + // Create a string showing the result of the operation + + switch(status) { + case ADDED: + // Replace # with number of legs + message = legMessages[status].replace("^^^", String.valueOf(legs)); + // Add text S. if >1 leg, or just . if one leg. + if (legs > 1) { + message += "S."; + } else { + message += "."; + } + break; + + case NOT_ADDED: + + // Deliberate fall through to next case as its the + // same code to be executed + case MISSING: + message = legMessages[status]; + break; + } + + return message; + } + + /** + * Initialise + */ + public void init() { + body = false; + neck = false; + head = false; + feelers = 0; + tail = false; + legs = 0; + } + + /** + * Add unique messages depending on type of player + * A subclass of this class calls this method + * e.g. See ComputerBug or PlayerBug classes + * + * @param messages an array of messages + * @param bodyPart the bodypart the messages relate to. + */ + public void addMessages(String[] messages, PARTS bodyPart) { + + switch (bodyPart) { + case FEELERS: + feelerMessages = messages; + break; + + case HEAD: + headMessages = messages; + break; + + case NECK: + neckMessages = messages; + break; + + case BODY: + bodyMessages = messages; + break; + + case TAIL: + tailMessages = messages; + break; + + case LEGS: + legMessages = messages; + break; + } + } + + /** + * Returns a string array containing + * the "bug" that can be output to console + * + * @return the bug ready to draw + */ + public ArrayList draw() { + ArrayList bug = new ArrayList<>(); + StringBuilder lineOutput; + + // Feelers + if (feelers > 0) { + for (int i = 0; i < 4; i++) { + lineOutput = new StringBuilder(addSpaces(10)); + for (int j = 0; j < feelers; j++) { + lineOutput.append("A "); + } + bug.add(lineOutput.toString()); + } + } + + if (head) { + lineOutput = new StringBuilder(addSpaces(8) + "HHHHHHH"); + bug.add(lineOutput.toString()); + lineOutput = new StringBuilder(addSpaces(8) + "H" + addSpaces(5) + "H"); + bug.add(lineOutput.toString()); + lineOutput = new StringBuilder(addSpaces(8) + "H O O H"); + bug.add(lineOutput.toString()); + lineOutput = new StringBuilder(addSpaces(8) + "H" + addSpaces(5) + "H"); + bug.add(lineOutput.toString()); + lineOutput = new StringBuilder(addSpaces(8) + "H" + addSpaces(2) + "V" + addSpaces(2) + "H"); + bug.add(lineOutput.toString()); + lineOutput = new StringBuilder(addSpaces(8) + "HHHHHHH"); + bug.add(lineOutput.toString()); + } + + if (neck) { + for (int i = 0; i < 2; i++) { + lineOutput = new StringBuilder(addSpaces(10) + "N N"); + bug.add(lineOutput.toString()); + } + } + + if (body) { + lineOutput = new StringBuilder(addSpaces(5) + "BBBBBBBBBBBB"); + bug.add(lineOutput.toString()); + for (int i = 0; i < 2; i++) { + lineOutput = new StringBuilder(addSpaces(5) + "B" + addSpaces(10) + "B"); + bug.add(lineOutput.toString()); + } + if (tail) { + lineOutput = new StringBuilder("TTTTTB" + addSpaces(10) + "B"); + bug.add(lineOutput.toString()); + } + lineOutput = new StringBuilder(addSpaces(5) + "BBBBBBBBBBBB"); + bug.add(lineOutput.toString()); + } + + if (legs > 0) { + for (int i = 0; i < 2; i++) { + lineOutput = new StringBuilder(addSpaces(5)); + for (int j = 0; j < legs; j++) { + lineOutput.append(" L"); + } + bug.add(lineOutput.toString()); + } + } + + return bug; + } + + /** + * Check if the bug is complete i.e. it has + * 2 (MAX_FEELERS) feelers, a head, a neck, a body + * a tail and 6 (MAX_FEET) feet. + * + * @return true if complete. + */ + public boolean complete() { + return (feelers == MAX_FEELERS) + && head + && neck + && body + && tail + && (legs == MAX_LEGS); + } + + /** + * Simulate tabs be creating a string of X spaces. + * + * @param number contains number of spaces needed. + * @return a String containing the spaces + */ + private String addSpaces(int number) { + char[] spaces = new char[number]; + Arrays.fill(spaces, ' '); + return new String(spaces); + + } +} \ No newline at end of file diff --git a/16 Bug/java/src/PlayerBug.java b/16 Bug/java/src/PlayerBug.java new file mode 100644 index 00000000..1a82e8b3 --- /dev/null +++ b/16 Bug/java/src/PlayerBug.java @@ -0,0 +1,18 @@ +public class PlayerBug extends Insect { + + // Create messages specific to the player. + + public PlayerBug() { + // Call superclass constructor for initialization. + super(); + addMessages(new String[]{"I NOW GIVE YOU A FEELER.", "YOU HAVE " + MAX_FEELERS + " FEELERS ALREADY.", "YOU DO NOT HAVE A HEAD."}, PARTS.FEELERS); + addMessages(new String[]{"YOU NEEDED A HEAD.", "YOU HAVE A HEAD.", "YOU DO NOT HAVE A NECK."}, PARTS.HEAD); + addMessages(new String[]{"YOU NOW HAVE A NECK.", "YOU DO NOT NEED A NECK.", "YOU DO NOT HAVE A BODY."}, PARTS.NECK); + addMessages(new String[]{"YOU NOW HAVE A BODY.", "YOU DO NOT NEED A BODY."}, PARTS.BODY); + addMessages(new String[]{"I NOW GIVE YOU A TAIL.", "YOU ALREADY HAVE A TAIL.", "YOU DO NOT HAVE A BODY."}, PARTS.TAIL); + addMessages(new String[]{"YOU NOW HAVE ^^^ LEG", "YOU HAVE " + MAX_LEGS + " FEET ALREADY.", "YOU DO NOT HAVE A BODY."}, PARTS.LEGS); + } + + +} +