From a64aa983febee70be1a623f5557599c218beebaa Mon Sep 17 00:00:00 2001 From: Taciano Dreckmann Perez Date: Thu, 24 Mar 2022 18:58:47 +0100 Subject: [PATCH] updated implementation with proper OO design, provided link to video --- 84_Super_Star_Trek/java/Enterprise.java | 331 +++++ 84_Super_Star_Trek/java/GalaxyMap.java | 649 ++++++++++ 84_Super_Star_Trek/java/GameCallback.java | 9 + 84_Super_Star_Trek/java/README.md | 12 +- .../java/SuperStarTrekGame.java | 1148 ++--------------- 84_Super_Star_Trek/java/Util.java | 113 ++ 6 files changed, 1238 insertions(+), 1024 deletions(-) create mode 100644 84_Super_Star_Trek/java/Enterprise.java create mode 100644 84_Super_Star_Trek/java/GalaxyMap.java create mode 100644 84_Super_Star_Trek/java/GameCallback.java create mode 100644 84_Super_Star_Trek/java/Util.java diff --git a/84_Super_Star_Trek/java/Enterprise.java b/84_Super_Star_Trek/java/Enterprise.java new file mode 100644 index 00000000..d362769e --- /dev/null +++ b/84_Super_Star_Trek/java/Enterprise.java @@ -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(""); + return; + } + if (energyToShields > energy + energyToShields) { + Util.println("SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION TREASURY.'"); + Util.println(""); + 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 ""; + } + +} diff --git a/84_Super_Star_Trek/java/GalaxyMap.java b/84_Super_Star_Trek/java/GalaxyMap.java new file mode 100644 index 00000000..77541ec9 --- /dev/null +++ b/84_Super_Star_Trek/java/GalaxyMap.java @@ -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(" "); + 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; + } + +} diff --git a/84_Super_Star_Trek/java/GameCallback.java b/84_Super_Star_Trek/java/GameCallback.java new file mode 100644 index 00000000..1d4530a3 --- /dev/null +++ b/84_Super_Star_Trek/java/GameCallback.java @@ -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); +} diff --git a/84_Super_Star_Trek/java/README.md b/84_Super_Star_Trek/java/README.md index 51edd8d4..c4f8a30a 100644 --- a/84_Super_Star_Trek/java/README.md +++ b/84_Super_Star_Trek/java/README.md @@ -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. \ No newline at end of file diff --git a/84_Super_Star_Trek/java/SuperStarTrekGame.java b/84_Super_Star_Trek/java/SuperStarTrekGame.java index 213b2a0b..d54e9708 100644 --- a/84_Super_Star_Trek/java/SuperStarTrekGame.java +++ b/84_Super_Star_Trek/java/SuperStarTrekGame.java @@ -1,10 +1,3 @@ -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; /** @@ -18,17 +11,10 @@ import java.util.stream.IntStream; * *** LEEDOM - APRIL & DECEMBER 1974, * *** WITH A LITTLE HELP FROM HIS FRIENDS . . . * - * Ported to Java in Jan-Feb 2022 by + * Ported to Java in Jan-Mar 2022 by * Taciano Dreckmann Perez (taciano.perez@gmail.com) */ -public class SuperStarTrekGame { - - // 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 = " * "; +public class SuperStarTrekGame implements GameCallback { // commands static final int COMMAND_NAV = 1; @@ -49,71 +35,34 @@ public class SuperStarTrekGame { static final int COMPUTER_COMMAND_DIR_DIST_CALC = 5; static final int COMPUTER_COMMAND_GALAXY_MAP = 6; - // 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; - // other constants - static final String QUADRANT_ROW = " "; static final String COMMANDS = "NAVSRSLRSPHATORSHEDAMCOMXXX"; - static final Random random = new Random(); // game state - final int galaxy[][] = new int[9][9]; // 8x8 galaxy map G - final int cardinalDirections[][] = new int[10][3]; // 9x2 vectors in cardinal directions C - final int klingonQuadrants[][] = new int[4][4]; // 3x3 position of klingons K - final int chartedGalaxy[][] = new int[9][9]; // 8x8 charted galaxy map Z - final double deviceStatus[] = new double[9]; // 8 device damage stats D - String quadrantMap = QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + leftStr(QUADRANT_ROW, 17); // current quadrant map - double stardate = toInt(random() * 20 + 20); // T - int energy = 3000; // E - boolean shipDocked = false; // D0 - int torpedoes = 10; // P - int shields = 0; // S - int missionDuration = 25 + toInt(random() * 10); // T9 (mission duration in stardates) - int basesInGalaxy = 0; // B9 - int remainingKlingons; // K7 - int klingonsInGalaxy = 0; // K9 - int quadrantX; // Q1 - int quadrantY; // Q2 - int sectorX; // S1 - int sectorY; // S2 - int klingons = 0; //K3 - int starbases = 0; // B3 - int stars = 0; // S3 - int starbaseX = 0; // X coordinate of starbase - int starbaseY = 0; // Y coord of starbase - double repairCost; // damage repair cost D4 + final GalaxyMap galaxyMap = new GalaxyMap(); + double stardate = Util.toInt(Util.random() * 20 + 20); + int missionDuration = Math.max((25 + Util.toInt(Util.random() * 10)), galaxyMap.getKlingonsInGalaxy()+1); // T9 (mission duration in stardates) boolean restart = false; // initial values - final double initialStardate = stardate; // T0 - final int initialEnergy = energy; // E0 - final int initialTorpedoes = torpedoes; // P0 - final int avgKlingonShieldEnergy = 200; // S9 + final double initialStardate = stardate; public static void main(String[] args) { final SuperStarTrekGame game = new SuperStarTrekGame(); printBanner(); while (true) { - game.setup(); + game.orders(); game.enterNewQuadrant(); game.restart = false; game.commandLoop(); } } - static void printBanner() { // 220 + static void printBanner() { IntStream.range(1, 10).forEach(i -> { - println(""); + Util.println(""); }); - println( + Util.println( """ ,------*------, ,------------- '--- ------' @@ -127,157 +76,27 @@ public class SuperStarTrekGame { ); } - double fnd(int i) { // 470 - return Math.sqrt((klingonQuadrants[i][1] - sectorX) ^ 2 + (klingonQuadrants[i][2] - sectorY) ^ 2); - } - - static int fnr() { // 475 - // Generate a random integer from 1 to 8 inclusive. - return toInt(random() * 7 + 1); - } - - void setup() { - this.initEnterprisePosition(); - this.setupWhatExistsInTheGalaxy(); - } - - void initEnterprisePosition() { // 480 - quadrantX = fnr(); - quadrantY = fnr(); - sectorX = fnr(); - sectorY = fnr(); - 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; - IntStream.range(1, 8).forEach(i -> deviceStatus[i] = 0); - } - - void setupWhatExistsInTheGalaxy() { // 810 - // KLINGONS, STARBASES, STARS - IntStream.range(1, 8).forEach(x -> { - IntStream.range(1, 8).forEach(y -> { - klingons = 0; - chartedGalaxy[x][y] = 0; - float random = 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 (random() > .96) { - starbases = 1; - basesInGalaxy = +1; - } - galaxy[x][y] = klingons * 100 + starbases * 10 + fnr(); - }); - }); - if (klingonsInGalaxy > missionDuration) missionDuration = klingonsInGalaxy + 1; - if (basesInGalaxy == 0) { - if (galaxy[quadrantX][quadrantY] < 200) { - galaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY] + 120; - klingonsInGalaxy = +1; - } - basesInGalaxy = 1; - galaxy[quadrantX][quadrantY] = +10; - quadrantX = fnr(); - quadrantY = fnr(); - } - remainingKlingons = klingonsInGalaxy; - println("YOUR ORDERS ARE AS FOLLOWS:\n" + - " DESTROY THE " + klingonsInGalaxy + " KLINGON WARSHIP" + ((klingonsInGalaxy == 1) ? "" : "S") + " WHICH HAVE INVADED\n" + + void orders() { + Util.println("YOUR ORDERS ARE AS FOLLOWS:\n" + + " DESTROY THE " + galaxyMap.getKlingonsInGalaxy() + " KLINGON WARSHIP" + ((galaxyMap.getKlingonsInGalaxy() == 1) ? "" : "S") + " WHICH HAVE INVADED\n" + " THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS\n" + - " ON STARDATE " + initialStardate + missionDuration + " THIS GIVES YOU " + missionDuration + " DAYS. THERE " + ((basesInGalaxy == 1) ? "IS" : "ARE") + "\n" + - " " + basesInGalaxy + " STARBASE" + ((basesInGalaxy == 1) ? "" : "S") + " IN THE GALAXY FOR RESUPPLYING YOUR SHIP"); + " ON STARDATE " + initialStardate + missionDuration + " THIS GIVES YOU " + missionDuration + " DAYS. THERE " + ((galaxyMap.getBasesInGalaxy() == 1) ? "IS" : "ARE") + "\n" + + " " + galaxyMap.getBasesInGalaxy() + " STARBASE" + ((galaxyMap.getBasesInGalaxy() == 1) ? "" : "S") + " IN THE GALAXY FOR RESUPPLYING YOUR SHIP"); } - void enterNewQuadrant() { // 1320 - // ANY TIME NEW QUADRANT ENTERED - klingons = 0; - starbases = 0; - stars = 0; - repairCost = .5 * random(); - 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) { - println("YOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED\n" + - "IN THE GALACTIC QUADRANT, '" + quadrantName + "'."); - } else { - println("NOW ENTERING " + quadrantName + " QUADRANT . . ."); - } - 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) { - println("COMBAT AREA CONDITION RED"); - if (shields <= 200) { - 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, sectorX, sectorY); - // 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(avgKlingonShieldEnergy * (0.5 + 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]); - } - shortRangeSensorScan(); // 1980 + public void enterNewQuadrant() { + galaxyMap.newQuadrant(stardate, initialStardate); + shortRangeSensorScan(); } void commandLoop() { while (!this.restart) { - checkShipEnergy(); // 1990 + checkShipEnergy(); String cmdStr = ""; - while ("".equals(cmdStr)) cmdStr = inputStr("COMMAND"); + while ("".equals(cmdStr)) cmdStr = Util.inputStr("COMMAND"); boolean foundCommand = false; for (int i = 1; i <= 9; i++) { - if (leftStr(cmdStr, 3).equals(midStr(COMMANDS, 3 * i - 2, 3))) { + if (Util.leftStr(cmdStr, 3).equals(Util.midStr(COMMANDS, 3 * i - 2, 3))) { switch (i) { case COMMAND_NAV: navigation(); @@ -304,7 +123,7 @@ public class SuperStarTrekGame { foundCommand = true; break; case COMMAND_DAM: - damageControl(); + galaxyMap.getEnterprise().damageControl(this); foundCommand = true; break; case COMMAND_COM: @@ -326,819 +145,191 @@ public class SuperStarTrekGame { } void checkShipEnergy() { - int totalEnergy = (shields + energy); - if (totalEnergy < 10 && (energy <= 10 || deviceStatus[DEVICE_SHIELD_CONTROL] != 0)) { - println("\n** FATAL ERROR ** YOU'VE JUST STRANDED YOUR SHIP IN "); - println("SPACE"); - println("YOU HAVE INSUFFICIENT MANEUVERING ENERGY,"); - println(" AND SHIELD CONTROL"); - println("IS PRESENTLY INCAPABLE OF CROSS"); - println("-CIRCUITING TO ENGINE ROOM!!"); + final Enterprise enterprise = galaxyMap.getEnterprise(); + if (enterprise.getTotalEnergy() < 10 && (enterprise.getEnergy() <= 10 || enterprise.getDeviceStatus()[Enterprise.DEVICE_SHIELD_CONTROL] != 0)) { + Util.println("\n** FATAL ERROR ** YOU'VE JUST STRANDED YOUR SHIP IN "); + Util.println("SPACE"); + Util.println("YOU HAVE INSUFFICIENT MANEUVERING ENERGY,"); + Util.println(" AND SHIELD CONTROL"); + Util.println("IS PRESENTLY INCAPABLE OF CROSS"); + Util.println("-CIRCUITING TO ENGINE ROOM!!"); endGameFail(false); } } void printCommandOptions() { - println("ENTER ONE OF THE FOLLOWING:"); - println(" NAV (TO SET COURSE)"); - println(" SRS (FOR SHORT RANGE SENSOR SCAN)"); - println(" LRS (FOR LONG RANGE SENSOR SCAN)"); - println(" PHA (TO FIRE PHASERS)"); - println(" TOR (TO FIRE PHOTON TORPEDOES)"); - println(" SHE (TO RAISE OR LOWER SHIELDS)"); - println(" DAM (FOR DAMAGE CONTROL REPORTS)"); - println(" COM (TO CALL ON LIBRARY-COMPUTER)"); - println(" XXX (TO RESIGN YOUR COMMAND)\n"); + Util.println("ENTER ONE OF THE FOLLOWING:"); + Util.println(" NAV (TO SET COURSE)"); + Util.println(" SRS (FOR SHORT RANGE SENSOR SCAN)"); + Util.println(" LRS (FOR LONG RANGE SENSOR SCAN)"); + Util.println(" PHA (TO FIRE PHASERS)"); + Util.println(" TOR (TO FIRE PHOTON TORPEDOES)"); + Util.println(" SHE (TO RAISE OR LOWER SHIELDS)"); + Util.println(" DAM (FOR DAMAGE CONTROL REPORTS)"); + Util.println(" COM (TO CALL ON LIBRARY-COMPUTER)"); + Util.println(" XXX (TO RESIGN YOUR COMMAND)\n"); } - void navigation() { // 2290 - float course = toInt(inputFloat("COURSE (0-9)")); + void navigation() { + float course = Util.toInt(Util.inputFloat("COURSE (0-9)")); if (course == 9) course = 1; if (course < 1 || course >= 9) { - println(" LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'"); + Util.println(" LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'"); return; } - println("WARP FACTOR (0-" + ((deviceStatus[DEVICE_WARP_ENGINES] < 0) ? "0.2" : "8") + ")"); - float warp = inputFloat(""); - if (deviceStatus[DEVICE_WARP_ENGINES] < 0 && warp > .2) { - // 2470 - println("WARP ENGINES ARE DAMAGED. MAXIMUM SPEED = WARP 0.2"); + final Enterprise enterprise = galaxyMap.getEnterprise(); + final double[] deviceStatus = enterprise.getDeviceStatus(); + Util.println("WARP FACTOR (0-" + ((deviceStatus[Enterprise.DEVICE_WARP_ENGINES] < 0) ? "0.2" : "8") + ")"); + float warp = Util.inputFloat(""); + if (deviceStatus[Enterprise.DEVICE_WARP_ENGINES] < 0 && warp > .2) { + Util.println("WARP ENGINES ARE DAMAGED. MAXIMUM SPEED = WARP 0.2"); return; } if (warp == 0) return; if (warp > 0 && warp <= 8) { - // 2490 - int n = toInt(warp * 8); - if (energy - n >= 0) { - klingonsMoveAndFire(); + int n = Util.toInt(warp * 8); + if (enterprise.getEnergy() - n >= 0) { + galaxyMap.klingonsMoveAndFire(this); repairDamagedDevices(course, warp, n); - moveStarship(course, warp, n); + galaxyMap.moveEnterprise(course, warp, n, stardate, initialStardate, missionDuration, this); } else { - println("ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE"); - println(" FOR MANEUVERING AT WARP" + warp + "!'"); - if (shields < n - energy || deviceStatus[DEVICE_SHIELD_CONTROL] < 0) return; - println("DEFLECTOR CONTROL ROOM ACKNOWLEDGES" + shields + "UNITS OF ENERGY"); - println(" PRESENTLY DEPLOYED TO SHIELDS."); - return; + Util.println("ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE"); + Util.println(" FOR MANEUVERING AT WARP " + warp + "!'"); + if (enterprise.getShields() < n - enterprise.getEnergy() || deviceStatus[Enterprise.DEVICE_SHIELD_CONTROL] < 0) return; + Util.println("DEFLECTOR CONTROL ROOM ACKNOWLEDGES " + enterprise.getShields() + " UNITS OF ENERGY"); + Util.println(" PRESENTLY DEPLOYED TO SHIELDS."); } } else { - println(" CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE"); - println(" WARP " + warp + "!'"); - return; + Util.println(" CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE"); + Util.println(" WARP " + warp + "!'"); } } - void klingonsMoveAndFire() { // 2590 - // KLINGONS MOVE/FIRE ON MOVING STARSHIP . . . - 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(); - } - void repairDamagedDevices(final float course, final float warp, final int N) { + final Enterprise enterprise = galaxyMap.getEnterprise(); // 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) { - println("DAMAGE CONTROL REPORT: "); - println(tab(8) + printDeviceName(i) + " REPAIR COMPLETED."); - } - } - } - if (random() > .2) moveStarship(course, warp, N); // 80% chance no damage nor repair - int randomDevice = fnr(); // random device - if (random() >= .6) { // 40% chance of repair of random device - deviceStatus[randomDevice] = deviceStatus[randomDevice] + random() * 3 + 1; - println("DAMAGE CONTROL REPORT: " + printDeviceName(randomDevice) + " STATE OF REPAIR IMPROVED\n"); + enterprise.repairDamagedDevices(warp); + if (Util.random() > .2) return; // 80% chance no damage nor repair + int randomDevice = Util.fnr(); // random device + final double[] deviceStatus = enterprise.getDeviceStatus(); + if (Util.random() >= .6) { // 40% chance of repair of random device + enterprise.setDeviceStatus(randomDevice, deviceStatus[randomDevice] + Util.random() * 3 + 1); + Util.println("DAMAGE CONTROL REPORT: " + Enterprise.printDeviceName(randomDevice) + " STATE OF REPAIR IMPROVED\n"); } else { // 60% chance of damage of random device - deviceStatus[randomDevice] = deviceStatus[randomDevice] - (random() * 5 + 1); // - println("DAMAGE CONTROL REPORT: " + printDeviceName(randomDevice) + " DAMAGED"); + enterprise.setDeviceStatus(randomDevice, deviceStatus[randomDevice] - (Util.random() * 5 + 1)); + Util.println("DAMAGE CONTROL REPORT: " + Enterprise.printDeviceName(randomDevice) + " DAMAGED"); } } - void moveStarship(final float course, final float warp, final int n) { // 3070 - insertMarker(MARKER_EMPTY, toInt(sectorX), toInt(sectorY)); - int ic1 = 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 = toInt(x / 8); - quadrantY = toInt(y / 8); - sectorX = toInt(x - quadrantX * 8); - sectorY = 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) { - println("LT. UHURA REPORTS MESSAGE FROM STARFLEET COMMAND:"); - println(" 'PERMISSION TO ATTEMPT CROSSING OF GALACTIC PERIMETER"); - println(" IS HEREBY *DENIED*. SHUT DOWN YOUR ENGINES.'"); - println("CHIEF ENGINEER SCOTT REPORTS 'WARP ENGINES SHUT DOWN"); - println(" AT SECTOR " + sectorX + "," + sectorY + " OF QUADRANT " + quadrantX + "," + quadrantY + ".'"); - if (stardate > initialStardate + missionDuration) endGameFail(false); - } - if (8 * quadrantX + quadrantY == 8 * initialQuadrantX + initialQuadrantY) { - break; - } - stardate += 1; - maneuverEnergySR(n); - enterNewQuadrant(); - return; - } else { - int S8 = toInt(sectorX) * 24 + toInt(sectorY) * 3 - 26; // S8 = pos - if (!(" ".equals(midStr(quadrantMap, S8, 2)))) { - sectorX = toInt(sectorX - x1); - sectorY = toInt(sectorY - x2); - println("WARP ENGINES SHUT DOWN AT "); - println("SECTOR " + sectorX + "," + sectorY + " DUE TO BAD NAVIGATION"); - break; - } - } - } - sectorX = toInt(sectorX); - sectorY = toInt(sectorY); - insertMarker(MARKER_ENTERPRISE, toInt(sectorX), toInt(sectorY)); - maneuverEnergySR(n); - double stardateDelta = 1; - if (warp < 1) stardateDelta = .1 * toInt(10 * warp); - stardate = stardate + stardateDelta; - if (stardate > initialStardate + missionDuration) endGameFail(false); - shortRangeSensorScan(); - } - - void maneuverEnergySR(final int N) { // 3910 - energy = energy - N - 10; - if (energy >= 0) return; - println("SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER."); - shields = shields + energy; - energy = 0; - if (shields <= 0) shields = 0; - } - - void longRangeSensorScan() { // 3390 + void longRangeSensorScan() { // LONG RANGE SENSOR SCAN CODE - if (deviceStatus[DEVICE_LONG_RANGE_SENSORS] < 0) { - println("LONG RANGE SENSORS ARE INOPERABLE"); - return; - } - println("LONG RANGE SCAN FOR QUADRANT " + quadrantX + "," + quadrantY); - final String rowStr = "-------------------"; - 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++) { - print(": "); - if (n[l] < 0) { - print("*** "); - continue; - } - print(": " + rightStr(Integer.toString(n[l] + 1000), 3) + " "); - } - println(": \n" + rowStr); - } + galaxyMap.longRangeSensorScan(); } - void firePhasers() { // 4260 - if (deviceStatus[DEVICE_PHASER_CONTROL] < 0) { - println("PHASERS INOPERATIVE"); - return; - } - if (klingons <= 0) { - printNoEnemyShipsMessage(); - return; - } - if (deviceStatus[DEVICE_LIBRARY_COMPUTER] < 0) println("COMPUTER FAILURE HAMPERS ACCURACY"); - println("PHASERS LOCKED ON TARGET; "); - int nrUnitsToFire; - while (true) { - println("ENERGY AVAILABLE = " + energy + " UNITS"); - nrUnitsToFire = toInt(inputFloat("NUMBER OF UNITS TO FIRE")); - if (nrUnitsToFire <= 0) return; - if (energy - nrUnitsToFire >= 0) break; - } - energy = energy - nrUnitsToFire; - if (deviceStatus[DEVICE_SHIELD_CONTROL] < 0) nrUnitsToFire = toInt(nrUnitsToFire * random()); - int h1 = toInt(nrUnitsToFire / klingons); - for (int i = 1; i <= 3; i++) { - if (klingonQuadrants[i][3] <= 0) break; - int hitPoints = toInt((h1 / fnd(0)) * (random() + 2)); - if (hitPoints <= .15 * klingonQuadrants[i][3]) { - println("SENSORS SHOW NO DAMAGE TO ENEMY AT " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]); - continue; - } - klingonQuadrants[i][3] = klingonQuadrants[i][3] - hitPoints; - println(hitPoints + " UNIT HIT ON KLINGON AT SECTOR " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]); - if (klingonQuadrants[i][3] <= 0) { - 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) endGameSuccess(); - } else { - println(" (SENSORS SHOW" + klingonQuadrants[i][3] + "UNITS REMAINING)"); - } - } - klingonsShoot(); + void firePhasers() { + galaxyMap.firePhasers(this); } - void printNoEnemyShipsMessage() { // 4270 - println("SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY SHIPS"); - println(" IN THIS QUADRANT'"); - } - - void firePhotonTorpedo() { // 4700 - // PHOTON TORPEDO CODE BEGINS HERE - if (torpedoes <= 0) { - println("ALL PHOTON TORPEDOES EXPENDED"); - return; - } - if (deviceStatus[DEVICE_PHOTON_TUBES] < 0) { - println("PHOTON TUBES ARE NOT OPERATIONAL"); - } - float c1 = inputFloat("PHOTON TORPEDO COURSE (1-9)"); - if (c1 == 9) c1 = 1; - if (c1 < 1 && c1 >= 9) { - println("ENSIGN CHEKOV REPORTS, 'INCORRECT COURSE DATA, SIR!'"); - return; - } - int ic1 = toInt(c1); - float x1 = cardinalDirections[ic1][1] + (cardinalDirections[ic1 + 1][1] - cardinalDirections[ic1][1]) * (c1 - ic1); - energy = energy - 2; - torpedoes = torpedoes - 1; - float x2 = cardinalDirections[ic1][2] + (cardinalDirections[ic1 + 1][2] - cardinalDirections[ic1][2]) * (c1 - ic1); - float x = sectorX; - float y = sectorY; - 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) { - println("TORPEDO MISSED"); // 5490 - klingonsShoot(); - return; - } - println(" " + x3 + "," + y3); - if (compareMarker(quadrantMap, MARKER_EMPTY, toInt(x), toInt(y))) { - continue; - } else if (compareMarker(quadrantMap, MARKER_KLINGON, toInt(x), toInt(y))) { - println("*** KLINGON DESTROYED ***"); - klingons = klingons - 1; - klingonsInGalaxy = klingonsInGalaxy - 1; - if (klingonsInGalaxy <= 0) 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, toInt(x), toInt(y))) { - println("STAR AT " + x3 + "," + y3 + " ABSORBED TORPEDO ENERGY."); - klingonsShoot(); - return; - } else if (compareMarker(quadrantMap, MARKER_STARBASE, toInt(x), toInt(y))) { - println("*** STARBASE DESTROYED ***"); - starbases = starbases - 1; - basesInGalaxy = basesInGalaxy - 1; - if (basesInGalaxy == 0 && klingonsInGalaxy <= stardate - initialStardate - missionDuration) { - println("THAT DOES IT, CAPTAIN!! YOU ARE HEREBY RELIEVED OF COMMAND"); - println("AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!"); - endGameFail(false); - } else { - println("STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER"); - println("COURT MARTIAL!"); - shipDocked = false; - } - } - insertMarker(MARKER_EMPTY, toInt(x), toInt(y)); - galaxy[quadrantX][quadrantY] = klingons * 100 + starbases * 10 + stars; - chartedGalaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY]; - klingonsShoot(); - } + void firePhotonTorpedo() { + galaxyMap.firePhotonTorpedo(stardate, initialStardate, missionDuration, this); } void shieldControl() { - if (deviceStatus[DEVICE_SHIELD_CONTROL] < 0) { - println("SHIELD CONTROL INOPERABLE"); - return; - } - println("ENERGY AVAILABLE = " + (energy + shields)); - int energyToShields = toInt(inputFloat("NUMBER OF UNITS TO SHIELDS")); - if (energyToShields < 0 || shields == energyToShields) { - println(""); - return; - } - if (energyToShields > energy + energyToShields) { - println("SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION TREASURY.'"); - println(""); - return; - } - energy = energy + shields - energyToShields; - shields = energyToShields; - println("DEFLECTOR CONTROL ROOM REPORT:"); - println(" 'SHIELDS NOW AT " + toInt(shields) + " UNITS PER YOUR COMMAND.'"); + galaxyMap.getEnterprise().shieldControl(); } - void shortRangeSensorScan() { // 6430 + void shortRangeSensorScan() { // SHORT RANGE SENSOR SCAN & STARTUP SUBROUTINE - 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 ((toInt(i) >= 1) && (toInt(i) <= 8) && (toInt(j) >= 1) && (toInt(j) <= 8)) { - if (compareMarker(quadrantMap, MARKER_STARBASE, i, j)) { - docked = true; - } - } - } - } - if (!docked) { - shipDocked = false; - if (klingons > 0) { - shipCondition = "*RED*"; - } else { - shipCondition = "GREEN"; - if (energy < initialEnergy * .1) { - shipCondition = "YELLOW"; - } - } - } else { - shipDocked = true; - shipCondition = "DOCKED"; - energy = initialEnergy; - torpedoes = initialTorpedoes; - println("SHIELDS DROPPED FOR DOCKING PURPOSES"); - shields = 0; - } - if (deviceStatus[DEVICE_SHORT_RANGE_SENSORS] < 0) { // are short range sensors out? - println("\n*** SHORT RANGE SENSORS ARE OUT ***\n"); - return; - } - final String row = "---------------------------------"; - 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 += " " + midStr(quadrantMap, j, 3); - } - switch (i) { - case 1: - println(sectorMapRow + " STARDATE " + toInt(stardate * 10) * .1); - break; - case 2: - println(sectorMapRow + " CONDITION " + shipCondition); - break; - case 3: - println(sectorMapRow + " QUADRANT " + quadrantX + "," + quadrantY); - break; - case 4: - println(sectorMapRow + " SECTOR " + sectorX + "," + sectorY); - break; - case 5: - println(sectorMapRow + " PHOTON TORPEDOES " + toInt(torpedoes)); - break; - case 6: - println(sectorMapRow + " TOTAL ENERGY " + toInt((energy + shields))); - break; - case 7: - println(sectorMapRow + " SHIELDS " + toInt(shields)); - break; - case 8: - println(sectorMapRow + " KLINGONS REMAINING " + toInt(klingonsInGalaxy)); - } - ; - } - println(row); // 7260 + galaxyMap.shortRangeSensorScan(stardate); } - void libraryComputer() { // 7290 + void libraryComputer() { // REM LIBRARY COMPUTER CODE - if (deviceStatus[DEVICE_LIBRARY_COMPUTER] < 0) { - println("COMPUTER DISABLED"); + if (galaxyMap.getEnterprise().getDeviceStatus()[Enterprise.DEVICE_LIBRARY_COMPUTER] < 0) { + Util.println("COMPUTER DISABLED"); return; } while (true) { - final float commandInput = inputFloat("COMPUTER ACTIVE AND AWAITING COMMAND"); + final float commandInput = Util.inputFloat("COMPUTER ACTIVE AND AWAITING COMMAND"); if (commandInput < 0) return; - println(""); - int command = toInt(commandInput) + 1; + Util.println(""); + int command = Util.toInt(commandInput) + 1; if (command >= COMPUTER_COMMAND_CUMULATIVE_GALACTIC_RECORD && command <= COMPUTER_COMMAND_GALAXY_MAP) { switch (command) { case COMPUTER_COMMAND_CUMULATIVE_GALACTIC_RECORD: - //GOTO 7540 - cumulativeGalacticRecord(true); + galaxyMap.cumulativeGalacticRecord(true); return; case COMPUTER_COMMAND_STATUS_REPORT: - //GOTO 7900 statusReport(); return; case COMPUTER_COMMAND_PHOTON_TORPEDO_DATA: - //GOTO 8070 - photonTorpedoData(); + galaxyMap.photonTorpedoData(); return; case COMPUTER_COMMAND_STARBASE_NAV_DATA: - //GOTO 8500 - starbaseNavData(); + galaxyMap.starbaseNavData(); return; case COMPUTER_COMMAND_DIR_DIST_CALC: - //GOTO 8150 - directionDistanceCalculator(); + galaxyMap.directionDistanceCalculator(); return; case COMPUTER_COMMAND_GALAXY_MAP: - //GOTO 7400 - cumulativeGalacticRecord(false); + galaxyMap.cumulativeGalacticRecord(false); return; } } else { // invalid command - println("FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:"); - println(" 0 = CUMULATIVE GALACTIC RECORD"); - println(" 1 = STATUS REPORT"); - println(" 2 = PHOTON TORPEDO DATA"); - println(" 3 = STARBASE NAV DATA"); - println(" 4 = DIRECTION/DISTANCE CALCULATOR"); - println(" 5 = GALAXY 'REGION NAME' MAP"); - println(""); + Util.println("FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:"); + Util.println(" 0 = CUMULATIVE GALACTIC RECORD"); + Util.println(" 1 = STATUS REPORT"); + Util.println(" 2 = PHOTON TORPEDO DATA"); + Util.println(" 3 = STARBASE NAV DATA"); + Util.println(" 4 = DIRECTION/DISTANCE CALCULATOR"); + Util.println(" 5 = GALAXY 'REGION NAME' MAP"); + Util.println(""); } } } - void cumulativeGalacticRecord(final boolean cumulativeReport) { // 7540 - if (cumulativeReport) { - println(""); - println(" "); - println("COMPUTER RECORD OF GALAXY FOR QUADRANT " + quadrantX + "," + quadrantY); - println(""); + void statusReport() { + Util.println(" STATUS REPORT:"); + Util.println("KLINGON" + ((galaxyMap.getKlingonsInGalaxy() > 1)? "S" : "") + " LEFT: " + galaxyMap.getKlingonsInGalaxy()); + Util.println("MISSION MUST BE COMPLETED IN " + .1 * Util.toInt((initialStardate + missionDuration - stardate) * 10) + " STARDATES"); + if (galaxyMap.getBasesInGalaxy() >= 1) { + Util.println("THE FEDERATION IS MAINTAINING " + galaxyMap.getBasesInGalaxy() + " STARBASE" + ((galaxyMap.getBasesInGalaxy() > 1)? "S" : "") + " IN THE GALAXY"); } else { - println(" THE GALAXY"); + Util.println("YOUR STUPIDITY HAS LEFT YOU ON YOUR OWN IN"); + Util.println(" THE GALAXY -- YOU HAVE NO STARBASES LEFT!"); } - println(" 1 2 3 4 5 6 7 8"); - final String rowDivider = " ----- ----- ----- ----- ----- ----- ----- -----"; - println(rowDivider); - for (int i = 1; i <= 8; i++) { - print(i + " "); - if (cumulativeReport) { - int y = 1; - String quadrantName = getQuadrantName(false, i, y); - int tabLen = toInt(15 - .5 * strlen(quadrantName)); - println(tab(tabLen) + quadrantName); - y = 5; - quadrantName = getQuadrantName(false, i, y); - tabLen = toInt(39 - .5 * strlen(quadrantName)); - println(tab(tabLen) + quadrantName); - } else { - for (int j = 1; j <= 8; j++) { - print(" "); - if (chartedGalaxy[i][j] == 0) { - print("***"); - } else { - print(rightStr(Integer.toString(chartedGalaxy[i][j] + 1000), 3)); - } - } - } - println(""); - println(rowDivider); - } - println(""); + galaxyMap.getEnterprise().damageControl(this); } - void statusReport() { // 7900 - println(" STATUS REPORT:"); - println("KLINGON" + ((klingonsInGalaxy > 1)? "S" : "") + " LEFT: " + klingonsInGalaxy); - println("MISSION MUST BE COMPLETED IN " + .1 * toInt((initialStardate + missionDuration - stardate) * 10) + "STARDATES"); - if (basesInGalaxy >= 1) { - println("THE FEDERATION IS MAINTAINING " + basesInGalaxy + " STARBASE" + ((basesInGalaxy > 1)? "S" : "") + " IN THE GALAXY"); - } else { - println("YOUR STUPIDITY HAS LEFT YOU ON YOUR OWN IN"); - println(" THE GALAXY -- YOU HAVE NO STARBASES LEFT!"); - } - damageControl(); + public void incrementStardate(double increment) { + this.stardate += increment; } - void photonTorpedoData() { // 8070 - // TORPEDO, BASE NAV, D/D CALCULATOR - if (klingons <= 0) { - printNoEnemyShipsMessage(); - return; - } - 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() { // 8150 - println("DIRECTION/DISTANCE CALCULATOR:"); - println("YOU ARE AT QUADRANT " + quadrantX + "," + quadrantY + " SECTOR " + sectorX + "," + sectorY); - print("PLEASE ENTER "); - int[] initialCoords = inputCoords(" INITIAL COORDINATES (X,Y)"); - int[] finalCoords = 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) { // 8220 - 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) { - println("DIRECTION = " + (from_x + to_y / from_y)); - } else { - println("DIRECTION = " + (from_x + 2 - to_y / from_y)); - } - } - println("DISTANCE = " + round(Math.sqrt(to_y ^ 2 + from_y ^ 2), 6)); - } - - void starbaseNavData() { // 8500 - if (starbases != 0) { - println("FROM ENTERPRISE TO STARBASE:"); - printDirection(sectorX, sectorY, starbaseX, starbaseY); - } else { - println("MR. SPOCK REPORTS, 'SENSORS SHOW NO STARBASES IN THIS"); - println(" QUADRANT.'"); - } - } - - /** - * Finds random empty coordinates in a quadrant. - * - * @param quadrantString - * @return an array with a pair of coordinates x, y - */ - int[] findEmptyPlaceInQuadrant(String quadrantString) { // 8590 - final int x = fnr(); - final int y = fnr(); - if (!compareMarker(quadrantString, MARKER_EMPTY, x, y)) { - return findEmptyPlaceInQuadrant(quadrantString); - } - return new int[]{x, y}; - } - - - void insertMarker(final String marker, final int x, final int y) { // 8670 - final int pos = toInt(y) * 3 + toInt(x) * 24 + 1; - if (marker.length() != 3) { - System.err.println("ERROR"); - System.exit(-1); - } - if (pos == 1) { - quadrantMap = marker + rightStr(quadrantMap, 189); - } - if (pos == 190) { - quadrantMap = leftStr(quadrantMap, 189) + marker; - } - quadrantMap = leftStr(quadrantMap, (pos - 1)) + marker + rightStr(quadrantMap, (190 - pos)); - } - - 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 ""; - } - - boolean compareMarker(final String quadrantString, final String marker, int x, int y) { // 8830 - final int markerRegion = (y - 1) * 3 + (x - 1) * 24 + 1; - if (midStr(quadrantString, markerRegion, 3).equals(marker)) { - return true; - } - return false; - } - - String getRegionName(final boolean regionNameOnly, final int y) { - if (!regionNameOnly) { - switch (y % 4) { - case 1: - return " I"; - case 2: - return " II"; - case 3: - return " III"; - case 4: - return " IV"; - } - } - return ""; - } - - String getQuadrantName(final boolean regionNameOnly, final int x, final int y) { // 9030 - 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 damageControl() { // 5690 - if (deviceStatus[DEVICE_DAMAGE_CONTROL] < 0) { - println("DAMAGE CONTROL REPORT NOT AVAILABLE"); - } else { - println("\nDEVICE STATE OF REPAIR"); - for (int deviceNr = 1; deviceNr <= 8; deviceNr++) { - print(printDeviceName(deviceNr) + leftStr(QUADRANT_ROW, 25 - strlen(printDeviceName(deviceNr))) + " " + toInt(deviceStatus[deviceNr] * 100) * .01 + "\n"); - } - } - if (!shipDocked) 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; - println("TECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;"); - println("ESTIMATED TIME TO REPAIR:'" + .01 * toInt(100 * deltaToRepair) + " STARDATES"); - final String reply = 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; - } - stardate = stardate + deltaToRepair + .1; - } - } - } - - void klingonsShoot() { // 6000 - if (klingons <= 0) return; // no klingons - if (shipDocked) { // enterprise is docked - println("STARBASE SHIELDS PROTECT THE ENTERPRISE"); - return; - } - for (int i = 1; i <= 3; i++) { - if (klingonQuadrants[i][3] <= 0) continue; - int hits = toInt((klingonQuadrants[i][3] / fnd(1)) * (2 + random())); // hit points - shields = shields - hits; - klingonQuadrants[i][3] = toInt(klingonQuadrants[i][3] / (3 + random())); // FIXME: RND(0) - println(hits + " UNIT HIT ON ENTERPRISE FROM SECTOR " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]); - if (shields <= 0) endGameFail(true); - println(" "); - if (hits < 20) continue; - if ((random() > .6) || (hits / shields <= .02)) continue; - int randomDevice = fnr(); - deviceStatus[randomDevice] = deviceStatus[randomDevice] - hits / shields - .5 * random(); - println("DAMAGE CONTROL REPORTS " + printDeviceName(randomDevice) + " DAMAGED BY THE HIT'"); - } - } - - void endGameFail(final boolean enterpriseDestroyed) { // 6220 + public void endGameFail(final boolean enterpriseDestroyed) { // 6220 if (enterpriseDestroyed) { - println("\nTHE ENTERPRISE HAS BEEN DESTROYED. THEN FEDERATION "); - println("WILL BE CONQUERED"); + Util.println("\nTHE ENTERPRISE HAS BEEN DESTROYED. THEN FEDERATION "); + Util.println("WILL BE CONQUERED"); } - println("\nIT IS STARDATE " + stardate); - println("THERE WERE " + klingonsInGalaxy + " KLINGON BATTLE CRUISERS LEFT AT"); - println("THE END OF YOUR MISSION."); + Util.println("\nIT IS STARDATE " + stardate); + Util.println("THERE WERE " + galaxyMap.getKlingonsInGalaxy() + " KLINGON BATTLE CRUISERS LEFT AT"); + Util.println("THE END OF YOUR MISSION."); repeatGame(); } - void endGameSuccess() { // 6370 - println("CONGRATULATION, CAPTAIN! THE LAST KLINGON BATTLE CRUISER"); - println("MENACING THE FEDERATION HAS BEEN DESTROYED.\n"); - println("YOUR EFFICIENCY RATING IS " + (Math.sqrt(1000 * (remainingKlingons / (stardate - initialStardate))))); + public void endGameSuccess() { + Util.println("CONGRATULATION, CAPTAIN! THE LAST KLINGON BATTLE CRUISER"); + Util.println("MENACING THE FEDERATION HAS BEEN DESTROYED.\n"); + Util.println("YOUR EFFICIENCY RATING IS " + (Math.sqrt(1000 * (galaxyMap.getRemainingKlingons() / (stardate - initialStardate))))); repeatGame(); } - void repeatGame() {// 6290 - println("\n"); - if (basesInGalaxy != 0) { - println("THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER"); - println("FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER,"); - final String reply = inputStr("LET HIM STEP FORWARD AND ENTER 'AYE'"); + void repeatGame() { + Util.println("\n"); + if (galaxyMap.getBasesInGalaxy() != 0) { + Util.println("THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER"); + Util.println("FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER,"); + final String reply = Util.inputStr("LET HIM STEP FORWARD AND ENTER 'AYE'"); if ("AYE".equals(reply)) { this.restart = true; } else { @@ -1147,93 +338,4 @@ public class SuperStarTrekGame { } } - static int toInt(final double num) { - int x = (int) Math.floor(num); - if (x < 0) x *= -1; - return x; - } - - static void println(final String s) { - System.out.println(s); - } - - static void print(final String s) { - System.out.print(s); - } - - static String tab(final int n) { - return IntStream.range(1, n).mapToObj(num -> " ").collect(Collectors.joining()); - } - - static int strlen(final String s) { - return s.length(); - } - - 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 ""; - } - } - - 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(); - } - } - } - - 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(); - } - } - } - - static String leftStr(final String input, final int len) { - if (input == null || input.length() < len) return input; - return input.substring(0, len); - } - - 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); - } - - static String rightStr(final String input, final int len) { - if (input == null || input.length() < len) return ""; - return input.substring(input.length() - len); - } - - static float random() { - return random.nextFloat(); - } - - private 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(); - } } diff --git a/84_Super_Star_Trek/java/Util.java b/84_Super_Star_Trek/java/Util.java new file mode 100644 index 00000000..17c07ecd --- /dev/null +++ b/84_Super_Star_Trek/java/Util.java @@ -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(); + } + + +}