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 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/ch/epfl/xblast/PlayerAction.java (limited to 'src/ch/epfl') 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 +} -- 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(-) (limited to 'src/ch/epfl') 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(+) (limited to 'src/ch/epfl') 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(-) (limited to 'src/ch/epfl') 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(-) (limited to 'src/ch/epfl') 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(-) (limited to 'src/ch/epfl') 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 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(-) (limited to 'src/ch/epfl') 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 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 (limited to 'src/ch/epfl') 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(-) (limited to 'src/ch/epfl') 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 (limited to 'src/ch/epfl') 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 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(-) (limited to 'src/ch/epfl') 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(+) (limited to 'src/ch/epfl') 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(-) (limited to 'src/ch/epfl') 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(-) (limited to 'src/ch/epfl') 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(-) (limited to 'src/ch/epfl') 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 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/ch/epfl') 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)) -- 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 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'src/ch/epfl') diff --git a/src/ch/epfl/xblast/Lists.java b/src/ch/epfl/xblast/Lists.java index cc6a1c8..bf0cafa 100644 --- a/src/ch/epfl/xblast/Lists.java +++ b/src/ch/epfl/xblast/Lists.java @@ -2,6 +2,7 @@ package ch.epfl.xblast; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; /** @@ -181,4 +182,51 @@ public final class Lists { .collect(Collectors.toList())); } + /** + * Maps linearly two lists. + * + * @param kl the keys list + * @param vl the values list + * @param the key type + * @param the value type + * @return the map + */ + public static Map linearMap(List kl, List vl) { + if (Objects.isNull(kl) || Objects.isNull(vl) || kl.size() != vl.size()) + throw new IllegalArgumentException(); + + return Collections.unmodifiableMap(IntStream + .range(0, kl.size()).mapToObj(i -> i) + .filter(i -> Objects.nonNull(vl.get(i))) + .collect(Collectors.toMap(kl::get, vl::get))); + } + + /** + * Merges two maps Map and Map -> Map. + * + * @param m1 key maps + * @param m2 values maps + * @param the keys type + * @param the intermediate keys type + * @param the values type + * @return the traversing map + */ + public static Map traverseMaps(Map m1, Map m2) { + return Collections.unmodifiableMap(m1.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> m2.get(e.getValue())))); + } + + /** + * Returns an inverted copy of a bijective map. + * + * @param m the map to invert + * @param the keys type + * @param the values type + * @return the inverted map + */ + public static Map invertMap(Map m) { + return Collections.unmodifiableMap(m.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey))); + } + } -- cgit v1.2.3 From e19a8b7ac050d1565fdf781b3ab86cda30042e85 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Tue, 10 May 2016 17:11:13 +0200 Subject: Implement Cell and SubCell natural ordering --- src/ch/epfl/xblast/Cell.java | 13 ++++++++++++- src/ch/epfl/xblast/SubCell.java | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'src/ch/epfl') diff --git a/src/ch/epfl/xblast/Cell.java b/src/ch/epfl/xblast/Cell.java index 8214066..6874b54 100644 --- a/src/ch/epfl/xblast/Cell.java +++ b/src/ch/epfl/xblast/Cell.java @@ -10,7 +10,7 @@ import java.util.List; * @author Pacien TRAN-GIRARD (261948) * @author Timothée FLOURE (257420) */ -public final class Cell { +public final class Cell implements Comparable { /** * The width of the board (number of columns). @@ -194,4 +194,15 @@ public final class Cell { return String.format("(%d,%d)", this.x, this.y); } + /** + * Compares two Cells relatively to their row major indexes. + * + * @param cell the other Cell to compare + * @return the comparison result + */ + @Override + public int compareTo(Cell cell) { + return Integer.compare(this.rowMajorIndex(), cell.rowMajorIndex()); + } + } diff --git a/src/ch/epfl/xblast/SubCell.java b/src/ch/epfl/xblast/SubCell.java index 4278fab..fd9db0b 100644 --- a/src/ch/epfl/xblast/SubCell.java +++ b/src/ch/epfl/xblast/SubCell.java @@ -6,7 +6,7 @@ package ch.epfl.xblast; * @author Pacien TRAN-GIRARD (261948) * @author Timothée FLOURE (257420) */ -public final class SubCell { +public final class SubCell implements Comparable { /** * The number of x-subdivisions of each Cell. @@ -165,4 +165,15 @@ public final class SubCell { return this.y * SUB_COLUMNS + this.x; } + /** + * Compares two SubCells relatively to their row major indexes. + * + * @param subCell the other SubCell to compare + * @return the comparison result + */ + @Override + public int compareTo(SubCell subCell) { + return Integer.compare(this.rowMajorIndex(), subCell.rowMajorIndex()); + } + } -- cgit v1.2.3 From 53bd5fc4c164eb45d5e0aa34dc103579c9619a32 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Tue, 10 May 2016 17:12:01 +0200 Subject: Refactor XBlastComponent --- src/ch/epfl/xblast/client/GameState.java | 13 +- src/ch/epfl/xblast/client/XBlastComponent.java | 194 +++++++++++++------------ 2 files changed, 114 insertions(+), 93 deletions(-) (limited to 'src/ch/epfl') diff --git a/src/ch/epfl/xblast/client/GameState.java b/src/ch/epfl/xblast/client/GameState.java index 2ac27e3..480c6a1 100644 --- a/src/ch/epfl/xblast/client/GameState.java +++ b/src/ch/epfl/xblast/client/GameState.java @@ -8,8 +8,11 @@ import ch.epfl.xblast.client.painter.ScorePainter; import ch.epfl.xblast.client.painter.TimeLinePainter; import java.awt.*; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; /** * The client representation of a game state. @@ -131,7 +134,7 @@ public final class GameState { } /** - * @return list containing all the images composing the actual score + * @return list containing all the images composing the score */ public List scores() { return this.scores; @@ -144,4 +147,12 @@ public final class GameState { return this.ticks; } + /** + * @return map of players' scores + */ + Map playerScores() { + return Collections.unmodifiableMap(this.players.stream() + .collect(Collectors.toMap(Player::id, Player::lives))); + } + } diff --git a/src/ch/epfl/xblast/client/XBlastComponent.java b/src/ch/epfl/xblast/client/XBlastComponent.java index 0b4f0bf..388a208 100644 --- a/src/ch/epfl/xblast/client/XBlastComponent.java +++ b/src/ch/epfl/xblast/client/XBlastComponent.java @@ -20,52 +20,98 @@ import java.util.stream.IntStream; */ public final class XBlastComponent extends JComponent { - private static final ImageObserver IMG_OBSERVER = null; - - 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(); - - 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)); - - private static final Font TXT_FONT = new Font("Arial", Font.BOLD, 25); - private static final Color TXT_COLOR = Color.WHITE; - - 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); - } + private static class Grid { + + 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 FRAME_DIMENSION = totalDimension(); + + private static final List CELL_POSITIONS = buildCellGrid(CELL_DIMENSION); + private static final List SCORE_BG_POSITIONS = buildScoreGrid(); + private static final List TIME_LINE_POSITIONS = buildTimeLineGrid(); + private static final Map SCORE_TXT_POSITIONS = buildScoreMap(659); + + private static Dimension totalDimension() { + return new Dimension( + CELL_DIMENSION.width * Cell.COLUMNS, + CELL_DIMENSION.height * Cell.ROWS + SCORE_IMG_DIMENSION.height + TIME_LED_DIMENSION.height); + } + + 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())); + } + + 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 buildScoreGrid() { + return buildLine( + SCORE_IMG_DIMENSION, + FRAME_DIMENSION.width / SCORE_IMG_DIMENSION.width, + new Point(0, CELL_DIMENSION.height * Cell.ROWS)); + } + + private static List buildTimeLineGrid() { + return buildLine( + TIME_LED_DIMENSION, + FRAME_DIMENSION.width / TIME_LED_DIMENSION.width, + new Point(0, CELL_DIMENSION.height * Cell.ROWS + SCORE_IMG_DIMENSION.height)); + } + + private static Map buildScoreMap(int vShift) { + Map m = new EnumMap<>(PlayerID.class); + m.put(PlayerID.PLAYER_1, new Point(96, vShift)); + m.put(PlayerID.PLAYER_2, new Point(240, vShift)); + m.put(PlayerID.PLAYER_3, new Point(768, vShift)); + m.put(PlayerID.PLAYER_4, new Point(912, vShift)); + return m; + } + + private static Point positionForPlayer(GameState.Player p) { + return new Point( + 4 * p.position().x() - 24, + 3 * p.position().y() - 52); + } - 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())); } - 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 class Painter { + + private static final ImageObserver IMG_OBSERVER = null; + private static final Font TXT_FONT = new Font("Arial", Font.BOLD, 25); + private static final Color TXT_COLOR = Color.WHITE; + + private static void drawString(Graphics2D g, Point pos, String str) { + g.setColor(TXT_COLOR); + g.setFont(TXT_FONT); + g.drawString(str, pos.x, pos.y); + } + + private static void drawImage(Graphics2D g, Point pos, Image img) { + g.drawImage(img, pos.x, pos.y, IMG_OBSERVER); + } + + private static void drawImageMap(Graphics2D g, Map m) { + for (Map.Entry e : m.entrySet()) + drawImage(g, e.getKey(), e.getValue()); + } + + private static void drawStringMap(Graphics2D g, Map m) { + for (Map.Entry e : m.entrySet()) + drawString(g, e.getKey(), e.getValue().toString()); + } - 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 Comparator buildPlayerComparator(PlayerID blackSheep) { Comparator coordinateComparator = - (p1, p2) -> p2.position().y() - p1.position().y(); + (p1, p2) -> p1.position().compareTo(p2.position()); Comparator idComparator = (p1, p2) -> p1.id() == blackSheep ? -1 : 0; @@ -73,54 +119,9 @@ public final class XBlastComponent extends JComponent { return coordinateComparator.thenComparing(idComparator); } - 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 players the list of players to be displayed - */ - private void drawPlayers(Graphics2D g, List players) { - for (GameState.Player p : Lists.sorted(players, buildPlayerComparator(this.playerID))) - drawImage(g, p.image(), playerPosition(p)); - } - - /** - * Writes the remaining lives of each playerID on the graphic context. - * - * @param g the graphic context - * @param players list of players - */ - private void writeScores(Graphics2D g, List players) { - g.setColor(TXT_COLOR); - g.setFont(TXT_FONT); - - players.stream().forEach(p -> writeScore(g, p)); - } - - 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 playerID. * @@ -130,8 +131,7 @@ public final class XBlastComponent extends JComponent { public void setGameState(GameState gs, PlayerID pid) { this.gameState = gs; this.playerID = pid; - - repaint(); + this.repaint(); } /** @@ -141,7 +141,7 @@ public final class XBlastComponent extends JComponent { */ @Override public Dimension getPreferredSize() { - return PREFERRED_WINDOW_DIMENSION; + return Grid.FRAME_DIMENSION; } /** @@ -156,13 +156,23 @@ public final class XBlastComponent extends JComponent { 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()); + Painter.drawImageMap(g, Lists.linearMap(Grid.CELL_POSITIONS, this.gameState.board())); + Painter.drawImageMap(g, Lists.linearMap(Grid.CELL_POSITIONS, this.gameState.explosions())); + Painter.drawImageMap(g, Lists.linearMap(Grid.SCORE_BG_POSITIONS, this.gameState.scores())); + Painter.drawImageMap(g, Lists.linearMap(Grid.TIME_LINE_POSITIONS, this.gameState.ticks())); + Painter.drawStringMap(g, Lists.traverseMaps(Lists.invertMap(Grid.SCORE_TXT_POSITIONS), this.gameState.playerScores())); + drawPlayers(g, Lists.sorted(this.gameState.players(), buildPlayerComparator(this.playerID))); + } - drawPlayers(g, this.gameState.players()); - writeScores(g, this.gameState.players()); + /** + * 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 p : players) + Painter.drawImage(g, Grid.positionForPlayer(p), p.image()); } } -- cgit v1.2.3