diff --git a/CachedLevels.java b/CachedLevels.java index ef3e1ec..d3d848b 100644 --- a/CachedLevels.java +++ b/CachedLevels.java @@ -32,7 +32,7 @@ public Record getRecord() { Scanner s = new Scanner(new InputStreamReader(lu.getInputStream())); if(s.hasNext()) c = new Record(s.nextLong(), s.next(), s.nextLong()); else System.out.println("none found"); - wr.close(); + s.close(); } catch (IOException e) { e.printStackTrace(); @@ -46,14 +46,21 @@ public boolean setRecord(Record r, long time) { connect(); wr.write(URLEncoder.encode("?player=" + player + "&seed=" + r.seed + "&time=" + time, "UTF-8")); wr.flush(); - Scanner s = new Scanner(new InputStreamReader(lu.getInputStream())); if(s.hasNextInt() && s.nextInt() == 1) recordHolder = true; - wr.close(); s.close(); } catch (IOException e) { e.printStackTrace(); } return recordHolder; } + + public void close() { + try { + wr.flush(); + wr.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/CachedSearch.java b/CachedSearch.java index 50c737a..1dfd51a 100644 --- a/CachedSearch.java +++ b/CachedSearch.java @@ -67,6 +67,7 @@ public int hashCode() { * @param obsticals constant locations search must avoid */ public CachedSearch(Vector2 start, Collection obsticals) { + //also keep search in bounding region this.start = start; this.obsticals = obsticals; @@ -102,6 +103,18 @@ public boolean addMinimumPath(Vector2 end, Collection floor, int newFlo private boolean search(Vector2 end, Collection floor, int newFloorCost) { if(obsticals.contains(end)) return false; + int minX = Math.min(start.x, end.x); + int maxX = Math.max(start.x, end.x); + int minY = Math.min(start.y, end.y); + int maxY = Math.max(start.y, end.y); + + for(Vector2 v : floor != null ? floor : obsticals) { + if(v.x < minX) minX = v.x; + if(v.x > maxX) maxX = v.x; + if(v.y < minY) minY = v.y; + if(v.y > maxY) maxY = v.y; + } + Queue pathQueue = new PriorityQueue((a, b)-> a.getCost() + Vector2.manDistance(a.getPosition(), start) - b.getCost() - Vector2.manDistance(b.getPosition(), start)); pathQueue.add(new GridState(end)); List closed = new ArrayList(); @@ -116,20 +129,22 @@ private boolean search(Vector2 end, Collection floor, int newFloorCost) return true; } - for(Vector2 childDir : Generator.directions) { + for(Vector2 childDir : Vector2.directions) { Vector2 childPos = Vector2.add(parent.getPosition(), childDir); - GridState child = new GridState(parent, childPos, (floor != null && floor.contains(childPos)) ? 1 : newFloorCost); - if(!obsticals.contains(childPos)) { - int prevIndex = 0; - if((prevIndex = closed.indexOf(child)) != -1) { - if(child.getCost() < closed.get(prevIndex).getCost()) { - closed.remove(prevIndex); + if(childPos.x >= minX-1 && childPos.x <= maxX+1 && childPos.y >= minY-1 && childPos.y <= maxY+1) { + GridState child = new GridState(parent, childPos, (floor != null && floor.contains(childPos)) ? 1 : newFloorCost); + if(!obsticals.contains(childPos)) { + int prevIndex = 0; + if((prevIndex = closed.indexOf(child)) != -1) { + if(child.getCost() < closed.get(prevIndex).getCost()) { + closed.remove(prevIndex); + pathQueue.add(child); + } + } else if(pathQueue.removeIf(a -> child.equals(a) && child.getCost() < a.getCost())) { + pathQueue.add(child); + } else { pathQueue.add(child); } - } else if(pathQueue.removeIf(a -> child.equals(a) && child.getCost() < a.getCost())) { - pathQueue.add(child); - } else { - pathQueue.add(child); } } } diff --git a/GUI.java b/GUI.java index a7cf178..ca394bf 100644 --- a/GUI.java +++ b/GUI.java @@ -1,98 +1,279 @@ +import java.io.File; +import java.io.IOException; + +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; import javafx.application.Application; +import javafx.event.ActionEvent; import javafx.event.EventHandler; +import javafx.scene.Camera; +import javafx.scene.Group; +import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.input.KeyEvent; -import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.Box; +import javafx.scene.shape.DrawMode; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Text; +import javafx.stage.Modality; import javafx.stage.Stage; +import javafx.util.Duration; public class GUI extends Application { - - private static final int sizeOfGame = 500; - private Generator generator; - private BlockType[][] level; - //private Button buttonGenerate = new Button("Generate"); - - public static void main(String[] args) { - launch(args); + private final int sceneScale = 100; + private final Vector2 sceneDimensions = new Vector2(500, 500); + private final int cameraHeight = 10; + private final Image img = new Image("diffuse_box.jpg"); + private final Image img4 = new Image("diffuse_tile2.jpg"); + private final Image img5 = new Image("fireball2.png"); + private final Image img6 = new Image("golf_hole.png"); + private final Image img7 = new Image("wall.jpeg"); + + private Box[] boxes; + private Box playerBox; + private IGenerator g; + + private PhongMaterial box; + private PhongMaterial floor; + private PhongMaterial goal; + private PhongMaterial player; + private PhongMaterial edge; + + private PerspectiveCamera camera; + private Group level; + + public static void main(String args[]){ + launch(args); } + + @Override + public void start(Stage stage) throws IOException { + //Create global generator + g = new Generator(); + + //Create global materials + setupMaterials(); + + //Setup camera without fixed eye above level + camera = new PerspectiveCamera(false); + camera.setTranslateZ(-sceneScale * cameraHeight); + + //Create and fill level group + level = new Group(); + newLevel(); + + //Create scene with depth buffer enabled + Scene scene = new Scene(level, sceneDimensions.x, sceneDimensions.y, true); + scene.setFill(Color.BLACK); + + // edge = new PhongMaterial(); + // edge.setDiffuseMap(img2); + // edge.setSpecularPower(500); + scene.setCamera(camera); + + //Set up input events + setupKeyEvents(scene); + + //Set up engine events + g.setOnPlayerMove(new EventHandler() { + @Override + public void handle(ActionEvent event) { + movePlayer(g.getPlayerLocation(), camera); + } + }); + g.setOnBoxMove(new EventHandler() { + @Override + public void handle(ActionEvent event) { + moveBox(g.getBoxMovedBox(), g.getBoxLocations()[g.getBoxMovedBox()]); + } + }); + g.setOnWin(new EventHandler() { + @Override + public void handle(ActionEvent event) { + Stage vic = new Stage(); + vic.initModality(Modality.APPLICATION_MODAL); + vic.initOwner(stage); + VBox vbox = new VBox (20); + vbox.getChildren().add(new Text ("VICTORY")); + Scene on = new Scene (vbox, 300, 200); + vic.setScene(on); + vic.show(); + //newLevel(); + System.out.println("You won"); + } + }); + g.setOnLose(new EventHandler() { + @Override + public void handle(ActionEvent event) { - @Override - public void start(Stage stage) throws Exception { - generator = new Generator(); - level = generator.getEnvironment(); - // pane auto resizes the children while group cannot - Pane pane = new Pane(); - Scene scene = new Scene(pane, sizeOfGame, sizeOfGame); -// buttonGenerate.setOnAction(new EventHandler() { -// @Override -// -// public void handle(ActionEvent arg0) { -// buttonGenerate.setVisible(true); -// generator.generateLevel(); -// level = generator.getEnvironment(); -// -// -// } -// -// }); + newLevel(); + System.out.println("You lost"); + } + }); + //Display stage + stage.setScene(scene); + stage.show(); + } + + private void centreCamera(Vector2 pos) { + //Camera center is top left corner so offset by half screen dimensions + camera.setTranslateX(sceneScale * pos.x - sceneDimensions.x/2.0 * camera.getScaleX()); + camera.setTranslateY(sceneScale * pos.y - sceneDimensions.y/2.0 * camera.getScaleY()); + } + + private void newLevel() { + //Generate a new level + g.generateLevel(5, 25, 100); + + //Clear previous level group + level.getChildren().clear(); + + //Create and cache player and boxes + createPlayer(g.getPlayerLocation()); + level.getChildren().add(playerBox); + createBoxes(); + level.getChildren().addAll(boxes); + + //Create floors goals and edges + addFloor(level); + addGoals(level); + addEdges(level); + + //Position camera + centreCamera(g.getPlayerLocation()); + } + + + private void createPlayer(Vector2 start) { + playerBox = new Box(sceneScale, sceneScale, sceneScale); + playerBox.setMaterial(player); + playerBox.setTranslateX(start.x * sceneScale); + playerBox.setTranslateY(start.y * sceneScale); + } + + public void createBoxes() { + Vector2[] positions = g.getBoxLocations(); + boxes = new Box[positions.length]; + for(int i=0; i() { @Override public void handle(KeyEvent key) { - Vector2 movedTo = null; switch (key.getCode()) { - case LEFT: - movedTo = generator.moveCharacter(0); - break; - case UP: - movedTo = generator.moveCharacter(1); - break; - case RIGHT: - movedTo = generator.moveCharacter(2); - break; - case DOWN: - movedTo = generator.moveCharacter(3); - break; - case ENTER: - generator.generateLevel(5, 20, 100); - level = generator.getEnvironment(); - changeToImage(level, pane); - default: - break; - - } - - if (movedTo != null) { - changeToImage(level, pane); + case LEFT: g.moveCharacter(Vector2.LEFT); break; + case UP: g.moveCharacter(Vector2.DOWN); break; + case RIGHT: g.moveCharacter(Vector2.RIGHT); break; + case DOWN: g.moveCharacter(Vector2.UP) ;break; + case BACK_SPACE : g.undo(); break; + case ENTER: newLevel(); break; + default: break; } } - }); - - changeToImage(level, pane); - // System.out.println(level.length); - // System.out.println(level[0].length); - //pane.getChildren().addAll(buttonGenerate); - stage.setScene(scene); - stage.show(); - } - - /** - * my representation may or may not be upside down compared to the numbered - * system.out.print directions for up and down were swapped in the generator - * class. need clarifications on this. - * - * @author Jzhang - */ - - public void changeToImage(BlockType[][] level, Pane pane) { - for (int x = 0; x < level.length; x++) { - for (int y = 0; y < level[0].length; y++) { - Sprite block = new Sprite(level[x][level[0].length - y - 1].ordinal(), x * (sizeOfGame / level.length), - y * (sizeOfGame / level[0].length), sizeOfGame / level.length, sizeOfGame / level[0].length); - pane.getChildren().addAll(block); - } - } - } -} \ No newline at end of file +} diff --git a/Generator.java b/Generator.java index 43ab7f6..4369a73 100644 --- a/Generator.java +++ b/Generator.java @@ -1,129 +1,133 @@ -import java.util.Arrays; +import java.io.File; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; -public class Generator { - public static final Vector2[] directions = {new Vector2(-1, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(0, -1)}; - public static final Random random = new Random(); +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; + +import javafx.event.ActionEvent; +import javafx.event.EventHandler; + + +public class Generator implements IGenerator { + public static Random random = new Random(); - private BlockType[][] level; + private State start; + private State goal; + private State curr; private List next; - private int minX, maxX, minY, maxY; + + private int movedBox; + private Vector2 playerPosition; - private Vector2[] goals; - - public static void main(String[] args) { - @SuppressWarnings("unused") - Generator b = new Generator(); - } - - public Generator() { - generateLevel(5, 20, 100); - } - - public Vector2 moveCharacter(int dir) { - Vector2 newPos = Vector2.add(playerPosition, directions[dir]); - switch(getLevelPosition(newPos)) { - case goal : - case floor : - setLevelPosition(newPos, BlockType.player); - setLevelPosition(playerPosition, Arrays.asList(goals).contains(playerPosition) ? BlockType.goal : BlockType.floor); - return (playerPosition = newPos); - case box : - if(getLevelPosition(newPos).equals(BlockType.box)) { - for(State s : next) { - if(s.getPlayerLocation().equals(newPos) && s.getMovedBoxLocation().equals(Vector2.add(newPos, directions[dir])) && getLevelPosition(s.getMovedBoxLocation()) != BlockType.wall) { - next = s.getStates(false); - setLevelPosition(newPos, BlockType.player); - setLevelPosition(s.getMovedBoxLocation(), BlockType.box); - setLevelPosition(playerPosition, Arrays.asList(goals).contains(playerPosition) ? BlockType.goal : BlockType.floor); - return (playerPosition = newPos); - } + private Set floor; + private Set edge; + + private EventHandler positionHandler; + private EventHandler boxHandler; + private EventHandler winHandler; + private EventHandler loseHandler; + + + public void moveCharacter(int dir) { + Vector2 newPos = Vector2.add(playerPosition, Vector2.directions[dir]); + for(int i=0; i no lose + loseHandler.handle(null); + } else if(hasGoal(s.getMovedBoxLocation())) { + for(Vector2 box : curr.getBoxLocations()) if(!hasGoal(box)) return; + playSound("win.wav"); + winHandler.handle(null); + } + return; } } - default: return null; + return; + } } + if(floor.contains(newPos)) { + playerPosition = newPos; + positionHandler.handle(null); + } + } + + public void generateLevel(int numBoxes, int numIterations, int difficulty, long seed) { + random.setSeed(seed); + generateLevel(numBoxes, numIterations, difficulty); } public void generateLevel(int numBoxes, int numIterations, int difficulty) { - Set traversed = new HashSet(); - goals = randomPositions(numBoxes); - - State s = new State(goals); - for(int i=0; i(); + edge = new HashSet(); + goal = new State(randomPositions(numBoxes)); + start = goal; + for(int i=0; i maxX) maxX = box.x; - if(box.y < minY) minY = box.y; - if(box.y > maxY) maxY = box.y; + for(Vector2 v : floor) { + for(Vector2 dir : Vector2.directions) { + Vector2 adjacent = Vector2.add(v, dir); + if(!floor.contains(adjacent)) edge.add(adjacent); } } - for(Vector2 floor : traversed) { - if(floor.x < minX) minX = floor.x; - if(floor.x > maxX) maxX = floor.x; - if(floor.y < minY) minY = floor.y; - if(floor.y > maxY) maxY = floor.y; - } - - minX--; - minY--; - maxX++; - maxY++; - - int paddedSize = Math.max(Math.abs(maxX - minX)+1, Math.abs(maxY - minY)+1); - - //Create 2D array with default false and add fence-post error - level = new BlockType[paddedSize][paddedSize]; - - for(Vector2 v : traversed) setLevelPosition(v, BlockType.floor); - for(Vector2 v : goals) setLevelPosition(v, BlockType.goal); - for(Vector2 box : s.getBoxLocations()) setLevelPosition(box, BlockType.box); - setLevelPosition(s.getPlayerLocation(), BlockType.player); - - for(int y=level[0].length-1; y>=0; y--) for(int x=0; x=0; y--) { - for(int x=0; x getFloor() { + return floor; + } + + public Vector2[] getGoals() { + return goal.getBoxLocations(); + } + + public boolean hasGoal(Vector2 g) { + for(Vector2 v : goal.getBoxLocations()) if(v.equals(g)) return true; + return false; + } + + public Set getEdges() { + return edge; } private Vector2[] randomPositions(int num) { Vector2[] positions = new Vector2[num]; - int[] xRandom = randomSequence(1, num ); - int[] yRandom = randomSequence(1, num); + int[] xRandom = randomSequence(1, num + 1); + int[] yRandom = randomSequence(1, num + 1); for(int i=0; i eventHandler) { + positionHandler = eventHandler; + } + + public void setOnBoxMove(EventHandler eventHandler) { + boxHandler = eventHandler; + } + + public void setOnWin(EventHandler eventHandler) { + winHandler = eventHandler; + } + + public void setOnLose(EventHandler eventHandler) { + loseHandler = eventHandler; + } + + public void undo() { + if(!curr.equals(start)) { + movedBox = curr.getMovedBox(); + + curr = curr.getParent(); + next = curr.getStates(false); + playerPosition = curr.getPlayerLocation(); + + positionHandler.handle(null); + boxHandler.handle(null); + } + } } diff --git a/IGenerator.java b/IGenerator.java new file mode 100644 index 0000000..7735488 --- /dev/null +++ b/IGenerator.java @@ -0,0 +1,21 @@ +import java.util.Set; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; + +public interface IGenerator { + public void moveCharacter(int dir); + public void generateLevel(int numBoxes, int numIterations, int difficulty, long seed); + public void generateLevel(int numBoxes, int numIterations, int difficulty); + public int getBoxMovedBox(); + public Vector2[] getBoxLocations(); + public Vector2 getPlayerLocation(); + public Set getFloor(); + public Vector2[] getGoals(); + public boolean hasGoal(Vector2 g); + public Set getEdges(); + public void undo(); + public void setOnPlayerMove(EventHandler eventHandler); + public void setOnBoxMove(EventHandler eventHandler); + public void setOnWin(EventHandler eventHandler); + public void setOnLose(EventHandler eventHandler); +} diff --git a/Record.java b/Record.java new file mode 100644 index 0000000..5829278 --- /dev/null +++ b/Record.java @@ -0,0 +1,16 @@ +public class Record { + public final long seed; + public final String opponent; + public final long time; + + public Record(long seed, String opponent, long time) { + this.seed = seed; + this.opponent = opponent; + this.time = time; + } + + @Override + public String toString() { + return seed + " " + opponent + " " + time; + } +} diff --git a/State.java b/State.java index df679d6..3fcb78c 100644 --- a/State.java +++ b/State.java @@ -15,6 +15,7 @@ private State(State parent, Vector2[] boxLocations, Vector2 playerStart, int box this.boxLocations = boxLocations; this.playerStart = playerStart; this.boxMoved = boxMoved; + this.dirMoved = dirMoved; } public State(Vector2[] boxLocations, Vector2 playerStart) { @@ -49,12 +50,16 @@ public int getMovedBox() { return boxMoved; } + public int getMovedDirection() { + return dirMoved; + } + public List getStates(boolean reverse) { List states = new ArrayList(boxLocations.length << 2); CachedSearch search = new CachedSearch(getPlayerLocation(), Arrays.asList(boxLocations)); for(int box = 0; box < boxLocations.length; box++) { - for(int dir = 0; dir < Generator.directions.length; dir++) { + for(int dir = 0; dir < Vector2.directions.length; dir++) { State found = (reverse ? getPreviousBoxState(search, box, dir, null, 1) : getNextBoxState(search, box, dir, null, 1)); if(found != null) states.add(found); } @@ -65,7 +70,7 @@ public List getStates(boolean reverse) { public State getRandomState(Set floor, boolean reverse, int newFloorCost) { CachedSearch search = new CachedSearch(getPlayerLocation(), Arrays.asList(boxLocations)); for(int box : Generator.randomSequence(0, boxLocations.length)) { - for(int dir : Generator.randomSequence(0, Generator.directions.length)) { + for(int dir : Generator.randomSequence(0, Vector2.directions.length)) { State s = reverse ? getPreviousBoxState(search, box, dir, floor, newFloorCost) : getNextBoxState(search, box, dir, floor, newFloorCost); if(s != null) return s; } @@ -79,8 +84,8 @@ public void print() { } private State getNextBoxState(CachedSearch search, int box, int dir, Set floor, int newFloorCost) { - Vector2 newBoxLocation = Vector2.add(boxLocations[box], Generator.directions[dir]); - Vector2 playerAccess = Vector2.subtract(boxLocations[box], Generator.directions[dir]); + Vector2 newBoxLocation = Vector2.add(boxLocations[box], Vector2.directions[dir]); + Vector2 playerAccess = Vector2.subtract(boxLocations[box], Vector2.directions[dir]); if(!search.hasObstical(newBoxLocation) && search.addMinimumPath(playerAccess, floor, newFloorCost)) { Vector2[] newBoxLocations = boxLocations.clone(); @@ -91,8 +96,8 @@ private State getNextBoxState(CachedSearch search, int box, int dir, Set floor, int newFloorCost) { - Vector2 newBoxLocation = Vector2.add(boxLocations[box], Generator.directions[dir]); - Vector2 playerAccess = Vector2.add(boxLocations[box], Vector2.scale(Generator.directions[dir], 2)); + Vector2 newBoxLocation = Vector2.add(boxLocations[box], Vector2.directions[dir]); + Vector2 playerAccess = Vector2.add(boxLocations[box], Vector2.scale(Vector2.directions[dir], 2)); if(!search.hasObstical(newBoxLocation) && search.addMinimumPath(playerAccess, floor, newFloorCost)) { floor.add(boxLocations[box]); @@ -103,4 +108,22 @@ private State getPreviousBoxState(CachedSearch search, int box, int dir, Set floor, int box) { + CachedSearch search = new CachedSearch(getPlayerLocation(), Arrays.asList(boxLocations)); + for(int dir=0; dir 0 ? 1 : -1, 0) : new Vector2(0, v.y > 0 ? 1 : -1); - } - - public static int manMagnitude(Vector2 v) { - return Math.abs(v.x + v.y); - } - @Override public boolean equals(Object o) { diff --git a/diffuse_box.jpg b/diffuse_box.jpg new file mode 100644 index 0000000..1d69520 Binary files /dev/null and b/diffuse_box.jpg differ diff --git a/diffuse_tile.jpeg b/diffuse_tile.jpeg new file mode 100644 index 0000000..6338bf3 Binary files /dev/null and b/diffuse_tile.jpeg differ diff --git a/diffuse_tile.jpg b/diffuse_tile.jpg new file mode 100644 index 0000000..783264f Binary files /dev/null and b/diffuse_tile.jpg differ diff --git a/diffuse_tile.png b/diffuse_tile.png new file mode 100644 index 0000000..b7146df Binary files /dev/null and b/diffuse_tile.png differ diff --git a/diffuse_tile2.jpg b/diffuse_tile2.jpg new file mode 100644 index 0000000..58d02d1 Binary files /dev/null and b/diffuse_tile2.jpg differ diff --git a/fireball.jpg b/fireball.jpg new file mode 100644 index 0000000..7606a61 Binary files /dev/null and b/fireball.jpg differ diff --git a/fireball2.png b/fireball2.png new file mode 100644 index 0000000..70f9b35 Binary files /dev/null and b/fireball2.png differ diff --git a/golf_hole.jpg b/golf_hole.jpg new file mode 100644 index 0000000..2072506 Binary files /dev/null and b/golf_hole.jpg differ diff --git a/golf_hole.png b/golf_hole.png new file mode 100644 index 0000000..fe9a041 Binary files /dev/null and b/golf_hole.png differ diff --git a/normal_box.jpg b/normal_box.jpg new file mode 100644 index 0000000..2a1f9c6 Binary files /dev/null and b/normal_box.jpg differ diff --git a/normal_tile.jpg b/normal_tile.jpg new file mode 100644 index 0000000..c1e38c7 Binary files /dev/null and b/normal_tile.jpg differ diff --git a/normal_tile.png b/normal_tile.png new file mode 100644 index 0000000..b7146df Binary files /dev/null and b/normal_tile.png differ diff --git a/sea.JPG b/sea.JPG new file mode 100644 index 0000000..39b843f Binary files /dev/null and b/sea.JPG differ diff --git a/wall.jpeg b/wall.jpeg new file mode 100644 index 0000000..6e1fd37 Binary files /dev/null and b/wall.jpeg differ diff --git a/win.wav b/win.wav new file mode 100644 index 0000000..3a422da Binary files /dev/null and b/win.wav differ