Ported 79. Slalom to Java.

Also, documented one possible bug in the orignal program.
This commit is contained in:
Aldrin Misquitta
2022-02-22 14:02:22 +00:00
parent 2166bb13bd
commit 34f97d69b8
2 changed files with 381 additions and 0 deletions

View File

@@ -14,3 +14,11 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
### Bugs
- In the original version, the data pointer doesn't reset after a race is completed. This causes subsequent races to
error at some future point at line 540,
540 READ Q

373
79_Slalom/java/Slalom.java Normal file
View File

@@ -0,0 +1,373 @@
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;
/**
* Slalom
* <p>
* Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
*
* There is a bug in the original version where the data pointer doesn't reset after a race is completed. This causes subsequent races to error at
* some future point on line "540 READ Q"
*/
public class Slalom {
private static final int MAX_NUM_GATES = 25;
private static final int[] MAX_SPEED = {
14, 18, 26, 29, 18,
25, 28, 32, 29, 20,
29, 29, 25, 21, 26,
29, 20, 21, 20, 18,
26, 25, 33, 31, 22
};
public static void main(String[] args) {
var random = new Random();
printIntro();
Scanner scanner = new Scanner(System.in);
int numGates = readNumberOfGatesChoice(scanner);
printMenu();
MenuChoice menuChoice;
do {
menuChoice = readMenuOption(scanner);
switch (menuChoice) {
case INS:
printInstructions();
break;
case MAX:
printApproxMaxSpeeds(numGates);
break;
case RUN:
run(numGates, scanner, random);
break;
}
} while (menuChoice != MenuChoice.RUN);
}
private static void run(int numGates, Scanner scan, Random random) {
int rating = readSkierRating(scan);
boolean gameInProgress = true;
var medals = new Medals(0, 0, 0);
while (gameInProgress) {
System.out.println("THE STARTER COUNTS DOWN...5...4...3...2...1...GO!");
System.out.println("YOU'RE OFF!");
int speed = random.nextInt(18 - 9) + 9;
float totalTimeTaken = 0;
try {
totalTimeTaken = runThroughGates(numGates, scan, random, speed);
System.out.printf("%nYOU TOOK %.2f SECONDS.%n", totalTimeTaken + random.nextFloat());
medals = evaluateAndUpdateMedals(totalTimeTaken, numGates, rating, medals);
} catch (WipedOutOrSnaggedAFlag | DisqualifiedException e) {
//end of this race! Print time taken and stop
System.out.printf("%nYOU TOOK %.2f SECONDS.%n", totalTimeTaken + random.nextFloat());
}
gameInProgress = readRaceAgainChoice(scan);
}
System.out.println("THANKS FOR THE RACE");
if (medals.getGold() >= 1) System.out.printf("GOLD MEDALS: %d%n", medals.getGold());
if (medals.getSilver() >= 1) System.out.printf("SILVER MEDALS: %d%n", medals.getSilver());
if (medals.getBronze() >= 1) System.out.printf("BRONZE MEDALS: %d%n", medals.getBronze());
}
private static Medals evaluateAndUpdateMedals(float totalTimeTaken, int numGates, int rating,
Medals medals) {
var m = totalTimeTaken;
m = m / numGates;
int goldMedals = medals.getGold();
int silverMedals = medals.getSilver();
int bronzeMedals = medals.getBronze();
if (m < 1.5 - (rating * 0.1)) {
System.out.println("YOU WON A GOLD MEDAL!");
goldMedals++;
} else if (m < 2.9 - rating * 0.1) {
System.out.println("YOU WON A SILVER MEDAL");
silverMedals++;
} else if (m < 4.4 - rating * 0.01) {
System.out.println("YOU WON A BRONZE MEDAL");
bronzeMedals++;
}
return new Medals(goldMedals, silverMedals, bronzeMedals);
}
/**
* @return the total time taken through all the gates.
*/
private static float runThroughGates(int numGates, Scanner scan, Random random, int speed) throws DisqualifiedException, WipedOutOrSnaggedAFlag {
float totalTimeTaken = 0.0f;
for (int i = 0; i < numGates; i++) {
var gateNum = i + 1;
boolean stillInRace = true;
boolean gateCompleted = false;
while (!gateCompleted) {
System.out.printf("%nHERE COMES GATE # %d:%n", gateNum);
printSpeed(speed);
var tmpSpeed = speed;
int chosenOption = readOption(scan);
switch (chosenOption) {
case 0:
//how long
printHowLong(totalTimeTaken, random);
break;
case 1:
//speed up a lot
speed = speed + random.nextInt(10 - 5) + 5;
break;
case 2:
//speed up a little
speed = speed + random.nextInt(5 - 3) + 3;
break;
case 3:
//speed up a teensy
speed = speed + random.nextInt(4 - 1) + 1;
break;
case 4:
//keep going at the same speed
break;
case 5:
//check a teensy
speed = speed - random.nextInt(4 - 1) + 1;
break;
case 6:
//check a little
speed = speed - random.nextInt(5 - 3) + 3;
break;
case 7:
//check a lot
speed = speed - random.nextInt(10 - 5) + 5;
break;
case 8:
//cheat
System.out.println("***CHEAT");
if (random.nextFloat() < 0.7) {
System.out.println("AN OFFICIAL CAUGHT YOU!");
stillInRace = false;
} else {
System.out.println("YOU MADE IT!");
totalTimeTaken = totalTimeTaken + 1.5f;
}
break;
}
if (stillInRace) {
printSpeed(speed);
stillInRace = checkAndProcessIfOverMaxSpeed(random, speed, MAX_SPEED[i]);
if (!stillInRace) throw new WipedOutOrSnaggedAFlag();
} else {
throw new DisqualifiedException();//we've been dis-qualified
}
if (speed < 7) {
System.out.println("LET'S BE REALISTIC, OK? LET'S GO BACK AND TRY AGAIN...");
speed = tmpSpeed;
gateCompleted = false;
} else {
totalTimeTaken = totalTimeTaken + (MAX_SPEED[i] - speed + 1);
if (speed > MAX_SPEED[i]) {
totalTimeTaken = totalTimeTaken + 0.5f;
}
gateCompleted = true;
}
}
}
return totalTimeTaken;
}
private static boolean checkAndProcessIfOverMaxSpeed(Random random, int speed, int maxSpeed) {
boolean stillInRace = true;
if (speed > maxSpeed) {
if (random.nextFloat() >= (speed - maxSpeed) * 0.1 + 0.2) {
System.out.println("YOU WENT OVER THE MAXIMUM SPEED AND MADE IT!");
} else {
System.out.print("YOU WENT OVER THE MAXIMUM SPEED AND ");
if (random.nextBoolean()) {
System.out.println("WIPED OUT!");
} else {
System.out.println("SNAGGED A FLAG!");
}
stillInRace = false;
}
} else if (speed > maxSpeed - 1) {
System.out.println("CLOSE ONE!");
}
return stillInRace;
}
private static boolean readRaceAgainChoice(Scanner scan) {
System.out.print("\nDO YOU WANT TO RACE AGAIN? ");
String raceAgain = "";
final String YES = "YES";
final String NO = "NO";
while (!YES.equals(raceAgain) && !NO.equals(raceAgain)) {
raceAgain = scan.nextLine();
if (!(YES.equals(raceAgain) || NO.equals(raceAgain))) {
System.out.println("PLEASE TYPE 'YES' OR 'NO'");
}
}
return raceAgain.equals(YES);
}
private static void printSpeed(int speed) {
System.out.printf("%3d M.P.H.%n", speed);
}
private static void printHowLong(float t, Random random) {
System.out.printf("YOU'VE TAKEN %.2f SECONDS.%n", t + random.nextFloat());
}
private static int readOption(Scanner scan) {
Integer option = null;
while (option == null) {
System.out.print("OPTION? ");
try {
option = scan.nextInt();
} catch (InputMismatchException ex) {
System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE\n");
}
scan.nextLine();
if (option != null && (option > 8 || option < 0)) {
System.out.println("WHAT?");
option = null;
}
}
return option;
}
private static int readSkierRating(Scanner scan) {
int rating = 0;
while (rating < 1 || rating > 3) {
System.out.print("RATE YOURSELF AS A SKIER, (1=WORST, 3=BEST)? ");
try {
rating = scan.nextInt();
if (rating < 1 || rating > 3) {
System.out.println("THE BOUNDS ARE 1-3");
}
} catch (InputMismatchException ex) {
System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE\n");
}
scan.nextLine();
}
return rating;
}
private static void printApproxMaxSpeeds(int numGates) {
System.out.println("GATE MAX");
System.out.println(" # M.P.H.");
System.out.println("---------");
for (int i = 0; i < numGates; i++) {
System.out.println((i+1) + " " + MAX_SPEED[i]);
}
}
private static void printInstructions() {
System.out.println("\n*** SLALOM: THIS IS THE 1976 WINTER OLYMPIC GIANT SLALOM. YOU ARE");
System.out.println(" THE AMERICAN TEAM'S ONLY HOPE OF A GOLD MEDAL.");
System.out.println();
System.out.println(" 0 -- TYPE THIS IS YOU WANT TO SEE HOW LONG YOU'VE TAKEN.");
System.out.println(" 1 -- TYPE THIS IF YOU WANT TO SPEED UP A LOT.");
System.out.println(" 2 -- TYPE THIS IF YOU WANT TO SPEED UP A LITTLE.");
System.out.println(" 3 -- TYPE THIS IF YOU WANT TO SPEED UP A TEENSY.");
System.out.println(" 4 -- TYPE THIS IF YOU WANT TO KEEP GOING THE SAME SPEED.");
System.out.println(" 5 -- TYPE THIS IF YOU WANT TO CHECK A TEENSY.");
System.out.println(" 6 -- TYPE THIS IF YOU WANT TO CHECK A LITTLE.");
System.out.println(" 7 -- TYPE THIS IF YOU WANT TO CHECK A LOT.");
System.out.println(" 8 -- TYPE THIS IF YOU WANT TO CHEAT AND TRY TO SKIP A GATE.");
System.out.println();
System.out.println(" THE PLACE TO USE THESE OPTIONS IS WHEN THE COMPUTER ASKS:");
System.out.println();
System.out.println("OPTION?");
System.out.println();
System.out.println(" GOOD LUCK!");
}
private static MenuChoice readMenuOption(Scanner scan) {
System.out.print("COMMAND--? ");
MenuChoice menuChoice = null;
while (menuChoice == null) {
String choice = scan.next();
if (Arrays.stream(MenuChoice.values()).anyMatch(a -> a.name().equals(choice))) {
menuChoice = MenuChoice.valueOf(choice);
} else {
System.out.print("\""+ choice + "\" IS AN ILLEGAL COMMAND--RETRY? ");
}
scan.nextLine();
}
return menuChoice;
}
private static void printMenu() {
System.out.println("TYPE INS FOR INSTRUCTIONS");
System.out.println("TYPE MAX FOR APPROXIMATE MAXIMUM SPEEDS");
System.out.println("TYPE RUN FOR THE BEGINNING OF THE RACE");
}
private static int readNumberOfGatesChoice(Scanner scan) {
int numGates = 0;
while (numGates < 1) {
System.out.print("HOW MANY GATES DOES THIS COURSE HAVE (1 TO 25)? ");
numGates = scan.nextInt();
if (numGates > MAX_NUM_GATES) {
System.out.println(MAX_NUM_GATES + " IS THE LIMIT.");
numGates = MAX_NUM_GATES;
}
}
return numGates;
}
private static void printIntro() {
System.out.println(" SLALOM");
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.println("\n\n");
}
private enum MenuChoice {
INS, MAX, RUN
}
private static class DisqualifiedException extends Exception {
}
private static class WipedOutOrSnaggedAFlag extends Exception {
}
private static class Medals {
private int gold = 0;
private int silver = 0;
private int bronze = 0;
public Medals(int gold, int silver, int bronze) {
this.gold = gold;
this.silver = silver;
this.bronze = bronze;
}
public int getGold() {
return gold;
}
public int getSilver() {
return silver;
}
public int getBronze() {
return bronze;
}
}
}