From e50e68d0523ece7cdf772b12904a6c0dafa2ec4a Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 13:46:36 +0200 Subject: PlayerAction Enum and rename RandomSimulation to ConsoleSimulation --- src/ch/epfl/xblast/PlayerAction.java | 14 ++++ .../epfl/xblast/simulation/ConsoleSimulation.java | 80 ++++++++++++++++++++++ .../epfl/xblast/simulation/RandomSimulation.java | 80 ---------------------- 3 files changed, 94 insertions(+), 80 deletions(-) create mode 100644 src/ch/epfl/xblast/PlayerAction.java create mode 100644 test/ch/epfl/xblast/simulation/ConsoleSimulation.java delete mode 100644 test/ch/epfl/xblast/simulation/RandomSimulation.java diff --git a/src/ch/epfl/xblast/PlayerAction.java b/src/ch/epfl/xblast/PlayerAction.java new file mode 100644 index 0000000..f4da746 --- /dev/null +++ b/src/ch/epfl/xblast/PlayerAction.java @@ -0,0 +1,14 @@ +package ch.epfl.xblast; + +/** + * @author Timothée FLOURE (257420) + */ +public enum PlayerAction { + JOIN_GAME, + MOVE_N, + MOVE_E, + MOVE_S, + MOVE_W, + STOP, + DROP_BOMB +} diff --git a/test/ch/epfl/xblast/simulation/ConsoleSimulation.java b/test/ch/epfl/xblast/simulation/ConsoleSimulation.java new file mode 100644 index 0000000..35c0eb8 --- /dev/null +++ b/test/ch/epfl/xblast/simulation/ConsoleSimulation.java @@ -0,0 +1,80 @@ +package ch.epfl.xblast.simulation; + +import ch.epfl.xblast.Cell; +import ch.epfl.xblast.PlayerID; +import ch.epfl.xblast.server.Board; +import ch.epfl.xblast.server.BoardTest; +import ch.epfl.xblast.server.GameState; +import ch.epfl.xblast.server.Player; +import ch.epfl.xblast.server.debug.GameStatePrinter; +import ch.epfl.xblast.server.debug.RandomEventGenerator; + +import java.util.Arrays; +import java.util.List; + +/** + * Random game simulation. + * + * @author Pacien TRAN-GIRARD (261948) + */ +public class ConsoleSimulation { + + private static final long DISPLAY_DELAY = 50; // in milliseconds + + private static final int PLAYER_LIVES = 4; + private static final int PLAYER_MAX_BOMBS = 4; + private static final int PLAYER_BOMB_RANGE = 4; + + private static final int SEED = 2016; + private static final int SPEED_CHANGE_PROB = 30; + private static final int BOMB_PROB = 100; + private static final RandomEventGenerator RANDOM_EVENT_GENERATOR = new RandomEventGenerator(SEED, SPEED_CHANGE_PROB, BOMB_PROB); + + private static Board buildTestBoard() { + return Board.ofQuadrantNWBlocksWalled(BoardTest.buildNWQuadrantMap()); + } + + private static Player newPlayer(PlayerID id, Cell pos) { + return new Player(id, PLAYER_LIVES, pos, PLAYER_MAX_BOMBS, PLAYER_BOMB_RANGE); + } + + private static List buildPlayersList() { + return Arrays.asList( + newPlayer(PlayerID.PLAYER_1, new Cell(1, 1)), + newPlayer(PlayerID.PLAYER_2, new Cell(13, 1)), + newPlayer(PlayerID.PLAYER_3, new Cell(13, 11)), + newPlayer(PlayerID.PLAYER_4, new Cell(1, 11))); + } + + private static GameState buildInitialGameState() { + return new GameState(buildTestBoard(), buildPlayersList()); + } + + private static GameState nextGameState(GameState gs) { + return gs.next(RANDOM_EVENT_GENERATOR.randomSpeedChangeEvents(), RANDOM_EVENT_GENERATOR.randomBombDropEvents()); + } + + private static boolean isSimulationOver(GameState gs) { + return gs == null || gs.isGameOver(); + } + + private static void delay() { + try { + Thread.sleep(DISPLAY_DELAY); + } catch (InterruptedException e) { + e.printStackTrace(); + System.exit(1); + } + } + + private static void displayGameState(GameState gs) { + GameStatePrinter.printGameState(gs); + delay(); + } + + public static void main(String[] args) { + for (GameState gs = buildInitialGameState(); !isSimulationOver(gs); gs = nextGameState(gs)) + displayGameState(gs); + } + +} diff --git a/test/ch/epfl/xblast/simulation/RandomSimulation.java b/test/ch/epfl/xblast/simulation/RandomSimulation.java deleted file mode 100644 index 3888650..0000000 --- a/test/ch/epfl/xblast/simulation/RandomSimulation.java +++ /dev/null @@ -1,80 +0,0 @@ -package ch.epfl.xblast.simulation; - -import ch.epfl.xblast.Cell; -import ch.epfl.xblast.PlayerID; -import ch.epfl.xblast.server.Board; -import ch.epfl.xblast.server.BoardTest; -import ch.epfl.xblast.server.GameState; -import ch.epfl.xblast.server.Player; -import ch.epfl.xblast.server.debug.GameStatePrinter; -import ch.epfl.xblast.server.debug.RandomEventGenerator; - -import java.util.Arrays; -import java.util.List; - -/** - * Random game simulation. - * - * @author Pacien TRAN-GIRARD (261948) - */ -public class RandomSimulation { - - private static final long DISPLAY_DELAY = 50; // in milliseconds - - private static final int PLAYER_LIVES = 4; - private static final int PLAYER_MAX_BOMBS = 4; - private static final int PLAYER_BOMB_RANGE = 4; - - private static final int SEED = 2016; - private static final int SPEED_CHANGE_PROB = 30; - private static final int BOMB_PROB = 100; - private static final RandomEventGenerator RANDOM_EVENT_GENERATOR = new RandomEventGenerator(SEED, SPEED_CHANGE_PROB, BOMB_PROB); - - private static Board buildTestBoard() { - return Board.ofQuadrantNWBlocksWalled(BoardTest.buildNWQuadrantMap()); - } - - private static Player newPlayer(PlayerID id, Cell pos) { - return new Player(id, PLAYER_LIVES, pos, PLAYER_MAX_BOMBS, PLAYER_BOMB_RANGE); - } - - private static List buildPlayersList() { - return Arrays.asList( - newPlayer(PlayerID.PLAYER_1, new Cell(1, 1)), - newPlayer(PlayerID.PLAYER_2, new Cell(13, 1)), - newPlayer(PlayerID.PLAYER_3, new Cell(13, 11)), - newPlayer(PlayerID.PLAYER_4, new Cell(1, 11))); - } - - private static GameState buildInitialGameState() { - return new GameState(buildTestBoard(), buildPlayersList()); - } - - private static GameState nextGameState(GameState gs) { - return gs.next(RANDOM_EVENT_GENERATOR.randomSpeedChangeEvents(), RANDOM_EVENT_GENERATOR.randomBombDropEvents()); - } - - private static boolean isSimulationOver(GameState gs) { - return gs == null || gs.isGameOver(); - } - - private static void delay() { - try { - Thread.sleep(DISPLAY_DELAY); - } catch (InterruptedException e) { - e.printStackTrace(); - System.exit(1); - } - } - - private static void displayGameState(GameState gs) { - GameStatePrinter.printGameState(gs); - delay(); - } - - public static void main(String[] args) { - for (GameState gs = buildInitialGameState(); !isSimulationOver(gs); gs = nextGameState(gs)) - displayGameState(gs); - } - -} -- cgit v1.2.3 From d7c29fd93719727544f22d079ca098750d98edb1 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 14:08:32 +0200 Subject: Implement board tiles re-ordering --- .../epfl/xblast/client/GameStateDeserializer.java | 42 ++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/ch/epfl/xblast/client/GameStateDeserializer.java b/src/ch/epfl/xblast/client/GameStateDeserializer.java index 20b44f9..143641d 100644 --- a/src/ch/epfl/xblast/client/GameStateDeserializer.java +++ b/src/ch/epfl/xblast/client/GameStateDeserializer.java @@ -1,15 +1,13 @@ package ch.epfl.xblast.client; -import ch.epfl.xblast.Lists; -import ch.epfl.xblast.PlayerID; -import ch.epfl.xblast.RunLengthEncoder; -import ch.epfl.xblast.SubCell; +import ch.epfl.xblast.*; import ch.epfl.xblast.client.GameState.Player; import java.awt.*; import java.util.*; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * The client game state deserializer. @@ -116,6 +114,38 @@ public final class GameStateDeserializer { .collect(Collectors.toList()); } + /** + * Unserializes a spiral-ordered byte representation of a board. + * + * @param serializedBoard the run-length-compressed, serialized spiral ordered board + * @return the row-major-ordered board + * @throws IllegalArgumentException if the length of the serialized board is invalid + */ + private static List deserializeBoard(List serializedBoard) { + List spiralOrdered = deserializeImageChunk(serializedBoard, ImageCollection.BOARD_IMAGES); + + if (spiralOrdered.size() != Cell.SPIRAL_ORDER.size()) + throw new IllegalArgumentException(); + + Map imageMap = IntStream + .range(0, Cell.SPIRAL_ORDER.size()).mapToObj(i -> i) + .collect(Collectors.toMap(Cell.SPIRAL_ORDER::get, spiralOrdered::get)); + + return Cell.ROW_MAJOR_ORDER.stream() + .map(imageMap::get) + .collect(Collectors.toList()); + } + + /** + * Unserializes a byte representation of explosions and bombs. + * + * @param serializedExplosions the run-length-compressed, serialized explosions + * @return the explosions + */ + private static List deserializeExplosions(List serializedExplosions) { + return deserializeImageChunk(serializedExplosions, ImageCollection.EXPLOSIONS_IMAGES); + } + /** * Unserializes a byte representation of a Player. * @@ -176,10 +206,10 @@ public final class GameStateDeserializer { public static GameState deserialize(List serializedData) { VariableLengthChunk chunks = new VariableLengthChunk(serializedData); - List board = deserializeImageChunk(chunks.head(), ImageCollection.BOARD_IMAGES); + List board = deserializeBoard(chunks.head()); chunks = chunks.tail(); - List explosions = deserializeImageChunk(chunks.head(), ImageCollection.EXPLOSIONS_IMAGES); + List explosions = deserializeExplosions(chunks.head()); chunks = chunks.tail(); List players = deserializePlayers(chunks.head(SERIALIZED_PLAYERS_LENGTH)); -- cgit v1.2.3 From 6dc49829aa2bb4a2767d2c2d0c361cdcf8804dcd Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 14:10:52 +0200 Subject: Add missing doc-block --- src/ch/epfl/xblast/client/GameStateDeserializer.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ch/epfl/xblast/client/GameStateDeserializer.java b/src/ch/epfl/xblast/client/GameStateDeserializer.java index 143641d..2c06dbc 100644 --- a/src/ch/epfl/xblast/client/GameStateDeserializer.java +++ b/src/ch/epfl/xblast/client/GameStateDeserializer.java @@ -190,6 +190,13 @@ public final class GameStateDeserializer { .collect(Collectors.toList())); } + /** + * Unserializes a byte representation of the ticks. + * + * @param serializedTicks the serialized ticks + * @return the ticks + * @throws IllegalArgumentException if the byte chunk is invalid. + */ private static int deserializeTicks(List serializedTicks) { if (Objects.isNull(serializedTicks) || serializedTicks.size() != SERIALIZED_TICKS_LENGTH) throw new IllegalArgumentException(); -- cgit v1.2.3 From 10a9bc4bc745b49b2739d0e98eb8ad799fb8753e Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 14:14:24 +0200 Subject: Enforce immutability --- src/ch/epfl/xblast/client/GameStateDeserializer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ch/epfl/xblast/client/GameStateDeserializer.java b/src/ch/epfl/xblast/client/GameStateDeserializer.java index 2c06dbc..e2039ed 100644 --- a/src/ch/epfl/xblast/client/GameStateDeserializer.java +++ b/src/ch/epfl/xblast/client/GameStateDeserializer.java @@ -108,10 +108,10 @@ public final class GameStateDeserializer { * @return a list of Images corresponding to the serialized element */ private static List deserializeImageChunk(List data, ImageCollection imageCollection) { - return RunLengthEncoder.decode(data).stream() + return Collections.unmodifiableList(RunLengthEncoder.decode(data).stream() .map(Byte::toUnsignedInt) .map(imageCollection::imageOrNull) - .collect(Collectors.toList()); + .collect(Collectors.toList())); } /** @@ -131,9 +131,9 @@ public final class GameStateDeserializer { .range(0, Cell.SPIRAL_ORDER.size()).mapToObj(i -> i) .collect(Collectors.toMap(Cell.SPIRAL_ORDER::get, spiralOrdered::get)); - return Cell.ROW_MAJOR_ORDER.stream() + return Collections.unmodifiableList(Cell.ROW_MAJOR_ORDER.stream() .map(imageMap::get) - .collect(Collectors.toList()); + .collect(Collectors.toList())); } /** -- cgit v1.2.3 From 68a71bf1ac4359e584ac36cc33e2d37d323563a6 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 14:28:51 +0200 Subject: Validate explosions length --- src/ch/epfl/xblast/client/GameStateDeserializer.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ch/epfl/xblast/client/GameStateDeserializer.java b/src/ch/epfl/xblast/client/GameStateDeserializer.java index e2039ed..8b203b1 100644 --- a/src/ch/epfl/xblast/client/GameStateDeserializer.java +++ b/src/ch/epfl/xblast/client/GameStateDeserializer.java @@ -141,9 +141,15 @@ public final class GameStateDeserializer { * * @param serializedExplosions the run-length-compressed, serialized explosions * @return the explosions + * @throws IllegalArgumentException if the length of the serialized explosions is invalid */ private static List deserializeExplosions(List serializedExplosions) { - return deserializeImageChunk(serializedExplosions, ImageCollection.EXPLOSIONS_IMAGES); + List explosions = deserializeImageChunk(serializedExplosions, ImageCollection.EXPLOSIONS_IMAGES); + + if (explosions.size() != Cell.ROW_MAJOR_ORDER.size()) + throw new IllegalArgumentException(); + + return explosions; } /** -- cgit v1.2.3 From 3ba97db409c1a0713b514a394407da325db0feb8 Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 15:13:40 +0200 Subject: Use proper player images for ScorePainter --- src/ch/epfl/xblast/client/painter/ScorePainter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch/epfl/xblast/client/painter/ScorePainter.java b/src/ch/epfl/xblast/client/painter/ScorePainter.java index e412dd8..367d9f6 100644 --- a/src/ch/epfl/xblast/client/painter/ScorePainter.java +++ b/src/ch/epfl/xblast/client/painter/ScorePainter.java @@ -60,7 +60,7 @@ public class ScorePainter { throw new IllegalArgumentException(); return Arrays.asList( - ImageCollection.PLAYERS_IMAGES.imageOrNull(imageIDForPlayer(p)), + ImageCollection.SCORES_IMAGES.imageOrNull(imageIDForPlayer(p)), TEXT_MIDDLE_IMG, TEXT_RIGHT_IMG); } -- cgit v1.2.3 From fc7783bebc429a2ba368f96be8ced16c9d7509d4 Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 15:15:10 +0200 Subject: Basic Graphical Simulation --- .../xblast/simulation/GraphicalSimulation.java | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/ch/epfl/xblast/simulation/GraphicalSimulation.java diff --git a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java new file mode 100644 index 0000000..1a0d88f --- /dev/null +++ b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java @@ -0,0 +1,57 @@ +package ch.epfl.xblast.simulation; + +import ch.epfl.xblast.PlayerID; +import ch.epfl.xblast.client.*; +import ch.epfl.xblast.server.*; +import ch.epfl.xblast.server.GameState; +import ch.epfl.xblast.server.debug.RandomEventGenerator; +import ch.epfl.xblast.server.painter.BoardPainter; + +import javax.swing.*; +import java.util.List; + +/** + * @author Timothée FLOURE (257420) + */ +public class GraphicalSimulation { + private static final GameState INITIAL_GAMESTATE = Level.DEFAULT_LEVEL.initialState(); + private static final BoardPainter BOARD_PAINTER = Level.DEFAULT_LEVEL.painter(); + + private static final int SEED = 2016; + private static final int SPEED_CHANGE_PROB = 30; + private static final int BOMB_PROB = 100; + private static final RandomEventGenerator RANDOM_EVENT_GENERATOR = new RandomEventGenerator(SEED, SPEED_CHANGE_PROB, BOMB_PROB); + + private static ch.epfl.xblast.client.GameState getClientData(GameState gs) { + List serializedGameState = GameStateSerializer.serialize(BOARD_PAINTER, gs); + return GameStateDeserializer.deserialize(serializedGameState); + } + + private static boolean isSimulationOver(GameState gs) { + return gs == null || gs.isGameOver(); + } + + private static GameState nextGameState(GameState gs) { + return gs.next(RANDOM_EVENT_GENERATOR.randomSpeedChangeEvents(), RANDOM_EVENT_GENERATOR.randomBombDropEvents()); + } + + private static void displayGameState(ch.epfl.xblast.client.GameState gs, XBlastComponent gui) { + gui.setGameState(gs, PlayerID.PLAYER_1); + } + + private static JFrame buildFrame() { + JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + return frame; + } + + public static void main(String[] args) { + JFrame frame = buildFrame(); + XBlastComponent xblast = new XBlastComponent(); + frame.setContentPane(xblast); + + for (GameState gs = INITIAL_GAMESTATE; !isSimulationOver(gs); gs = nextGameState(gs)) + displayGameState(getClientData(gs), xblast); + } +} -- cgit v1.2.3 From 07672117b406c6f967e02e997a484114beca37be Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 15:25:47 +0200 Subject: Fix scores separator in ScorePainter --- src/ch/epfl/xblast/client/painter/ScorePainter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch/epfl/xblast/client/painter/ScorePainter.java b/src/ch/epfl/xblast/client/painter/ScorePainter.java index 367d9f6..4fb6412 100644 --- a/src/ch/epfl/xblast/client/painter/ScorePainter.java +++ b/src/ch/epfl/xblast/client/painter/ScorePainter.java @@ -45,7 +45,7 @@ public class ScorePainter { return players.stream() .flatMap(p -> Stream.concat( buildPlayerScorePanel(p).stream(), - p.id().ordinal() % 2 == 0 ? buildSeparator().stream() : Stream.empty())) + p.id().ordinal() == 1 ? buildSeparator().stream() : Stream.empty())) .collect(Collectors.toList()); } -- cgit v1.2.3 From c27e85228fe3c7de8465959940c1cde64bdb7887 Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 16:18:02 +0200 Subject: Fix the window size in GraphicalSimulation --- test/ch/epfl/xblast/simulation/GraphicalSimulation.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java index 1a0d88f..5706577 100644 --- a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java +++ b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java @@ -42,6 +42,7 @@ public class GraphicalSimulation { private static JFrame buildFrame() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); return frame; } @@ -50,6 +51,7 @@ public class GraphicalSimulation { JFrame frame = buildFrame(); XBlastComponent xblast = new XBlastComponent(); frame.setContentPane(xblast); + frame.pack(); for (GameState gs = INITIAL_GAMESTATE; !isSimulationOver(gs); gs = nextGameState(gs)) displayGameState(getClientData(gs), xblast); -- cgit v1.2.3 From 9850fe44401a1401aef5e6bbf683b507befa03dd Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 16:19:56 +0200 Subject: Implementation of the XBlastComponent Class --- src/ch/epfl/xblast/client/XBlastComponent.java | 171 +++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/ch/epfl/xblast/client/XBlastComponent.java diff --git a/src/ch/epfl/xblast/client/XBlastComponent.java b/src/ch/epfl/xblast/client/XBlastComponent.java new file mode 100644 index 0000000..ac9f223 --- /dev/null +++ b/src/ch/epfl/xblast/client/XBlastComponent.java @@ -0,0 +1,171 @@ +package ch.epfl.xblast.client; + +import ch.epfl.xblast.PlayerID; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +/** + * @author Timothée FLOURE (257420) + */ +public final class XBlastComponent extends JComponent { + private static final int PREFERRED_WIDTH = 960; + private static final int PREFERRED_HEIGHT = 688; + private static final int BLOCK_WIDTH = 64; + private static final int BLOCK_HEIGHT = 48; + private static final int SCORE_IMAGE_WIDTH = 48; + private static final int SCORE_IMAGE_HEIGHT = 48; + private static final int TIME_LED_WIDTH = 16; + private static final int TIME_LED_HEIGHT = 16; + + private static final Font font = new Font("Arial", Font.BOLD, 25); + private static final int SCORES_TEXT_VERTICAL_POSITION = 659; + private static final int SCORES_P1_TEXT_HORIZONTAL_POSITION = 96; + private static final int SCORES_P2_TEXT_HORIZONTAL_POSITION = 240; + private static final int SCORES_P3_TEXT_HORIZONTAL_POSITION = 768; + private static final int SCORES_P4_TEXT_HORIZONTAL_POSITION = 912; + + /** + * Displayed gameState. + */ + private GameState gamestate; + + /** + * Display a list of images on the given graphic context. + * + * @param g the graphic context + * @param images the given list of images, ordered in row-major order + */ + private void drawMap(Graphics2D g, List images) { + int x = 0; + int y = 0; + + for (Image img : images) { + if (x + BLOCK_WIDTH > PREFERRED_WIDTH) { + y += BLOCK_HEIGHT; // Or img.getHeight(null) ? + x = 0; + } + + if (img != null) + g.drawImage(img, x, y, null); + + x += BLOCK_WIDTH; // Or img.getWidth(null) ? + } + } + + /** + * Compute the horizontal position of the player in px. + * + * @param player the given player + * @return the horizontal position in pixel + */ + private int playerXPosition(GameState.Player player) { return 4 * player.position().x() - 24; } + + /** + * Compute the vertical position of the player in px. + * + * @param player the given player + * @return the vertical position in pixel + */ + private int playerYPosition(GameState.Player player) { return 3 * player.position().y() - 52; } + + /** + * Draw the players on the graphic context. + * + * @param g the graphic context + * @param players the list of players to be displayed + */ + private void drawPlayers(Graphics2D g, List players) { + for (GameState.Player player : players) { + g.drawImage(player.image(), playerXPosition(player), playerYPosition(player), null); + } + } + + /** + * Draw the scores on the graphic context. + * + * @param g the graphic context + * @param scores the list of images composing the scores to be displayed + */ + private void drawScores(Graphics2D g, List scores) { + int x = 0; + int y = PREFERRED_HEIGHT - SCORE_IMAGE_HEIGHT - TIME_LED_HEIGHT; + for (Image img : scores) { + g.drawImage(img, x, y, null); + x += SCORE_IMAGE_WIDTH; + } + } + + /** + * Write the remaining lives of each player on the graphic context. + * + * @param g the graphic context + * @param players list of players + */ + private void writeScores(Graphics2D g,List players) { + g.setColor(Color.WHITE); + g.setFont(font); + + // Ugly !!!!! + g.drawString(Integer.toString(players.get(0).lives()), SCORES_P1_TEXT_HORIZONTAL_POSITION,SCORES_TEXT_VERTICAL_POSITION); + g.drawString(Integer.toString(players.get(1).lives()), SCORES_P2_TEXT_HORIZONTAL_POSITION,SCORES_TEXT_VERTICAL_POSITION); + g.drawString(Integer.toString(players.get(2).lives()), SCORES_P3_TEXT_HORIZONTAL_POSITION,SCORES_TEXT_VERTICAL_POSITION); + g.drawString(Integer.toString(players.get(3).lives()), SCORES_P4_TEXT_HORIZONTAL_POSITION,SCORES_TEXT_VERTICAL_POSITION); + + } + + /** + * Draw the time "line" on the graphic context + * + * @param g the graphic context + * @param time the list of images composing the line + */ + private void drawTime(Graphics2D g, List time) { + int x = 0; + int y = PREFERRED_HEIGHT - TIME_LED_HEIGHT; + for (Image img : time) { + g.drawImage(img, x, y, null); + x += TIME_LED_WIDTH; + } + } + + /** + * Display the given GameState from the point of view of the given player. + * + * @param gameState GameState to be displayer + * @param player player related to the view + */ + public void setGameState(GameState gameState, PlayerID player) { + this.gamestate = gameState; + repaint(); + } + + /** + * Returns the value of the preferredSize property. + * + * @return the value of the preferredSize property + */ + @Override + public Dimension getPreferredSize() { + return new Dimension(PREFERRED_WIDTH,PREFERRED_HEIGHT); + } + + /** + * Draw the component. + * + * @param g0 the Graphics context + */ + @Override + protected void paintComponent(Graphics g0) { + if (this.gamestate != null) { + Graphics2D g = (Graphics2D) g0; + drawMap(g, this.gamestate.board()); + drawMap(g, this.gamestate.explosions()); + drawPlayers(g, this.gamestate.players()); + drawScores(g, this.gamestate.scores()); + writeScores(g, this.gamestate.players()); + drawTime(g, this.gamestate.ticks()); + }; + } +} -- cgit v1.2.3 From c12373535ca703ac2fbbd1d1775547b142d3970c Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 16:20:22 +0200 Subject: FIx bug in GameState.Player (client) : the may be "null" --- src/ch/epfl/xblast/client/GameState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch/epfl/xblast/client/GameState.java b/src/ch/epfl/xblast/client/GameState.java index 7652cd5..2ac27e3 100644 --- a/src/ch/epfl/xblast/client/GameState.java +++ b/src/ch/epfl/xblast/client/GameState.java @@ -41,7 +41,7 @@ public final class GameState { this.id = id; this.lives = lives; this.position = Objects.requireNonNull(position); - this.image = Objects.requireNonNull(image); + this.image = image; } /** -- cgit v1.2.3 From aa7cd18ee5b13ee4373db58dc8ab9157a348c72e Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 16:37:10 +0200 Subject: Implementation of the KeyboardEventHandler Class --- .../epfl/xblast/client/KeyboardEventHandler.java | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/ch/epfl/xblast/client/KeyboardEventHandler.java diff --git a/src/ch/epfl/xblast/client/KeyboardEventHandler.java b/src/ch/epfl/xblast/client/KeyboardEventHandler.java new file mode 100644 index 0000000..bebc4a5 --- /dev/null +++ b/src/ch/epfl/xblast/client/KeyboardEventHandler.java @@ -0,0 +1,38 @@ +package ch.epfl.xblast.client; + +import ch.epfl.xblast.PlayerAction; + +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Map; +import java.util.function.Consumer; + +/** + * @author Timothée FLOURE (257420) + */ +public class KeyboardEventHandler extends KeyAdapter{ + private final Map playerActionMap; + private final Consumer consumer; + + /** + * Instantiates a new KeyboardEventHandler. + * + * @param playerActionMap the map of key codes to PlayerAction. + * @param consumer consumer related to the EventHandler + */ + public KeyboardEventHandler(Map playerActionMap, Consumer consumer) { + this.playerActionMap = playerActionMap; + this.consumer = consumer; + } + + /** + * Executes the command related to the given keycode. + * + * @param e event (pressed key) + */ + @Override + public void keyTyped(KeyEvent e) { + if (playerActionMap.containsKey(e.getKeyCode())) + consumer.accept(playerActionMap.get(e.getKeyCode())); + } +} -- cgit v1.2.3 From 38711042dd2c39cf51cd2d063f2eb38d0bbb5a45 Mon Sep 17 00:00:00 2001 From: Timothée Floure Date: Mon, 9 May 2016 16:37:36 +0200 Subject: Implement KeyboardEventHandler to the Graphical Simulation --- .../xblast/simulation/GraphicalSimulation.java | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java index 5706577..7e4454d 100644 --- a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java +++ b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java @@ -1,5 +1,6 @@ package ch.epfl.xblast.simulation; +import ch.epfl.xblast.PlayerAction; import ch.epfl.xblast.PlayerID; import ch.epfl.xblast.client.*; import ch.epfl.xblast.server.*; @@ -8,7 +9,11 @@ import ch.epfl.xblast.server.debug.RandomEventGenerator; import ch.epfl.xblast.server.painter.BoardPainter; import javax.swing.*; +import java.awt.event.KeyEvent; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Consumer; /** * @author Timothée FLOURE (257420) @@ -22,6 +27,18 @@ public class GraphicalSimulation { private static final int BOMB_PROB = 100; private static final RandomEventGenerator RANDOM_EVENT_GENERATOR = new RandomEventGenerator(SEED, SPEED_CHANGE_PROB, BOMB_PROB); + private static Map buildPlayerActionMap() { + Map playerActionMap = new HashMap<>(); + playerActionMap.put(KeyEvent.VK_UP, PlayerAction.MOVE_N); + playerActionMap.put(KeyEvent.VK_RIGHT, PlayerAction.MOVE_E); + playerActionMap.put(KeyEvent.VK_DOWN, PlayerAction.MOVE_S); + playerActionMap.put(KeyEvent.VK_LEFT, PlayerAction.MOVE_W); + playerActionMap.put(KeyEvent.VK_SPACE, PlayerAction.DROP_BOMB); + playerActionMap.put(KeyEvent.VK_SHIFT, PlayerAction.STOP); + + return playerActionMap; + } + private static ch.epfl.xblast.client.GameState getClientData(GameState gs) { List serializedGameState = GameStateSerializer.serialize(BOARD_PAINTER, gs); return GameStateDeserializer.deserialize(serializedGameState); @@ -48,11 +65,19 @@ public class GraphicalSimulation { } public static void main(String[] args) { + + // Build the window JFrame frame = buildFrame(); XBlastComponent xblast = new XBlastComponent(); frame.setContentPane(xblast); frame.pack(); + // Attach a KeyboardEventHandler + Consumer c = System.out::println; + frame.addKeyListener(new KeyboardEventHandler(buildPlayerActionMap(), c)); + frame.requestFocusInWindow(); + + // Run the Simulation for (GameState gs = INITIAL_GAMESTATE; !isSimulationOver(gs); gs = nextGameState(gs)) displayGameState(getClientData(gs), xblast); } -- cgit v1.2.3 From e9b85677fd581791bb1b5dffbe7261a876879084 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 19:43:30 +0200 Subject: Refactor simulations --- .../epfl/xblast/simulation/ConsoleSimulation.java | 67 ++----------------- .../xblast/simulation/GraphicalSimulation.java | 78 +++++++++++----------- test/ch/epfl/xblast/simulation/Simulation.java | 54 +++++++++++++++ 3 files changed, 98 insertions(+), 101 deletions(-) create mode 100644 test/ch/epfl/xblast/simulation/Simulation.java diff --git a/test/ch/epfl/xblast/simulation/ConsoleSimulation.java b/test/ch/epfl/xblast/simulation/ConsoleSimulation.java index 35c0eb8..b029afb 100644 --- a/test/ch/epfl/xblast/simulation/ConsoleSimulation.java +++ b/test/ch/epfl/xblast/simulation/ConsoleSimulation.java @@ -1,80 +1,23 @@ package ch.epfl.xblast.simulation; -import ch.epfl.xblast.Cell; -import ch.epfl.xblast.PlayerID; -import ch.epfl.xblast.server.Board; -import ch.epfl.xblast.server.BoardTest; import ch.epfl.xblast.server.GameState; -import ch.epfl.xblast.server.Player; import ch.epfl.xblast.server.debug.GameStatePrinter; -import ch.epfl.xblast.server.debug.RandomEventGenerator; - -import java.util.Arrays; -import java.util.List; /** - * Random game simulation. + * Random game simulation printed in the console. * * @author Pacien TRAN-GIRARD (261948) */ -public class ConsoleSimulation { - - private static final long DISPLAY_DELAY = 50; // in milliseconds - - private static final int PLAYER_LIVES = 4; - private static final int PLAYER_MAX_BOMBS = 4; - private static final int PLAYER_BOMB_RANGE = 4; - - private static final int SEED = 2016; - private static final int SPEED_CHANGE_PROB = 30; - private static final int BOMB_PROB = 100; - private static final RandomEventGenerator RANDOM_EVENT_GENERATOR = new RandomEventGenerator(SEED, SPEED_CHANGE_PROB, BOMB_PROB); - - private static Board buildTestBoard() { - return Board.ofQuadrantNWBlocksWalled(BoardTest.buildNWQuadrantMap()); - } - - private static Player newPlayer(PlayerID id, Cell pos) { - return new Player(id, PLAYER_LIVES, pos, PLAYER_MAX_BOMBS, PLAYER_BOMB_RANGE); - } - - private static List buildPlayersList() { - return Arrays.asList( - newPlayer(PlayerID.PLAYER_1, new Cell(1, 1)), - newPlayer(PlayerID.PLAYER_2, new Cell(13, 1)), - newPlayer(PlayerID.PLAYER_3, new Cell(13, 11)), - newPlayer(PlayerID.PLAYER_4, new Cell(1, 11))); - } - - private static GameState buildInitialGameState() { - return new GameState(buildTestBoard(), buildPlayersList()); - } - - private static GameState nextGameState(GameState gs) { - return gs.next(RANDOM_EVENT_GENERATOR.randomSpeedChangeEvents(), RANDOM_EVENT_GENERATOR.randomBombDropEvents()); - } - - private static boolean isSimulationOver(GameState gs) { - return gs == null || gs.isGameOver(); - } - - private static void delay() { - try { - Thread.sleep(DISPLAY_DELAY); - } catch (InterruptedException e) { - e.printStackTrace(); - System.exit(1); - } - } +public class ConsoleSimulation extends Simulation { - private static void displayGameState(GameState gs) { + @Override + void displayGameState(GameState gs) { GameStatePrinter.printGameState(gs); delay(); } public static void main(String[] args) { - for (GameState gs = buildInitialGameState(); !isSimulationOver(gs); gs = nextGameState(gs)) - displayGameState(gs); + (new ConsoleSimulation()).runSimulation(); } } diff --git a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java index 7e4454d..8009d9f 100644 --- a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java +++ b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java @@ -2,33 +2,37 @@ package ch.epfl.xblast.simulation; import ch.epfl.xblast.PlayerAction; import ch.epfl.xblast.PlayerID; -import ch.epfl.xblast.client.*; -import ch.epfl.xblast.server.*; +import ch.epfl.xblast.client.GameStateDeserializer; +import ch.epfl.xblast.client.KeyboardEventHandler; +import ch.epfl.xblast.client.XBlastComponent; import ch.epfl.xblast.server.GameState; -import ch.epfl.xblast.server.debug.RandomEventGenerator; +import ch.epfl.xblast.server.GameStateSerializer; +import ch.epfl.xblast.server.Level; import ch.epfl.xblast.server.painter.BoardPainter; import javax.swing.*; import java.awt.event.KeyEvent; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Consumer; /** + * Graphical game simulation. + * * @author Timothée FLOURE (257420) + * @author Pacien TRAN-GIRARD (261948) */ -public class GraphicalSimulation { - private static final GameState INITIAL_GAMESTATE = Level.DEFAULT_LEVEL.initialState(); - private static final BoardPainter BOARD_PAINTER = Level.DEFAULT_LEVEL.painter(); +public class GraphicalSimulation extends Simulation { - private static final int SEED = 2016; - private static final int SPEED_CHANGE_PROB = 30; - private static final int BOMB_PROB = 100; - private static final RandomEventGenerator RANDOM_EVENT_GENERATOR = new RandomEventGenerator(SEED, SPEED_CHANGE_PROB, BOMB_PROB); + private static final BoardPainter BOARD_PAINTER = Level.DEFAULT_LEVEL.painter(); + private static final Consumer PLAYER_ACTION_CONSUMER = System.out::println; + private static final PlayerID MAIN_PLAYER = PlayerID.PLAYER_1; - private static Map buildPlayerActionMap() { + private static Map buildPlayerActionMap() { Map playerActionMap = new HashMap<>(); + playerActionMap.put(KeyEvent.VK_UP, PlayerAction.MOVE_N); playerActionMap.put(KeyEvent.VK_RIGHT, PlayerAction.MOVE_E); playerActionMap.put(KeyEvent.VK_DOWN, PlayerAction.MOVE_S); @@ -36,49 +40,45 @@ public class GraphicalSimulation { playerActionMap.put(KeyEvent.VK_SPACE, PlayerAction.DROP_BOMB); playerActionMap.put(KeyEvent.VK_SHIFT, PlayerAction.STOP); - return playerActionMap; + return Collections.unmodifiableMap(playerActionMap); } - private static ch.epfl.xblast.client.GameState getClientData(GameState gs) { + private static ch.epfl.xblast.client.GameState translateToClientData(GameState gs) { List serializedGameState = GameStateSerializer.serialize(BOARD_PAINTER, gs); return GameStateDeserializer.deserialize(serializedGameState); } - private static boolean isSimulationOver(GameState gs) { - return gs == null || gs.isGameOver(); - } - - private static GameState nextGameState(GameState gs) { - return gs.next(RANDOM_EVENT_GENERATOR.randomSpeedChangeEvents(), RANDOM_EVENT_GENERATOR.randomBombDropEvents()); - } - - private static void displayGameState(ch.epfl.xblast.client.GameState gs, XBlastComponent gui) { - gui.setGameState(gs, PlayerID.PLAYER_1); - } - - private static JFrame buildFrame() { + private static JFrame buildFrame(XBlastComponent gui) { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); + + frame.setContentPane(gui); + frame.pack(); return frame; } - public static void main(String[] args) { + private void attachKeyboardHandler(JFrame frame) { + frame.addKeyListener(new KeyboardEventHandler(buildPlayerActionMap(), PLAYER_ACTION_CONSUMER)); + frame.requestFocusInWindow(); + } - // Build the window - JFrame frame = buildFrame(); - XBlastComponent xblast = new XBlastComponent(); - frame.setContentPane(xblast); - frame.pack(); + private final XBlastComponent gui; - // Attach a KeyboardEventHandler - Consumer c = System.out::println; - frame.addKeyListener(new KeyboardEventHandler(buildPlayerActionMap(), c)); - frame.requestFocusInWindow(); + private GraphicalSimulation() { + this.gui = new XBlastComponent(); + attachKeyboardHandler(buildFrame(this.gui)); + } - // Run the Simulation - for (GameState gs = INITIAL_GAMESTATE; !isSimulationOver(gs); gs = nextGameState(gs)) - displayGameState(getClientData(gs), xblast); + @Override + void displayGameState(GameState gs) { + this.gui.setGameState(translateToClientData(gs), MAIN_PLAYER); + delay(); } + + public static void main(String[] args) { + (new GraphicalSimulation()).runSimulation(); + } + } diff --git a/test/ch/epfl/xblast/simulation/Simulation.java b/test/ch/epfl/xblast/simulation/Simulation.java new file mode 100644 index 0000000..e70c688 --- /dev/null +++ b/test/ch/epfl/xblast/simulation/Simulation.java @@ -0,0 +1,54 @@ +package ch.epfl.xblast.simulation; + +import ch.epfl.xblast.server.GameState; +import ch.epfl.xblast.server.Level; +import ch.epfl.xblast.server.debug.RandomEventGenerator; + +/** + * An abstract game simulation. + * + * @author Pacien TRAN-GIRARD (261948) + */ +abstract class Simulation { + + private static final GameState INITIAL_GAME_STATE = Level.DEFAULT_LEVEL.initialState(); + + private static final int RNG_SEED = 2016; + private static final int SPEED_CHANGE_PROB = 30; + private static final int BOMB_PROB = 100; + + private static final long DISPLAY_DELAY = 50; // in milliseconds + + static void delay() { + try { + Thread.sleep(DISPLAY_DELAY); + } catch (InterruptedException e) { + e.printStackTrace(); + System.exit(1); + } + } + + private final RandomEventGenerator randomEventGenerator; + + Simulation() { + this.randomEventGenerator = new RandomEventGenerator(RNG_SEED, SPEED_CHANGE_PROB, BOMB_PROB); + } + + private boolean isSimulationOver(GameState gs) { + return gs == null || gs.isGameOver(); + } + + private GameState nextGameState(GameState gs) { + return gs.next( + this.randomEventGenerator.randomSpeedChangeEvents(), + this.randomEventGenerator.randomBombDropEvents()); + } + + void runSimulation() { + for (GameState gs = INITIAL_GAME_STATE; !isSimulationOver(gs); gs = nextGameState(gs)) + displayGameState(gs); + } + + abstract void displayGameState(GameState gs); + +} -- cgit v1.2.3 From 83c7a8c1b46ca173dfcb3252d9136dfe15d3376d Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 19:50:58 +0200 Subject: Clean keyboard handler --- .../epfl/xblast/client/KeyboardEventHandler.java | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ch/epfl/xblast/client/KeyboardEventHandler.java b/src/ch/epfl/xblast/client/KeyboardEventHandler.java index bebc4a5..a0d3848 100644 --- a/src/ch/epfl/xblast/client/KeyboardEventHandler.java +++ b/src/ch/epfl/xblast/client/KeyboardEventHandler.java @@ -1,38 +1,47 @@ package ch.epfl.xblast.client; +import ch.epfl.xblast.Lists; import ch.epfl.xblast.PlayerAction; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.Map; +import java.util.Objects; import java.util.function.Consumer; /** + * The game action keyboard event handler. + * * @author Timothée FLOURE (257420) + * @author Pacien TRAN-GIRARD (261948) */ -public class KeyboardEventHandler extends KeyAdapter{ - private final Map playerActionMap; - private final Consumer consumer; +public class KeyboardEventHandler extends KeyAdapter { + + private final Map playerActionMap; + private final Consumer consumer; /** * Instantiates a new KeyboardEventHandler. * * @param playerActionMap the map of key codes to PlayerAction. - * @param consumer consumer related to the EventHandler + * @param consumer consumer related to the EventHandler */ - public KeyboardEventHandler(Map playerActionMap, Consumer consumer) { - this.playerActionMap = playerActionMap; + public KeyboardEventHandler(Map playerActionMap, Consumer consumer) { + this.playerActionMap = Lists.immutableMap(playerActionMap); this.consumer = consumer; } /** - * Executes the command related to the given keycode. + * Executes the command related to the given key code. * * @param e event (pressed key) */ @Override public void keyTyped(KeyEvent e) { - if (playerActionMap.containsKey(e.getKeyCode())) - consumer.accept(playerActionMap.get(e.getKeyCode())); + PlayerAction act = this.playerActionMap.get(e.getKeyCode()); + + if (Objects.nonNull(act)) + this.consumer.accept(act); } + } -- cgit v1.2.3 From f589243d394523ded22b5ddedfe2f1d7662bfc44 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 19:59:26 +0200 Subject: Reformat player action enum --- src/ch/epfl/xblast/PlayerAction.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ch/epfl/xblast/PlayerAction.java b/src/ch/epfl/xblast/PlayerAction.java index f4da746..703bc9d 100644 --- a/src/ch/epfl/xblast/PlayerAction.java +++ b/src/ch/epfl/xblast/PlayerAction.java @@ -1,9 +1,13 @@ package ch.epfl.xblast; /** + * The player action enum. + * * @author Timothée FLOURE (257420) + * @author Pacien TRAN-GIRARD (261948) */ public enum PlayerAction { + JOIN_GAME, MOVE_N, MOVE_E, @@ -11,4 +15,5 @@ public enum PlayerAction { MOVE_W, STOP, DROP_BOMB + } -- cgit v1.2.3 From 0a04160109c59dc4c257a8363a4098a45ba257aa Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 21:39:41 +0200 Subject: Refactor component --- src/ch/epfl/xblast/client/XBlastComponent.java | 206 ++++++++++++------------- 1 file changed, 98 insertions(+), 108 deletions(-) diff --git a/src/ch/epfl/xblast/client/XBlastComponent.java b/src/ch/epfl/xblast/client/XBlastComponent.java index ac9f223..cab6fde 100644 --- a/src/ch/epfl/xblast/client/XBlastComponent.java +++ b/src/ch/epfl/xblast/client/XBlastComponent.java @@ -1,143 +1,129 @@ package ch.epfl.xblast.client; +import ch.epfl.xblast.Cell; import ch.epfl.xblast.PlayerID; import javax.swing.*; import java.awt.*; +import java.awt.image.ImageObserver; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** + * The game graphical component. + * + * @author Pacien TRAN-GIRARD (261948) * @author Timothée FLOURE (257420) */ public final class XBlastComponent extends JComponent { - private static final int PREFERRED_WIDTH = 960; - private static final int PREFERRED_HEIGHT = 688; - private static final int BLOCK_WIDTH = 64; - private static final int BLOCK_HEIGHT = 48; - private static final int SCORE_IMAGE_WIDTH = 48; - private static final int SCORE_IMAGE_HEIGHT = 48; - private static final int TIME_LED_WIDTH = 16; - private static final int TIME_LED_HEIGHT = 16; - - private static final Font font = new Font("Arial", Font.BOLD, 25); - private static final int SCORES_TEXT_VERTICAL_POSITION = 659; - private static final int SCORES_P1_TEXT_HORIZONTAL_POSITION = 96; - private static final int SCORES_P2_TEXT_HORIZONTAL_POSITION = 240; - private static final int SCORES_P3_TEXT_HORIZONTAL_POSITION = 768; - private static final int SCORES_P4_TEXT_HORIZONTAL_POSITION = 912; - /** - * Displayed gameState. - */ - private GameState gamestate; + private static final ImageObserver IMG_OBSERVER = null; - /** - * Display a list of images on the given graphic context. - * - * @param g the graphic context - * @param images the given list of images, ordered in row-major order - */ - private void drawMap(Graphics2D g, List images) { - int x = 0; - int y = 0; + private static final Dimension CELL_DIMENSION = new Dimension(64, 48); + private static final Dimension SCORE_IMG_DIMENSION = new Dimension(48, 48); + private static final Dimension TIME_LED_DIMENSION = new Dimension(16, 16); + private static final Dimension PREFERRED_WINDOW_DIMENSION = frameDimension(); - for (Image img : images) { - if (x + BLOCK_WIDTH > PREFERRED_WIDTH) { - y += BLOCK_HEIGHT; // Or img.getHeight(null) ? - x = 0; - } + private static final List CELL_POSITIONS = buildCellGrid(CELL_DIMENSION); + private static final List SCORE_TXT_POSITIONS = buildScorePositionList(659); + private static final List SCORE_BG_POSITIONS = + buildLine(SCORE_IMG_DIMENSION, 20, new Point(0, CELL_DIMENSION.height * Cell.ROWS)); + private static final List TIME_LINE_POSITIONS = + buildLine(TIME_LED_DIMENSION, 60, new Point(0, CELL_DIMENSION.height * Cell.ROWS + SCORE_IMG_DIMENSION.height)); - if (img != null) - g.drawImage(img, x, y, null); + private static final Font TXT_FONT = new Font("Arial", Font.BOLD, 25); + private static final Color TXT_COLOR = Color.WHITE; - x += BLOCK_WIDTH; // Or img.getWidth(null) ? - } + private static Dimension frameDimension() { + return new Dimension( + CELL_DIMENSION.width * Cell.COLUMNS, + CELL_DIMENSION.height * Cell.ROWS + SCORE_IMG_DIMENSION.height + TIME_LED_DIMENSION.height); } - /** - * Compute the horizontal position of the player in px. - * - * @param player the given player - * @return the horizontal position in pixel - */ - private int playerXPosition(GameState.Player player) { return 4 * player.position().x() - 24; } + private static List buildCellGrid(Dimension elementDim) { + return Collections.unmodifiableList(Cell.ROW_MAJOR_ORDER.stream() + .map(c -> new Point(c.x() * elementDim.width, c.y() * elementDim.height)) + .collect(Collectors.toList())); + } - /** - * Compute the vertical position of the player in px. - * - * @param player the given player - * @return the vertical position in pixel - */ - private int playerYPosition(GameState.Player player) { return 3 * player.position().y() - 52; } + private static List buildLine(Dimension elementDim, int len, Point shift) { + return Collections.unmodifiableList(IntStream.range(0, len) + .mapToObj(x -> new Point(x * elementDim.width + shift.x, shift.y)) + .collect(Collectors.toList())); + } + + private static List buildScorePositionList(int vShift) { + return Arrays.asList( + new Point(96, vShift), + new Point(240, vShift), + new Point(768, vShift), + new Point(912, vShift)); + } + + private static void drawImage(Graphics2D g, Image img, Point pos) { + g.drawImage(img, pos.x, pos.y, IMG_OBSERVER); + } + + private static void drawGrid(Graphics2D g, List grid, List imgs) { + for (int i = 0; i < grid.size() && i < imgs.size(); ++i) + drawImage(g, imgs.get(i), grid.get(i)); + } + + private static Point playerPosition(GameState.Player p) { + return new Point( + 4 * p.position().x() - 24, + 3 * p.position().y() - 52); + } + + private GameState gameState; + private PlayerID playerID; /** * Draw the players on the graphic context. * - * @param g the graphic context + * @param g the graphic context * @param players the list of players to be displayed */ private void drawPlayers(Graphics2D g, List players) { - for (GameState.Player player : players) { - g.drawImage(player.image(), playerXPosition(player), playerYPosition(player), null); - } - } + // TODO: draw in preferred order - /** - * Draw the scores on the graphic context. - * - * @param g the graphic context - * @param scores the list of images composing the scores to be displayed - */ - private void drawScores(Graphics2D g, List scores) { - int x = 0; - int y = PREFERRED_HEIGHT - SCORE_IMAGE_HEIGHT - TIME_LED_HEIGHT; - for (Image img : scores) { - g.drawImage(img, x, y, null); - x += SCORE_IMAGE_WIDTH; - } + for (GameState.Player p : players) + drawImage(g, p.image(), playerPosition(p)); } /** - * Write the remaining lives of each player on the graphic context. + * Writes the remaining lives of each playerID on the graphic context. * - * @param g the graphic context + * @param g the graphic context * @param players list of players */ - private void writeScores(Graphics2D g,List players) { - g.setColor(Color.WHITE); - g.setFont(font); - - // Ugly !!!!! - g.drawString(Integer.toString(players.get(0).lives()), SCORES_P1_TEXT_HORIZONTAL_POSITION,SCORES_TEXT_VERTICAL_POSITION); - g.drawString(Integer.toString(players.get(1).lives()), SCORES_P2_TEXT_HORIZONTAL_POSITION,SCORES_TEXT_VERTICAL_POSITION); - g.drawString(Integer.toString(players.get(2).lives()), SCORES_P3_TEXT_HORIZONTAL_POSITION,SCORES_TEXT_VERTICAL_POSITION); - g.drawString(Integer.toString(players.get(3).lives()), SCORES_P4_TEXT_HORIZONTAL_POSITION,SCORES_TEXT_VERTICAL_POSITION); + private void writeScores(Graphics2D g, List players) { + g.setColor(TXT_COLOR); + g.setFont(TXT_FONT); + players.stream().forEach(p -> writeScore(g, p)); } - /** - * Draw the time "line" on the graphic context - * - * @param g the graphic context - * @param time the list of images composing the line - */ - private void drawTime(Graphics2D g, List time) { - int x = 0; - int y = PREFERRED_HEIGHT - TIME_LED_HEIGHT; - for (Image img : time) { - g.drawImage(img, x, y, null); - x += TIME_LED_WIDTH; - } + private void writeScore(Graphics2D g, GameState.Player p) { + String score = Integer.toString(p.lives()); + Point pos = SCORE_TXT_POSITIONS.get(p.id().ordinal()); + g.drawString(score, pos.x, pos.y); } /** - * Display the given GameState from the point of view of the given player. + * Display the given GameState from the point of view of the given playerID. * - * @param gameState GameState to be displayer - * @param player player related to the view + * @param gs GameState to be displayed + * @param pid playerID related to the view */ - public void setGameState(GameState gameState, PlayerID player) { - this.gamestate = gameState; + public void setGameState(GameState gs, PlayerID pid) { + this.gameState = gs; + this.playerID = pid; + repaint(); } @@ -148,7 +134,7 @@ public final class XBlastComponent extends JComponent { */ @Override public Dimension getPreferredSize() { - return new Dimension(PREFERRED_WIDTH,PREFERRED_HEIGHT); + return PREFERRED_WINDOW_DIMENSION; } /** @@ -158,14 +144,18 @@ public final class XBlastComponent extends JComponent { */ @Override protected void paintComponent(Graphics g0) { - if (this.gamestate != null) { - Graphics2D g = (Graphics2D) g0; - drawMap(g, this.gamestate.board()); - drawMap(g, this.gamestate.explosions()); - drawPlayers(g, this.gamestate.players()); - drawScores(g, this.gamestate.scores()); - writeScores(g, this.gamestate.players()); - drawTime(g, this.gamestate.ticks()); - }; + Graphics2D g = (Graphics2D) g0; + + if (Objects.isNull(this.gameState)) + return; + + drawGrid(g, CELL_POSITIONS, this.gameState.board()); + drawGrid(g, CELL_POSITIONS, this.gameState.explosions()); + drawGrid(g, SCORE_BG_POSITIONS, this.gameState.scores()); + drawGrid(g, TIME_LINE_POSITIONS, this.gameState.ticks()); + + drawPlayers(g, this.gameState.players()); + writeScores(g, this.gameState.players()); } + } -- cgit v1.2.3 From 596ce316680de8a82fbe5622f830b2a985a84644 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 22:15:12 +0200 Subject: Implement player painting order --- src/ch/epfl/xblast/Lists.java | 14 ++++++++++++++ src/ch/epfl/xblast/client/XBlastComponent.java | 19 +++++++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/ch/epfl/xblast/Lists.java b/src/ch/epfl/xblast/Lists.java index 073e0cc..da3f919 100644 --- a/src/ch/epfl/xblast/Lists.java +++ b/src/ch/epfl/xblast/Lists.java @@ -167,4 +167,18 @@ public final class Lists { .collect(Collectors.toList())); } + /** + * Returns an immutable copy of the given list, sorted using the supplied comparator. + * + * @param l the list to sort + * @param cmp the Comparator to use + * @param the type of the list's elements + * @return the sorted list + */ + public static List sorted(List l, Comparator cmp) { + List r = new ArrayList<>(l); + Collections.sort(r, cmp); + return Collections.unmodifiableList(r); + } + } diff --git a/src/ch/epfl/xblast/client/XBlastComponent.java b/src/ch/epfl/xblast/client/XBlastComponent.java index cab6fde..0b4f0bf 100644 --- a/src/ch/epfl/xblast/client/XBlastComponent.java +++ b/src/ch/epfl/xblast/client/XBlastComponent.java @@ -1,15 +1,14 @@ package ch.epfl.xblast.client; import ch.epfl.xblast.Cell; +import ch.epfl.xblast.Lists; import ch.epfl.xblast.PlayerID; import javax.swing.*; import java.awt.*; import java.awt.image.ImageObserver; -import java.util.Arrays; -import java.util.Collections; +import java.util.*; import java.util.List; -import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -64,6 +63,16 @@ public final class XBlastComponent extends JComponent { new Point(912, vShift)); } + private static Comparator buildPlayerComparator(PlayerID blackSheep) { + Comparator coordinateComparator = + (p1, p2) -> p2.position().y() - p1.position().y(); + + Comparator idComparator = + (p1, p2) -> p1.id() == blackSheep ? -1 : 0; + + return coordinateComparator.thenComparing(idComparator); + } + private static void drawImage(Graphics2D g, Image img, Point pos) { g.drawImage(img, pos.x, pos.y, IMG_OBSERVER); } @@ -89,9 +98,7 @@ public final class XBlastComponent extends JComponent { * @param players the list of players to be displayed */ private void drawPlayers(Graphics2D g, List players) { - // TODO: draw in preferred order - - for (GameState.Player p : players) + for (GameState.Player p : Lists.sorted(players, buildPlayerComparator(this.playerID))) drawImage(g, p.image(), playerPosition(p)); } -- cgit v1.2.3 From d9854415ffb5affb5021146a61bf76ebec0769e2 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 22:19:58 +0200 Subject: Reorder members --- src/ch/epfl/xblast/Lists.java | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ch/epfl/xblast/Lists.java b/src/ch/epfl/xblast/Lists.java index da3f919..cc6a1c8 100644 --- a/src/ch/epfl/xblast/Lists.java +++ b/src/ch/epfl/xblast/Lists.java @@ -65,6 +65,20 @@ public final class Lists { return r; } + /** + * Returns an immutable copy of the given list, sorted using the supplied comparator. + * + * @param l the list to sort + * @param cmp the Comparator to use + * @param the type of the list's elements + * @return the sorted list + */ + public static List sorted(List l, Comparator cmp) { + List r = new ArrayList<>(l); + Collections.sort(r, cmp); + return Collections.unmodifiableList(r); + } + /** * Returns a copy of the given list surrounded with element e. * @@ -167,18 +181,4 @@ public final class Lists { .collect(Collectors.toList())); } - /** - * Returns an immutable copy of the given list, sorted using the supplied comparator. - * - * @param l the list to sort - * @param cmp the Comparator to use - * @param the type of the list's elements - * @return the sorted list - */ - public static List sorted(List l, Comparator cmp) { - List r = new ArrayList<>(l); - Collections.sort(r, cmp); - return Collections.unmodifiableList(r); - } - } -- cgit v1.2.3 From e85032012f1d5ded1b6a49a88d345cc60a6ac88b Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 9 May 2016 22:52:56 +0200 Subject: Properly handle keyboard events --- src/ch/epfl/xblast/client/KeyboardEventHandler.java | 2 +- test/ch/epfl/xblast/simulation/GraphicalSimulation.java | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ch/epfl/xblast/client/KeyboardEventHandler.java b/src/ch/epfl/xblast/client/KeyboardEventHandler.java index a0d3848..2060959 100644 --- a/src/ch/epfl/xblast/client/KeyboardEventHandler.java +++ b/src/ch/epfl/xblast/client/KeyboardEventHandler.java @@ -37,7 +37,7 @@ public class KeyboardEventHandler extends KeyAdapter { * @param e event (pressed key) */ @Override - public void keyTyped(KeyEvent e) { + public void keyPressed(KeyEvent e) { PlayerAction act = this.playerActionMap.get(e.getKeyCode()); if (Objects.nonNull(act)) diff --git a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java index 8009d9f..9329cc7 100644 --- a/test/ch/epfl/xblast/simulation/GraphicalSimulation.java +++ b/test/ch/epfl/xblast/simulation/GraphicalSimulation.java @@ -11,6 +11,7 @@ import ch.epfl.xblast.server.Level; import ch.epfl.xblast.server.painter.BoardPainter; import javax.swing.*; +import java.awt.*; import java.awt.event.KeyEvent; import java.util.Collections; import java.util.HashMap; @@ -48,27 +49,28 @@ public class GraphicalSimulation extends Simulation { return GameStateDeserializer.deserialize(serializedGameState); } - private static JFrame buildFrame(XBlastComponent gui) { + private static JFrame buildFrame(Container content) { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); - frame.setContentPane(gui); + frame.setContentPane(content); frame.pack(); return frame; } - private void attachKeyboardHandler(JFrame frame) { - frame.addKeyListener(new KeyboardEventHandler(buildPlayerActionMap(), PLAYER_ACTION_CONSUMER)); - frame.requestFocusInWindow(); + private void attachKeyboardHandler(Component comp) { + comp.addKeyListener(new KeyboardEventHandler(buildPlayerActionMap(), PLAYER_ACTION_CONSUMER)); + comp.requestFocusInWindow(); } private final XBlastComponent gui; private GraphicalSimulation() { this.gui = new XBlastComponent(); - attachKeyboardHandler(buildFrame(this.gui)); + buildFrame(this.gui); + attachKeyboardHandler(this.gui); } @Override -- cgit v1.2.3 From 766114c05bcd0b0388988aaf606e046857e6946a Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Tue, 10 May 2016 17:10:31 +0200 Subject: Add lists and maps functions --- src/ch/epfl/xblast/Lists.java | 48 ++++++++++++++++++++++++++++++++++++++ test/ch/epfl/xblast/ListsTest.java | 47 +++++++++++++++++++++++++++++++++---- 2 files changed, 91 inse