From 29ae17d535fb3568cf4bb616bf882fb7e4dc80bc Mon Sep 17 00:00:00 2001 From: Kristian Knarvik Date: Wed, 7 Mar 2018 21:40:06 +0100 Subject: [PATCH] Getneightbourhood without edge detection. --- .gitignore | 3 +- flowchart.txt | 2 + src/inf101/v18/grid/MyGrid.java | 6 +- src/inf101/v18/rogue101/examples/Rabbit.java | 7 +- src/inf101/v18/rogue101/game/Game.java | 43 +++--- src/inf101/v18/rogue101/items/Manga.java | 8 +- src/inf101/v18/rogue101/map/GameMap.java | 67 ++++++-- src/inf101/v18/rogue101/map/maps/level1.txt | 2 +- src/inf101/v18/rogue101/objects/Player.java | 143 +++++++++++++++++- .../v18/rogue101/tests/GameMapTest.java | 39 ++++- src/inf101/v18/rogue101/tests/PlayerTest.java | 124 ++++++++++++--- 11 files changed, 365 insertions(+), 79 deletions(-) diff --git a/.gitignore b/.gitignore index 3d7c373..4c558dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /bin/ .DS_Store -*.xml \ No newline at end of file +*.xml +*.iml \ No newline at end of file diff --git a/flowchart.txt b/flowchart.txt index 8591ba7..905aaf1 100644 --- a/flowchart.txt +++ b/flowchart.txt @@ -4,5 +4,7 @@ IPlayer, INonPlayer -> IActor -> IItem -> Comparable GameEvent -> Event +Game -> Mapreader.readFile() +Game -> createItem() Game - Rabbit.doTurn(Game) - Game.move() Game.doTurn() - Game.beginTurn() \ No newline at end of file diff --git a/src/inf101/v18/grid/MyGrid.java b/src/inf101/v18/grid/MyGrid.java index aa1540e..afa9a9c 100644 --- a/src/inf101/v18/grid/MyGrid.java +++ b/src/inf101/v18/grid/MyGrid.java @@ -23,8 +23,7 @@ public class MyGrid implements IGrid { * new MyGrid(10, 10, ((x, y) -> String.format("(%d,%d)", x, y)); * * - * @param width - * @param height + * @param area * @param initialiser * The initialiser function */ @@ -43,8 +42,7 @@ public class MyGrid implements IGrid { /** * Construct a grid with the given dimensions. * - * @param width - * @param height + * @param area * @param initElement * What the cells should initially hold (possibly null) */ diff --git a/src/inf101/v18/rogue101/examples/Rabbit.java b/src/inf101/v18/rogue101/examples/Rabbit.java index a4392f5..f4ce4f6 100644 --- a/src/inf101/v18/rogue101/examples/Rabbit.java +++ b/src/inf101/v18/rogue101/examples/Rabbit.java @@ -28,7 +28,7 @@ public class Rabbit implements INonPlayer { for (IItem item : game.getLocalItems()) { if (item instanceof Carrot) { System.out.println("found carrot!"); - int eaten = item.handleDamage(game, this, 5); + int eaten = item.handleDamage(game, this, getDamage()); if (eaten > 0) { System.out.println("ate carrot worth " + eaten + "!"); food += eaten; @@ -63,7 +63,7 @@ public class Rabbit implements INonPlayer { @Override public int getAttack() { - return 1000; + return 10; } @Override @@ -73,7 +73,7 @@ public class Rabbit implements INonPlayer { @Override public int getDamage() { - return 1000; + return 5; } @Override @@ -106,5 +106,4 @@ public class Rabbit implements INonPlayer { hp -= amount; return amount; } - } diff --git a/src/inf101/v18/rogue101/game/Game.java b/src/inf101/v18/rogue101/game/Game.java index 682c9f6..4c3c77a 100644 --- a/src/inf101/v18/rogue101/game/Game.java +++ b/src/inf101/v18/rogue101/game/Game.java @@ -18,17 +18,13 @@ import inf101.v18.grid.IGrid; import inf101.v18.grid.ILocation; import inf101.v18.rogue101.Main; import inf101.v18.rogue101.examples.Carrot; +import inf101.v18.rogue101.items.Manga; import inf101.v18.rogue101.examples.Rabbit; import inf101.v18.rogue101.map.GameMap; import inf101.v18.rogue101.map.IGameMap; import inf101.v18.rogue101.map.IMapView; import inf101.v18.rogue101.map.MapReader; -import inf101.v18.rogue101.objects.Dust; -import inf101.v18.rogue101.objects.IActor; -import inf101.v18.rogue101.objects.IItem; -import inf101.v18.rogue101.objects.INonPlayer; -import inf101.v18.rogue101.objects.IPlayer; -import inf101.v18.rogue101.objects.Wall; +import inf101.v18.rogue101.objects.*; import javafx.scene.canvas.GraphicsContext; import javafx.scene.input.KeyCode; import javafx.scene.paint.Color; @@ -63,9 +59,7 @@ public class Game implements IGame { this.painter = painter; this.printer = printer; - // TODO: (*very* optional) for advanced factory technique, use - // something like "itemFactories.put("R", () -> new Rabbit());" - // must be done *before* you read the map + addFactory(); // NOTE: in a more realistic situation, we will have multiple levels (one map // per level), and (at least for a Roguelike game) the levels should be @@ -90,8 +84,13 @@ public class Game implements IGame { } public Game(String mapString) { - printer = new Printer(1280, 720); - painter = new TurtlePainter(1280, 720, null); + //printer = new Printer(1280, 720); + //painter = new TurtlePainter(1280, 720, null); + printer = null; + painter = null; + + addFactory(); + IGrid inputGrid = MapReader.readString(mapString); this.map = new GameMap(inputGrid.getArea()); for (ILocation loc : inputGrid.locations()) { @@ -149,7 +148,7 @@ public class Game implements IGame { } if (random.nextInt(100) < 20) { - ILocation loc = map.getLocation((int)(Math.random() * map.getWidth()), (int)(Math.random() * map.getHeight())); + ILocation loc = map.getLocation(random.nextInt(map.getWidth()), random.nextInt(map.getHeight())); if (!map.hasActors(loc) && !map.hasItems(loc) && !map.hasWall(loc)) { map.add(loc, new Carrot()); } @@ -250,20 +249,18 @@ public class Game implements IGame { return map.canGo(currentLocation, dir); } + private void addFactory() { + itemFactories.put("#", Wall::new); + itemFactories.put("@", Player::new); + itemFactories.put("C", Carrot::new); + itemFactories.put("R", Rabbit::new); + itemFactories.put("M", Manga::new); + itemFactories.put(".", Dust::new); + } + @Override public IItem createItem(String sym) { switch (sym) { - case "#": - return new Wall(); - case ".": - // TODO: add Dust - return null; - case "R": - return new Rabbit(); - case "C": - return new Carrot(); - case "@": - // TODO: add Player case " ": return null; default: diff --git a/src/inf101/v18/rogue101/items/Manga.java b/src/inf101/v18/rogue101/items/Manga.java index fbe1580..16a5870 100644 --- a/src/inf101/v18/rogue101/items/Manga.java +++ b/src/inf101/v18/rogue101/items/Manga.java @@ -1,4 +1,4 @@ -package inf101.v18.rogue101.examples; +package inf101.v18.rogue101.items; import inf101.v18.gfx.gfxmode.ITurtle; import inf101.v18.rogue101.game.IGame; @@ -8,7 +8,7 @@ import javafx.scene.paint.Color; public class Manga implements IItem { int hp = getMaxHealth(); - @Override + /*@Override public boolean draw(ITurtle painter, double w, double h) { painter.save(); painter.jump(-10); @@ -22,7 +22,7 @@ public class Manga implements IItem { painter.draw(getSize()/2); painter.restore(); return true; - } + }*/ @Override public int getCurrentHealth() { @@ -46,7 +46,7 @@ public class Manga implements IItem { @Override public int getSize() { - return 20; + return 5; } @Override diff --git a/src/inf101/v18/rogue101/map/GameMap.java b/src/inf101/v18/rogue101/map/GameMap.java index eb74745..6b91b00 100644 --- a/src/inf101/v18/rogue101/map/GameMap.java +++ b/src/inf101/v18/rogue101/map/GameMap.java @@ -1,13 +1,8 @@ package inf101.v18.rogue101.map; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; +import com.sun.org.apache.bcel.internal.generic.ILOAD; import inf101.v18.gfx.gfxmode.ITurtle; import inf101.v18.gfx.textmode.Printer; import inf101.v18.grid.GridDirection; @@ -16,7 +11,9 @@ import inf101.v18.grid.ILocation; import inf101.v18.grid.IMultiGrid; import inf101.v18.grid.MultiGrid; import inf101.v18.rogue101.Main; +import inf101.v18.rogue101.examples.Carrot; import inf101.v18.rogue101.game.IllegalMoveException; +import inf101.v18.rogue101.items.Manga; import inf101.v18.rogue101.objects.IActor; import inf101.v18.rogue101.objects.IItem; import inf101.v18.rogue101.objects.Wall; @@ -48,6 +45,7 @@ public class GameMap implements IGameMap { @Override public void add(ILocation loc, IItem item) { + // keep track of location of all items items.put(item, loc); // also keep track of whether we need to redraw this cell @@ -55,8 +53,26 @@ public class GameMap implements IGameMap { // do the actual adding List list = grid.get(loc); + + for (int i = 0; i < list.size(); i++) { + if (item.compareTo(list.get(i)) >= 0) { + list.add(i, item); + return; + } + } list.add(item); - // TODO: should be sorted! + } + + public void addRandomItems(ILocation loc) { + if (!this.hasActors(loc) && !this.hasWall(loc)) { + Random random = new Random(); + if (random.nextInt(100) < 20) { + this.add(loc, new Carrot()); + } + if (random.nextInt(100) < 1) { + this.add(loc, new Manga()); + } + } } @Override @@ -243,12 +259,39 @@ public class GameMap implements IGameMap { @Override public List getNeighbourhood(ILocation loc, int dist) { - if (dist < 0 || loc == null) + if (dist < 0 || loc == null) { throw new IllegalArgumentException(); - else if (dist == 0) + } else if (dist == 0){ return new ArrayList<>(); // empty! + } - // TODO: implement this! - throw new UnsupportedOperationException(); + //Goes in wider and wider squares, until we have reached dist. + List locations = new ArrayList<>(); + for (int i = 1; i <= dist; i++) { + ILocation current = loc; + for (int j = 0; j < i; j++) { + current = current.go(GridDirection.WEST); + current = current.go(GridDirection.NORTH); + } + for (int j = 0; j < i * 2 + 1; j++) { + locations.add(current); + if (j < i * 2) { + current = current.go(GridDirection.EAST); + } + } + for (int j = 0; j < i * 2; j++) { + current = current.go(GridDirection.SOUTH); + locations.add(current); + } + for (int j = 0; j < i * 2; j++) { + current = current.go(GridDirection.WEST); + locations.add(current); + } + for (int j = 0; j < i * 2 - 1; j++) { + current = current.go(GridDirection.NORTH); + locations.add(current); + } + } + return locations; } } diff --git a/src/inf101/v18/rogue101/map/maps/level1.txt b/src/inf101/v18/rogue101/map/maps/level1.txt index 2d23518..4f96b34 100644 --- a/src/inf101/v18/rogue101/map/maps/level1.txt +++ b/src/inf101/v18/rogue101/map/maps/level1.txt @@ -9,7 +9,7 @@ #. ...R..C. ..R.R..........C.RC....... # #..C.....R..... ........RR R..R.....R..# #...R..R.R..............R .R..R........# -#.R.....R........RRR.......R.. .C....R.# +#.R.....R...M....RRR.......R.. .C....R.# #.C.. ..R. .....R.RC..C....R...R..C. .# #. R..............R R..R........C.....R# #........############################### diff --git a/src/inf101/v18/rogue101/objects/Player.java b/src/inf101/v18/rogue101/objects/Player.java index 4b64044..2e47ffa 100644 --- a/src/inf101/v18/rogue101/objects/Player.java +++ b/src/inf101/v18/rogue101/objects/Player.java @@ -1,4 +1,143 @@ -package inf101.v18.rogue101.items; +package inf101.v18.rogue101.objects; -public class Player { +import inf101.v18.grid.GridDirection; +import inf101.v18.rogue101.game.IGame; +import javafx.scene.input.KeyCode; +import java.util.ArrayList; +import java.util.List; + +public class Player implements IPlayer { + private int hp = getMaxHealth(); + private List equipped = new ArrayList<>(); + + @Override + public void keyPressed(IGame game, KeyCode key) { + if (key == KeyCode.LEFT || key == KeyCode.A) { + tryToMove(game, GridDirection.WEST); + } else if (key == KeyCode.RIGHT || key == KeyCode.D) { + tryToMove(game, GridDirection.EAST); + } else if (key == KeyCode.UP || key == KeyCode.W) { + tryToMove(game, GridDirection.NORTH); + } else if (key == KeyCode.DOWN || key == KeyCode.S) { + tryToMove(game, GridDirection.SOUTH); + } else if (key == KeyCode.E) { + pickUp(game); + } else if (key == KeyCode.Q) { + drop(game); + } + showStatus(game); + } + + private void pickUp(IGame game) { + //TODO: If there are several items on a tile, let player choose what to pick up. + if (equipped.size() < 5) { + List items = game.getLocalItems(); + for (IItem item : items) { + IItem pickedUp = game.pickUp(item); + if (pickedUp != null) { + equipped.add(pickedUp); + game.displayMessage("Picked up " + pickedUp.getName()); + return; + } + } + game.displayMessage("There is nothing to pick up."); + } else { + game.displayMessage("Your inventory is full."); + } + } + + private void pickUpV2(IGame game) { + List items = game.getLocalItems(); + if (items.size() < 1) { + game.displayMessage("There is nothing to pick up."); + return; + } + StringBuilder msg = new StringBuilder("Items on this tile:"); + for (int i = 0; i < items.size(); i++) { + msg.append(" [").append(i).append("] ").append(items.get(i).getName()); + } + game.displayMessage(msg.toString()); + if (equipped.size() < 5) { + //TODO: Let user choose 1-5. + } else { + game.displayMessage("Your inventory is full."); + } + } + + private void drop(IGame game) { + //TODO: Find a way to implement V2. + if (!equipped.isEmpty()) { + if (game.drop(equipped.get(0))) { + equipped.remove(0); + game.displayMessage("Item dropped."); + } else { + game.displayMessage("The ground rejected the item."); + } + } else { + game.displayMessage("You have nothing to drop."); + } + } + + private void showStatus(IGame game) { + List items = new ArrayList<>(); + for (IItem item : equipped) { + String name = item.getName(); + items.add(Character.toUpperCase(name.charAt(0)) + name.substring(1)); + } + game.formatStatus("HP: %d/%d ATK: %d DEF: %s INV: %s", hp, getMaxHealth(), getAttack(), getDefence(), String.join(",", items)); + } + + private void tryToMove(IGame game, GridDirection dir) { + if (game.canGo(dir)) { + game.move(dir); + } else { + game.displayMessage("Umm, it is not possible to move in that direction."); + } + } + + @Override + public int getAttack() { + return 10; + } + + @Override + public int getDamage() { + return 10; + } + + @Override + public int getCurrentHealth() { + return hp; + } + + @Override + public int getDefence() { + return 1; + } + + @Override + public int getMaxHealth() { + return 100; + } + + @Override + public String getName() { + return "Person"; + } + + @Override + public int getSize() { + return 10; + } + + @Override + public String getSymbol() { + return "X"; + } + + @Override + public int handleDamage(IGame game, IItem source, int amount) { + hp -= amount; + return amount; + } } diff --git a/src/inf101/v18/rogue101/tests/GameMapTest.java b/src/inf101/v18/rogue101/tests/GameMapTest.java index d4a6bef..a3c2017 100644 --- a/src/inf101/v18/rogue101/tests/GameMapTest.java +++ b/src/inf101/v18/rogue101/tests/GameMapTest.java @@ -2,19 +2,50 @@ package inf101.v18.rogue101.tests; import static org.junit.Assert.*; +import inf101.v18.rogue101.objects.IItem; import org.junit.Test; import inf101.v18.grid.ILocation; import inf101.v18.rogue101.map.GameMap; -class GameMapTest { +import java.util.List; + +public class GameMapTest { @Test - void testSortedAdd() { + public void testSortedAdd() { GameMap gameMap = new GameMap(20, 20); ILocation location = gameMap.getLocation(10, 10); - // TODO: - fail("Not yet implemented"); + for (int i = 0; i < 30; i++) { + gameMap.addRandomItems(location); + } + List items = gameMap.getAll(location); + for (int i = 0; i < items.size() - 1; i++) { + assertTrue(items.get(i).compareTo(items.get(i + 1)) >= 0); + } } + @Test + public void testGetNeighbours() { + GameMap gameMap = new GameMap(20, 20); + ILocation location = gameMap.getLocation(0, 0); + + List neighbours = gameMap.getNeighbourhood(location, 1); + for (ILocation neighbour : neighbours) { + assertTrue(location.gridDistanceTo(neighbour) <= 1); + } + assertEquals(8, neighbours.size()); + + neighbours = gameMap.getNeighbourhood(location, 2); + for (ILocation neighbour : neighbours) { + assertTrue(location.gridDistanceTo(neighbour) <= 2); + } + assertEquals(24, neighbours.size()); + + neighbours = gameMap.getNeighbourhood(location, 3); + for (ILocation neighbour : neighbours) { + assertTrue(location.gridDistanceTo(neighbour) <= 3); + } + assertEquals(48, neighbours.size()); + } } diff --git a/src/inf101/v18/rogue101/tests/PlayerTest.java b/src/inf101/v18/rogue101/tests/PlayerTest.java index d089f93..9c7dbfd 100644 --- a/src/inf101/v18/rogue101/tests/PlayerTest.java +++ b/src/inf101/v18/rogue101/tests/PlayerTest.java @@ -7,36 +7,112 @@ import org.junit.Test; import inf101.v18.grid.GridDirection; import inf101.v18.grid.ILocation; import inf101.v18.rogue101.game.Game; -import inf101.v18.rogue101.game.IGame; -import inf101.v18.rogue101.map.GameMap; -import inf101.v18.rogue101.objects.IItem; import inf101.v18.rogue101.objects.IPlayer; import javafx.scene.input.KeyCode; -class PlayerTest { - public static String TEST_MAP = "40 5\n" // - + "########################################\n" // - + "#...... ..C.R ......R.R......... ..R...#\n" // - + "#.R@R...... ..........RC..R...... ... .#\n" // - + "#... ..R........R......R. R........R.RR#\n" // - + "########################################\n" // - ; +public class PlayerTest { + //Tiny maps in collaboration with Stian J. Husum + private static String NO_WALLS_MAP = "1 1\n" + + "@\n"; + + private static String EMPTY_MAP = "5 5\n" + + "#####\n" + + "# #\n" + + "# @ #\n" + + "# #\n" + + "#####\n"; + + private static String RABBIT_MAP = "5 5\n" + + "#####\n" + + "#RRR#\n" + + "#R@R#\n" + + "#RRR#\n" + + "#####\n"; + + private static String CARROT_MAP = "5 5\n" + + "#####\n" + + "#CCC#\n" + + "#C@C#\n" + + "#CCC#\n" + + "#####\n"; @Test - void testPlayer1() { - // new game with our test map - Game game = new Game(TEST_MAP); - // pick (3,2) as the "current" position; this is where the player is on the - // test map, so it'll set up the player and return it - IPlayer player = (IPlayer) game.setCurrent(3, 2); - - - // find players location + public void testOutOfBounds() { + Game game = new Game(NO_WALLS_MAP); + IPlayer player = (IPlayer) game.setCurrent(0, 0); ILocation loc = game.getLocation(); - // press "UP" key - player.keyPressed(game, KeyCode.UP); - // see that we moved north - assertEquals(loc.go(GridDirection.NORTH), game.getLocation()); + player.keyPressed(game, KeyCode.LEFT); + assertEquals(loc, game.getLocation()); } + @Test + public void testActor() { + Game game = new Game(RABBIT_MAP); + IPlayer player = (IPlayer) game.setCurrent(2, 2); + ILocation loc = game.getLocation(); + player.keyPressed(game, KeyCode.LEFT); + assertEquals(loc, game.getLocation()); + player.keyPressed(game, KeyCode.RIGHT); + assertEquals(loc, game.getLocation()); + player.keyPressed(game, KeyCode.UP); + assertEquals(loc, game.getLocation()); + player.keyPressed(game, KeyCode.DOWN); + assertEquals(loc, game.getLocation()); + } + + @Test + public void testItem() { + Game game = new Game(CARROT_MAP); + IPlayer player = (IPlayer) game.setCurrent(2, 2); + ILocation loc = game.getLocation(); + player.keyPressed(game, KeyCode.RIGHT); + assertEquals(loc.go(GridDirection.EAST), game.getLocation()); + game.doTurn(); + player.keyPressed(game, KeyCode.LEFT); + game.doTurn(); + player.keyPressed(game, KeyCode.LEFT); + assertEquals(loc.go(GridDirection.WEST), game.getLocation()); + game.doTurn(); + player.keyPressed(game, KeyCode.UP); + game.doTurn(); + player.keyPressed(game, KeyCode.RIGHT); + assertEquals(loc.go(GridDirection.NORTH), game.getLocation()); + game.doTurn(); + player.keyPressed(game, KeyCode.DOWN); + game.doTurn(); + player.keyPressed(game, KeyCode.DOWN); + assertEquals(loc.go(GridDirection.SOUTH), game.getLocation()); + } + + @Test + public void testWallsAndKeys() { + Game game = new Game(EMPTY_MAP); + IPlayer player = (IPlayer) game.setCurrent(2, 2); + ILocation loc = game.getLocation(); + player.keyPressed(game, KeyCode.UP); + game.doTurn(); + player.keyPressed(game, KeyCode.UP); + game.doTurn(); + assertEquals(loc.go(GridDirection.NORTH), game.getLocation()); + player.keyPressed(game, KeyCode.DOWN); + game.doTurn(); + player.keyPressed(game, KeyCode.RIGHT); + game.doTurn(); + player.keyPressed(game, KeyCode.RIGHT); + game.doTurn(); + assertEquals(loc.go(GridDirection.EAST), game.getLocation()); + player.keyPressed(game, KeyCode.LEFT); + game.doTurn(); + player.keyPressed(game, KeyCode.DOWN); + game.doTurn(); + player.keyPressed(game, KeyCode.DOWN); + game.doTurn(); + assertEquals(loc.go(GridDirection.SOUTH), game.getLocation()); + player.keyPressed(game, KeyCode.UP); + game.doTurn(); + player.keyPressed(game, KeyCode.LEFT); + game.doTurn(); + player.keyPressed(game, KeyCode.LEFT); + assertEquals(loc.go(GridDirection.WEST), game.getLocation()); + } }