finished Snake class-based program

This commit is contained in:
Xevion
2020-12-12 00:30:34 -06:00
parent cec8ed8317
commit 96ee69ce5b
11 changed files with 381 additions and 0 deletions

1
other/Snake/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.idea/**

11
other/Snake/Snake.iml Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,18 @@
F F
F F
F
F
F F
XXX F
F F
F
2
UROOOUOOLOOOOOOUOOOO
UOOOOOOOOOOOOOOOOOOO

View File

149
other/Snake/src/Board.java Normal file
View File

@@ -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;
}
}

107
other/Snake/src/Snake.java Normal file
View File

@@ -0,0 +1,107 @@
import java.util.ArrayList;
class UnfindableSnakeException extends SnakeException {
UnfindableSnakeException(String errorMessage) {
super(errorMessage);
}
}
public class Snake
{
public ArrayList<Vector2Int> 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<Vector2Int> 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;
}
}

View File

@@ -0,0 +1,3 @@
public class Snake2
{
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,18 @@
F F
F F
F
F
F F
XXX F
F F
F
2
UROOOUOOLOOOOOOUOOOO
UOOOOOOOOOOOOOOOOOOO

View File