mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 23:26:40 -08:00
357 lines
14 KiB
Java
357 lines
14 KiB
Java
import java.util.*;
|
||
|
||
/**
|
||
* SPLAT simulates a parachute jump in which you try to open your parachute at the last possible moment without going
|
||
* splat! You may select your own terminal velocity or let the computer do it for you. You many also select the
|
||
* acceleration due to gravity or, again, let the computer do it in which case you might wind up on any of eight
|
||
* planets (out to Neptune), the moon, or the sun.
|
||
* <p>
|
||
* The computer then tells you the height you’re jumping from and asks for the seconds of free fall. It then divides
|
||
* your free fall time into eight intervals and gives you progress reports on your way down. The computer also keeps
|
||
* track of all prior jumps in the array A and lets you know how you compared with previous successful jumps. If you
|
||
* want to recall information from previous runs, then you should store array A in a disk or take file and read it
|
||
* before each run.
|
||
* <p>
|
||
* John Yegge created this program while at the Oak Ridge Associated Universities.
|
||
* <p>
|
||
* Ported from BASIC by jason plumb (@breedx2)
|
||
* </p>
|
||
*/
|
||
public class Splat {
|
||
private static final Random random = new Random();
|
||
private final Scanner scanner = new Scanner(System.in);
|
||
private final List<Float> pastSuccessfulJumpDistances = new ArrayList<>();
|
||
|
||
public static void main(String[] args) {
|
||
new Splat().run();
|
||
}
|
||
|
||
public void run() {
|
||
showIntroduction();
|
||
|
||
while (true) {
|
||
|
||
InitialJumpConditions initial = buildInitialConditions();
|
||
|
||
System.out.println();
|
||
System.out.printf(" ALTITUDE = %d FT\n", initial.getAltitude());
|
||
System.out.printf(" TERM. VELOCITY = %.2f FT/SEC +/-5%%\n", initial.getOriginalTerminalVelocity());
|
||
System.out.printf(" ACCELERATION = %.2f FT/SEC/SEC +/-5%%\n", initial.getOriginalAcceleration());
|
||
|
||
System.out.println("SET THE TIMER FOR YOUR FREEFALL.");
|
||
float freefallTime = promptFloat("HOW MANY SECONDS ");
|
||
System.out.println("HERE WE GO.\n");
|
||
System.out.println("TIME (SEC) DIST TO FALL (FT)");
|
||
System.out.println("========== =================");
|
||
|
||
JumpResult jump = executeJump(initial, freefallTime);
|
||
showJumpResults(initial, jump);
|
||
|
||
if (!playAgain()) {
|
||
System.out.println("SSSSSSSSSS.");
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void showIntroduction() {
|
||
System.out.printf("%33s%s\n", " ", "SPLAT");
|
||
System.out.printf("%15s%s\n", " ", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||
System.out.print("\n\n\n");
|
||
System.out.println("WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES A PARACHUTE");
|
||
System.out.println("JUMP. TRY TO OPEN YOUR CHUTE AT THE LAST POSSIBLE");
|
||
System.out.println("MOMENT WITHOUT GOING SPLAT.");
|
||
}
|
||
|
||
private InitialJumpConditions buildInitialConditions() {
|
||
System.out.print("\n\n");
|
||
float terminalVelocity = promptTerminalVelocity();
|
||
float acceleration = promptGravitationalAcceleration();
|
||
return InitialJumpConditions.create(terminalVelocity, acceleration);
|
||
}
|
||
|
||
private float promptTerminalVelocity() {
|
||
if (askYesNo("SELECT YOUR OWN TERMINAL VELOCITY")) {
|
||
float terminalVelocity = promptFloat("WHAT TERMINAL VELOCITY (MI/HR) ");
|
||
return mphToFeetPerSec(terminalVelocity);
|
||
}
|
||
float terminalVelocity = (int) (1000 * random.nextFloat());
|
||
System.out.printf("OK. TERMINAL VELOCITY = %.2f MI/HR\n", terminalVelocity);
|
||
return mphToFeetPerSec(terminalVelocity);
|
||
}
|
||
|
||
private float promptFloat(String prompt){
|
||
while(true){
|
||
System.out.print(prompt);
|
||
try {
|
||
return scanner.nextFloat();
|
||
} catch (Exception e) {
|
||
scanner.next(); // clear current input
|
||
}
|
||
}
|
||
}
|
||
|
||
private float promptGravitationalAcceleration() {
|
||
if (askYesNo("WANT TO SELECT ACCELERATION DUE TO GRAVITY")) {
|
||
return promptFloat("WHAT ACCELERATION (FT/SEC/SEC) ");
|
||
}
|
||
return chooseRandomAcceleration();
|
||
}
|
||
|
||
private JumpResult executeJump(InitialJumpConditions initial, float chuteOpenTime) {
|
||
JumpResult jump = new JumpResult(initial.getAltitude());
|
||
for (float time = 0.0f; time < chuteOpenTime; time += chuteOpenTime / 8) {
|
||
if (!jump.hasReachedTerminalVelocity() && time > initial.getTimeOfTerminalAccelerationReached()) {
|
||
jump.setReachedTerminalVelocity();
|
||
System.out.printf("TERMINAL VELOCITY REACHED AT T PLUS %f SECONDS.\n", initial.getTimeOfTerminalAccelerationReached());
|
||
}
|
||
float newDistance = computeDistance(initial, time, jump.hasReachedTerminalVelocity());
|
||
jump.setDistance(newDistance);
|
||
|
||
if (jump.isSplat()) {
|
||
return jump;
|
||
}
|
||
System.out.printf("%10.2f %f\n", time, jump.getDistance());
|
||
}
|
||
return jump;
|
||
}
|
||
|
||
private float computeDistance(InitialJumpConditions initial, float i, boolean hasReachedTerminalVelocity) {
|
||
final float V = initial.getTerminalVelocity();
|
||
final float A = initial.getAcceleration();
|
||
if (hasReachedTerminalVelocity) {
|
||
return initial.getAltitude() - ((V * V / (2 * A)) + (V * (i - (V / A))));
|
||
}
|
||
return initial.getAltitude() - ((A / 2) * i * i);
|
||
}
|
||
|
||
private void showJumpResults(InitialJumpConditions initial, JumpResult jump) {
|
||
if (jump.isSplat()) {
|
||
showSplatMessage(initial, jump);
|
||
showCleverSplatMessage();
|
||
return;
|
||
}
|
||
System.out.println("CHUTE OPEN");
|
||
int worseJumpCount = countWorseHistoricalJumps(jump);
|
||
int successfulJumpCt = pastSuccessfulJumpDistances.size();
|
||
pastSuccessfulJumpDistances.add(jump.getDistance());
|
||
|
||
if (pastSuccessfulJumpDistances.size() <= 2) {
|
||
List<String> ordinals = Arrays.asList("1ST", "2ND", "3RD");
|
||
System.out.printf("AMAZING!!! NOT BAD FOR YOUR %s SUCCESSFUL JUMP!!!\n", ordinals.get(successfulJumpCt));
|
||
return;
|
||
}
|
||
|
||
int betterThanCount = successfulJumpCt - worseJumpCount;
|
||
if (betterThanCount <= 0.1 * successfulJumpCt) {
|
||
System.out.printf("WOW! THAT'S SOME JUMPING. OF THE %d SUCCESSFUL JUMPS\n", successfulJumpCt);
|
||
System.out.printf("BEFORE YOURS, ONLY %d OPENED THEIR CHUTES LOWER THAN\n", betterThanCount);
|
||
System.out.println("YOU DID.");
|
||
} else if (betterThanCount <= 0.25 * successfulJumpCt) {
|
||
System.out.printf("PRETTY GOOD! %d SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY\n", successfulJumpCt);
|
||
System.out.printf("%d OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES\n", betterThanCount);
|
||
System.out.println("OPENED.");
|
||
} else if (betterThanCount <= 0.5 * successfulJumpCt) {
|
||
System.out.printf("NOT BAD. THERE HAVE BEEN %d SUCCESSFUL JUMPS BEFORE YOURS.\n", successfulJumpCt);
|
||
System.out.printf("YOU WERE BEATEN OUT BY %d OF THEM.\n", betterThanCount);
|
||
} else if (betterThanCount <= 0.75 * successfulJumpCt) {
|
||
System.out.printf("CONSERVATIVE, AREN'T YOU? YOU RANKED ONLY %d IN THE\n", betterThanCount);
|
||
System.out.printf("%d SUCCESSFUL JUMPS BEFORE YOURS.\n", successfulJumpCt);
|
||
} else if (betterThanCount <= -0.9 * successfulJumpCt) {
|
||
System.out.println("HUMPH! DON'T YOU HAVE ANY SPORTING BLOOD? THERE WERE");
|
||
System.out.printf("%d SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN %d JUMPS\n", successfulJumpCt, worseJumpCount);
|
||
System.out.println("BETTER THAN THE WORST. SHAPE UP!!!\n");
|
||
} else {
|
||
System.out.printf("HEY! YOU PULLED THE RIP CORD MUCH TOO SOON. %d SUCCESSFUL\n", successfulJumpCt);
|
||
System.out.printf("JUMPS BEFORE YOURS AND YOU CAME IN NUMBER %d! GET WITH IT!\n", betterThanCount);
|
||
}
|
||
}
|
||
|
||
private void showSplatMessage(InitialJumpConditions initial, JumpResult jump) {
|
||
double timeOfSplat = computeTimeOfSplat(initial, jump);
|
||
System.out.printf("%10.2f SPLAT\n", timeOfSplat);
|
||
}
|
||
|
||
/**
|
||
* Returns the number of jumps for which this jump was better
|
||
*/
|
||
private double computeTimeOfSplat(InitialJumpConditions initial, JumpResult jump) {
|
||
final float V = initial.getTerminalVelocity();
|
||
final float A = initial.getAcceleration();
|
||
if (jump.hasReachedTerminalVelocity()) {
|
||
return (V / A) + ((initial.getAltitude() - (V * V / (2 * A))) / V);
|
||
}
|
||
return Math.sqrt(2 * initial.getAltitude() / A);
|
||
}
|
||
|
||
private int countWorseHistoricalJumps(JumpResult jump) {
|
||
return (int) pastSuccessfulJumpDistances.stream()
|
||
.filter(distance -> jump.getDistance() < distance)
|
||
.count();
|
||
}
|
||
|
||
private void showCleverSplatMessage() {
|
||
List<String> messages = Arrays.asList(
|
||
"REQUIESCAT IN PACE.",
|
||
"MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.",
|
||
"REST IN PEACE.",
|
||
"SON-OF-A-GUN.",
|
||
"#$%&&%!$",
|
||
"A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.",
|
||
"HMMM. SHOULD HAVE PICKED A SHORTER TIME.",
|
||
"MUTTER. MUTTER. MUTTER.",
|
||
"PUSHING UP DAISIES.",
|
||
"EASY COME, EASY GO."
|
||
);
|
||
System.out.println(messages.get(random.nextInt(10)));
|
||
}
|
||
|
||
private boolean playAgain() {
|
||
if (askYesNo("DO YOU WANT TO PLAY AGAIN ")) {
|
||
return true;
|
||
}
|
||
return askYesNo("PLEASE");
|
||
}
|
||
|
||
private float mphToFeetPerSec(float speed) {
|
||
return speed * (5280.0f / 3600.0f);
|
||
}
|
||
|
||
private boolean askYesNo(String prompt) {
|
||
System.out.printf("%s (YES OR NO) ", prompt);
|
||
while (true) {
|
||
String answer = scanner.next();
|
||
switch (answer) {
|
||
case "YES":
|
||
return true;
|
||
case "NO":
|
||
return false;
|
||
default:
|
||
System.out.print("YES OR NO ");
|
||
}
|
||
}
|
||
}
|
||
|
||
private float chooseRandomAcceleration() {
|
||
Planet planet = Planet.pickRandom();
|
||
System.out.printf("%s %s. ACCELERATION=%.2f FT/SEC/SEC.\n", planet.getMessage(), planet.name(), planet.getAcceleration());
|
||
return planet.getAcceleration();
|
||
}
|
||
|
||
enum Planet {
|
||
MERCURY("FINE. YOU'RE ON", 12.2f),
|
||
VENUS("ALL RIGHT. YOU'RE ON", 28.3f),
|
||
EARTH("THEN YOU'RE ON", 32.16f),
|
||
MOON("FINE. YOU'RE ON THE", 5.15f),
|
||
MARS("ALL RIGHT. YOU'RE ON", 12.5f),
|
||
JUPITER("THEN YOU'RE ON", 85.2f),
|
||
SATURN("FINE. YOU'RE ON", 37.6f),
|
||
URANUS("ALL RIGHT. YOU'RE ON", 33.8f),
|
||
NEPTUNE("THEN YOU'RE ON", 39.6f),
|
||
SUN("FINE. YOU'RE ON THE", 896.0f);
|
||
|
||
private static final Random random = new Random();
|
||
private final String message;
|
||
private final float acceleration;
|
||
|
||
Planet(String message, float acceleration) {
|
||
this.message = message;
|
||
this.acceleration = acceleration;
|
||
}
|
||
|
||
static Planet pickRandom() {
|
||
return values()[random.nextInt(Planet.values().length)];
|
||
}
|
||
|
||
String getMessage() {
|
||
return message;
|
||
}
|
||
|
||
float getAcceleration() {
|
||
return acceleration;
|
||
}
|
||
}
|
||
|
||
// Mutable
|
||
static class JumpResult {
|
||
private boolean reachedTerminalVelocity = false;
|
||
private float distance; // from the ground
|
||
|
||
public JumpResult(float distance) {
|
||
this.distance = distance;
|
||
}
|
||
|
||
boolean isSplat() {
|
||
return distance <= 0;
|
||
}
|
||
|
||
boolean hasReachedTerminalVelocity() {
|
||
return reachedTerminalVelocity;
|
||
}
|
||
|
||
float getDistance() {
|
||
return distance;
|
||
}
|
||
|
||
void setDistance(float distance) {
|
||
this.distance = distance;
|
||
}
|
||
|
||
void setReachedTerminalVelocity() {
|
||
reachedTerminalVelocity = true;
|
||
}
|
||
}
|
||
|
||
// Immutable
|
||
static class InitialJumpConditions {
|
||
private final float originalTerminalVelocity;
|
||
private final float originalAcceleration;
|
||
private final float terminalVelocity;
|
||
private final float acceleration;
|
||
private final int altitude;
|
||
|
||
private InitialJumpConditions(float originalTerminalVelocity, float originalAcceleration,
|
||
float terminalVelocity, float acceleration, int altitude) {
|
||
this.originalTerminalVelocity = originalTerminalVelocity;
|
||
this.originalAcceleration = originalAcceleration;
|
||
this.terminalVelocity = terminalVelocity;
|
||
this.acceleration = acceleration;
|
||
this.altitude = altitude;
|
||
}
|
||
|
||
// Create initial jump conditions with adjusted velocity/acceleration and a random initial altitude
|
||
private static InitialJumpConditions create(float terminalVelocity, float gravitationalAcceleration) {
|
||
final int altitude = (int) (9001.0f * random.nextFloat() + 1000);
|
||
return new InitialJumpConditions(terminalVelocity, gravitationalAcceleration,
|
||
plusMinus5Percent(terminalVelocity), plusMinus5Percent(gravitationalAcceleration), altitude);
|
||
}
|
||
|
||
private static float plusMinus5Percent(float value) {
|
||
return value + ((value * random.nextFloat()) / 20.0f) - ((value * random.nextFloat()) / 20.0f);
|
||
}
|
||
|
||
float getOriginalTerminalVelocity() {
|
||
return originalTerminalVelocity;
|
||
}
|
||
|
||
float getOriginalAcceleration() {
|
||
return originalAcceleration;
|
||
}
|
||
|
||
float getTerminalVelocity() {
|
||
return terminalVelocity;
|
||
}
|
||
|
||
float getAcceleration() {
|
||
return acceleration;
|
||
}
|
||
|
||
int getAltitude() {
|
||
return altitude;
|
||
}
|
||
|
||
float getTimeOfTerminalAccelerationReached() {
|
||
return terminalVelocity / acceleration;
|
||
}
|
||
}
|
||
} |