diff --git a/oolab/map_0.log b/oolab/map_0.log new file mode 100644 index 0000000..7b8bfaa --- /dev/null +++ b/oolab/map_0.log @@ -0,0 +1,228 @@ +Move information: +Animal was placed at (1,1) + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | |^| | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal moved from (1,1) to (1,2) + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |^| | | | | | | | | + 1: | | | | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal moved from (1,2) to (1,1) + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | |^| | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal changed direction from Północ to Wschód + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | |>| | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal changed direction from Wschód to Północ + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | |^| | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal moved from (1,1) to (1,2) + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |^| | | | | | | | | + 1: | | | | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal moved from (1,2) to (1,3) + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | |^|*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | | | | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal changed direction from Północ to Wschód + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | |>|*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | | | | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal changed direction from Wschód to Południe + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | |v|*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | | | | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal moved from (1,3) to (1,2) + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |v| | | | | | | | | + 1: | | | | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal moved from (1,2) to (1,1) + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | |v| | | | | | | | | + 0: |*| | | | | | | | |*| + -1: --------------------- + +----------------- +Move information: +Animal moved from (1,1) to (1,0) + +Current map state: + + y\x 0 1 2 3 4 5 6 7 8 9 + 9: --------------------- + 8: | | | | | | | | | |*| + 7: | | | |*| | | |*| | | + 6: | | | | |*| | | | | | + 5: | | | | | | | |*| | | + 4: | | | |*| | | | | | | + 3: | | |*| | | | | | | | + 2: | |*| | | | | | | | | + 1: | | | | | | | | | | | + 0: |*|v| | | | | | | |*| + -1: --------------------- + +----------------- diff --git a/oolab/src/main/java/agh/ics/oop/OptionsParser.java b/oolab/src/main/java/agh/ics/oop/OptionsParser.java index c0d7fb7..915365d 100644 --- a/oolab/src/main/java/agh/ics/oop/OptionsParser.java +++ b/oolab/src/main/java/agh/ics/oop/OptionsParser.java @@ -2,8 +2,8 @@ import agh.ics.oop.model.MoveDirection; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; public class OptionsParser { @@ -29,19 +29,37 @@ public static List parseOptions(String[] options) { // }; // current_index++; // } - List moves = new ArrayList<>(); - for (String option : options) { + Stream optionsStream = Stream.of(options); + + var result = optionsStream.map(option -> { if (!(option.equals("f") || option.equals("b") || option.equals("l") || option.equals("r"))) { throw new IllegalArgumentException(option + " is not legal move specification"); } - moves.add(switch (option) { + return switch (option) { case "f" -> MoveDirection.FORWARD; case "b" -> MoveDirection.BACKWARD; case "l" -> MoveDirection.LEFT; case "r" -> MoveDirection.RIGHT; default -> null; - }); - } - return moves; + } + ; + }).toList(); + + return result; + +// List moves = new ArrayList<>(); +// for (String option : options) { +// if (!(option.equals("f") || option.equals("b") || option.equals("l") || option.equals("r"))) { +// throw new IllegalArgumentException(option + " is not legal move specification"); +// } +// moves.add(switch (option) { +// case "f" -> MoveDirection.FORWARD; +// case "b" -> MoveDirection.BACKWARD; +// case "l" -> MoveDirection.LEFT; +// case "r" -> MoveDirection.RIGHT; +// default -> null; +// }); +// } +// return moves; } } diff --git a/oolab/src/main/java/agh/ics/oop/World.java b/oolab/src/main/java/agh/ics/oop/World.java index f6e71e5..70db483 100644 --- a/oolab/src/main/java/agh/ics/oop/World.java +++ b/oolab/src/main/java/agh/ics/oop/World.java @@ -5,7 +5,9 @@ import agh.ics.oop.model.RectangularMap; import agh.ics.oop.model.Vector2d; import agh.ics.oop.model.util.ConsoleMapDisplay; +import agh.ics.oop.model.util.FileMapDisplay; +import java.time.LocalDateTime; import java.util.List; public class World { @@ -17,6 +19,11 @@ public static void main(String[] args) { var rectangularMap = new RectangularMap(10, 10, 1); var observer = new ConsoleMapDisplay(); grassField.addObserver(observer); + grassField.addObserver((worldMap, message) -> { + System.out.println(String.format("%s %s", LocalDateTime.now(), message)); + }); + FileMapDisplay fileMapDisplay = new FileMapDisplay(); + grassField.addObserver(fileMapDisplay); rectangularMap.addObserver(observer); var simulation = new Simulation(positions, moves, grassField); var simulation2 = new Simulation(positions, moves, rectangularMap); diff --git a/oolab/src/main/java/agh/ics/oop/model/AbstractWorldMap.java b/oolab/src/main/java/agh/ics/oop/model/AbstractWorldMap.java index 1b77a94..dd966c4 100644 --- a/oolab/src/main/java/agh/ics/oop/model/AbstractWorldMap.java +++ b/oolab/src/main/java/agh/ics/oop/model/AbstractWorldMap.java @@ -4,9 +4,7 @@ import agh.ics.oop.model.util.IncorrectPositionException; import agh.ics.oop.model.util.MapVisualizer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.util.*; public abstract class AbstractWorldMap implements WorldMap { protected final MapVisualizer visualizer; @@ -69,13 +67,13 @@ public void place(Animal animal) throws IncorrectPositionException { } @Override - public WorldElement objectAt(Vector2d position) { - return animals.get(position); + public Optional objectAt(Vector2d position) { + return Optional.ofNullable(animals.get(position)); } @Override public boolean isOccupied(Vector2d position) { - return objectAt(position) != null; + return objectAt(position).isPresent(); } @Override @@ -105,4 +103,10 @@ public String toString() { public int getId() { return mapId; } + + @Override + public List getOrderedAnimals() { + return animals.values().stream().sorted(Comparator.comparing((Animal animal) -> animal.getPosition().getX() + ).thenComparing((Animal animal) -> animal.getPosition().getY())).toList(); + } } diff --git a/oolab/src/main/java/agh/ics/oop/model/Animal.java b/oolab/src/main/java/agh/ics/oop/model/Animal.java index 5ed3df1..fad9d86 100644 --- a/oolab/src/main/java/agh/ics/oop/model/Animal.java +++ b/oolab/src/main/java/agh/ics/oop/model/Animal.java @@ -71,4 +71,14 @@ public int hashCode() { public Vector2d getPosition() { return localizationOnMap; } + + @Override + public String getResourceString() { + return switch (facingDirection) { + case NORTH -> "up.png"; + case EAST -> "right.png"; + case SOUTH -> "down.png"; + case WEST -> "left.png"; + }; + } } diff --git a/oolab/src/main/java/agh/ics/oop/model/Grass.java b/oolab/src/main/java/agh/ics/oop/model/Grass.java index 30bbfa9..b286f73 100644 --- a/oolab/src/main/java/agh/ics/oop/model/Grass.java +++ b/oolab/src/main/java/agh/ics/oop/model/Grass.java @@ -11,6 +11,11 @@ public Vector2d getPosition() { return position; } + @Override + public String getResourceString() { + return "grass.png"; + } + @Override public String toString() { return "*"; diff --git a/oolab/src/main/java/agh/ics/oop/model/GrassField.java b/oolab/src/main/java/agh/ics/oop/model/GrassField.java index 17bdd9d..eb9d2f3 100644 --- a/oolab/src/main/java/agh/ics/oop/model/GrassField.java +++ b/oolab/src/main/java/agh/ics/oop/model/GrassField.java @@ -5,6 +5,8 @@ import java.util.HashMap; import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; public class GrassField extends AbstractWorldMap { private final HashMap grasses; @@ -17,20 +19,6 @@ public GrassField(int grassCount, int mapId) { var upperBound = (int) Math.sqrt(grassCount * 10); lowerVisualisationBoundary = new Vector2d(upperBound, upperBound); upperVisualisationBoundary = new Vector2d(0, 0); -//obsolete code - will be removed after pr 5, kept as fallback in case Random position generator is broken -// for (var i = 0; i < grassCount; ++i) { -// -// var possiblePosition = new Vector2d((int) (Math.random() * upperBound), (int) (Math.random() * upperBound)); -// -// while (grass.get(possiblePosition) != null) { -// possiblePosition = new Vector2d((int) (Math.random() * upperBound), (int) (Math.random() * upperBound)); -// } -// -// upperVisualisationBoundary = upperVisualisationBoundary.upperRight(possiblePosition); -// lowerVisualisationBoundary = lowerVisualisationBoundary.lowerLeft(possiblePosition); -// -// grass.put(possiblePosition, new Grass(possiblePosition)); -// } RandomPositionGenerator randomPositionGenerator = new RandomPositionGenerator(upperBound, upperBound, grassCount); for (Vector2d grassPosition : randomPositionGenerator) { grasses.put(grassPosition, new Grass(grassPosition)); @@ -43,22 +31,18 @@ public GrassField(int grassCount, int mapId) { @Override - public WorldElement objectAt(Vector2d position) { + public Optional objectAt(Vector2d position) { var animalAt = super.objectAt(position); - if (animalAt != null) { + if (animalAt.isPresent()) { return animalAt; } - return grasses.get(position); + return Optional.ofNullable(grasses.get(position)); } @Override public List getElements() { - var temporaryAnimals = super.getElements(); - - temporaryAnimals.addAll(grasses.values()); - - return temporaryAnimals; + return Stream.concat(super.getElements().stream(), grasses.values().stream()).toList(); } @Override @@ -72,12 +56,5 @@ public Boundary getCurrentBounds() { return new Boundary(drawingLowerBoundary, drawingUpperBoundary); } - //Methods used for testing - protected Vector2d getUpperVisualisationBoundary() { - return upperVisualisationBoundary; - } - protected Vector2d getLowerVisualisationBoundary() { - return lowerVisualisationBoundary; - } } diff --git a/oolab/src/main/java/agh/ics/oop/model/MapChangeListener.java b/oolab/src/main/java/agh/ics/oop/model/MapChangeListener.java index 5ad2ee2..90eda16 100644 --- a/oolab/src/main/java/agh/ics/oop/model/MapChangeListener.java +++ b/oolab/src/main/java/agh/ics/oop/model/MapChangeListener.java @@ -1,5 +1,6 @@ package agh.ics.oop.model; +@FunctionalInterface public interface MapChangeListener { void mapChanged(WorldMap worldMap, String message); } diff --git a/oolab/src/main/java/agh/ics/oop/model/WorldElement.java b/oolab/src/main/java/agh/ics/oop/model/WorldElement.java index 70d47a0..054d9ad 100644 --- a/oolab/src/main/java/agh/ics/oop/model/WorldElement.java +++ b/oolab/src/main/java/agh/ics/oop/model/WorldElement.java @@ -6,4 +6,6 @@ //assumption turns out to be false public interface WorldElement { public Vector2d getPosition(); + + public String getResourceString(); } diff --git a/oolab/src/main/java/agh/ics/oop/model/WorldMap.java b/oolab/src/main/java/agh/ics/oop/model/WorldMap.java index 93a4b2d..2896dea 100644 --- a/oolab/src/main/java/agh/ics/oop/model/WorldMap.java +++ b/oolab/src/main/java/agh/ics/oop/model/WorldMap.java @@ -1,54 +1,57 @@ -package agh.ics.oop.model; - -import agh.ics.oop.model.util.Boundary; -import agh.ics.oop.model.util.IncorrectPositionException; - -import java.util.List; - -/** - * The interface responsible for interacting with the map of the world. - * Assumes that Vector2d and MoveDirection classes are defined. - * - * @author apohllo, idzik - */ -public interface WorldMap extends agh.ics.oop.model.MoveValidator { - - /** - * Place a animal on the map. - * - * @param animal The animal to place on the map. - */ - void place(Animal animal) throws IncorrectPositionException; - - /** - * Moves an animal (if it is present on the map) according to specified direction. - * If the move is not possible, this method has no effect. - */ - void move(Animal animal, MoveDirection direction); - - /** - * Return true if given position on the map is occupied. Should not be - * confused with canMove since there might be empty positions where the animal - * cannot move. - * - * @param position Position to check. - * @return True if the position is occupied. - */ - boolean isOccupied(Vector2d position); - - /** - * Return an animal at a given position. - * - * @param position The position of the animal. - * @return animal or null if the position is not occupied. - */ - WorldElement objectAt(Vector2d position); - - boolean canMoveTo(Vector2d position); - - List getElements(); - - Boundary getCurrentBounds(); - - int getId(); -} +package agh.ics.oop.model; + +import agh.ics.oop.model.util.Boundary; +import agh.ics.oop.model.util.IncorrectPositionException; + +import java.util.List; +import java.util.Optional; + +/** + * The interface responsible for interacting with the map of the world. + * Assumes that Vector2d and MoveDirection classes are defined. + * + * @author apohllo, idzik + */ +public interface WorldMap extends agh.ics.oop.model.MoveValidator { + + /** + * Place a animal on the map. + * + * @param animal The animal to place on the map. + */ + void place(Animal animal) throws IncorrectPositionException; + + /** + * Moves an animal (if it is present on the map) according to specified direction. + * If the move is not possible, this method has no effect. + */ + void move(Animal animal, MoveDirection direction); + + /** + * Return true if given position on the map is occupied. Should not be + * confused with canMove since there might be empty positions where the animal + * cannot move. + * + * @param position Position to check. + * @return True if the position is occupied. + */ + boolean isOccupied(Vector2d position); + + /** + * Return an animal at a given position. + * + * @param position The position of the animal. + * @return animal or null if the position is not occupied. + */ + Optional objectAt(Vector2d position); + + boolean canMoveTo(Vector2d position); + + List getElements(); + + Boundary getCurrentBounds(); + + int getId(); + + List getOrderedAnimals(); +} diff --git a/oolab/src/main/java/agh/ics/oop/model/util/FileMapDisplay.java b/oolab/src/main/java/agh/ics/oop/model/util/FileMapDisplay.java new file mode 100644 index 0000000..dc8c6b3 --- /dev/null +++ b/oolab/src/main/java/agh/ics/oop/model/util/FileMapDisplay.java @@ -0,0 +1,27 @@ +package agh.ics.oop.model.util; + +import agh.ics.oop.model.MapChangeListener; +import agh.ics.oop.model.WorldMap; + +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; + +public class FileMapDisplay implements MapChangeListener { + + @Override + public void mapChanged(WorldMap worldMap, String message) { + File file = new File(String.format("map_%d.log", worldMap.getId())); + try (PrintWriter writer = new PrintWriter(new FileWriter(file, true))) { + writer.println("Move information:"); + writer.println(message); + + writer.println("\nCurrent map state:\n"); + writer.println(worldMap.toString()); + + writer.println("-----------------"); + } catch (Exception e) { + System.out.println(String.format("Failed to save state to file due to error: %s", e.getMessage())); + } + } +} diff --git a/oolab/src/main/java/agh/ics/oop/model/util/MapVisualizer.java b/oolab/src/main/java/agh/ics/oop/model/util/MapVisualizer.java index c10b2d7..911c68e 100644 --- a/oolab/src/main/java/agh/ics/oop/model/util/MapVisualizer.java +++ b/oolab/src/main/java/agh/ics/oop/model/util/MapVisualizer.java @@ -1,85 +1,88 @@ -package agh.ics.oop.model.util; - -import agh.ics.oop.model.Vector2d; -import agh.ics.oop.model.WorldMap; - -/** - * The map visualizer converts the {@link WorldMap} map into a string - * representation. - * - * @author apohllo, idzik - */ -public class MapVisualizer { - private static final String EMPTY_CELL = " "; - private static final String FRAME_SEGMENT = "-"; - private static final String CELL_SEGMENT = "|"; - private final WorldMap map; - - /** - * Initializes the MapVisualizer with an instance of map to visualize. - * - * @param map - */ - public MapVisualizer(WorldMap map) { - this.map = map; - } - - /** - * Convert selected region of the map into a string. It is assumed that the - * indices of the map will have no more than two characters (including the - * sign). - * - * @param lowerLeft The lower left corner of the region that is drawn. - * @param upperRight The upper right corner of the region that is drawn. - * @return String representation of the selected region of the map. - */ - public String draw(Vector2d lowerLeft, Vector2d upperRight) { - StringBuilder builder = new StringBuilder(); - for (int i = upperRight.getY() + 1; i >= lowerLeft.getY() - 1; i--) { - if (i == upperRight.getY() + 1) { - builder.append(drawHeader(lowerLeft, upperRight)); - } - builder.append(String.format("%3d: ", i)); - for (int j = lowerLeft.getX(); j <= upperRight.getX() + 1; j++) { - if (i < lowerLeft.getY() || i > upperRight.getY()) { - builder.append(drawFrame(j <= upperRight.getX())); - } else { - builder.append(CELL_SEGMENT); - if (j <= upperRight.getX()) { - builder.append(drawObject(new Vector2d(j, i))); - } - } - } - builder.append(System.lineSeparator()); - } - return builder.toString(); - } - - private String drawFrame(boolean innerSegment) { - if (innerSegment) { - return FRAME_SEGMENT + FRAME_SEGMENT; - } else { - return FRAME_SEGMENT; - } - } - - private String drawHeader(Vector2d lowerLeft, Vector2d upperRight) { - StringBuilder builder = new StringBuilder(); - builder.append(" y\\x "); - for (int j = lowerLeft.getX(); j < upperRight.getX() + 1; j++) { - builder.append(String.format("%2d", j)); - } - builder.append(System.lineSeparator()); - return builder.toString(); - } - - private String drawObject(Vector2d currentPosition) { - if (this.map.isOccupied(currentPosition)) { - Object object = this.map.objectAt(currentPosition); - if (object != null) { - return object.toString(); - } - } - return EMPTY_CELL; - } -} +package agh.ics.oop.model.util; + +import agh.ics.oop.model.Vector2d; +import agh.ics.oop.model.WorldElement; +import agh.ics.oop.model.WorldMap; + +import java.util.Optional; + +/** + * The map visualizer converts the {@link WorldMap} map into a string + * representation. + * + * @author apohllo, idzik + */ +public class MapVisualizer { + private static final String EMPTY_CELL = " "; + private static final String FRAME_SEGMENT = "-"; + private static final String CELL_SEGMENT = "|"; + private final WorldMap map; + + /** + * Initializes the MapVisualizer with an instance of map to visualize. + * + * @param map + */ + public MapVisualizer(WorldMap map) { + this.map = map; + } + + /** + * Convert selected region of the map into a string. It is assumed that the + * indices of the map will have no more than two characters (including the + * sign). + * + * @param lowerLeft The lower left corner of the region that is drawn. + * @param upperRight The upper right corner of the region that is drawn. + * @return String representation of the selected region of the map. + */ + public String draw(Vector2d lowerLeft, Vector2d upperRight) { + StringBuilder builder = new StringBuilder(); + for (int i = upperRight.getY() + 1; i >= lowerLeft.getY() - 1; i--) { + if (i == upperRight.getY() + 1) { + builder.append(drawHeader(lowerLeft, upperRight)); + } + builder.append(String.format("%3d: ", i)); + for (int j = lowerLeft.getX(); j <= upperRight.getX() + 1; j++) { + if (i < lowerLeft.getY() || i > upperRight.getY()) { + builder.append(drawFrame(j <= upperRight.getX())); + } else { + builder.append(CELL_SEGMENT); + if (j <= upperRight.getX()) { + builder.append(drawObject(new Vector2d(j, i))); + } + } + } + builder.append(System.lineSeparator()); + } + return builder.toString(); + } + + private String drawFrame(boolean innerSegment) { + if (innerSegment) { + return FRAME_SEGMENT + FRAME_SEGMENT; + } else { + return FRAME_SEGMENT; + } + } + + private String drawHeader(Vector2d lowerLeft, Vector2d upperRight) { + StringBuilder builder = new StringBuilder(); + builder.append(" y\\x "); + for (int j = lowerLeft.getX(); j < upperRight.getX() + 1; j++) { + builder.append(String.format("%2d", j)); + } + builder.append(System.lineSeparator()); + return builder.toString(); + } + + private String drawObject(Vector2d currentPosition) { + if (this.map.isOccupied(currentPosition)) { + Optional object = this.map.objectAt(currentPosition); + if (object.isPresent()) { + return object.get().toString(); + } + } + return EMPTY_CELL; + } +} diff --git a/oolab/src/main/java/agh/ics/oop/presenter/MainWindowPresenter.java b/oolab/src/main/java/agh/ics/oop/presenter/MainWindowPresenter.java index 18edd81..1318825 100644 --- a/oolab/src/main/java/agh/ics/oop/presenter/MainWindowPresenter.java +++ b/oolab/src/main/java/agh/ics/oop/presenter/MainWindowPresenter.java @@ -31,7 +31,7 @@ public class MainWindowPresenter { public void onSimulationStartClicked(ActionEvent actionEvent) throws IOException { - if (moveslisttextfield.getText().strip().length() > 0) { + if (!moveslisttextfield.getText().strip().isEmpty()) { try { List moves = OptionsParser.parseOptions(moveslisttextfield.getText().split(" ")); List positions = List.of(new Vector2d(1, 1)); diff --git a/oolab/src/main/java/agh/ics/oop/presenter/SimulationPresenter.java b/oolab/src/main/java/agh/ics/oop/presenter/SimulationPresenter.java index 3e88ea2..a714367 100644 --- a/oolab/src/main/java/agh/ics/oop/presenter/SimulationPresenter.java +++ b/oolab/src/main/java/agh/ics/oop/presenter/SimulationPresenter.java @@ -1,8 +1,6 @@ package agh.ics.oop.presenter; -import agh.ics.oop.model.AbstractWorldMap; -import agh.ics.oop.model.MapChangeListener; -import agh.ics.oop.model.WorldMap; +import agh.ics.oop.model.*; import javafx.application.Platform; import javafx.fxml.FXML; import javafx.geometry.HPos; @@ -95,12 +93,25 @@ private void setLabelsOy() { } private void addElementsToMap() { - for (var element : worldMap.getElements()) { - if (worldMap.isOccupied(element.getPosition())) { - var label = new Label(element.toString()); - var pos = element.getPosition(); - mapGrid.add(label, pos.getX() - minX + 1, maxY - pos.getY() + 1); - GridPane.setHalignment(label, HPos.CENTER); +// for (var element : worldMap.getElements()) { +// if (worldMap.isOccupied(element.getPosition())) { +// var label = new Label(element.toString()); +// var pos = element.getPosition(); +// mapGrid.add(label, pos.getX() - minX + 1, maxY - pos.getY() + 1); +// GridPane.setHalignment(label, HPos.CENTER); +// } +// } + for (int i = 0; i <= width; ++i) { + for (int j = 0; j <= height; ++j) { + Vector2d positionToCheck = new Vector2d(i + minX, j + minY); + if (worldMap.isOccupied(positionToCheck)) { + WorldElement element = worldMap.objectAt(positionToCheck).get(); + var label = new Label(element.toString()); + System.out.println(label); + //mapGrid.add(label, positionToCheck.getX() - minX + 1, maxY - positionToCheck.getY() + 1); + mapGrid.add(new WorldElementBox(element), i + 1, height - j + 1); + GridPane.setHalignment(label, HPos.CENTER); + } } } } diff --git a/oolab/src/main/java/agh/ics/oop/presenter/WorldElementBox.java b/oolab/src/main/java/agh/ics/oop/presenter/WorldElementBox.java new file mode 100644 index 0000000..c64431e --- /dev/null +++ b/oolab/src/main/java/agh/ics/oop/presenter/WorldElementBox.java @@ -0,0 +1,71 @@ +package agh.ics.oop.presenter; + +import agh.ics.oop.model.Animal; +import agh.ics.oop.model.WorldElement; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.VBox; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class WorldElementBox extends VBox { + private static final int IMAGE_HEIGHT = 20; + private static final int IMAGE_WIDTH = 20; + private static final Map images = new HashMap<>(); + + static { + String[] images = {"up.png", "right.png", "down.png", "left.png", "grass.png"}; + for (String url : images) { + WorldElementBox.images.put(url, new Image(url)); + } + } + + private ImageView lastImageView; + private String lastImageUrl; + private String lastLabel; + + public WorldElementBox(WorldElement element) { + updateImage(element); + updateLabel(element); + this.setAlignment(Pos.CENTER); + } + + private void updateImage(WorldElement element) { + String currImageUrl = element.getResourceString(); + + if (!Objects.equals(lastImageUrl, currImageUrl)) { + ImageView currImageView = new ImageView(images.get(currImageUrl)); + currImageView.setFitHeight(IMAGE_HEIGHT); + currImageView.setFitWidth(IMAGE_WIDTH); + + this.getChildren().remove(lastImageView); + this.getChildren().add(currImageView); + + lastImageUrl = currImageUrl; + lastImageView = currImageView; + } + } + + private void updateLabel(WorldElement element) { + + String currLabel = " "; + + if (element instanceof Animal) { + currLabel = "Z " + element.getPosition().toString(); + } else { + currLabel = "Trawa"; + } + + if (!Objects.equals(currLabel, lastLabel)) { + Label positionLabel = new Label(currLabel); + + this.getChildren().add(positionLabel); + + lastLabel = currLabel; + } + } +} diff --git a/oolab/src/main/resources/down.png b/oolab/src/main/resources/down.png new file mode 100644 index 0000000..4dc0160 Binary files /dev/null and b/oolab/src/main/resources/down.png differ diff --git a/oolab/src/main/resources/grass.png b/oolab/src/main/resources/grass.png new file mode 100644 index 0000000..cfb9782 Binary files /dev/null and b/oolab/src/main/resources/grass.png differ diff --git a/oolab/src/main/resources/left.png b/oolab/src/main/resources/left.png new file mode 100644 index 0000000..531a5e8 Binary files /dev/null and b/oolab/src/main/resources/left.png differ diff --git a/oolab/src/main/resources/right.png b/oolab/src/main/resources/right.png new file mode 100644 index 0000000..c263a77 Binary files /dev/null and b/oolab/src/main/resources/right.png differ diff --git a/oolab/src/main/resources/up.png b/oolab/src/main/resources/up.png new file mode 100644 index 0000000..3a51ea2 Binary files /dev/null and b/oolab/src/main/resources/up.png differ diff --git a/oolab/src/test/java/agh/ics/oop/model/GrassFieldTest.java b/oolab/src/test/java/agh/ics/oop/model/GrassFieldTest.java index af9510e..2286005 100644 --- a/oolab/src/test/java/agh/ics/oop/model/GrassFieldTest.java +++ b/oolab/src/test/java/agh/ics/oop/model/GrassFieldTest.java @@ -21,7 +21,7 @@ void animalIsPlacedOnValidCoordinates() { try { testMap.place(testAnimal); - Assertions.assertEquals(testAnimal, testMap.objectAt(properPosition)); + Assertions.assertEquals(testAnimal, testMap.objectAt(properPosition).get()); } catch (IncorrectPositionException ex) { Assertions.fail("Exception was thrown"); } @@ -58,7 +58,7 @@ void animalMovesIfPositionValid() { var expectedPosition = new Vector2d(1, 2); - Assertions.assertEquals(testAnimal, testMap.objectAt(expectedPosition)); + Assertions.assertEquals(testAnimal, testMap.objectAt(expectedPosition).get()); } catch (IncorrectPositionException ex) { Assertions.fail("Exception was thrown" + ex.getMessage()); } @@ -76,7 +76,7 @@ void animalWontMoveIfPositionInvalid() { testMap.place(testAnimal); testMap.place(blockingAnimal); testMap.move(testAnimal, MoveDirection.BACKWARD); - Assertions.assertEquals(testAnimal, testMap.objectAt(testPosition)); + Assertions.assertEquals(testAnimal, testMap.objectAt(testPosition).get()); } catch (IncorrectPositionException e) { Assertions.fail("Exception was thrown" + e.getMessage()); } @@ -118,7 +118,7 @@ void objectThatIsOnPositionIsReturned() { Assertions.fail("Exception was thrown" + e.getMessage()); } - Assertions.assertEquals(testAnimal, testMap.objectAt(occupiedPosition)); + Assertions.assertEquals(testAnimal, testMap.objectAt(occupiedPosition).get()); } @Test diff --git a/oolab/src/test/java/agh/ics/oop/model/RectangularMapTest.java b/oolab/src/test/java/agh/ics/oop/model/RectangularMapTest.java index 72a117b..8b00dba 100644 --- a/oolab/src/test/java/agh/ics/oop/model/RectangularMapTest.java +++ b/oolab/src/test/java/agh/ics/oop/model/RectangularMapTest.java @@ -4,6 +4,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.List; + public class RectangularMapTest { @Test @@ -14,7 +16,7 @@ void animalIsPlacedOnValidCoordinates() { try { testMap.place(testAnimal); - Assertions.assertEquals(testAnimal, testMap.objectAt(properPosition)); + Assertions.assertEquals(testAnimal, testMap.objectAt(properPosition).get()); } catch (IncorrectPositionException e) { Assertions.fail("Exception was thrown" + e.getMessage()); } @@ -66,7 +68,7 @@ void animalMovesIfPositionValid() { var expectedPosition = new Vector2d(1, 2); Assertions.assertFalse(testMap.isOccupied(testPosition)); - Assertions.assertEquals(testAnimal, testMap.objectAt(expectedPosition)); + Assertions.assertEquals(testAnimal, testMap.objectAt(expectedPosition).get()); } @Test @@ -83,7 +85,7 @@ void animalWontMoveIfPositionInvalid() { testMap.move(testAnimal, MoveDirection.BACKWARD); Assertions.assertTrue(testMap.isOccupied(testPosition)); - Assertions.assertEquals(testAnimal, testMap.objectAt(testPosition)); + Assertions.assertEquals(testAnimal, testMap.objectAt(testPosition).get()); } @Test @@ -129,7 +131,7 @@ void objectThatIsOnPositionIsReturned() { Assertions.fail("Exception was thrown" + e.getMessage()); } - Assertions.assertEquals(testAnimal, testMap.objectAt(occupiedPosition)); + Assertions.assertEquals(testAnimal, testMap.objectAt(occupiedPosition).get()); } @Test @@ -179,4 +181,33 @@ void rectangularMapReturnsProperAmountOfElements() { } } + @Test + void animalsAreSortedByX() { + RectangularMap testMap = new RectangularMap(10, 10, 0); + Animal animal1 = new Animal(new Vector2d(0, 1)); + Animal animal2 = new Animal(new Vector2d(1, 1)); + List expectedAnimals = List.of(animal1, animal2); + try { + testMap.place(animal1); + testMap.place(animal2); + Assertions.assertEquals(expectedAnimals, testMap.getOrderedAnimals()); + } catch (IncorrectPositionException e) { + Assertions.fail("Exception was thrown" + e.getMessage()); + } + } + + @Test + void animalsAreSortedByY() { + RectangularMap testMap = new RectangularMap(10, 10, 0); + Animal animal1 = new Animal(new Vector2d(1, 0)); + Animal animal2 = new Animal(new Vector2d(1, 1)); + List expectedAnimals = List.of(animal1, animal2); + try { + testMap.place(animal1); + testMap.place(animal2); + Assertions.assertEquals(expectedAnimals, testMap.getOrderedAnimals()); + } catch (IncorrectPositionException e) { + Assertions.fail("Exception was thrown" + e.getMessage()); + } + } } diff --git a/oolab_kotlin/.gitignore b/oolab_kotlin/.gitignore new file mode 100644 index 0000000..b1dff0d --- /dev/null +++ b/oolab_kotlin/.gitignore @@ -0,0 +1,45 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Kotlin ### +.kotlin + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/oolab_kotlin/.idea/.gitignore b/oolab_kotlin/.idea/.gitignore new file mode 100644 index 0000000..1c2fda5 --- /dev/null +++ b/oolab_kotlin/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/oolab_kotlin/.idea/codeStyles/Project.xml b/oolab_kotlin/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..1874b0f --- /dev/null +++ b/oolab_kotlin/.idea/codeStyles/Project.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/oolab_kotlin/.idea/codeStyles/codeStyleConfig.xml b/oolab_kotlin/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..307554b --- /dev/null +++ b/oolab_kotlin/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/oolab_kotlin/.idea/gradle.xml b/oolab_kotlin/.idea/gradle.xml new file mode 100644 index 0000000..5ac4c8f --- /dev/null +++ b/oolab_kotlin/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/oolab_kotlin/.idea/kotlinc.xml b/oolab_kotlin/.idea/kotlinc.xml new file mode 100644 index 0000000..0a4fba6 --- /dev/null +++ b/oolab_kotlin/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/oolab_kotlin/.idea/misc.xml b/oolab_kotlin/.idea/misc.xml new file mode 100644 index 0000000..5d2b310 --- /dev/null +++ b/oolab_kotlin/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/oolab_kotlin/.idea/vcs.xml b/oolab_kotlin/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/oolab_kotlin/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/oolab_kotlin/build.gradle b/oolab_kotlin/build.gradle new file mode 100644 index 0000000..b3a60ff --- /dev/null +++ b/oolab_kotlin/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' version '2.0.20' +} + +group = 'org.example' +version = '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.jetbrains.kotlin:kotlin-test' + testImplementation 'io.kotest:kotest-runner-junit5:5.5.4' + testImplementation 'io.kotest:kotest-assertions-core:5.5.4' +} + +test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(21) +} \ No newline at end of file diff --git a/oolab_kotlin/gradle.properties b/oolab_kotlin/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/oolab_kotlin/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/oolab_kotlin/gradle/wrapper/gradle-wrapper.jar b/oolab_kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/oolab_kotlin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/oolab_kotlin/gradle/wrapper/gradle-wrapper.properties b/oolab_kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..faa62f6 --- /dev/null +++ b/oolab_kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Jan 19 18:51:04 CET 2025 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/oolab_kotlin/gradlew b/oolab_kotlin/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/oolab_kotlin/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/oolab_kotlin/gradlew.bat b/oolab_kotlin/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/oolab_kotlin/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/oolab_kotlin/settings.gradle b/oolab_kotlin/settings.gradle new file mode 100644 index 0000000..56aa75b --- /dev/null +++ b/oolab_kotlin/settings.gradle @@ -0,0 +1,5 @@ +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' +} +rootProject.name = 'oolab_kotlin' + diff --git a/oolab_kotlin/src/main/kotlin/Animal.kt b/oolab_kotlin/src/main/kotlin/Animal.kt new file mode 100644 index 0000000..ec3a7f0 --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/Animal.kt @@ -0,0 +1,60 @@ +package org.example + +import java.util.* + +class Animal( var localizationOnMap: Vector2d = Vector2d(2, 2), val id:Int){ + var facingDirection: MapDirection = MapDirection.NORTH; + + fun move(direction: MoveDirection, validator: MoveValidator){ + when(direction) { + MoveDirection.LEFT -> facingDirection = facingDirection.previous(); + MoveDirection.RIGHT -> facingDirection = facingDirection.next(); + MoveDirection.FORWARD -> { + var newLoc = localizationOnMap + facingDirection.toUnitVector(); + if(validator.canMoveTo(newLoc)){ + localizationOnMap = newLoc; + } + } + MoveDirection.BACKWARD -> { + var newLoc = localizationOnMap - facingDirection.toUnitVector(); + if(validator.canMoveTo(newLoc)){ + localizationOnMap = newLoc; + } + } + } + } + + override fun toString(): String { + return return String.format( + when (facingDirection) { + MapDirection.NORTH -> "^" + MapDirection.EAST -> ">" + MapDirection.WEST -> "<" + MapDirection.SOUTH -> "v" + } + ) + } + + fun isAt(position: Vector2d): Boolean { + return localizationOnMap == position; + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Animal + + if (localizationOnMap != other.localizationOnMap) return false + if (facingDirection != other.facingDirection) return false + if (id != other.id) return false + + return true + } + + override fun hashCode(): Int { + return Objects.hash(localizationOnMap, facingDirection, id); + } + + +} \ No newline at end of file diff --git a/oolab_kotlin/src/main/kotlin/BouncyMap.kt b/oolab_kotlin/src/main/kotlin/BouncyMap.kt new file mode 100644 index 0000000..e35c998 --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/BouncyMap.kt @@ -0,0 +1,65 @@ +package org.example + +class BouncyMap(val width: Int, val height: Int): WorldMap{ + private val lowerLeft: Vector2d = Vector2d(0, 0); + private val upperRight: Vector2d = Vector2d(width-1, height-1); + val bounds: Boundary = Boundary(lowerLeft, upperRight); + private var animals: HashMap = HashMap(); + override fun place(animal: Animal): Boolean { + if(animals.containsValue(animal)){ + for (pos in animals.keys){ + if (animals[pos] == animal){ + animals.remove(pos) + break + } + } + } + val animalProposedLocalisation = animal.localizationOnMap + if(!isOccupied(animalProposedLocalisation)){ + animals[animalProposedLocalisation] = animal; + } else{ + var randomPos: Vector2d? = animals.randomFreePosition(upperRight); + if(randomPos != null) { + animal.localizationOnMap = randomPos; + animals[randomPos] = animal; + } else{ + randomPos = animals.randomPosition(); + if(randomPos != null){ + animals.remove(randomPos); + animal.localizationOnMap = randomPos; + animals[randomPos] = animal + } else { + return false; + } + } + } + return true; + } + + + override fun move(animal: Animal, direction: MoveDirection) { + val oldPosition = animal.localizationOnMap; + animal.move(direction, this) + + animals.remove(oldPosition); + place(animal); + + } + + override fun isOccupied(position: Vector2d): Boolean { + return objectAt(position) != null; + } + + override fun objectAt(position: Vector2d): Animal? { + if (animals.containsKey(position)) { + return animals[position]; + } else { + return null; + } + } + + + override fun canMoveTo(position: Vector2d): Boolean { + return position.follows(lowerLeft) && position.precedes(upperRight); + } +} \ No newline at end of file diff --git a/oolab_kotlin/src/main/kotlin/Boundary.kt b/oolab_kotlin/src/main/kotlin/Boundary.kt new file mode 100644 index 0000000..b83eca4 --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/Boundary.kt @@ -0,0 +1,4 @@ +package org.example + +data class Boundary(val lowerLeft: Vector2d, val upperRight: Vector2d) { +} \ No newline at end of file diff --git a/oolab_kotlin/src/main/kotlin/Main.kt b/oolab_kotlin/src/main/kotlin/Main.kt new file mode 100644 index 0000000..a47efa7 --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/Main.kt @@ -0,0 +1,13 @@ +package org.example + +//TIP To Run code, press or +// click the icon in the gutter. +fun main() { + var animal: Animal = Animal(Vector2d(1,1), 0); + var animal2: Animal = Animal(Vector2d(1,1), 1); + var bouncyMap: BouncyMap = BouncyMap(10, 10) + + println(bouncyMap.place(animal)) + println(bouncyMap.place(animal2)) + println(animal2.localizationOnMap) +} \ No newline at end of file diff --git a/oolab_kotlin/src/main/kotlin/MapDirection.kt b/oolab_kotlin/src/main/kotlin/MapDirection.kt new file mode 100644 index 0000000..8773ec6 --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/MapDirection.kt @@ -0,0 +1,27 @@ +package org.example + +enum class MapDirection() { + NORTH, + EAST, + SOUTH, + WEST, ; + + override fun toString(): String { + return when(this) { + NORTH -> "Północ"; + EAST -> "Wschód"; + SOUTH -> "Południe"; + WEST -> "Zachód"; + } + } + fun next(): MapDirection { + return entries[this.ordinal + 1% entries.size]; + } + + fun previous(): MapDirection { + return entries[(this.ordinal - 1 + entries.size)% entries.size]; + } + + + +} \ No newline at end of file diff --git a/oolab_kotlin/src/main/kotlin/MoveDirection.kt b/oolab_kotlin/src/main/kotlin/MoveDirection.kt new file mode 100644 index 0000000..8f7a8ec --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/MoveDirection.kt @@ -0,0 +1,9 @@ +package org.example + +enum class MoveDirection { + FORWARD, + BACKWARD, + LEFT, + RIGHT, + +} \ No newline at end of file diff --git a/oolab_kotlin/src/main/kotlin/MoveValidator.kt b/oolab_kotlin/src/main/kotlin/MoveValidator.kt new file mode 100644 index 0000000..ddca5f7 --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/MoveValidator.kt @@ -0,0 +1,5 @@ +package org.example + +interface MoveValidator { + fun canMoveTo(position: Vector2d): Boolean; +} \ No newline at end of file diff --git a/oolab_kotlin/src/main/kotlin/RandomExtensions.kt b/oolab_kotlin/src/main/kotlin/RandomExtensions.kt new file mode 100644 index 0000000..ecce4cf --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/RandomExtensions.kt @@ -0,0 +1,19 @@ +package org.example + +import com.sun.jdi.IntegerType + +fun Map.randomPosition(): Vector2d?{ + return this.keys.randomOrNull(); +} + +fun Map.randomFreePosition(mapSize: Vector2d): Vector2d?{ + //var possiblyFreePosition = Vector2d((0..= other.x && y >= other.y; + } + + operator fun plus(other: Vector2d): Vector2d{ + return Vector2d(x + other.x, y + other.y); + } + operator fun minus(other: Vector2d): Vector2d{ + return Vector2d(x - other.x, y - other.y); + } + fun upperRight(other: Vector2d): Vector2d{ + return Vector2d(max(x, other.x), max(y, other.y)); + } + fun lowerLeft(other: Vector2d): Vector2d{ + return Vector2d(min(x, other.x), min(y, other.y)); + } + fun opposite(): Vector2d{ + return Vector2d(-1*x, -1*y); + } +} + +fun MapDirection.toUnitVector(): Vector2d { + return when (this) { + MapDirection.NORTH -> Vector2d(0, 1) + MapDirection.WEST -> Vector2d(-1, 0) + MapDirection.SOUTH -> Vector2d(0, -1) + MapDirection.EAST -> Vector2d(1, 0) + } +} \ No newline at end of file diff --git a/oolab_kotlin/src/main/kotlin/WorldMap.kt b/oolab_kotlin/src/main/kotlin/WorldMap.kt new file mode 100644 index 0000000..fab6c10 --- /dev/null +++ b/oolab_kotlin/src/main/kotlin/WorldMap.kt @@ -0,0 +1,13 @@ +package org.example + +import jdk.internal.foreign.abi.Binding + +interface WorldMap : MoveValidator { + fun place(animal: Animal): Boolean; + + fun move(animal: Animal, direction: MoveDirection); + + fun isOccupied(position: Vector2d) : Boolean; + + fun objectAt(position: Vector2d): Animal?; +} \ No newline at end of file diff --git a/oolab_kotlin/src/test/kotlin/BouncyMapTest.kt b/oolab_kotlin/src/test/kotlin/BouncyMapTest.kt new file mode 100644 index 0000000..66081b9 --- /dev/null +++ b/oolab_kotlin/src/test/kotlin/BouncyMapTest.kt @@ -0,0 +1,54 @@ +import io.kotest.core.spec.style.ShouldSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNot +import io.kotest.matchers.shouldNotBe +import org.example.Animal +import org.example.BouncyMap +import org.example.MoveDirection +import org.example.Vector2d + +class BouncyMapTest : ShouldSpec({ + should("place animal in empty map"){ + val bouncyMap = BouncyMap(height = 5, width = 5) + val animal = Animal(Vector2d(0, 0), 0) + + bouncyMap.place(animal) shouldBe true; + } + + should("be able to move to valid position"){ + val bouncyMap = BouncyMap(height = 5, width = 5) + val validPosition = Vector2d(1,1) + + bouncyMap.canMoveTo(validPosition) shouldBe true; + } + + should("not be able to move to invalid position"){ + val bouncyMap = BouncyMap(height = 5, width = 5) + val invalidPosition = Vector2d(7,7) + + bouncyMap.canMoveTo(invalidPosition) shouldBe false; + } + + should("not place animal on top of other"){ + val bouncyMap = BouncyMap(height = 5, width = 5) + val animal1 = Animal(Vector2d(1,1), 0) + val animal2 = Animal(Vector2d(1,1), 1) + + bouncyMap.place(animal1) shouldBe true; + bouncyMap.place(animal2) shouldBe true; + animal1.localizationOnMap shouldBe Vector2d(1,1); + animal2.localizationOnMap shouldNotBe Vector2d(1,1); + } + + should("update animal position if it is in the map"){ + var animal = Animal(Vector2d(1,1), 0) + val bouncyMap = BouncyMap(height = 5, width = 5) + bouncyMap.place(animal) shouldBe true; + animal.move(MoveDirection.FORWARD, bouncyMap) + bouncyMap.place(animal) shouldBe true; + animal.localizationOnMap shouldBe Vector2d(1,2); + bouncyMap.objectAt(Vector2d(1,1)) shouldBe null; + + } + +}) \ No newline at end of file