mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 07:29:02 -08:00
Java version of Bombs Away
This commit is contained in:
505
12 Bombs Away/java/src/BombsAway.java
Normal file
505
12 Bombs Away/java/src/BombsAway.java
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game of Bombs Away
|
||||||
|
*
|
||||||
|
* Based on the Basic game of Bombs Away here
|
||||||
|
* https://github.com/coding-horror/basic-computer-games/blob/main/12%20Bombs%20Away/bombsaway.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 BombsAway {
|
||||||
|
|
||||||
|
public static final int MAX_PILOT_MISSIONS = 160;
|
||||||
|
public static final int MAX_CASUALTIES = 100;
|
||||||
|
public static final int MISSED_TARGET_CONST_1 = 2;
|
||||||
|
public static final int MISSED_TARGET_CONST_2 = 30;
|
||||||
|
public static final int CHANCE_OF_BEING_SHOT_DOWN_BASE = 100;
|
||||||
|
public static final double SIXTY_FIVE_PERCENT = .65;
|
||||||
|
|
||||||
|
private enum GAME_STATE {
|
||||||
|
START,
|
||||||
|
CHOOSE_SIDE,
|
||||||
|
CHOOSE_PLANE,
|
||||||
|
CHOOSE_TARGET,
|
||||||
|
CHOOSE_MISSIONS,
|
||||||
|
CHOOSE_ENEMY_DEFENCES,
|
||||||
|
FLY_MISSION,
|
||||||
|
DIRECT_HIT,
|
||||||
|
MISSED_TARGET,
|
||||||
|
PROCESS_FLAK,
|
||||||
|
SHOT_DOWN,
|
||||||
|
MADE_IT_THROUGH_FLAK,
|
||||||
|
PLAY_AGAIN,
|
||||||
|
GAME_OVER
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SIDE {
|
||||||
|
ITALY(1),
|
||||||
|
ALLIES(2),
|
||||||
|
JAPAN(3),
|
||||||
|
GERMANY(4);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
SIDE(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TARGET {
|
||||||
|
ALBANIA(1),
|
||||||
|
GREECE(2),
|
||||||
|
NORTH_AFRICA(3),
|
||||||
|
RUSSIA(4),
|
||||||
|
ENGLAND(5),
|
||||||
|
FRANCE(6);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
TARGET(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ENEMY_DEFENCES {
|
||||||
|
GUNS(1),
|
||||||
|
MISSILES(2),
|
||||||
|
BOTH(3);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
ENEMY_DEFENCES(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AIRCRAFT {
|
||||||
|
LIBERATOR(1),
|
||||||
|
B29(2),
|
||||||
|
B17(3),
|
||||||
|
LANCASTER(4);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
AIRCRAFT(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for keyboard input
|
||||||
|
private final Scanner kbScanner;
|
||||||
|
|
||||||
|
// Current game state
|
||||||
|
private GAME_STATE gameState;
|
||||||
|
|
||||||
|
private SIDE side;
|
||||||
|
|
||||||
|
private int missions;
|
||||||
|
|
||||||
|
private int chanceToHit;
|
||||||
|
private int percentageHitRateOfGunners;
|
||||||
|
|
||||||
|
public BombsAway() {
|
||||||
|
|
||||||
|
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.
|
||||||
|
case START:
|
||||||
|
intro();
|
||||||
|
chanceToHit = 0;
|
||||||
|
percentageHitRateOfGunners = 0;
|
||||||
|
|
||||||
|
gameState = GAME_STATE.CHOOSE_SIDE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHOOSE_SIDE:
|
||||||
|
side = getSide("WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4) ? ");
|
||||||
|
if (side == null) {
|
||||||
|
System.out.println("TRY AGAIN...");
|
||||||
|
} else {
|
||||||
|
// Different game paths depending on which side was chosen
|
||||||
|
switch (side) {
|
||||||
|
case ITALY:
|
||||||
|
case GERMANY:
|
||||||
|
gameState = GAME_STATE.CHOOSE_TARGET;
|
||||||
|
break;
|
||||||
|
case ALLIES:
|
||||||
|
case JAPAN:
|
||||||
|
gameState = GAME_STATE.CHOOSE_PLANE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHOOSE_TARGET:
|
||||||
|
String prompt;
|
||||||
|
if (side == SIDE.ITALY) {
|
||||||
|
prompt = "YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3) ? ";
|
||||||
|
} else {
|
||||||
|
// Germany
|
||||||
|
System.out.println("A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),");
|
||||||
|
prompt = "ENGLAND(2), OR FRANCE(3) ? ";
|
||||||
|
}
|
||||||
|
TARGET target = getTarget(prompt);
|
||||||
|
if (target == null) {
|
||||||
|
System.out.println("TRY AGAIN...");
|
||||||
|
} else {
|
||||||
|
displayTargetMessage(target);
|
||||||
|
gameState = GAME_STATE.CHOOSE_MISSIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CHOOSE_MISSIONS:
|
||||||
|
missions = getNumberFromKeyboard("HOW MANY MISSIONS HAVE YOU FLOWN? ");
|
||||||
|
|
||||||
|
if(missions <25) {
|
||||||
|
System.out.println("FRESH OUT OF TRAINING, EH?");
|
||||||
|
gameState = GAME_STATE.FLY_MISSION;
|
||||||
|
} else if(missions < 100) {
|
||||||
|
System.out.println("THAT'S PUSHING THE ODDS!");
|
||||||
|
gameState = GAME_STATE.FLY_MISSION;
|
||||||
|
} else if(missions >=160) {
|
||||||
|
System.out.println("MISSIONS, NOT MILES...");
|
||||||
|
System.out.println("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS.");
|
||||||
|
System.out.println("NOW THEN, ");
|
||||||
|
} else {
|
||||||
|
// No specific message if missions is 100-159, but still valid
|
||||||
|
gameState = GAME_STATE.FLY_MISSION;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHOOSE_PLANE:
|
||||||
|
switch(side) {
|
||||||
|
case ALLIES:
|
||||||
|
AIRCRAFT plane = getPlane("AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)? ");
|
||||||
|
if(plane == null) {
|
||||||
|
System.out.println("TRY AGAIN...");
|
||||||
|
} else {
|
||||||
|
switch(plane) {
|
||||||
|
|
||||||
|
case LIBERATOR:
|
||||||
|
System.out.println("YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.");
|
||||||
|
break;
|
||||||
|
case B29:
|
||||||
|
System.out.println("YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.");
|
||||||
|
break;
|
||||||
|
case B17:
|
||||||
|
System.out.println("YOU'RE CHASING THE BISMARK IN THE NORTH SEA.");
|
||||||
|
break;
|
||||||
|
case LANCASTER:
|
||||||
|
System.out.println("YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameState = GAME_STATE.CHOOSE_MISSIONS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JAPAN:
|
||||||
|
System.out.println("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.");
|
||||||
|
if(yesEntered(displayTextAndGetInput("YOUR FIRST KAMIKAZE MISSION(Y OR N) ? "))) {
|
||||||
|
if(randomNumber(1) > SIXTY_FIVE_PERCENT) {
|
||||||
|
gameState = GAME_STATE.DIRECT_HIT;
|
||||||
|
} else {
|
||||||
|
// It's a miss
|
||||||
|
gameState = GAME_STATE.MISSED_TARGET;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gameState = GAME_STATE.PROCESS_FLAK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FLY_MISSION:
|
||||||
|
double missionResult = (MAX_PILOT_MISSIONS * randomNumber(1));
|
||||||
|
if(missions > missionResult) {
|
||||||
|
gameState = GAME_STATE.DIRECT_HIT;
|
||||||
|
} else {
|
||||||
|
gameState = GAME_STATE.MISSED_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DIRECT_HIT:
|
||||||
|
System.out.println("DIRECT HIT!!!! " + (int) Math.round(randomNumber(MAX_CASUALTIES)) + " KILLED.");
|
||||||
|
System.out.println("MISSION SUCCESSFUL.");
|
||||||
|
gameState = GAME_STATE.PLAY_AGAIN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MISSED_TARGET:
|
||||||
|
System.out.println("MISSED TARGET BY " + (int) Math.round(MISSED_TARGET_CONST_1 + MISSED_TARGET_CONST_2 * (randomNumber(1))) + " MILES!");
|
||||||
|
System.out.println("NOW YOU'RE REALLY IN FOR IT !!");
|
||||||
|
System.out.println();
|
||||||
|
gameState = GAME_STATE.CHOOSE_ENEMY_DEFENCES;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHOOSE_ENEMY_DEFENCES:
|
||||||
|
boolean bothWeapons = true;
|
||||||
|
|
||||||
|
ENEMY_DEFENCES enemyDefences = getEnemyDefences("DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3) ? ");
|
||||||
|
if(enemyDefences == null) {
|
||||||
|
System.out.println("TRY AGAIN...");
|
||||||
|
} else {
|
||||||
|
switch(enemyDefences) {
|
||||||
|
case MISSILES:
|
||||||
|
case GUNS:
|
||||||
|
bothWeapons = false;
|
||||||
|
|
||||||
|
// fall through on purpose to BOTH since its pretty much identical code other than the chance to hit
|
||||||
|
// increasing if both weapons are part of the defence.
|
||||||
|
|
||||||
|
case BOTH:
|
||||||
|
percentageHitRateOfGunners = getNumberFromKeyboard("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ");
|
||||||
|
if(percentageHitRateOfGunners < 10) {
|
||||||
|
System.out.println("YOU LIE, BUT YOU'LL PAY...");
|
||||||
|
}
|
||||||
|
if(bothWeapons) {
|
||||||
|
chanceToHit = 35;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gameState = GAME_STATE.PROCESS_FLAK;
|
||||||
|
|
||||||
|
// Determine if the players airplan makes it through the Flak.
|
||||||
|
case PROCESS_FLAK:
|
||||||
|
double calc = (CHANCE_OF_BEING_SHOT_DOWN_BASE * randomNumber(1));
|
||||||
|
|
||||||
|
if ((chanceToHit + percentageHitRateOfGunners) > calc) {
|
||||||
|
gameState = GAME_STATE.SHOT_DOWN;
|
||||||
|
} else {
|
||||||
|
gameState = GAME_STATE.MADE_IT_THROUGH_FLAK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHOT_DOWN:
|
||||||
|
System.out.println("* * * * BOOM * * * *");
|
||||||
|
System.out.println("YOU HAVE BEEN SHOT DOWN.....");
|
||||||
|
System.out.println("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR");
|
||||||
|
System.out.println("LAST TRIBUTE...");
|
||||||
|
gameState = GAME_STATE.PLAY_AGAIN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MADE_IT_THROUGH_FLAK:
|
||||||
|
System.out.println("YOU MADE IT THROUGH TREMENDOUS FLAK!!");
|
||||||
|
gameState = GAME_STATE.PLAY_AGAIN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PLAY_AGAIN:
|
||||||
|
if(yesEntered(displayTextAndGetInput("ANOTHER MISSION (Y OR N) ? "))) {
|
||||||
|
gameState = GAME_STATE.START;
|
||||||
|
} else {
|
||||||
|
System.out.println("CHICKEN !!!");
|
||||||
|
gameState = GAME_STATE.GAME_OVER;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (gameState != GAME_STATE.GAME_OVER) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a (brief) intro
|
||||||
|
*/
|
||||||
|
public void intro() {
|
||||||
|
System.out.println("YOU ARE A PILOT IN A WORLD WAR II BOMBER.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the side the player is going to play on.
|
||||||
|
* @param message displayed before the kb input
|
||||||
|
* @return the SIDE enum selected by the player
|
||||||
|
*/
|
||||||
|
private SIDE getSide(String message) {
|
||||||
|
int valueEntered = getNumberFromKeyboard(message);
|
||||||
|
for(SIDE side : SIDE.values()) {
|
||||||
|
if(side.getValue() == valueEntered) {
|
||||||
|
return side;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input out of range
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the target the player is going for.
|
||||||
|
* @param message displayed before the kb input
|
||||||
|
* @return the TARGET enum selected by the player
|
||||||
|
*/
|
||||||
|
private TARGET getTarget(String message) {
|
||||||
|
int valueEntered = getNumberFromKeyboard(message);
|
||||||
|
|
||||||
|
for(TARGET target : TARGET.values()) {
|
||||||
|
if(target.getValue() == valueEntered) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input out of range
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the airplane the player is going to fly.
|
||||||
|
* @param message displayed before the kb input
|
||||||
|
* @return the AIRCRAFT enum selected by the player
|
||||||
|
*/
|
||||||
|
private AIRCRAFT getPlane(String message) {
|
||||||
|
int valueEntered = getNumberFromKeyboard(message);
|
||||||
|
|
||||||
|
for(AIRCRAFT plane : AIRCRAFT.values()) {
|
||||||
|
if(plane.getValue() == valueEntered) {
|
||||||
|
return plane;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input out of range
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the type of enemy defences.
|
||||||
|
*
|
||||||
|
* @param message displayed before kb input
|
||||||
|
* @return the ENEMY_DEFENCES enum as selected by player
|
||||||
|
*/
|
||||||
|
private ENEMY_DEFENCES getEnemyDefences(String message) {
|
||||||
|
int valueEntered = getNumberFromKeyboard(message);
|
||||||
|
for (ENEMY_DEFENCES enemyDefences : ENEMY_DEFENCES.values()) {
|
||||||
|
if(enemyDefences.getValue() == valueEntered) {
|
||||||
|
return enemyDefences;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input out of range
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// output a specific message based on the target selected
|
||||||
|
private void displayTargetMessage(TARGET target) {
|
||||||
|
|
||||||
|
switch (target) {
|
||||||
|
|
||||||
|
case ALBANIA:
|
||||||
|
System.out.println("SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.");
|
||||||
|
break;
|
||||||
|
case GREECE:
|
||||||
|
System.out.println("BE CAREFUL!!!");
|
||||||
|
break;
|
||||||
|
case NORTH_AFRICA:
|
||||||
|
System.out.println("YOU'RE GOING FOR THE OIL, EH?");
|
||||||
|
break;
|
||||||
|
case RUSSIA:
|
||||||
|
System.out.println("YOU'RE NEARING STALINGRAD.");
|
||||||
|
break;
|
||||||
|
case ENGLAND:
|
||||||
|
System.out.println("NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR.");
|
||||||
|
break;
|
||||||
|
case FRANCE:
|
||||||
|
System.out.println("NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts a string from the keyboard, and converts to an int
|
||||||
|
*
|
||||||
|
* @param message displayed text on screen before keyboard input
|
||||||
|
*
|
||||||
|
* @return the number entered by the player
|
||||||
|
*/
|
||||||
|
private int getNumberFromKeyboard(String message) {
|
||||||
|
|
||||||
|
String answer = displayTextAndGetInput(message);
|
||||||
|
return Integer.parseInt(answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether player entered Y or YES to a question.
|
||||||
|
*
|
||||||
|
* @param text player string from kb
|
||||||
|
* @return true of Y or YES was entered, otherwise false
|
||||||
|
*/
|
||||||
|
private boolean yesEntered(String text) {
|
||||||
|
return stringIsAnyValue(text, "Y", "YES");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* Used as a single digit of the computer player
|
||||||
|
*
|
||||||
|
* @return random number
|
||||||
|
*/
|
||||||
|
private double randomNumber(int range) {
|
||||||
|
return (Math.random()
|
||||||
|
* (range));
|
||||||
|
}
|
||||||
|
}
|
||||||
8
12 Bombs Away/java/src/BombsAwayGame.java
Normal file
8
12 Bombs Away/java/src/BombsAwayGame.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
public class BombsAwayGame {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
BombsAway bombsAway = new BombsAway();
|
||||||
|
bombsAway.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user