updated implementation with proper OO design, provided link to video

This commit is contained in:
Taciano Dreckmann Perez
2022-03-24 18:58:47 +01:00
parent 06c8d2be69
commit a64aa983fe
6 changed files with 1238 additions and 1024 deletions

View File

@@ -0,0 +1,331 @@
import java.util.stream.IntStream;
/**
* The starship Enterprise.
*/
public class Enterprise {
public static final int COORD_X = 0;
public static final int COORD_Y = 1;
// devices
static final int DEVICE_WARP_ENGINES = 1;
static final int DEVICE_SHORT_RANGE_SENSORS = 2;
static final int DEVICE_LONG_RANGE_SENSORS = 3;
static final int DEVICE_PHASER_CONTROL = 4;
static final int DEVICE_PHOTON_TUBES = 5;
static final int DEVICE_DAMAGE_CONTROL = 6;
static final int DEVICE_SHIELD_CONTROL = 7;
static final int DEVICE_LIBRARY_COMPUTER = 8;
final double[] deviceStatus = new double[9]; // 8 device damage stats
// position
final int[][] cardinalDirections = new int[10][3]; // 9x2 vectors in cardinal directions
int quadrantX;
int quadrantY;
int sectorX;
int sectorY;
// ship status
boolean docked = false;
int energy = 3000;
int torpedoes = 10;
int shields = 0;
double repairCost;
final int initialEnergy = energy;
final int initialTorpedoes = torpedoes;
public Enterprise() {
// random initial position
this.setQuadrant(new int[]{ Util.fnr(), Util.fnr() });
this.setSector(new int[]{ Util.fnr(), Util.fnr() });
// init cardinal directions
IntStream.range(1, 9).forEach(i -> {
cardinalDirections[i][1] = 0;
cardinalDirections[i][2] = 0;
});
cardinalDirections[3][1] = -1;
cardinalDirections[2][1] = -1;
cardinalDirections[4][1] = -1;
cardinalDirections[4][2] = -1;
cardinalDirections[5][2] = -1;
cardinalDirections[6][2] = -1;
cardinalDirections[1][2] = 1;
cardinalDirections[2][2] = 1;
cardinalDirections[6][1] = 1;
cardinalDirections[7][1] = 1;
cardinalDirections[8][1] = 1;
cardinalDirections[8][2] = 1;
cardinalDirections[9][2] = 1;
// init devices
IntStream.range(1, 8).forEach(i -> deviceStatus[i] = 0);
}
public int getShields() {
return shields;
}
/**
* Enterprise is hit by enemies.
* @param hits the number of hit points
*/
public void sufferHitPoints(int hits) {
this.shields = shields - hits;
}
public int getEnergy() {
return energy;
}
public void replenishSupplies() {
this.energy = this.initialEnergy;
this.torpedoes = this.initialTorpedoes;
}
public void decreaseEnergy(final double amount) {
this.energy -= amount;
}
public void decreaseTorpedoes(final int amount) {
torpedoes -= amount;
}
public void dropShields() {
this.shields = 0;
}
public int getTotalEnergy() {
return (shields + energy);
}
public int getInitialEnergy() {
return initialEnergy;
}
public int getTorpedoes() {
return torpedoes;
}
public double[] getDeviceStatus() {
return deviceStatus;
}
public int[][] getCardinalDirections() {
return cardinalDirections;
}
public void setDeviceStatus(final int device, final double status) {
this.deviceStatus[device] = status;
}
public boolean isDocked() {
return docked;
}
public void setDocked(boolean docked) {
this.docked = docked;
}
public int[] getQuadrant() {
return new int[] {quadrantX, quadrantY};
}
public void setQuadrant(final int[] quadrant) {
this.quadrantX = quadrant[COORD_X];
this.quadrantY = quadrant[COORD_Y];
}
public int[] getSector() {
return new int[] {sectorX, sectorY};
}
public void setSector(final int[] sector) {
this.sectorX = sector[COORD_X];
this.sectorY = sector[COORD_Y];
}
public int[] moveShip(final float course, final int n, final String quadrantMap, final double stardate, final double initialStardate, final int missionDuration, final GameCallback callback) {
int ic1 = Util.toInt(course);
float x1 = cardinalDirections[ic1][1] + (cardinalDirections[ic1 + 1][1] - cardinalDirections[ic1][1]) * (course - ic1);
float x = sectorX;
float y = sectorY;
float x2 = cardinalDirections[ic1][2] + (cardinalDirections[ic1 + 1][2] - cardinalDirections[ic1][2]) * (course - ic1);
final int initialQuadrantX = quadrantX;
final int initialQuadrantY = quadrantY;
for (int i = 1; i <= n; i++) {
sectorX += x1;
sectorY += x2;
if (sectorX < 1 || sectorX >= 9 || sectorY < 1 || sectorY >= 9) {
// exceeded quadrant limits
x = 8 * quadrantX + x + n * x1;
y = 8 * quadrantY + y + n * x2;
quadrantX = Util.toInt(x / 8);
quadrantY = Util.toInt(y / 8);
sectorX = Util.toInt(x - quadrantX * 8);
sectorY = Util.toInt(y - quadrantY * 8);
if (sectorX == 0) {
quadrantX = quadrantX - 1;
sectorX = 8;
}
if (sectorY == 0) {
quadrantY = quadrantY - 1;
sectorY = 8;
}
boolean hitEdge = false;
if (quadrantX < 1) {
hitEdge = true;
quadrantX = 1;
sectorX = 1;
}
if (quadrantX > 8) {
hitEdge = true;
quadrantX = 8;
sectorX = 8;
}
if (quadrantY < 1) {
hitEdge = true;
quadrantY = 8;
sectorY = 8;
}
if (quadrantY > 8) {
hitEdge = true;
quadrantY = 8;
sectorY = 8;
}
if (hitEdge) {
Util.println("LT. UHURA REPORTS MESSAGE FROM STARFLEET COMMAND:");
Util.println(" 'PERMISSION TO ATTEMPT CROSSING OF GALACTIC PERIMETER");
Util.println(" IS HEREBY *DENIED*. SHUT DOWN YOUR ENGINES.'");
Util.println("CHIEF ENGINEER SCOTT REPORTS 'WARP ENGINES SHUT DOWN");
Util.println(" AT SECTOR " + sectorX + "," + sectorY + " OF QUADRANT " + quadrantX + "," + quadrantY + ".'");
if (stardate > initialStardate + missionDuration) callback.endGameFail(false);
}
if (8 * quadrantX + quadrantY == 8 * initialQuadrantX + initialQuadrantY) {
break;
}
callback.incrementStardate(1);
maneuverEnergySR(n);
callback.enterNewQuadrant();
return this.getSector();
} else {
int S8 = Util.toInt(sectorX) * 24 + Util.toInt(sectorY) * 3 - 26; // S8 = pos
if (!(" ".equals(Util.midStr(quadrantMap, S8, 2)))) {
sectorX = Util.toInt(sectorX - x1);
sectorY = Util.toInt(sectorY - x2);
Util.println("WARP ENGINES SHUT DOWN AT ");
Util.println("SECTOR " + sectorX + "," + sectorY + " DUE TO BAD NAVIGATION");
break;
}
}
}
sectorX = Util.toInt(sectorX);
sectorY = Util.toInt(sectorY);
return this.getSector();
}
void randomRepairCost() {
repairCost = .5 * Util.random();
}
public void repairDamagedDevices(final float warp) {
// repair damaged devices and print damage report
for (int i = 1; i <= 8; i++) {
if (deviceStatus[i] < 0) {
deviceStatus[i] += Math.min(warp, 1);
if ((deviceStatus[i] > -.1) && (deviceStatus[i] < 0)) {
deviceStatus[i] = -.1;
break;
} else if (deviceStatus[i] >= 0) {
Util.println("DAMAGE CONTROL REPORT: ");
Util.println(Util.tab(8) + printDeviceName(i) + " REPAIR COMPLETED.");
}
}
}
}
public void maneuverEnergySR(final int N) {
energy = energy - N - 10;
if (energy >= 0) return;
Util.println("SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER.");
shields = shields + energy;
energy = 0;
if (shields <= 0) shields = 0;
}
void shieldControl() {
if (deviceStatus[DEVICE_SHIELD_CONTROL] < 0) {
Util.println("SHIELD CONTROL INOPERABLE");
return;
}
Util.println("ENERGY AVAILABLE = " + (energy + shields));
int energyToShields = Util.toInt(Util.inputFloat("NUMBER OF UNITS TO SHIELDS"));
if (energyToShields < 0 || shields == energyToShields) {
Util.println("<SHIELDS UNCHANGED>");
return;
}
if (energyToShields > energy + energyToShields) {
Util.println("SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION TREASURY.'");
Util.println("<SHIELDS UNCHANGED>");
return;
}
energy = energy + shields - energyToShields;
shields = energyToShields;
Util.println("DEFLECTOR CONTROL ROOM REPORT:");
Util.println(" 'SHIELDS NOW AT " + Util.toInt(shields) + " UNITS PER YOUR COMMAND.'");
}
void damageControl(GameCallback callback) {
if (deviceStatus[DEVICE_DAMAGE_CONTROL] < 0) {
Util.println("DAMAGE CONTROL REPORT NOT AVAILABLE");
} else {
Util.println("\nDEVICE STATE OF REPAIR");
for (int deviceNr = 1; deviceNr <= 8; deviceNr++) {
Util.print(printDeviceName(deviceNr) + Util.leftStr(GalaxyMap.QUADRANT_ROW, 25 - Util.strlen(printDeviceName(deviceNr))) + " " + Util.toInt(deviceStatus[deviceNr] * 100) * .01 + "\n");
}
}
if (!docked) return;
double deltaToRepair = 0;
for (int i = 1; i <= 8; i++) {
if (deviceStatus[i] < 0) deltaToRepair += .1;
}
if (deltaToRepair > 0) {
deltaToRepair += repairCost;
if (deltaToRepair >= 1) deltaToRepair = .9;
Util.println("TECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;");
Util.println("ESTIMATED TIME TO REPAIR:'" + .01 * Util.toInt(100 * deltaToRepair) + " STARDATES");
final String reply = Util.inputStr("WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N)");
if ("Y".equals(reply)) {
for (int deviceNr = 1; deviceNr <= 8; deviceNr++) {
if (deviceStatus[deviceNr] < 0) deviceStatus[deviceNr] = 0;
}
callback.incrementStardate(deltaToRepair + .1);
}
}
}
public static String printDeviceName(final int deviceNumber) { // 8790
// PRINTS DEVICE NAME
switch (deviceNumber) {
case DEVICE_WARP_ENGINES:
return "WARP ENGINES";
case DEVICE_SHORT_RANGE_SENSORS:
return "SHORT RANGE SENSORS";
case DEVICE_LONG_RANGE_SENSORS:
return "LONG RANGE SENSORS";
case DEVICE_PHASER_CONTROL:
return "PHASER CONTROL";
case DEVICE_PHOTON_TUBES:
return "PHOTON TUBES";
case DEVICE_DAMAGE_CONTROL:
return "DAMAGE CONTROL";
case DEVICE_SHIELD_CONTROL:
return "SHIELD CONTROL";
case DEVICE_LIBRARY_COMPUTER:
return "LIBRARY-COMPUTER";
}
return "";
}
}

View File

@@ -0,0 +1,649 @@
import java.util.stream.IntStream;
/**
* Map of the galaxy divided in Quadrants and Sectors,
* populated with stars, starbases, klingons, and the Enterprise.
*/
public class GalaxyMap {
// markers
static final String MARKER_EMPTY = " ";
static final String MARKER_ENTERPRISE = "<*>";
static final String MARKER_KLINGON = "+K+";
static final String MARKER_STARBASE = ">!<";
static final String MARKER_STAR = " * ";
static final int AVG_KLINGON_SHIELD_ENERGY = 200;
// galaxy map
public static final String QUADRANT_ROW = " ";
String quadrantMap = QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + Util.leftStr(QUADRANT_ROW, 17); // current quadrant map
final int[][] galaxy = new int[9][9]; // 8x8 galaxy map G
final int[][] klingonQuadrants = new int[4][4]; // 3x3 position of klingons K
final int[][] chartedGalaxy = new int[9][9]; // 8x8 charted galaxy map Z
// galaxy state
int basesInGalaxy = 0;
int remainingKlingons;
int klingonsInGalaxy = 0;
final Enterprise enterprise = new Enterprise();
// quadrant state
int klingons = 0;
int starbases = 0;
int stars = 0;
int starbaseX = 0; // X coordinate of starbase
int starbaseY = 0; // Y coord of starbase
public Enterprise getEnterprise() {
return enterprise;
}
public int getBasesInGalaxy() {
return basesInGalaxy;
}
public int getRemainingKlingons() {
return remainingKlingons;
}
public int getKlingonsInGalaxy() {
return klingonsInGalaxy;
}
double fnd(int i) {
return Math.sqrt((klingonQuadrants[i][1] - enterprise.getSector()[Enterprise.COORD_X]) ^ 2 + (klingonQuadrants[i][2] - enterprise.getSector()[Enterprise.COORD_Y]) ^ 2);
}
public GalaxyMap() {
int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
// populate Klingons, Starbases, Stars
IntStream.range(1, 8).forEach(x -> {
IntStream.range(1, 8).forEach(y -> {
klingons = 0;
chartedGalaxy[x][y] = 0;
float random = Util.random();
if (random > .98) {
klingons = 3;
klingonsInGalaxy += 3;
} else if (random > .95) {
klingons = 2;
klingonsInGalaxy += 2;
} else if (random > .80) {
klingons = 1;
klingonsInGalaxy += 1;
}
starbases = 0;
if (Util.random() > .96) {
starbases = 1;
basesInGalaxy = +1;
}
galaxy[x][y] = klingons * 100 + starbases * 10 + Util.fnr();
});
});
if (basesInGalaxy == 0) {
if (galaxy[quadrantX][quadrantY] < 200) {
galaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY] + 120;
klingonsInGalaxy = +1;
}
basesInGalaxy = 1;
galaxy[quadrantX][quadrantY] = +10;
enterprise.setQuadrant(new int[]{ Util.fnr(), Util.fnr() });
}
remainingKlingons = klingonsInGalaxy;
}
void newQuadrant(final double stardate, final double initialStardate) { // 1320
final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
klingons = 0;
starbases = 0;
stars = 0;
enterprise.randomRepairCost();
chartedGalaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY];
if (!(quadrantX < 1 || quadrantX > 8 || quadrantY < 1 || quadrantY > 8)) {
final String quadrantName = getQuadrantName(false, quadrantX, quadrantY);
if (initialStardate == stardate) {
Util.println("YOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED\n" +
"IN THE GALACTIC QUADRANT, '" + quadrantName + "'.");
} else {
Util.println("NOW ENTERING " + quadrantName + " QUADRANT . . .");
}
Util.println("");
klingons = (int) Math.round(galaxy[quadrantX][quadrantY] * .01);
starbases = (int) Math.round(galaxy[quadrantX][quadrantY] * .1) - 10 * klingons;
stars = galaxy[quadrantX][quadrantY] - 100 * klingons - 10 * starbases;
if (klingons != 0) {
Util.println("COMBAT AREA CONDITION RED");
if (enterprise.getShields() <= 200) {
Util.println(" SHIELDS DANGEROUSLY LOW");
}
}
IntStream.range(1, 3).forEach(i -> {
klingonQuadrants[i][1] = 0;
klingonQuadrants[i][2] = 0;
});
}
IntStream.range(1, 3).forEach(i -> {
klingonQuadrants[i][3] = 0;
});
// position enterprise in quadrant
insertMarker(MARKER_ENTERPRISE, enterprise.getSector()[Enterprise.COORD_X], enterprise.getSector()[Enterprise.COORD_Y]);
// position klingons
if (klingons >= 1) {
for (int i = 1; i <= klingons; i++) {
final int[] emptyCoordinate = findEmptyPlaceInQuadrant(quadrantMap);
insertMarker(MARKER_KLINGON, emptyCoordinate[0], emptyCoordinate[1]);
klingonQuadrants[i][1] = emptyCoordinate[0];
klingonQuadrants[i][2] = emptyCoordinate[1];
klingonQuadrants[i][3] = (int) Math.round(AVG_KLINGON_SHIELD_ENERGY * (0.5 + Util.random()));
}
}
// position bases
if (starbases >= 1) {
final int[] emptyCoordinate = findEmptyPlaceInQuadrant(quadrantMap);
starbaseX = emptyCoordinate[0];
starbaseY = emptyCoordinate[1];
insertMarker(MARKER_STARBASE, emptyCoordinate[0], emptyCoordinate[1]);
}
// position stars
for (int i = 1; i <= stars; i++) {
final int[] emptyCoordinate = findEmptyPlaceInQuadrant(quadrantMap);
insertMarker(MARKER_STAR, emptyCoordinate[0], emptyCoordinate[1]);
}
}
public void klingonsMoveAndFire(GameCallback callback) {
for (int i = 1; i <= klingons; i++) {
if (klingonQuadrants[i][3] == 0) continue;
insertMarker(MARKER_EMPTY, klingonQuadrants[i][1], klingonQuadrants[i][2]);
final int[] newCoords = findEmptyPlaceInQuadrant(quadrantMap);
klingonQuadrants[i][1] = newCoords[0];
klingonQuadrants[i][2] = newCoords[1];
insertMarker(MARKER_KLINGON, klingonQuadrants[i][1], klingonQuadrants[i][2]);
}
klingonsShoot(callback);
}
void klingonsShoot(GameCallback callback) {
if (klingons <= 0) return; // no klingons
if (enterprise.isDocked()) {
Util.println("STARBASE SHIELDS PROTECT THE ENTERPRISE");
return;
}
for (int i = 1; i <= 3; i++) {
if (klingonQuadrants[i][3] <= 0) continue;
int hits = Util.toInt((klingonQuadrants[i][3] / fnd(1)) * (2 + Util.random()));
enterprise.sufferHitPoints(hits);
klingonQuadrants[i][3] = Util.toInt(klingonQuadrants[i][3] / (3 + Util.random()));
Util.println(hits + " UNIT HIT ON ENTERPRISE FROM SECTOR " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]);
if (enterprise.getShields() <= 0) callback.endGameFail(true);
Util.println(" <SHIELDS DOWN TO " + enterprise.getShields() + " UNITS>");
if (hits < 20) continue;
if ((Util.random() > .6) || (hits / enterprise.getShields() <= .02)) continue;
int randomDevice = Util.fnr();
enterprise.setDeviceStatus(randomDevice, enterprise.getDeviceStatus()[randomDevice]- hits / enterprise.getShields() - .5 * Util.random());
Util.println("DAMAGE CONTROL REPORTS " + Enterprise.printDeviceName(randomDevice) + " DAMAGED BY THE HIT'");
}
}
public void moveEnterprise(final float course, final float warp, final int n, final double stardate, final double initialStardate, final int missionDuration, final GameCallback callback) {
insertMarker(MARKER_EMPTY, Util.toInt(enterprise.getSector()[Enterprise.COORD_X]), Util.toInt(enterprise.getSector()[Enterprise.COORD_Y]));
final int[] sector = enterprise.moveShip(course, n, quadrantMap, stardate, initialStardate, missionDuration, callback);
int sectorX = sector[Enterprise.COORD_X];
int sectorY = sector[Enterprise.COORD_Y];
insertMarker(MARKER_ENTERPRISE, Util.toInt(sectorX), Util.toInt(sectorY));
enterprise.maneuverEnergySR(n);
double stardateDelta = 1;
if (warp < 1) stardateDelta = .1 * Util.toInt(10 * warp);
callback.incrementStardate(stardateDelta);
if (stardate > initialStardate + missionDuration) callback.endGameFail(false);
}
void shortRangeSensorScan(final double stardate) {
final int sectorX = enterprise.getSector()[Enterprise.COORD_X];
final int sectorY = enterprise.getSector()[Enterprise.COORD_Y];
boolean docked = false;
String shipCondition; // ship condition (docked, red, yellow, green)
for (int i = sectorX - 1; i <= sectorX + 1; i++) {
for (int j = sectorY - 1; j <= sectorY + 1; j++) {
if ((Util.toInt(i) >= 1) && (Util.toInt(i) <= 8) && (Util.toInt(j) >= 1) && (Util.toInt(j) <= 8)) {
if (compareMarker(quadrantMap, MARKER_STARBASE, i, j)) {
docked = true;
}
}
}
}
if (!docked) {
enterprise.setDocked(false);
if (klingons > 0) {
shipCondition = "*RED*";
} else {
shipCondition = "GREEN";
if (enterprise.getEnergy() < enterprise.getInitialEnergy() * .1) {
shipCondition = "YELLOW";
}
}
} else {
enterprise.setDocked(true);
shipCondition = "DOCKED";
enterprise.replenishSupplies();
Util.println("SHIELDS DROPPED FOR DOCKING PURPOSES");
enterprise.dropShields();
}
if (enterprise.getDeviceStatus()[Enterprise.DEVICE_SHORT_RANGE_SENSORS] < 0) { // are short range sensors out?
Util.println("\n*** SHORT RANGE SENSORS ARE OUT ***\n");
return;
}
final String row = "---------------------------------";
Util.println(row);
for (int i = 1; i <= 8; i++) {
String sectorMapRow = "";
for (int j = (i - 1) * 24 + 1; j <= (i - 1) * 24 + 22; j += 3) {
sectorMapRow += " " + Util.midStr(quadrantMap, j, 3);
}
switch (i) {
case 1:
Util.println(sectorMapRow + " STARDATE " + Util.toInt(stardate * 10) * .1);
break;
case 2:
Util.println(sectorMapRow + " CONDITION " + shipCondition);
break;
case 3:
Util.println(sectorMapRow + " QUADRANT " + enterprise.getQuadrant()[Enterprise.COORD_X] + "," + enterprise.getQuadrant()[Enterprise.COORD_Y]);
break;
case 4:
Util.println(sectorMapRow + " SECTOR " + sectorX + "," + sectorY);
break;
case 5:
Util.println(sectorMapRow + " PHOTON TORPEDOES " + Util.toInt(enterprise.getTorpedoes()));
break;
case 6:
Util.println(sectorMapRow + " TOTAL ENERGY " + Util.toInt(enterprise.getTotalEnergy()));
break;
case 7:
Util.println(sectorMapRow + " SHIELDS " + Util.toInt(enterprise.getShields()));
break;
case 8:
Util.println(sectorMapRow + " KLINGONS REMAINING " + Util.toInt(klingonsInGalaxy));
}
}
Util.println(row);
}
void longRangeSensorScan() {
final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
if (enterprise.getDeviceStatus()[Enterprise.DEVICE_LONG_RANGE_SENSORS] < 0) {
Util.println("LONG RANGE SENSORS ARE INOPERABLE");
return;
}
Util.println("LONG RANGE SCAN FOR QUADRANT " + quadrantX + "," + quadrantY);
final String rowStr = "-------------------";
Util.println(rowStr);
final int[] n = new int[4];
for (int i = quadrantX - 1; i <= quadrantX + 1; i++) {
n[1] = -1;
n[2] = -2;
n[3] = -3;
for (int j = quadrantY - 1; j <= quadrantY + 1; j++) {
if (i > 0 && i < 9 && j > 0 && j < 9) {
n[j - quadrantY + 2] = galaxy[i][j];
chartedGalaxy[i][j] = galaxy[i][j];
}
}
for (int l = 1; l <= 3; l++) {
Util.print(": ");
if (n[l] < 0) {
Util.print("*** ");
continue;
}
Util.print(Util.rightStr(Integer.toString(n[l] + 1000), 3) + " ");
}
Util.println(": \n" + rowStr);
}
}
void firePhasers(GameCallback callback) {
final double[] deviceStatus = enterprise.getDeviceStatus();
final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
if (deviceStatus[Enterprise.DEVICE_PHASER_CONTROL] < 0) {
Util.println("PHASERS INOPERATIVE");
return;
}
if (klingons <= 0) {
printNoEnemyShipsMessage();
return;
}
if (deviceStatus[Enterprise.DEVICE_LIBRARY_COMPUTER] < 0) Util.println("COMPUTER FAILURE HAMPERS ACCURACY");
Util.println("PHASERS LOCKED ON TARGET; ");
int nrUnitsToFire;
while (true) {
Util.println("ENERGY AVAILABLE = " + enterprise.getEnergy() + " UNITS");
nrUnitsToFire = Util.toInt(Util.inputFloat("NUMBER OF UNITS TO FIRE"));
if (nrUnitsToFire <= 0) return;
if (enterprise.getEnergy() - nrUnitsToFire >= 0) break;
}
enterprise.decreaseEnergy(nrUnitsToFire);
if (deviceStatus[Enterprise.DEVICE_SHIELD_CONTROL] < 0) nrUnitsToFire = Util.toInt(nrUnitsToFire * Util.random());
int h1 = Util.toInt(nrUnitsToFire / klingons);
for (int i = 1; i <= 3; i++) {
if (klingonQuadrants[i][3] <= 0) break;
int hitPoints = Util.toInt((h1 / fnd(0)) * (Util.random() + 2));
if (hitPoints <= .15 * klingonQuadrants[i][3]) {
Util.println("SENSORS SHOW NO DAMAGE TO ENEMY AT " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]);
continue;
}
klingonQuadrants[i][3] = klingonQuadrants[i][3] - hitPoints;
Util.println(hitPoints + " UNIT HIT ON KLINGON AT SECTOR " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]);
if (klingonQuadrants[i][3] <= 0) {
Util.println("*** KLINGON DESTROYED ***");
klingons -= 1;
klingonsInGalaxy -= 1;
insertMarker(MARKER_EMPTY, klingonQuadrants[i][1], klingonQuadrants[i][2]);
klingonQuadrants[i][3] = 0;
galaxy[quadrantX][quadrantY] -= 100;
chartedGalaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY];
if (klingonsInGalaxy <= 0) callback.endGameSuccess();
} else {
Util.println(" (SENSORS SHOW " + klingonQuadrants[i][3] + " UNITS REMAINING)");
}
}
klingonsShoot(callback);
}
void firePhotonTorpedo(final double stardate, final double initialStardate, final double missionDuration, GameCallback callback) {
if (enterprise.getTorpedoes() <= 0) {
Util.println("ALL PHOTON TORPEDOES EXPENDED");
return;
}
if (enterprise.getDeviceStatus()[Enterprise.DEVICE_PHOTON_TUBES] < 0) {
Util.println("PHOTON TUBES ARE NOT OPERATIONAL");
}
float c1 = Util.inputFloat("PHOTON TORPEDO COURSE (1-9)");
if (c1 == 9) c1 = 1;
if (c1 < 1 && c1 >= 9) {
Util.println("ENSIGN CHEKOV REPORTS, 'INCORRECT COURSE DATA, SIR!'");
return;
}
int ic1 = Util.toInt(c1);
final int[][] cardinalDirections = enterprise.getCardinalDirections();
float x1 = cardinalDirections[ic1][1] + (cardinalDirections[ic1 + 1][1] - cardinalDirections[ic1][1]) * (c1 - ic1);
enterprise.decreaseEnergy(2);
enterprise.decreaseTorpedoes(1);
float x2 = cardinalDirections[ic1][2] + (cardinalDirections[ic1 + 1][2] - cardinalDirections[ic1][2]) * (c1 - ic1);
float x = enterprise.getSector()[Enterprise.COORD_X];
float y = enterprise.getSector()[Enterprise.COORD_Y];
Util.println("TORPEDO TRACK:");
while (true) {
x = x + x1;
y = y + x2;
int x3 = Math.round(x);
int y3 = Math.round(y);
if (x3 < 1 || x3 > 8 || y3 < 1 || y3 > 8) {
Util.println("TORPEDO MISSED"); // 5490
klingonsShoot(callback);
return;
}
Util.println(" " + x3 + "," + y3);
if (compareMarker(quadrantMap, MARKER_EMPTY, Util.toInt(x), Util.toInt(y))) {
continue;
} else if (compareMarker(quadrantMap, MARKER_KLINGON, Util.toInt(x), Util.toInt(y))) {
Util.println("*** KLINGON DESTROYED ***");
klingons = klingons - 1;
klingonsInGalaxy = klingonsInGalaxy - 1;
if (klingonsInGalaxy <= 0) callback.endGameSuccess();
for (int i = 1; i <= 3; i++) {
if (x3 == klingonQuadrants[i][1] && y3 == klingonQuadrants[i][2]) break;
}
int i = 3;
klingonQuadrants[i][3] = 0;
} else if (compareMarker(quadrantMap, MARKER_STAR, Util.toInt(x), Util.toInt(y))) {
Util.println("STAR AT " + x3 + "," + y3 + " ABSORBED TORPEDO ENERGY.");
klingonsShoot(callback);
return;
} else if (compareMarker(quadrantMap, MARKER_STARBASE, Util.toInt(x), Util.toInt(y))) {
Util.println("*** STARBASE DESTROYED ***");
starbases = starbases - 1;
basesInGalaxy = basesInGalaxy - 1;
if (basesInGalaxy == 0 && klingonsInGalaxy <= stardate - initialStardate - missionDuration) {
Util.println("THAT DOES IT, CAPTAIN!! YOU ARE HEREBY RELIEVED OF COMMAND");
Util.println("AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!");
callback.endGameFail(false);
} else {
Util.println("STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER");
Util.println("COURT MARTIAL!");
enterprise.setDocked(false);
}
}
insertMarker(MARKER_EMPTY, Util.toInt(x), Util.toInt(y));
final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
galaxy[quadrantX][quadrantY] = klingons * 100 + starbases * 10 + stars;
chartedGalaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY];
klingonsShoot(callback);
}
}
public void cumulativeGalacticRecord(final boolean cumulativeReport) {
final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
if (cumulativeReport) {
Util.println("");
Util.println(" ");
Util.println("COMPUTER RECORD OF GALAXY FOR QUADRANT " + quadrantX + "," + quadrantY);
Util.println("");
} else {
Util.println(" THE GALAXY");
}
Util.println(" 1 2 3 4 5 6 7 8");
final String rowDivider = " ----- ----- ----- ----- ----- ----- ----- -----";
Util.println(rowDivider);
for (int i = 1; i <= 8; i++) {
Util.print(i + " ");
if (cumulativeReport) {
int y = 1;
String quadrantName = getQuadrantName(false, i, y);
int tabLen = Util.toInt(15 - .5 * Util.strlen(quadrantName));
Util.println(Util.tab(tabLen) + quadrantName);
y = 5;
quadrantName = getQuadrantName(false, i, y);
tabLen = Util.toInt(39 - .5 * Util.strlen(quadrantName));
Util.println(Util.tab(tabLen) + quadrantName);
} else {
for (int j = 1; j <= 8; j++) {
Util.print(" ");
if (chartedGalaxy[i][j] == 0) {
Util.print("***");
} else {
Util.print(Util.rightStr(Integer.toString(chartedGalaxy[i][j] + 1000), 3));
}
}
}
Util.println("");
Util.println(rowDivider);
}
Util.println("");
}
public void photonTorpedoData() {
int sectorX = enterprise.getSector()[Enterprise.COORD_X];
int sectorY = enterprise.getSector()[Enterprise.COORD_Y];
if (klingons <= 0) {
printNoEnemyShipsMessage();
return;
}
Util.println("FROM ENTERPRISE TO KLINGON BATTLE CRUISER" + ((klingons > 1)? "S" : ""));
for (int i = 1; i <= 3; i++) {
if (klingonQuadrants[i][3] > 0) {
printDirection(sectorX, sectorY, klingonQuadrants[i][1], klingonQuadrants[i][2]);
}
}
}
void directionDistanceCalculator() {
int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
int sectorX = enterprise.getSector()[Enterprise.COORD_X];
int sectorY = enterprise.getSector()[Enterprise.COORD_Y];
Util.println("DIRECTION/DISTANCE CALCULATOR:");
Util.println("YOU ARE AT QUADRANT " + quadrantX + "," + quadrantY + " SECTOR " + sectorX + "," + sectorY);
Util.print("PLEASE ENTER ");
int[] initialCoords = Util.inputCoords(" INITIAL COORDINATES (X,Y)");
int[] finalCoords = Util.inputCoords(" FINAL COORDINATES (X,Y)");
printDirection(initialCoords[0], initialCoords[1], finalCoords[0], finalCoords[1]);
}
void printDirection(int from_x, int from_y, int to_x, int to_y) {
to_y = to_y - from_y; // delta 2
from_y = from_x - to_x; // delta 1
if (to_y > 0) {
if (from_y < 0) {
from_x = 7;
} else {
from_x = 1;
int tempA = from_y;
from_y = to_y;
to_y = tempA;
}
} else {
if (from_y > 0) {
from_x = 3;
} else {
from_x = 5;
int tempA = from_y;
from_y = to_y;
to_y = tempA;
}
}
from_y = Math.abs(from_y);
to_y = Math.abs(to_y);
if (from_y > 0 || to_y > 0) {
if (from_y >= to_y) {
Util.println("DIRECTION = " + (from_x + to_y / from_y));
} else {
Util.println("DIRECTION = " + (from_x + 2 - to_y / from_y));
}
}
Util.println("DISTANCE = " + Util.round(Math.sqrt(to_y ^ 2 + from_y ^ 2), 6));
}
void starbaseNavData() {
int sectorX = enterprise.getSector()[Enterprise.COORD_X];
int sectorY = enterprise.getSector()[Enterprise.COORD_Y];
if (starbases != 0) {
Util.println("FROM ENTERPRISE TO STARBASE:");
printDirection(sectorX, sectorY, starbaseX, starbaseY);
} else {
Util.println("MR. SPOCK REPORTS, 'SENSORS SHOW NO STARBASES IN THIS");
Util.println(" QUADRANT.'");
}
}
void printNoEnemyShipsMessage() {
Util.println("SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY SHIPS");
Util.println(" IN THIS QUADRANT'");
}
String getRegionName(final boolean regionNameOnly, final int y) {
if (!regionNameOnly) {
switch (y % 4) {
case 0:
return " I";
case 1:
return " II";
case 2:
return " III";
case 3:
return " IV";
}
}
return "";
}
String getQuadrantName(final boolean regionNameOnly, final int x, final int y) {
if (y <= 4) {
switch (x) {
case 1:
return "ANTARES" + getRegionName(regionNameOnly, y);
case 2:
return "RIGEL" + getRegionName(regionNameOnly, y);
case 3:
return "PROCYON" + getRegionName(regionNameOnly, y);
case 4:
return "VEGA" + getRegionName(regionNameOnly, y);
case 5:
return "CANOPUS" + getRegionName(regionNameOnly, y);
case 6:
return "ALTAIR" + getRegionName(regionNameOnly, y);
case 7:
return "SAGITTARIUS" + getRegionName(regionNameOnly, y);
case 8:
return "POLLUX" + getRegionName(regionNameOnly, y);
}
} else {
switch (x) {
case 1:
return "SIRIUS" + getRegionName(regionNameOnly, y);
case 2:
return "DENEB" + getRegionName(regionNameOnly, y);
case 3:
return "CAPELLA" + getRegionName(regionNameOnly, y);
case 4:
return "BETELGEUSE" + getRegionName(regionNameOnly, y);
case 5:
return "ALDEBARAN" + getRegionName(regionNameOnly, y);
case 6:
return "REGULUS" + getRegionName(regionNameOnly, y);
case 7:
return "ARCTURUS" + getRegionName(regionNameOnly, y);
case 8:
return "SPICA" + getRegionName(regionNameOnly, y);
}
}
return "UNKNOWN - ERROR";
}
void insertMarker(final String marker, final int x, final int y) {
final int pos = Util.toInt(y) * 3 + Util.toInt(x) * 24 + 1;
if (marker.length() != 3) {
System.err.println("ERROR");
System.exit(-1);
}
if (pos == 1) {
quadrantMap = marker + Util.rightStr(quadrantMap, 189);
}
if (pos == 190) {
quadrantMap = Util.leftStr(quadrantMap, 189) + marker;
}
quadrantMap = Util.leftStr(quadrantMap, (pos - 1)) + marker + Util.rightStr(quadrantMap, (190 - pos));
}
/**
* Finds random empty coordinates in a quadrant.
*
* @param quadrantString
* @return an array with a pair of coordinates x, y
*/
int[] findEmptyPlaceInQuadrant(final String quadrantString) {
final int x = Util.fnr();
final int y = Util.fnr();
if (!compareMarker(quadrantString, MARKER_EMPTY, x, y)) {
return findEmptyPlaceInQuadrant(quadrantString);
}
return new int[]{x, y};
}
boolean compareMarker(final String quadrantString, final String marker, final int x, final int y) {
final int markerRegion = (y - 1) * 3 + (x - 1) * 24 + 1;
if (Util.midStr(quadrantString, markerRegion, 3).equals(marker)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,9 @@
/**
* Interface for decoupling inversion of control from GalaxyMap and Enterprise towards the game class.
*/
public interface GameCallback {
void enterNewQuadrant();
void incrementStardate(double increment);
void endGameSuccess();
void endGameFail(boolean enterpriseDestroyed);
}

View File

@@ -1,3 +1,13 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Oracle Java](https://openjdk.java.net/)
Conversion to [Oracle Java](https://openjdk.java.net/) by [Taciano Dreckmann Perez](https://github.com/taciano-perez).
Overview of Java classes:
- SuperStarTrekInstructions: displays game instructions
- SuperStarTrekGame: main game class
- GalaxyMap: map of the galaxy divided in quadrants and sectors, containing stars, bases, klingons, and the Enterprise
- Enterprise: the starship Enterprise
- GameCallback: interface allowing other classes to interact with the game class without circular dependencies
- Util: utility methods
[This video](https://www.youtube.com/watch?v=cU3NKOnRNCI) describes the approach and the different steps followed to translate the game.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,113 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* Convenience utility methods for the Super Star Trek game.
*/
public class Util {
static final Random random = new Random();
public static float random() {
return random.nextFloat();
}
public static int fnr() { // 475
// Generate a random integer from 1 to 8 inclusive.
return toInt(random() * 7 + 1);
}
public static int toInt(final double num) {
int x = (int) Math.floor(num);
if (x < 0) x *= -1;
return x;
}
public static void println(final String s) {
System.out.println(s);
}
public static void print(final String s) {
System.out.print(s);
}
public static String tab(final int n) {
return IntStream.range(1, n).mapToObj(num -> " ").collect(Collectors.joining());
}
public static int strlen(final String s) {
return s.length();
}
public static String inputStr(final String message) {
System.out.print(message + "? ");
final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
return reader.readLine();
} catch (IOException ioe) {
ioe.printStackTrace();
return "";
}
}
public static int[] inputCoords(final String message) {
while (true) {
final String input = inputStr(message);
try {
final String[] splitInput = input.split(",");
if (splitInput.length == 2) {
int x = Integer.parseInt(splitInput[0]);
int y = Integer.parseInt(splitInput[0]);
return new int[]{x, y};
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static float inputFloat(final String message) {
while (true) {
System.out.print(message + "? ");
final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
final String input = reader.readLine();
if (input.length() > 0) {
return Float.parseFloat(input);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static String leftStr(final String input, final int len) {
if (input == null || input.length() < len) return input;
return input.substring(0, len);
}
public static String midStr(final String input, final int start, final int len) {
if (input == null || input.length() < ((start - 1) + len)) return input;
return input.substring(start - 1, (start - 1) + len);
}
public static String rightStr(final String input, final int len) {
if (input == null || input.length() < len) return "";
return input.substring(input.length() - len);
}
public static double round(double value, int places) {
if (places < 0) throw new IllegalArgumentException();
BigDecimal bd = new BigDecimal(Double.toString(value));
bd = bd.setScale(places, RoundingMode.HALF_UP);
return bd.doubleValue();
}
}