diff --git a/BlockType.java b/BlockType.java new file mode 100644 index 0000000..7fe71c5 --- /dev/null +++ b/BlockType.java @@ -0,0 +1,4 @@ + +public enum BlockType { + wall, floor, box, goal, player +} diff --git a/CachedLevels.java b/CachedLevels.java new file mode 100644 index 0000000..ad8d546 --- /dev/null +++ b/CachedLevels.java @@ -0,0 +1,43 @@ +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Scanner; + +public class CachedLevels { + private String player; + + public CachedLevels(String player) { + this.player = player; + } + + public Record getRecord() { + Record c = null; + try { + HttpURLConnection conn = (HttpURLConnection) new URL("http://sokoban.heliohost.org/levelEdit.php?player=" + player).openConnection(); + conn.setDoOutput(true); + Scanner s = new Scanner(new InputStreamReader(conn.getInputStream())); + if(s.hasNext()) c = new Record(s.nextLong(), s.next(), s.nextLong()); + s.close(); + conn.disconnect(); + } catch (IOException e) { + e.printStackTrace(); + } + return c; + } + + public boolean setRecord(long seed, long time) { + boolean recordHolder = false; + try { + HttpURLConnection conn = (HttpURLConnection) new URL("http://sokoban.heliohost.org/levelEdit.php?player=" + player + "&seed=" + seed + "&time=" + time).openConnection(); + conn.setDoOutput(true); + Scanner s = new Scanner(new InputStreamReader(conn.getInputStream())); + if(s.hasNextInt() && s.nextInt() == 1) recordHolder = true; + s.close(); + conn.disconnect(); + } catch (IOException e) { + e.printStackTrace(); + } + return recordHolder; + } +} diff --git a/CachedSearch.java b/CachedSearch.java new file mode 100644 index 0000000..a87a5d4 --- /dev/null +++ b/CachedSearch.java @@ -0,0 +1,156 @@ +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; +//TODO search for nearest globalTraversed then to goal + +/** + * Stores past search information to optimize successive searches from the same point + * @author KW + */ +public class CachedSearch { + /** + * Stores a series of ordered positions + * @author KW + */ + private class GridState { + private GridState parent; + private Vector2 position; + private int cost; + + public GridState(GridState parent, Vector2 position, int cost) { + this.cost = cost; + if((this.parent = parent) != null) this.cost += parent.getCost(); + this.position = position; + } + + public GridState(Vector2 position) { + this(null, position, 0); + } + + public GridState getParent() { + return parent; + } + + public Vector2 getPosition() { + return position; + } + + public int getCost() { + return cost; + } + + @Override + public boolean equals(Object o) { + if(o == null || !(o instanceof GridState)) return false; + GridState s = (GridState)o; + return s.position.equals(position); + } + + @Override + public int hashCode() { + return 21 + position.hashCode(); + } + } + + + private Vector2 start; + private Collection obsticals; + private Set successPaths; + + /** + * Instantiates a new CachedSearch + * @param start constant start of search + * @param obsticals constant locations search must avoid + */ + public CachedSearch(Vector2 start, Collection obsticals) { + this.start = start; + this.obsticals = obsticals; + + successPaths = new HashSet(); + successPaths.add(start); + } + + /** + * Attempts to find a path to the given end point + * @param end the location to search for + * @return true if end is accessible from start + */ + public boolean hasPath(Vector2 end) { + return search(end, null, 1); + + } + + public boolean hasObstical(Vector2 obstical) { + return obsticals.contains(obstical); + } + + public boolean addMinimumPath(Vector2 end, Collection floor, int newFloorCost) { + return search(end, floor, newFloorCost); + } + + /** + * Attempts to find a path to the given end point + * @param end the location to search for + * @param floor a subset of locations to search within + * @param subOptimality number of unnecessary twists in path + * @return path an ordered continuous collection from start to end or null if no path exists + */ + 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(); + + while(!pathQueue.isEmpty()) { + GridState parent = pathQueue.poll(); + closed.add(parent); + + if(floor != null ? parent.getPosition().equals(start) : successPaths.contains(parent.getPosition())) { + if(floor != null) for(GridState g = parent; g != null; g = g.getParent()) floor.add(g.getPosition()); + else for(GridState g : closed) successPaths.add(g.getPosition()); + return true; + } + + for(Vector2 childDir : Vector2.directions) { + Vector2 childPos = Vector2.add(parent.getPosition(), childDir); + 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); + } + } + } + } + } + return false; + } +} + + + diff --git a/FileInput.java b/FileInput.java new file mode 100644 index 0000000..a326b6e --- /dev/null +++ b/FileInput.java @@ -0,0 +1,30 @@ +import java.io.File; +import java.io.FileNotFoundException; + +import javafx.application.Application; +import javafx.stage.FileChooser; +import javafx.stage.Stage; + +/** + * + * @author AM + * Last Edited 24/05/2017 : Antony.J + */ +public final class FileInput extends Application { + File file; + public void start(Stage stage) throws FileNotFoundException { + final FileChooser fileChooser = new FileChooser(); + + String userDirectoryString = System.getProperty("user.dir"); + userDirectoryString += "/gameFiles"; + File userDirectory = new File(userDirectoryString); + + fileChooser.setInitialDirectory(userDirectory); + + file = fileChooser.showOpenDialog(stage); + } + + public File getFile(){ + return file; + } +} \ No newline at end of file diff --git a/GUI.java b/GUI.java new file mode 100644 index 0000000..43b7634 --- /dev/null +++ b/GUI.java @@ -0,0 +1,555 @@ +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; +import javafx.animation.FadeTransition; +import javafx.animation.Interpolator; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; +import javafx.animation.TranslateTransition; +import javafx.application.Application; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.Point3D; +import javafx.scene.Camera; +import javafx.scene.Group; +import javafx.scene.Parent; +import javafx.scene.PerspectiveCamera; +import javafx.scene.PointLight; +import javafx.scene.Scene; +import javafx.scene.SceneAntialiasing; +import javafx.scene.SubScene; +import javafx.scene.image.Image; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.SwipeEvent; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.Box; +import javafx.scene.shape.DrawMode; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import javafx.scene.text.TextAlignment; +import javafx.scene.transform.Rotate; +import javafx.stage.Stage; +import javafx.util.Duration; + +/** + * + * Last Edited 24/05/2017 04:00 : Antony.J + * Last Edited 24/05/2017 12:35 : Antony.J + * + */ +public class GUI extends Application { + private final int sceneScale = 100; + private final Vector2 sceneDimensions = new Vector2(500, 500); + private final int cameraHeight = 10; + private final float cameraAngle = 20; + private final float edgeHeight = 20; + + private String type = ""; + private boolean canPlay = true; + + private Box[] boxes; + private Group playerGroup; + private IGenerator g; + + IngameMenu im = new IngameMenu(); + Parent root = im.createContent(); + private int themeNum = 1; + + public int getThemeNum() { + return themeNum; + } + + public void setThemeNum(int themeNum) { + this.themeNum = themeNum; + } + + private PhongMaterial box; + private PhongMaterial floor; + private PhongMaterial goal; + private PhongMaterial player; + private PhongMaterial edge; + + private TranslateTransition cameraTransition; + private FadeTransition textTransition; + private Timeline playerTransition; + private Rotate rotation = new Rotate(); + + private PerspectiveCamera camera; + private Group level; + private Text text; + + private CachedLevels cachedLevels; + private Text time; + private final DateFormat formatter = new SimpleDateFormat("mm:ss"); + + private Timer timer; + private boolean settingRecord; + private Timeline timeline; + private Record record; + private long currTime; + private long startTime; + + @Override + public void start(Stage stage) throws IOException { + + if(getType().equals("PLAY")){ + g = new Generator(); + + } + if(getType().equals("USER")){ + g = new GeneratorUser(); + } + + text = new Text(""); + text.setFill(Color.WHITE); + text.setFont(new Font(20)); + textTransition = new FadeTransition(); + textTransition.setNode(text); + textTransition.setDuration(Duration.millis(2000)); + textTransition.setInterpolator(Interpolator.EASE_IN); + textTransition.setFromValue(1); + textTransition.setToValue(0); + + time = new Text(); + time.setFont(new Font(50)); + time.setFill(Color.WHITE); + time.setTranslateY(200); + time.setTextAlignment(TextAlignment.LEFT); + time.setVisible(false); + Notification notification = new Notification(); + cachedLevels = new CachedLevels(System.getProperty("user.name")); + timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + if(!settingRecord) { + Record r = cachedLevels.getRecord(); + if(r != null && !r.equals(record)) { + record = r; + notification.showAlert(record.opponent + " challenged you"); + } + } + } + }, 10000, 10000); + + notification.onClick(new EventHandler() { + @Override + public void handle(MouseEvent m) { + if(!settingRecord) { // is the user is playing his generated game, then desable this + newLevel(record.seed); + text.setText(""); + settingRecord = true; + time.setVisible(true); + notification.hideAlert(); + + currTime = record.time; + timeline = new Timeline(); + timeline.setCycleCount(Timeline.INDEFINITE); + timeline.getKeyFrames().add( + new KeyFrame(Duration.millis(10), new EventHandler() { + public void handle(ActionEvent event) { + Date date = new Date(currTime -= 10); + time.setText(formatter.format(date)); + if (currTime < 10) { + currTime = 0; + timeline.stop(); + lost(); + } + } + })); + timeline.playFromStart(); + } + } + }); + + camera = new PerspectiveCamera(false); + camera.setTranslateZ(-sceneScale * cameraHeight); + camera.setRotationAxis(new Point3D(1, 0, 0)); + camera.setRotate(cameraAngle); + cameraTransition = new TranslateTransition(); + cameraTransition.setNode(camera); + cameraTransition.setDuration(Duration.millis(400)); + cameraTransition.setInterpolator(Interpolator.EASE_OUT); + + level = new Group(); + setupMaterials(); + newLevel(); + + //Group 3D nodes into a sub-scene with perspective camera + Group root3D = new Group(camera, text, level); + SubScene subScene = new SubScene(root3D, sceneDimensions.x, sceneDimensions.y, true, SceneAntialiasing.DISABLED); + subScene.setCamera(camera); + subScene.setFill(Color.BLACK); + + //Group sub-scene and 2D elements into stack pane + StackPane pane = new StackPane(); + pane.getChildren().addAll(subScene, notification, time, text); + + //Create main scene + Scene scene = new Scene(pane); + + setupKeyEvents(scene, stage, pane); + setupSwipeEvents(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) { + newLevel(); + won(); + System.out.println("You won"); + } + }); + g.setOnLose(new EventHandler() { + @Override + public void handle(ActionEvent event) { + newLevel(); + System.out.println("You lost"); + } + }); + + stage.setTitle("Project"); + stage.setScene(scene); + stage.show(); + } + + @Override + public void stop() { + timer.cancel(); + } + + private void won() { + if(settingRecord) { + timeline.stop(); + text.setText((cachedLevels.setRecord(record.seed, record.time - currTime) ? "You beat " : "You lost to ") + record.opponent); + time.setVisible(false); + record = null; + } else { + text.setText("You " + (cachedLevels.setRecord(g.getSeed(), System.currentTimeMillis() - startTime) ? "set a record!" : "won!")); + } + newLevel(); + } + + private void lost() { + if(settingRecord) { + timeline.stop(); + text.setText("You lost to " + record.opponent); + time.setVisible(false); + + cachedLevels.setRecord(record.seed, record.time); + record = null; + + } else { + text.setText("You lost!"); + } + newLevel(); + } + + private void newLevel() { + if(getType().equals("PLAY")){ + // g = new Generator(); + g.generateLevel(5, 20, 100); + } + if(getType().equals("USER")){ + g.generateLevel(); + } + drawLevel(); + } + + private void newLevel(long seed) { + // g = new Generator(); + g.generateLevel(5, 20, 100, seed); + drawLevel(); + System.out.println("Game Got created......."); + } + + public void drawLevel() { + settingRecord = false; + startTime = System.currentTimeMillis(); + + createPlayerGroup(g.getPlayerLocation()); + createBoxes(); + level.getChildren().clear(); + level.getChildren().add(playerGroup); + level.getChildren().addAll(boxes); + addFloor(level); + addGoals(level); + addEdges(level); + centreCamera(g.getPlayerLocation()); + + textTransition.playFromStart(); + } + + private void centreCamera(Vector2 pos) { + cameraTransition.stop(); + cameraTransition.setToX(sceneScale * pos.x - sceneDimensions.x/2.0 * camera.getScaleX()); + cameraTransition.setToY(sceneScale * pos.y - sceneDimensions.y/2.0 * camera.getScaleY() + (cameraAngle*cameraHeight*sceneScale)/(90 - cameraAngle)); + cameraTransition.play(); + } + + + private void createPlayerGroup(Vector2 start) { + Box p = new Box(sceneScale, sceneScale, sceneScale); + p.setMaterial(player); + + PointLight l = new PointLight(); + l.setColor(Color.LIGHTGOLDENRODYELLOW); + l.setTranslateZ(-sceneScale); + + playerGroup = new Group(p); + playerGroup.setTranslateX(sceneScale * start.x); + playerGroup.setTranslateY(sceneScale * start.y); + + playerTransition = new Timeline(new KeyFrame(Duration.ZERO, new KeyValue(rotation.angleProperty(), -90)), new KeyFrame(Duration.millis(150), new KeyValue(rotation.angleProperty(), 0))); + + } + + public void createBoxes() { + Vector2[] positions = g.getBoxLocations(); + boxes = new Box[positions.length]; + for(int i=0; i() { + @Override + public void handle(KeyEvent key) { + if (canPlay == true) { + switch (key.getCode()) { + 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; + case ESCAPE: + try { + + pane.getChildren().add(root); + canPlay = false; + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + default: + break; + + } + } else if (canPlay == false) { + + switch (key.getCode()) { + case UP: + try { + im.moveItem(1, stage); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + case DOWN: + try { + im.moveItem(-1, stage); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + case ENTER: + try { + im.moveItem(0, stage); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + + case ESCAPE: + pane.getChildren().remove(root); + canPlay = true; + break; + + default: + break; + + } + } + } + }); + } + + public void setG(String type) { + this.type = type; + } + + private String getType(){ + return type; + } + + private void setupSwipeEvents(Scene scene) { + scene.setOnSwipeDown(new EventHandler() { + @Override + public void handle(SwipeEvent event) { + g.moveCharacter(Vector2.DOWN); + } + }); + + scene.setOnSwipeUp(new EventHandler() { + @Override + public void handle(SwipeEvent event) { + g.moveCharacter(Vector2.UP); + } + }); + + scene.setOnSwipeLeft(new EventHandler() { + @Override + public void handle(SwipeEvent event) { + g.moveCharacter(Vector2.LEFT); + } + }); + + scene.setOnSwipeRight(new EventHandler() { + @Override + public void handle(SwipeEvent event) { + g.moveCharacter(Vector2.RIGHT); + } + }); + } +} diff --git a/Generator.java b/Generator.java new file mode 100644 index 0000000..c850c06 --- /dev/null +++ b/Generator.java @@ -0,0 +1,188 @@ +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; + +/** + * + * Last Edited 24/05/2017 : Antony.J + * + */ +public class Generator implements IGenerator { + public static Random random = new Random(); + + private State start; + private State goal; + private State curr; + private List next; + + private int movedBox; + private long seed; + + private Vector2 playerPosition; + 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; + winHandler.handle(null); + } + return; + } + } + return; + } + } + if(floor.contains(newPos)) { + playerPosition = newPos; + System.out.println("Player Location: " + playerPosition.toString()); + positionHandler.handle(null); + } + } + + public void generateLevel(int numBoxes, int numIterations, int difficulty) { + generateLevel(numBoxes, numIterations, difficulty, System.currentTimeMillis()); + } + + public void generateLevel(int numBoxes, int numIterations, int difficulty, long seed) { + this.seed = seed; + random.setSeed(seed); + + floor = new HashSet(); + edge = new HashSet(); + goal = new State(randomPositions(numBoxes)); + start = goal; + for(int i=0; i 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 + 1); + int[] yRandom = randomSequence(1, num + 1); + for(int i=0; i 0; i--) { + int index = Generator.random.nextInt(i + 1); + int a = array[index]; + array[index] = array[i]; + array[i] = a; + } + return array; + } + + public void setOnPlayerMove(EventHandler 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 long getSeed() { + return seed; + } + + 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); + } + } + + @Override + public void generateLevel() { + + } +} diff --git a/GeneratorUser.java b/GeneratorUser.java new file mode 100644 index 0000000..60af891 --- /dev/null +++ b/GeneratorUser.java @@ -0,0 +1,236 @@ +import java.util.Set; + +import javafx.event.ActionEvent; +import javafx.event.EventHandler; + +/** + * + * @author AM + * + */ +public class GeneratorUser implements IGenerator { + private UserGrid userGrid; + + + private int movedBox; + private int prevDir; + private boolean boxMoved; + private Vector2 playerPosition; + Vector2[] boxLocations; + Vector2[] goalLocations; + + private Set floor; + private Set edge; + + private EventHandler positionHandler; + private EventHandler boxHandler; + private EventHandler winHandler; +// private EventHandler loseHandler; + + public GeneratorUser() { + prevDir = 5; // define undefine Dir + boxMoved = false; + userGrid = new UserGrid(); + + } + + // + // RELATED TO WEAREHOUSE GRID + // + + @Override + public void generateLevel() { + userGrid.createUserDefinedGrid(); + boxLocations = userGrid.getBoxLoctions(); + goalLocations = userGrid.getGoals(); + } + + @Override + public Vector2 getPlayerLocation() { + playerPosition = userGrid.getPlayerLocation(); + return playerPosition; + } + + @Override + public Set getFloor() { + this.floor = userGrid.getFloorSet(); + return floor; + } + + @Override + public Set getEdges() { + this.edge = userGrid.getEdgesSet(); + return edge; + } + + @Override + public Vector2[] getGoals() { + return goalLocations; + } + + @Override + public Vector2[] getBoxLocations() { + return boxLocations; + } + + // + // GET MOVED LOCATIONS + // + + + @Override + public void moveCharacter(int dir) { + prevDir = dir; + + Vector2 newPos = Vector2.add(playerPosition, Vector2.directions[dir]); + + if(floor.contains(newPos) && !userGrid.getUserDefinedGrid()[newPos.x][newPos.y].getBlockType().equals(BlockType.box)) { + userGrid.getUserDefinedGrid()[playerPosition.x][playerPosition.y].setBlockType(BlockType.floor); + userGrid.getUserDefinedGrid()[newPos.x][newPos.y].setBlockType(BlockType.player); + playerPosition = newPos; + positionHandler.handle(null); + boxMoved = false; + + }else { + for(int i=0; i < boxLocations.length; i++) { + if(boxLocations[i].equals(newPos)){ + Vector2 newBoxPos = Vector2.add(boxLocations[i], Vector2.directions[dir]); + if(floor.contains(newBoxPos) && !userGrid.getUserDefinedGrid()[newBoxPos.x][newBoxPos.y].getBlockType().equals(BlockType.box)) { + userGrid.getUserDefinedGrid()[playerPosition.x][playerPosition.y].setBlockType(BlockType.floor); + userGrid.getUserDefinedGrid()[newPos.x][newPos.y].setBlockType(BlockType.player); + userGrid.getUserDefinedGrid()[newBoxPos.x][newBoxPos.y].setBlockType(BlockType.box); + playerPosition = newPos; + movedBox = i; + boxLocations[i] = newBoxPos; + positionHandler.handle(null); + boxHandler.handle(null); + boxMoved = true; + } + int numWins = 0; + for(Vector2 goal: goalLocations) { + if(userGrid.getUserDefinedGrid()[goal.x][goal.y].getBlockType().equals(BlockType.box)) { + numWins++; + } + } + if(userGrid.getGoals().length == numWins) { + winHandler.handle(null); + } + } + } + } + } + + + @Override + public void undo() { + if(getOppositeDir() == 5) { + System.out.println("no undo"); + positionHandler.handle(null); + }else { + Vector2 newPos = Vector2.add(playerPosition, Vector2.directions[getOppositeDir()]); + if(!boxMoved) { + userGrid.getUserDefinedGrid()[playerPosition.x][playerPosition.y].setBlockType(BlockType.floor); + userGrid.getUserDefinedGrid()[newPos.x][newPos.y].setBlockType(BlockType.player); + playerPosition = newPos; + positionHandler.handle(null); + prevDir = 5; // assigning to be undefined dirr + }else { + Vector2 newBoxPos = Vector2.add(boxLocations[this.getBoxMovedBox()], Vector2.directions[getOppositeDir()]); + userGrid.getUserDefinedGrid()[boxLocations[this.getBoxMovedBox()].x][boxLocations[this.getBoxMovedBox()].y].setBlockType(BlockType.floor); + userGrid.getUserDefinedGrid()[playerPosition.x][playerPosition.y].setBlockType(BlockType.floor); + userGrid.getUserDefinedGrid()[newPos.x][newPos.y].setBlockType(BlockType.player); + userGrid.getUserDefinedGrid()[newBoxPos.x][newBoxPos.y].setBlockType(BlockType.box); + playerPosition = newPos; + boxLocations[this.getBoxMovedBox()] = newBoxPos; + positionHandler.handle(null); + boxHandler.handle(null); + prevDir = 5; // assigning to be undefined dirr + } + } + } + + @Override + public int getBoxMovedBox() { + return movedBox; + } + + + @Override + public boolean hasGoal(Vector2 g) { + Vector2[] goalLocs = getGoals(); + for(Vector2 goal: goalLocs) { + if(goal.equals(g)) + return true; + } + return false; + } + + + + // + // RELATED TO MOVEMENT IN GRID + // + + @Override + public void setOnPlayerMove(EventHandler eventHandler) { + positionHandler = eventHandler; + } + + @Override + public void setOnBoxMove(EventHandler eventHandler) { + boxHandler = eventHandler; + } + + @Override + public void setOnWin(EventHandler eventHandler) { + winHandler = eventHandler; + } + + @Override + public void setOnLose(EventHandler eventHandler) { + //loseHandler = eventHandler; + } + + + + private int getOppositeDir() { + int oppDir = 5; // assigning undefined Direction + switch(prevDir){ + case Vector2.LEFT: + oppDir = Vector2.RIGHT; + break; + case Vector2.RIGHT: + oppDir = Vector2.LEFT; + break; + case Vector2.UP: + oppDir = Vector2.DOWN; + break; + case Vector2.DOWN: + oppDir = Vector2.UP; + break; + default : + break; + } + return oppDir; + } + + + // + // PROBABLY NOT NEED THEM, SINCE THESE ARENT NECESSARY FOR USER + // + @Override + public void generateLevel(int numBoxes, int numIterations, int difficulty, long seed) { + // TODO Auto-generated method stub + } + @Override + public void generateLevel(int numBoxes, int numIterations, int difficulty) { + // TODO Auto-generated method stub + } + + @Override + public long getSeed() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/IGenerator.java b/IGenerator.java new file mode 100644 index 0000000..0593285 --- /dev/null +++ b/IGenerator.java @@ -0,0 +1,28 @@ +import java.util.Set; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; + +/** + * + * Last Edited 24/05/2017 : Antony.J + * + */ +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 void generateLevel(); + 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 long getSeed(); + public void setOnPlayerMove(EventHandler eventHandler); + public void setOnBoxMove(EventHandler eventHandler); + public void setOnWin(EventHandler eventHandler); + public void setOnLose(EventHandler eventHandler); +} diff --git a/IngameMenu.java b/IngameMenu.java new file mode 100644 index 0000000..665b0e2 --- /dev/null +++ b/IngameMenu.java @@ -0,0 +1,135 @@ + +import javafx.animation.TranslateTransition; +import javafx.application.Application; +import javafx.geometry.Pos; +import javafx.geometry.Side; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundImage; +import javafx.scene.layout.BackgroundPosition; +import javafx.scene.layout.BackgroundRepeat; +import javafx.scene.layout.BackgroundSize; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.util.Duration; + +public class IngameMenu extends Application { + private static final Font FONT = Font.font("", FontWeight.BOLD, 20); + + final FileChooser fileChooser = new FileChooser(); + private VBox menuBox; + private int currentItem = 0; + + public static void main(String[] args) { + launch(args); + } + + public void start(Stage stage) throws Exception { + stage.initStyle(StageStyle.TRANSPARENT); + stage.setAlwaysOnTop(true); + Scene scene = new Scene(createContent()); + stage.setScene(scene); + + stage.show(); + } + + public Parent createContent() { + StackPane root = new StackPane(); + root.setPrefSize(200, 300); + + MenuItem exit = new MenuItem("EXIT"); + MenuItem menu = new MenuItem("MENU"); + MenuItem sandbox = new MenuItem("SANDBOX MODE"); + MenuItem options = new MenuItem("OPTIONS"); + + menuBox = new VBox(30, menu, sandbox, options, exit); + menuBox.setAlignment(Pos.CENTER); + getMenuItem(0).setActive(true); + + root.getChildren().addAll(menuBox); + + return root; + } + + private static class MenuItem extends HBox { + private Text text; + private String name; + + public MenuItem(String name) { + + // constructor for distance between sprite and text + super(15); + this.name = name; + setAlignment(Pos.CENTER_LEFT); + + text = new Text(name); + text.setFont(FONT); + + getChildren().addAll(text); + setActive(false); + + } + + public String getMenuName() { + return name; + } + + public void setActive(boolean b) { + text.setFill(b ? Color.WHITE : Color.BISQUE); + } + } + + public void moveItem(int code, Stage stage) throws Exception { + if(code == 1) { + if (currentItem > 0) { + getMenuItem(currentItem).setActive(false); + getMenuItem(--currentItem).setActive(true); + + } + } + + if (code == -1) { + if (currentItem < menuBox.getChildren().size() - 1) { + getMenuItem(currentItem).setActive(false); + getMenuItem(++currentItem).setActive(true); + + } + } + + if (code == 0) { + switch (getMenuItem(currentItem).getMenuName()) { + case "MENU" : + Menu m = new Menu(); + m.start(stage); + case "OPTIONS": + break; + + case "EXIT": + System.exit(0); + } + + } + } + + private MenuItem getMenuItem(int index) { + if (index < 0) + return null; + return (MenuItem) menuBox.getChildren().get(index); + } +} \ No newline at end of file diff --git a/Menu.java b/Menu.java new file mode 100644 index 0000000..a9adcdf --- /dev/null +++ b/Menu.java @@ -0,0 +1,406 @@ + +import java.awt.Desktop; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; + +//import Menu.MenuItem; +import javafx.animation.FadeTransition; +import javafx.animation.TranslateTransition; +import javafx.application.Application; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.geometry.Side; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.image.Image; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundImage; +import javafx.scene.layout.BackgroundPosition; +import javafx.scene.layout.BackgroundRepeat; +import javafx.scene.layout.BackgroundSize; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.util.Duration; + + +/** + * + * Last Edited 24/05/2017 12:35 : Antony.J + * + */ +public class Menu extends Application { + + public static void main(String[] args) { + launch(args); + } + + + private static final Font FONT = Font.font("", FontWeight.BOLD, 18); +// final FileChooser fileChooser = new FileChooser(); + private VBox menuBox; + private VBox themeBox; + // navigator of the menu + private int currentItem = 0; + private int themeItem = 0; + private Stage stage; + GUI gui = new GUI(); + + public void start(Stage stage) throws Exception { + Scene scene = new Scene(createContent()); + + scene.setOnKeyPressed(event -> { + int temp; + if (event.getCode() == KeyCode.UP) { + if (currentItem > 0) { + temp = currentItem; + getMenuItem(currentItem - 1).setPrevActive(false); + getMenuItem(currentItem).setActive(false); + getMenuItem(--currentItem).setActive(true); + getMenuItem(temp).setPrevActive(true); + // depends on how many choices there are on menu + if (temp <= 2) + getMenuItem(++temp).setPrevActive(false); + + } + } + + if (event.getCode() == KeyCode.DOWN) { + if (currentItem < menuBox.getChildren().size() - 1) { + temp = currentItem; + getMenuItem(currentItem + 1).setPrevActive(false); + getMenuItem(currentItem).setActive(false); + getMenuItem(++currentItem).setActive(true); + getMenuItem(temp).setPrevActive(true); + if (temp >= 1) + getMenuItem(--temp).setPrevActive(false); + + } + } + + if (event.getCode() == KeyCode.ENTER) { + switch (getMenuItem(currentItem).getMenuName()) { + case "PLAY": + + gui.setG("PLAY"); + try { + gui.start(stage); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + + case "SANDBOX MODE": + gui.setG("USER"); + try { + gui.start(stage); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + + } + break; + + case "OPTIONS": + + try { + + // stage.initModality(Modality.APPLICATION_MODAL); + // stage.initOwner(stage); + Parent themeRoot = createTheme(); + Scene themeScene = new Scene (themeRoot); + + themeScene.setOnKeyPressed(themeEvent -> { + int themeTemp; + if (themeEvent.getCode() == KeyCode.UP) { + if (themeItem > 0) { + themeTemp = themeItem; + getThemeItem(themeItem - 1).setPrevActive(false); + getThemeItem(themeItem).setActive(false); + getThemeItem(--themeItem).setActive(true); + getThemeItem(themeTemp).setPrevActive(true); + // depends on how many choices there are on menu + if (themeTemp <= 2) + getMenuItem(++themeTemp).setPrevActive(false); + + } + } + + if (themeEvent.getCode() == KeyCode.DOWN) { + if (themeItem < themeBox.getChildren().size() - 1) { + themeTemp = themeItem; + getThemeItem(themeItem + 1).setPrevActive(false); + getThemeItem(themeItem).setActive(false); + getThemeItem(++themeItem).setActive(true); + getThemeItem(themeTemp).setPrevActive(true); + if (themeTemp >= 1) + getThemeItem(--themeTemp).setPrevActive(false); + + } + } + + if (themeEvent.getCode() == KeyCode.ENTER) { + + switch (getThemeItem(themeItem).getMenuName()) { + + case "THEME 1" : + System.out.println("Theme set to 1"); + gui.setThemeNum(1); + break; + + case "THEME 2" : + System.out.println("Theme set to 2"); + gui.setThemeNum(2); + break; + + case "THEME 3" : + + gui.setThemeNum(3); + break; + + case "THEME 4" : + + gui.setThemeNum(4); + break; + + + case "RETURN" : + try { + + start(stage); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + } + + } + }); + // System.out.println("how many"); + stage.setScene(themeScene); + stage.show(); + + } catch (Exception e) { + + e.printStackTrace(); + } + + break; + + case "EXIT": + System.exit(0); + } + } + }); + stage.setScene(scene); + + stage.show(); + } + + + private Parent createTheme() { + + Pane root = new Pane (); + root.setPrefSize(900,600); + + // new Image(url) + Image image = new Image("http://i.imgur.com/AGTcz1a.jpg"); + BackgroundImage background = new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, + new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true), BackgroundSize.DEFAULT); + +// ContentFrame frame = new ContentFrame(createMiddleContent()); + +// HBox hbox = new HBox(frame); + // space inbetween frames +// hbox.setTranslateX(120); +// hbox.setTranslateY(50); + + MenuItem t1 = new MenuItem("THEME 1"); + MenuItem t2 = new MenuItem("THEME 2"); + MenuItem t3 = new MenuItem("THEME 3"); + MenuItem t4 = new MenuItem("THEME 4"); + MenuItem back = new MenuItem("RETURN"); + + themeBox = new VBox(10, t1, t2, t3, t4, back); + themeBox.setAlignment(Pos.TOP_CENTER); + themeBox.setTranslateX(360); + themeBox.setTranslateY(300); + +// Text about = new Text("COMP2911 \nMenu Prototype"); +// about.setTranslateX(50); +// about.setTranslateY(500); +// about.setFill(Color.WHITE); +// about.setFont(FONT); +// about.setOpacity(0.2); + + getThemeItem(0).setActive(true); + root.setBackground(new Background(background)); + root.getChildren().addAll(themeBox); + + return root; + } + // main layout of the menu with UI + private Parent createContent() { + Pane root = new Pane(); + root.setPrefSize(900, 600); + + // new Image(url) + Image image = new Image("http://www.relumination.com/wp-content/uploads/2016/07/warehouse-led.jpg"); + BackgroundImage background = new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, + new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true), BackgroundSize.DEFAULT); + + ContentFrame frame = new ContentFrame(createMiddleContent()); + + HBox hbox = new HBox(frame); + // space inbetween frames + hbox.setTranslateX(120); + hbox.setTranslateY(50); + + MenuItem exit = new MenuItem("EXIT"); + MenuItem play = new MenuItem("PLAY"); + MenuItem sandbox = new MenuItem("SANDBOX MODE"); + MenuItem options = new MenuItem("OPTIONS"); + + menuBox = new VBox(10, play, sandbox, options, exit); + menuBox.setAlignment(Pos.TOP_CENTER); + menuBox.setTranslateX(360); + menuBox.setTranslateY(300); + + Text about = new Text("COMP2911 \nMenu Prototype"); + about.setTranslateX(50); + about.setTranslateY(500); + about.setFill(Color.WHITE); + about.setFont(FONT); + about.setOpacity(0.2); + + getMenuItem(0).setActive(true); + root.setBackground(new Background(background)); + root.getChildren().addAll(hbox, menuBox, about); + return root; + } + + // middle panel content + private Node createMiddleContent() { + String title = "Box Pusher"; + HBox letters = new HBox(0); + letters.setAlignment(Pos.CENTER); + for (int i = 0; i < title.length(); i++) { + Text letter = new Text(title.charAt(i) + ""); + letter.setFont(FONT); + letter.setFill(Color.WHITE); + letters.getChildren().add(letter); + + TranslateTransition tt = new TranslateTransition(Duration.seconds(2), letter); + tt.setDelay(Duration.millis(i * 50)); + tt.setToY(-25); + tt.setAutoReverse(true); + tt.setCycleCount(TranslateTransition.INDEFINITE); + tt.play(); + } + + return letters; + } + + private MenuItem getMenuItem(int index) { + if (index < 0) + return null; + return (MenuItem) menuBox.getChildren().get(index); + } + private MenuItem getThemeItem(int index) { + if (index < 0) + return null; + return (MenuItem) themeBox.getChildren().get(index); + } + + // pane with smoothed out edges + // took this design from the internet + private static class ContentFrame extends StackPane { + public ContentFrame(Node content) { + setAlignment(Pos.CENTER); + + Rectangle frame = new Rectangle(200, 200); + frame.setArcWidth(25); + frame.setArcHeight(25); + frame.setStroke(Color.WHITESMOKE); + + getChildren().addAll(frame, content); + } + } + + private static class MenuItem extends HBox { + + private Box bbLeft = new Box(40, 40, "PLAYER"); + private Box boxLeft = new Box(40, 40, "BOX"); + private Text text; + private String name; + + public MenuItem(String name) { + + // constructor for distance between sprite and text + super(15); + this.name = name; + setAlignment(Pos.CENTER_LEFT); + + text = new Text(name); + text.setFont(FONT); + + getChildren().addAll(bbLeft, boxLeft, text); + setPrevActive(false); + setActive(false); + + } + + public String getMenuName() { + return name; + } + + public void setPrevActive(boolean b) { + bbLeft.setVisible(b); + } + + public void setActive(boolean b) { + boxLeft.managedProperty().bind(boxLeft.visibleProperty()); + boxLeft.setVisible(b); + text.setFill(b ? Color.WHITE : Color.BISQUE); + } + } + + private static class Box extends Parent { + Rectangle r; + + public Box(int x, int y, String type) { + r = new Rectangle(x, y); + r.setStroke(Color.WHITE); + if (type.equals("BOX")) { + r.setFill(Color.BROWN); + } else if (type.equals("PLAYER")) + r.setFill(Color.BLUE); + + getChildren().add(r); + } + + } + + + +} \ No newline at end of file diff --git a/Notification.java b/Notification.java new file mode 100644 index 0000000..a998f08 --- /dev/null +++ b/Notification.java @@ -0,0 +1,64 @@ +import javafx.animation.Interpolator; +import javafx.animation.TranslateTransition; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.Group; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import javafx.util.Duration; + +public class Notification extends Group { + private TranslateTransition tt = new TranslateTransition(); + private Text t = new Text(); + private ImageView i = new ImageView(); + private final Vector2 in = new Vector2(400, 200); + private final Vector2 out = new Vector2(150, 200); + private final Vector2 mid = new Vector2(295, 200); + + public Notification() { + tt.setNode(this); + tt.setDuration(Duration.millis(2000)); + tt.setInterpolator(Interpolator.EASE_BOTH); + t.setX(50); + t.setY(20); + t.setFill(Color.BLACK); + t.setStroke(Color.WHITE); + + i.setImage(new Image("https://lh3.googleusercontent.com/3Ofcop0iBUBDcxHk_-fB3-Y9xaeIv9Tt6Mvvnv6W8085GbqUrJiLOZR35yLpaR3VqTR1ocG9YSwCVvt5MkeN3mA=s0")); + i.setPreserveRatio(true); + i.setFitWidth(40); + + + this.getChildren().addAll(i, t); + this.setTranslateX(in.x); + this.setTranslateY(in.y); + + tt.setOnFinished(new EventHandler() { + @Override + public void handle(ActionEvent event) { + if(tt.getToX() == out.x) { + tt.setToX(mid.x); + tt.play(); + } + } + }); + } + + public void onClick(EventHandler onClick) { + i.setOnMouseClicked(onClick); + } + + public void showAlert(String text) { + t.setText(text); + tt.setToX(out.x); + tt.playFromStart(); + } + + public void hideAlert() { + tt.setToX(in.x); + tt.playFromStart(); + } +} diff --git a/Record.java b/Record.java new file mode 100644 index 0000000..def73ec --- /dev/null +++ b/Record.java @@ -0,0 +1,23 @@ +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; + } + + @Override + public boolean equals(Object o) { + if(o == null || !(o instanceof Record)) return false; + Record r = (Record)o; + return r.seed == seed && r.opponent.equals(opponent) && r.time == time; + } +} diff --git a/State.java b/State.java new file mode 100644 index 0000000..3fcb78c --- /dev/null +++ b/State.java @@ -0,0 +1,129 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +public class State { + private State parent; + private Vector2[] boxLocations; + private Vector2 playerStart; + private int boxMoved; + private int dirMoved; + + private State(State parent, Vector2[] boxLocations, Vector2 playerStart, int boxMoved, int dirMoved) { + this.parent = parent; + this.boxLocations = boxLocations; + this.playerStart = playerStart; + this.boxMoved = boxMoved; + this.dirMoved = dirMoved; + } + + public State(Vector2[] boxLocations, Vector2 playerStart) { + this(null, boxLocations, playerStart, -1, -1); + } + + public State(Vector2[] boxLocations) { + this(null, boxLocations, Vector2.zero, -1, -1); + } + + public State getParent() { + return parent; + } + + public Vector2 getPlayerLocation() { + return playerStart; + } + + public Vector2[] getBoxLocations() { + return boxLocations; + } + + public Vector2 getBoxLocation(int index) { + return boxLocations[index]; + } + + public Vector2 getMovedBoxLocation() { + return boxMoved == -1 ? null : boxLocations[boxMoved]; + } + + 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 < 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); + } + } + return states; + } + + 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, Vector2.directions.length)) { + State s = reverse ? getPreviousBoxState(search, box, dir, floor, newFloorCost) : getNextBoxState(search, box, dir, floor, newFloorCost); + if(s != null) return s; + } + } + return null; + } + + public void print() { + for(Vector2 v : boxLocations) System.out.print("box(" + v.x + " " + v.y + ") "); + System.out.println(); + } + + private State getNextBoxState(CachedSearch search, int box, int dir, Set floor, int newFloorCost) { + 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(); + newBoxLocations[box] = newBoxLocation; + return new State(this, newBoxLocations, boxLocations[box], box, dir); + } + return null; + } + + private State getPreviousBoxState(CachedSearch search, int box, int dir, Set floor, int newFloorCost) { + 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]); + floor.add(newBoxLocation); + Vector2[] newBoxLocations = boxLocations.clone(); + newBoxLocations[box] = newBoxLocation; + return new State(this, newBoxLocations, playerAccess, box, dir); + } + return null; + } + + public boolean boxHasNextState(boolean reverse, Set floor, int box) { + CachedSearch search = new CachedSearch(getPlayerLocation(), Arrays.asList(boxLocations)); + for(int dir=0; dir getFloorSet() { + Set floorVector = new HashSet(); + for (int r = 0; r < GRID_ROW_SIZE; r++) { + for (int c = 0; c < GRID_COL_SIZE; c++) { + if (!userDefinedGrid[r][c].getBlockType().equals(BlockType.wall)) + floorVector.add(userDefinedGrid[r][c]); + } + } + return floorVector; + } + + public Set getEdgesSet() { + Set wallVector = new HashSet(); + for (int r = 0; r < GRID_ROW_SIZE; r++) { + for (int c = 0; c < GRID_COL_SIZE; c++) { + if (userDefinedGrid[r][c].getBlockType().equals(BlockType.wall)) + wallVector.add(userDefinedGrid[r][c]); + } + } + return wallVector; + } + + public Vector2[] getGoals() { + Vector2[] goals = new Vector2[numGoals]; + int i = 0; + for (int r = 0; r < GRID_ROW_SIZE; r++) { + for (int c = 0; c < GRID_COL_SIZE; c++) { + if (userDefinedGrid[r][c].getBlockType().equals(BlockType.goal)) { + goals[i] = userDefinedGrid[r][c]; + i++; + } + } + } + return goals; + } + + public Vector2[] getBoxLoctions() { + Vector2[] boxes = new Vector2[numBoxes]; // numGoals must be equal to + // num ofBoxes + int i = 0; + for (int r = 0; r < GRID_ROW_SIZE; r++) { + for (int c = 0; c < GRID_COL_SIZE; c++) { + if (userDefinedGrid[r][c].getBlockType().equals(BlockType.box)) { + boxes[i] = userDefinedGrid[r][c]; + i++; + } + } + } + return boxes; + } + + public int getNumGoals() { + return numGoals; + } + + public int getNumBoxes() { + return numBoxes; + } +} diff --git a/Vector2.java b/Vector2.java new file mode 100644 index 0000000..e4c6d0e --- /dev/null +++ b/Vector2.java @@ -0,0 +1,66 @@ +/** + * + * Last Edited 24/05/2017 : Antony.J + * + */ +public class Vector2 { + public final int x; + public final int y; + + public static final Vector2 zero = new Vector2(0, 0); + + public static final int LEFT = 0; + public static final int UP = 1; + public static final int RIGHT = 2; + public static final int DOWN = 3; + private BlockType blockType; + + public static final Vector2[] directions = {new Vector2(-1, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(0, -1)}; + + public Vector2(int x, int y) { + this.x = x; + this.y = y; + } + + public BlockType getBlockType() { + return this.blockType; + } + + public void setBlockType(BlockType type) { + this.blockType = type; + } + + public static Vector2 add(Vector2 v1, Vector2 v2) { + return new Vector2(v1.x + v2.x, v1.y + v2.y); + } + + public static Vector2 subtract(Vector2 v1, Vector2 v2) { + return new Vector2(v1.x - v2.x, v1.y - v2.y); + } + + public static Vector2 scale(Vector2 v, int scale) { + return new Vector2(v.x * scale, v.y * scale); + } + + public static int manDistance(Vector2 v1, Vector2 v2) { + return Math.abs(v1.x - v2.x) + Math.abs(v1.y - v2.y); + } + + + @Override + public boolean equals(Object o) { + if(o == null || !(o instanceof Vector2)) return false; + Vector2 v = (Vector2)o; + return x == v.x && y == v.y; + } + + @Override + public int hashCode() { + return 7 * x + 13 * y; + } + + @Override + public String toString() { + return "(" + x + ", " + y + ")"; + } +} diff --git a/sample.txt b/sample.txt new file mode 100644 index 0000000..70dcbb0 --- /dev/null +++ b/sample.txt @@ -0,0 +1,13 @@ +############ +##### #### +## #G B # # +## # G # +## G GBB # +### # +## G B # +# ## +# B B # +# B ### +# GB GP # +## #### +############