From 11f57db3c404c37d5ab3e0f9665b31e3049994f7 Mon Sep 17 00:00:00 2001 From: Szymon Rybski Date: Sun, 15 Dec 2024 18:20:13 +0100 Subject: [PATCH 1/3] finished part 1 --- .../main/java/agh/ics/oop/OptionsParser.java | 32 +++++++++++---- oolab/src/main/java/agh/ics/oop/World.java | 4 ++ .../agh/ics/oop/model/AbstractWorldMap.java | 16 +++++--- .../java/agh/ics/oop/model/GrassField.java | 35 +++-------------- .../agh/ics/oop/model/MapChangeListener.java | 1 + .../main/java/agh/ics/oop/model/WorldMap.java | 5 ++- .../agh/ics/oop/model/util/MapVisualizer.java | 9 +++-- .../agh/ics/oop/model/GrassFieldTest.java | 8 ++-- .../agh/ics/oop/model/RectangularMapTest.java | 39 +++++++++++++++++-- 9 files changed, 95 insertions(+), 54 deletions(-) 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..668bf52 100644 --- a/oolab/src/main/java/agh/ics/oop/World.java +++ b/oolab/src/main/java/agh/ics/oop/World.java @@ -6,6 +6,7 @@ import agh.ics.oop.model.Vector2d; import agh.ics.oop.model.util.ConsoleMapDisplay; +import java.time.LocalDateTime; import java.util.List; public class World { @@ -17,6 +18,9 @@ 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)); + }); 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/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/WorldMap.java b/oolab/src/main/java/agh/ics/oop/model/WorldMap.java index 93a4b2d..a464e92 100644 --- a/oolab/src/main/java/agh/ics/oop/model/WorldMap.java +++ b/oolab/src/main/java/agh/ics/oop/model/WorldMap.java @@ -4,6 +4,7 @@ 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. @@ -42,7 +43,7 @@ public interface WorldMap extends agh.ics.oop.model.MoveValidator { * @param position The position of the animal. * @return animal or null if the position is not occupied. */ - WorldElement objectAt(Vector2d position); + Optional objectAt(Vector2d position); boolean canMoveTo(Vector2d position); @@ -51,4 +52,6 @@ public interface WorldMap extends agh.ics.oop.model.MoveValidator { Boundary getCurrentBounds(); int getId(); + + List getOrderedAnimals(); } 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..ed17510 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,8 +1,11 @@ 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. @@ -75,9 +78,9 @@ private String drawHeader(Vector2d lowerLeft, Vector2d upperRight) { private String drawObject(Vector2d currentPosition) { if (this.map.isOccupied(currentPosition)) { - Object object = this.map.objectAt(currentPosition); - if (object != null) { - return object.toString(); + Optional object = this.map.objectAt(currentPosition); + if (object.isPresent()) { + return object.get().toString(); } } return EMPTY_CELL; 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()); + } + } } From cb3b90dfc775d457b5aaf7bd271a4fe3a761dea1 Mon Sep 17 00:00:00 2001 From: Szymon Rybski Date: Sun, 15 Dec 2024 22:48:51 +0100 Subject: [PATCH 2/3] part 2 finished - improvements required --- oolab/map_0.log | 228 ++++++++++++++++++ oolab/src/main/java/agh/ics/oop/World.java | 3 + .../main/java/agh/ics/oop/model/Animal.java | 10 + .../main/java/agh/ics/oop/model/Grass.java | 5 + .../java/agh/ics/oop/model/WorldElement.java | 2 + .../ics/oop/model/util/FileMapDisplay.java | 27 +++ .../oop/presenter/MainWindowPresenter.java | 2 +- .../oop/presenter/SimulationPresenter.java | 29 ++- .../ics/oop/presenter/WorldElementBox.java | 71 ++++++ oolab/src/main/resources/down.png | Bin 0 -> 7891 bytes oolab/src/main/resources/grass.png | Bin 0 -> 8469 bytes oolab/src/main/resources/left.png | Bin 0 -> 8190 bytes oolab/src/main/resources/right.png | Bin 0 -> 8123 bytes oolab/src/main/resources/up.png | Bin 0 -> 7760 bytes 14 files changed, 367 insertions(+), 10 deletions(-) create mode 100644 oolab/map_0.log create mode 100644 oolab/src/main/java/agh/ics/oop/model/util/FileMapDisplay.java create mode 100644 oolab/src/main/java/agh/ics/oop/presenter/WorldElementBox.java create mode 100644 oolab/src/main/resources/down.png create mode 100644 oolab/src/main/resources/grass.png create mode 100644 oolab/src/main/resources/left.png create mode 100644 oolab/src/main/resources/right.png create mode 100644 oolab/src/main/resources/up.png 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/World.java b/oolab/src/main/java/agh/ics/oop/World.java index 668bf52..70db483 100644 --- a/oolab/src/main/java/agh/ics/oop/World.java +++ b/oolab/src/main/java/agh/ics/oop/World.java @@ -5,6 +5,7 @@ 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; @@ -21,6 +22,8 @@ public static void main(String[] args) { 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/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/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/util/FileMapDisplay.java b/oolab/src/main/java/agh/ics/oop/model/util/FileMapDisplay.java new file mode 100644 index 0000000..11509d6 --- /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) { + e.printStackTrace(); + } + } +} 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 0000000000000000000000000000000000000000..4dc01601cd4326e77c5f5a1421d5dbd9e918d957 GIT binary patch literal 7891 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 W+IXEef*gC0o5tra8NAkajr(7WbDN3) literal 0 HcmV?d00001 diff --git a/oolab/src/main/resources/grass.png b/oolab/src/main/resources/grass.png new file mode 100644 index 0000000000000000000000000000000000000000..cfb978229244f995f9b496968aae9749dc7b8c26 GIT binary patch literal 8469 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 Q49O4;Rc)1`OICOP2YG4gW&i*H literal 0 HcmV?d00001 diff --git a/oolab/src/main/resources/right.png b/oolab/src/main/resources/right.png new file mode 100644 index 0000000000000000000000000000000000000000..c263a7722f3036e8cddb45c1535daffddf9c2702 GIT binary patch literal 8123 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 Date: Mon, 6 Jan 2025 16:45:40 +0100 Subject: [PATCH 3/3] minor change in error handling for file map display --- oolab/src/main/java/agh/ics/oop/model/util/FileMapDisplay.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 11509d6..dc8c6b3 100644 --- a/oolab/src/main/java/agh/ics/oop/model/util/FileMapDisplay.java +++ b/oolab/src/main/java/agh/ics/oop/model/util/FileMapDisplay.java @@ -21,7 +21,7 @@ public void mapChanged(WorldMap worldMap, String message) { writer.println("-----------------"); } catch (Exception e) { - e.printStackTrace(); + System.out.println(String.format("Failed to save state to file due to error: %s", e.getMessage())); } } }