From 96ee69ce5b800867df8c6f193001d70e98191ae5 Mon Sep 17 00:00:00 2001 From: Xevion Date: Sat, 12 Dec 2020 00:30:34 -0600 Subject: [PATCH] finished Snake class-based program --- other/Snake/.gitignore | 1 + other/Snake/Snake.iml | 11 ++ other/Snake/out/production/Snake/snake2.dat | 18 +++ other/Snake/out/production/Snake/snake2.out | 0 other/Snake/src/Board.java | 149 ++++++++++++++++++++ other/Snake/src/Snake.java | 107 ++++++++++++++ other/Snake/src/Snake2.java | 3 + other/Snake/src/SnakeRunner.java | 51 +++++++ other/Snake/src/Vector2Int.java | 23 +++ other/Snake/src/snake2.dat | 18 +++ other/Snake/src/snake2.out | 0 11 files changed, 381 insertions(+) create mode 100644 other/Snake/.gitignore create mode 100644 other/Snake/Snake.iml create mode 100644 other/Snake/out/production/Snake/snake2.dat create mode 100644 other/Snake/out/production/Snake/snake2.out create mode 100644 other/Snake/src/Board.java create mode 100644 other/Snake/src/Snake.java create mode 100644 other/Snake/src/Snake2.java create mode 100644 other/Snake/src/SnakeRunner.java create mode 100644 other/Snake/src/Vector2Int.java create mode 100644 other/Snake/src/snake2.dat create mode 100644 other/Snake/src/snake2.out diff --git a/other/Snake/.gitignore b/other/Snake/.gitignore new file mode 100644 index 0000000..b1001a6 --- /dev/null +++ b/other/Snake/.gitignore @@ -0,0 +1 @@ +.idea/** diff --git a/other/Snake/Snake.iml b/other/Snake/Snake.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/other/Snake/Snake.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/other/Snake/out/production/Snake/snake2.dat b/other/Snake/out/production/Snake/snake2.dat new file mode 100644 index 0000000..61f66e9 --- /dev/null +++ b/other/Snake/out/production/Snake/snake2.dat @@ -0,0 +1,18 @@ +F F + F F + F + F + + F F + + + + XXX F + + + F F + + F +2 +UROOOUOOLOOOOOOUOOOO +UOOOOOOOOOOOOOOOOOOO \ No newline at end of file diff --git a/other/Snake/out/production/Snake/snake2.out b/other/Snake/out/production/Snake/snake2.out new file mode 100644 index 0000000..e69de29 diff --git a/other/Snake/src/Board.java b/other/Snake/src/Board.java new file mode 100644 index 0000000..7ba4da0 --- /dev/null +++ b/other/Snake/src/Board.java @@ -0,0 +1,149 @@ +import java.util.Arrays; +import java.util.Locale; +import java.util.Scanner; + +class SnakeSimulationException extends SnakeException +{ + public SnakeSimulationException(String errorMessage) + { + super(errorMessage); + } +} + +class OutsideBoundariesException extends SnakeSimulationException +{ + public OutsideBoundariesException(Vector2Int position) + { + super(String.format(Locale.ENGLISH, "The snake crossed the grid boundary at (%d, %d).", position.x, position.y)); + } +} + +class InsideSelfException extends SnakeSimulationException +{ + public InsideSelfException(Vector2Int position) + { + super(String.format(Locale.ENGLISH, "The snake crossed into itself at (%d, %d).", position.x, position.y)); + } +} + +public class Board +{ + private final Snake snake; + private final char[][] board; + private int pelletCount; + + public Board(char[][] board) throws UnfindableSnakeException + { + char[][] copyBoard = new char[15][15]; + for (int y = 0; y < 15; y++) + copyBoard[y] = board[y].clone(); + + this.board = copyBoard; + snake = new Snake(copyBoard); + } + + public static char[][] GetCharBoard(Scanner input) + { + String[] lines = new String[15]; + + for (int i = 0; i < 15; i++) { + String line = input.nextLine(); + + // Ensure at least 15 characters available to read + int left = Math.max(0, 15 - line.length()); + while (left-- > 0) + line += " "; + + lines[i] = line; + } + return GetCharBoard(lines); + } + + public static char[][] GetCharBoard(String[] lines) + { + char[][] board = new char[15][15]; + + int y = 0; + for (String line : lines) { + for (int x = 0; x < 15; x++) + board[y][x] = line.charAt(x); + y++; + } + + return board; + } + + public void move(char movement) throws SnakeSimulationException + { + // Calculate the new snake head position and move the snake positions + Vector2Int head = snake.Head(); + Vector2Int moveDelta = snake.GetDelta(movement); + snake.setPreviousMovement(moveDelta); + snake.positions.add(0, head.plus(moveDelta)); + + if (!snake.WithinBoundaries()) { + throw new OutsideBoundariesException(snake.Head()); + } else if (snake.WithinSelf()) { + throw new InsideSelfException(snake.Head()); + } else { + // Only remove the end node if it did not consume a pellet. Simulates the 'extension' of the tail naturally. + if (!Consume()) + snake.positions.remove(snake.positions.size() - 1); + else + pelletCount++; + } + } + + /** + * If possible, consumes a pellet at the Snake's head position. Returns a boolean signaling if it consumed a pellet. + * + * @return Returns True if the snake has consumed a pellet with the latest function execution. + */ + private boolean Consume() + { + Vector2Int head = snake.Head(); + if (board[head.y][head.x] == 'F') { + board[head.y][head.x] = ' '; + return true; + } + return false; + } + + public void simulate(String dataset) throws SnakeSimulationException + { + for (int i = 0; i < dataset.length(); i++) { +// System.out.println(render()); +// System.out.println("---------------"); + this.move(dataset.charAt(i)); + } + } + + /** + * @return Returns a rendered version of the Board with Snake and Pellet positions marked. + */ + public String render() + { + // Make a copy of the original board + char[][] boardCopy = new char[15][15]; + for (int i = 0; i < 15; i++) + boardCopy[i] = Arrays.copyOf(this.board[i], 15); + + // Mark the snake's location + for (Vector2Int position : snake.positions) + if (position.x >= 0 && position.y >= 0 && position.x < 15 && position.y < 15) + boardCopy[position.y][position.x] = 'X'; + + // Join the char arrays into strings + String[] render = new String[15]; + for (int i = 0; i < 15; i++) + render[i] = String.valueOf(boardCopy[i]); + + // Return all lines, joined by newlines + return String.join("\n", render); + } + + public int getPelletCount() + { + return pelletCount; + } +} diff --git a/other/Snake/src/Snake.java b/other/Snake/src/Snake.java new file mode 100644 index 0000000..6b62b53 --- /dev/null +++ b/other/Snake/src/Snake.java @@ -0,0 +1,107 @@ +import java.util.ArrayList; + +class UnfindableSnakeException extends SnakeException { + UnfindableSnakeException(String errorMessage) { + super(errorMessage); + } +} + +public class Snake +{ + public ArrayList positions; + private Vector2Int previousMovement = GetDelta('R'); + + public Snake(char[][] board) throws UnfindableSnakeException + { + Vector2Int head = null; + // Find head of the snake - lazily using problem constraints + for (int y = 0; y < 15; y++) { + for (int x = 15 - 1; x >= 0; x--) { + if (board[y][x] == 'X') { + if (head == null) + head = new Vector2Int(x, y); + board[y][x] = ' '; + } + } + } + + // If the head was find, build the Snake's known positions + if (head != null) { + positions = new ArrayList<>(); + positions.add(head); + positions.add(new Vector2Int(head.x - 1, head.y)); + positions.add(new Vector2Int(head.x - 2, head.y)); + } else { + throw new UnfindableSnakeException("Could not find Snake head within the provided Grid."); + } + } + + public Snake(ArrayList positions) + { + this.positions = positions; + } + + /** + * @return Returns the current length of the snake + */ + public int Length() + { + return positions.size(); + } + + + /** + * @return Returns the 'head' (first) node in the snake. + */ + public Vector2Int Head() { return positions.get(0); } + + + /** + * @return Returns he 'tail' (last) node in the snake. + */ + public Vector2Int Tail() { return positions.get(positions.size() - 1); } + + public Vector2Int GetDelta(char movement) { + switch (movement) { + case 'U': + return new Vector2Int(0, -1); + case 'D': + return new Vector2Int(0, 1); + case 'L': + return new Vector2Int(-1, 0); + case 'R': + return new Vector2Int(1, 0); + case 'O': + return previousMovement; + default: + return new Vector2Int(); + } + } + + /** + * @return Whether or not the Snake's head is within the game's boundaries. + */ + public boolean WithinBoundaries() + { + Vector2Int head = positions.get(0); + return head.x >= 0 && head.y >= 0 && head.x < 15 && head.y < 15; + } + + /** + * @return Returns true if the Snake's body positions overlap onto itself + */ + public boolean WithinSelf() { + Vector2Int head = Head(); + + // Iterate along each previous node in the snake and check if it has moved into itself + for (int i = 1; i < positions.size(); i++) + if (head.equals(positions.get(i))) + return true; + return false; + } + + public void setPreviousMovement(Vector2Int previousMovement) + { + this.previousMovement = previousMovement; + } +} diff --git a/other/Snake/src/Snake2.java b/other/Snake/src/Snake2.java new file mode 100644 index 0000000..4622c4c --- /dev/null +++ b/other/Snake/src/Snake2.java @@ -0,0 +1,3 @@ +public class Snake2 +{ +} diff --git a/other/Snake/src/SnakeRunner.java b/other/Snake/src/SnakeRunner.java new file mode 100644 index 0000000..f921ada --- /dev/null +++ b/other/Snake/src/SnakeRunner.java @@ -0,0 +1,51 @@ +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Scanner; + +import static java.lang.System.out; + +class SnakeException extends Exception +{ + public SnakeException(String errorMessage) + { + super(errorMessage); + } +} + + +public class SnakeRunner +{ + public static void main(String[] args) throws UnfindableSnakeException, FileNotFoundException + { + // Read board input + Scanner input = new Scanner(new File("./src/snake2.dat")); + char[][] charBoard = Board.GetCharBoard(input); + + // Read datasets + String[] datasets = new String[input.nextInt()]; + input.nextLine(); + for (int i = 0; i < datasets.length; i++) + datasets[i] = input.nextLine(); + + // Simulate each dataset + int i = 0; + for (String dataset : datasets) { + Board board = new Board(charBoard); + boolean success = true; + try { +// out.println(dataset); + board.simulate(dataset); + } catch (SnakeSimulationException e) { +// out.printf("Could not fully simulate dataset %s...%n", i); +// e.printStackTrace(); + out.println("GAME OVER"); + success = false; + } + + if (success) + out.printf("%d Pellets%n", board.getPelletCount()); + + out.println(board.render()); + } + } +} diff --git a/other/Snake/src/Vector2Int.java b/other/Snake/src/Vector2Int.java new file mode 100644 index 0000000..7a5f2c3 --- /dev/null +++ b/other/Snake/src/Vector2Int.java @@ -0,0 +1,23 @@ +public class Vector2Int +{ + public int x; + public int y; + + public Vector2Int() { + this(0, 0); + } + + public Vector2Int(int x, int y) { + this.x = x; + this.y = y; + } + + public Vector2Int plus(Vector2Int other) + { + return new Vector2Int(this.x + other.x, this.y + other.y); + } + + public boolean equals(Vector2Int other) { + return this.x == other.x && this.y == other.y; + } +} diff --git a/other/Snake/src/snake2.dat b/other/Snake/src/snake2.dat new file mode 100644 index 0000000..61f66e9 --- /dev/null +++ b/other/Snake/src/snake2.dat @@ -0,0 +1,18 @@ +F F + F F + F + F + + F F + + + + XXX F + + + F F + + F +2 +UROOOUOOLOOOOOOUOOOO +UOOOOOOOOOOOOOOOOOOO \ No newline at end of file diff --git a/other/Snake/src/snake2.out b/other/Snake/src/snake2.out new file mode 100644 index 0000000..e69de29