From da137fe6d02b9a73e77a7660a8fcc8640cca8782 Mon Sep 17 00:00:00 2001 From: Aldrin Misquitta Date: Thu, 21 Jul 2022 16:55:40 +0530 Subject: [PATCH 1/2] Ported 56 Life for Two to Java --- 56_Life_for_Two/README.md | 6 +- 56_Life_for_Two/java/LifeForTwo.java | 305 +++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 56_Life_for_Two/java/LifeForTwo.java diff --git a/56_Life_for_Two/README.md b/56_Life_for_Two/README.md index 3cc22c9e..3d906342 100644 --- a/56_Life_for_Two/README.md +++ b/56_Life_for_Two/README.md @@ -6,6 +6,7 @@ There are two players; the game is played on a 5x5 board and each player has a s The # and * are regarded as the same except when deciding whether to generate a live cell. An empty cell having two `#` and one `*` for neighbors will generate a `#`, i.e. the live cell generated belongs to the player who has the majority of the 3 live cells surrounding the empty cell where life is to be generated, for example: +``` | | 1 | 2 | 3 | 4 | 5 | |:-:|:-:|:-:|:-:|:-:|:-:| | 1 | | | | | | @@ -13,9 +14,10 @@ The # and * are regarded as the same except when deciding whether to generate a | 3 | | | | # | | | 4 | | | # | | | | 5 | | | | | | +``` A new cell will be generated at (3,3) which will be a `#` since there are two `#` and one `*` surrounding. The board will then become: - +``` | | 1 | 2 | 3 | 4 | 5 | |:-:|:-:|:-:|:-:|:-:|:-:| | 1 | | | | | | @@ -23,7 +25,7 @@ A new cell will be generated at (3,3) which will be a `#` since there are two `# | 3 | | | # | # | | | 4 | | | | | | | 5 | | | | | | - +``` On the first most each player positions 3 pieces of life on the board by typing in the co-ordinates of the pieces. (In the event of the same cell being chosen by both players that cell is left empty.) The board is then adjusted to the next generation and printed out. diff --git a/56_Life_for_Two/java/LifeForTwo.java b/56_Life_for_Two/java/LifeForTwo.java new file mode 100644 index 00000000..1f68e794 --- /dev/null +++ b/56_Life_for_Two/java/LifeForTwo.java @@ -0,0 +1,305 @@ +import java.util.*; +import java.util.stream.IntStream; + +/** + * Life for Two + *

+ * The original BASIC program uses a grid with an extras border of cells all around, + * probably to simplify calculations and manipulations. This java program has the exact + * grid size and instead uses boundary check conditions in the logic. + *

+ * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) + */ +public class LifeForTwo { + + final static int GRID_SIZE = 5; + + //Pair of offset which when added to the current cell's coordinates, + // give the coordinates of the neighbours + final static int[] neighbourCellOffsets = { + -1, 0, + 1, 0, + 0, -1, + 0, 1, + -1, -1, + 1, -1, + -1, 1, + 1, 1 + }; + + //The best term that I could come with to describe these numbers was 'masks' + //They act like indicators to decide which player won the cell. The value is the score of the cell after all the + // generation calculations. + final static List maskPlayer1 = List.of(3, 102, 103, 120, 130, 121, 112, 111, 12); + final static List maskPlayer2 = List.of(21, 30, 1020, 1030, 1011, 1021, 1003, 1002, 1012); + + public static void main(String[] args) { + printIntro(); + Scanner scan = new Scanner(System.in); + scan.useDelimiter("\\D"); + + int[][] grid = new int[GRID_SIZE][GRID_SIZE]; + + initializeGrid(grid); + + //Read the initial 3 moves for each player + for (int b = 1; b <= 2; b++) { + System.out.printf("\nPLAYER %d - 3 LIVE PIECES.%n", b); + for (int k1 = 1; k1 <= 3; k1++) { + var player1Coordinates = readUntilValidCoordinates(scan, grid); + grid[player1Coordinates.x - 1][player1Coordinates.y - 1] = (b == 1 ? 3 : 30); + } + } + + printGrid(grid); + + calculatePlayersScore(grid); //Convert 3, 30 to 100, 1000 + + resetGridForNextGen(grid); + computeCellScoresForOneGen(grid); + + var playerScores = calculatePlayersScore(grid); + resetGridForNextGen(grid); + + boolean gameOver = false; + while (!gameOver) { + printGrid(grid); + if (playerScores.getPlayer1Score() == 0 && playerScores.getPlayer2Score() == 0) { + System.out.println("\nA DRAW"); + gameOver = true; + } else if (playerScores.getPlayer2Score() == 0) { + System.out.println("\nPLAYER 1 IS THE WINNER"); + gameOver = true; + } else if (playerScores.getPlayer1Score() == 0) { + System.out.println("\nPLAYER 2 IS THE WINNER"); + gameOver = true; + } else { + System.out.print("PLAYER 1 "); + Coordinate player1Move = readCoordinate(scan); + System.out.print("PLAYER 2 "); + Coordinate player2Move = readCoordinate(scan); + if (!player1Move.equals(player2Move)) { + grid[player1Move.x - 1][player1Move.y - 1] = 100; + grid[player2Move.x - 1][player2Move.y - 1] = 1000; + } + //In the original, B is assigned 99 when both players choose the same cell + //and that is used to control the flow + computeCellScoresForOneGen(grid); + playerScores = calculatePlayersScore(grid); + resetGridForNextGen(grid); + } + } + + } + + private static void initializeGrid(int[][] grid) { + for (int[] row : grid) { + Arrays.fill(row, 0); + } + } + + private static void computeCellScoresForOneGen(int[][] grid) { + for (int i = 0; i < GRID_SIZE; i++) { + for (int j = 0; j < GRID_SIZE; j++) { + if (grid[i][j] >= 100) { + calculateScoreForOccupiedCell(grid, i, j); + } + } + } + } + + private static Scores calculatePlayersScore(int[][] grid) { + int m2 = 0; + int m3 = 0; + for (int i = 0; i < GRID_SIZE; i++) { + for (int j = 0; j < GRID_SIZE; j++) { + if (grid[i][j] < 3) { + grid[i][j] = 0; + } else { + if (maskPlayer1.contains(grid[i][j])) { + m2++; + } else if (maskPlayer2.contains(grid[i][j])) { + m3++; + } + } + } + } + return new Scores(m2, m3); + } + + private static void resetGridForNextGen(int[][] grid) { + for (int i = 0; i < GRID_SIZE; i++) { + for (int j = 0; j < GRID_SIZE; j++) { + if (grid[i][j] < 3) { + grid[i][j] = 0; + } else { + if (maskPlayer1.contains(grid[i][j])) { + grid[i][j] = 100; + } else if (maskPlayer2.contains(grid[i][j])) { + grid[i][j] = 1000; + } else { + grid[i][j] = 0; + } + } + } + } + } + + private static void calculateScoreForOccupiedCell(int[][] grid, int i, int j) { + var b = 1; + if (grid[i][j] > 999) { + b = 10; + } + for (int k = 0; k < 15; k += 2) { + //check bounds + var neighbourX = i + neighbourCellOffsets[k]; + var neighbourY = j + neighbourCellOffsets[k + 1]; + if (neighbourX >= 0 && neighbourX < GRID_SIZE && + neighbourY >= 0 && neighbourY < GRID_SIZE) { + grid[neighbourX][neighbourY] = grid[neighbourX][neighbourY] + b; + } + + } + } + + private static void printGrid(int[][] grid) { + System.out.println(); + printRowEdge(); + System.out.println(); + for (int i = 0; i < grid.length; i++) { + System.out.printf("%d ", i + 1); + for (int j = 0; j < grid[i].length; j++) { + System.out.printf(" %c ", mapChar(grid[i][j])); + } + System.out.printf(" %d", i + 1); + System.out.println(); + } + printRowEdge(); + System.out.println(); + } + + private static void printRowEdge() { + System.out.print("0 "); + IntStream.range(1, GRID_SIZE + 1).forEach(i -> System.out.printf(" %s ", i)); + System.out.print(" 0"); + } + + private static char mapChar(int i) { + if (i == 3 || i == 100) { + return '*'; + } + if (i == 30 || i == 1000) { + return '#'; + } + return ' '; + } + + private static Coordinate readUntilValidCoordinates(Scanner scanner, int[][] grid) { + boolean coordinateInRange = false; + Coordinate coordinate = null; + while (!coordinateInRange) { + coordinate = readCoordinate(scanner); + if (coordinate.x <= 0 || coordinate.x > GRID_SIZE + || coordinate.y <= 0 || coordinate.y > GRID_SIZE + || grid[coordinate.x - 1][coordinate.y - 1] != 0) { + System.out.println("ILLEGAL COORDS. RETYPE"); + } else { + coordinateInRange = true; + } + } + return coordinate; + } + + private static Coordinate readCoordinate(Scanner scanner) { + Coordinate coordinate = null; + int x, y; + boolean valid = false; + + System.out.println("X,Y"); + System.out.print("XXXXXX\r"); + System.out.print("$$$$$$\r"); + System.out.print("&&&&&&\r"); + + while (!valid) { + try { + System.out.print("? "); + y = scanner.nextInt(); + x = scanner.nextInt(); + valid = true; + coordinate = new Coordinate(x, y); + } catch (InputMismatchException e) { + System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); + valid = false; + } finally { + scanner.nextLine(); + } + } + return coordinate; + } + + private static void printIntro() { + System.out.println(" LIFE2"); + System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); + System.out.println("\n\n"); + + System.out.println("\tU.B. LIFE GAME"); + } + + private static class Coordinate { + private final int x, y; + + public Coordinate(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + @Override + public String toString() { + return "Coordinate{" + + "x=" + x + + ", y=" + y + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Coordinate that = (Coordinate) o; + return x == that.x && y == that.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } + } + + private static class Scores { + private final int player1Score; + private final int player2Score; + + public Scores(int player1Score, int player2Score) { + this.player1Score = player1Score; + this.player2Score = player2Score; + } + + public int getPlayer1Score() { + return player1Score; + } + + public int getPlayer2Score() { + return player2Score; + } + } + + +} From 311eaed39751b20eb49f92bdcbf433b92db4c406 Mon Sep 17 00:00:00 2001 From: Aldrin Misquitta Date: Sun, 24 Jul 2022 07:56:44 +0530 Subject: [PATCH 2/2] Fixes #779 --- 84_Super_Star_Trek/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/84_Super_Star_Trek/README.md b/84_Super_Star_Trek/README.md index 1edeb869..07da653f 100644 --- a/84_Super_Star_Trek/README.md +++ b/84_Super_Star_Trek/README.md @@ -72,7 +72,7 @@ The relation between the Historical and Standard nomenclatures is shown in the s - If you don’t zap a Klingon hard enough (relative to his shield strength) you won’t damage him at all. Your sensors will tell the story. - Damage control will let you know when out-of-commission devices have been completely repaired. -9. Your engines will automatically shit down if you should attempt to leave the galaxy, or if you should try to maneuver through a star, or Starbase, or—heaven help you—a Klingon warship. +9. Your engines will automatically shut down if you should attempt to leave the galaxy, or if you should try to maneuver through a star, or Starbase, or—heaven help you—a Klingon warship. 10. In a pinch, or if you should miscalculate slightly, some shield control energy will be automatically diverted to warp engine control (if your shield are operational!).