diff options
-rw-r--r-- | src/ch/epfl/xblast/server/GameState.java | 373 | ||||
-rw-r--r-- | src/ch/epfl/xblast/server/GameStateTransitioner.java | 349 |
2 files changed, 372 insertions, 350 deletions
diff --git a/src/ch/epfl/xblast/server/GameState.java b/src/ch/epfl/xblast/server/GameState.java index b304a4d..564ef38 100644 --- a/src/ch/epfl/xblast/server/GameState.java +++ b/src/ch/epfl/xblast/server/GameState.java | |||
@@ -6,7 +6,6 @@ import ch.epfl.xblast.*; | |||
6 | import java.util.*; | 6 | import java.util.*; |
7 | import java.util.function.Function; | 7 | import java.util.function.Function; |
8 | import java.util.stream.Collectors; | 8 | import java.util.stream.Collectors; |
9 | import java.util.stream.Stream; | ||
10 | 9 | ||
11 | 10 | ||
12 | /** | 11 | /** |
@@ -59,329 +58,26 @@ public final class GameState { | |||
59 | } | 58 | } |
60 | 59 | ||
61 | /** | 60 | /** |
62 | * Maps bombs to their owning player ID. | 61 | * Maps the given bombs to their position. |
63 | * | ||
64 | * @param b the list of bombs | ||
65 | * @return the mapping | ||
66 | */ | ||
67 | private static Map<PlayerID, List<Bomb>> mapBombsToPlayers(List<Bomb> b) { | ||
68 | return b.stream().collect(Collectors.groupingBy(Bomb::ownerId)); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * Computes the next state of a blast. | ||
73 | * | ||
74 | * @param blasts0 existing particles | ||
75 | * @param board0 the game's board | ||
76 | * @param explosions0 active explosions | ||
77 | * @return the position of the explosion's particles for the next state. | ||
78 | */ | ||
79 | private static List<Sq<Cell>> nextBlasts(List<Sq<Cell>> blasts0, Board board0, List<Sq<Sq<Cell>>> explosions0) { | ||
80 | return Stream.concat( | ||
81 | blasts0.stream() | ||
82 | .filter(b -> board0.blockAt(b.head()).isFree()) | ||
83 | .map(Sq::tail), | ||
84 | explosions0.stream() | ||
85 | .filter(e -> !e.isEmpty()) | ||
86 | .map(Sq::head)) | ||
87 | .filter(b -> !b.isEmpty()) | ||
88 | .collect(Collectors.toList()); | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * Computes and returns the next board state of the given board according to the given events. | ||
93 | * | ||
94 | * @param board0 the previous board | ||
95 | * @param consumedBonuses the set of consumed bonuses | ||
96 | * @param blastedCells1 the set of newly blasted cells | ||
97 | * @return the next board | ||
98 | */ | ||
99 | private static Board nextBoard(Board board0, Set<Cell> consumedBonuses, Set<Cell> blastedCells1) { | ||
100 | return new Board(Cell.ROW_MAJOR_ORDER.stream() | ||
101 | .map(c -> GameState.nextBlockSeq(c, board0.blocksAt(c), consumedBonuses, blastedCells1)) | ||
102 | .collect(Collectors.toList())); | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * Returns the next Block sequence for the given cell according to the current state and given events. | ||
107 | * | ||
108 | * @param c the Cell | ||
109 | * @param bs0 the previous Block sequence | ||
110 | * @param consumedBonuses the bonus consumption event | ||
111 | * @param blastedCells1 the new Cell blast events | ||
112 | * @return the new Block sequence | ||
113 | */ | ||
114 | private static Sq<Block> nextBlockSeq(Cell c, Sq<Block> bs0, Set<Cell> consumedBonuses, Set<Cell> blastedCells1) { | ||
115 | Block b = bs0.head(); | ||
116 | |||
117 | if (consumedBonuses.contains(c) && b.isBonus()) | ||
118 | return Sq.constant(Block.FREE); | ||
119 | |||
120 | if (blastedCells1.contains(c)) | ||
121 | if (b == Block.DESTRUCTIBLE_WALL) | ||
122 | return Sq.repeat(Ticks.WALL_CRUMBLING_TICKS, Block.CRUMBLING_WALL) | ||
123 | .concat(Sq.constant(Block.getRandomBonusBlock())); | ||
124 | |||
125 | else if (b.isBonus()) | ||
126 | return Sq.repeat(Ticks.BONUS_DISAPPEARING_TICKS, b) | ||
127 | .concat(Sq.constant(Block.FREE)); | ||
128 | |||
129 | return bs0.tail(); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * Computes and returns the next player list given the current one and the given events and states. | ||
134 | * | ||
135 | * @param players0 the previous player list | ||
136 | * @param playerBonuses the map of player bonuses | ||
137 | * @param bombedCells1 the set of newly bombed cells | ||
138 | * @param board1 the newly updated board | ||
139 | * @param blastedCells1 the set of newly blasted cells | ||
140 | * @param speedChangeEvents the speed change events | ||
141 | * @return the next player list | ||
142 | */ | ||
143 | private static List<Player> nextPlayers(List<Player> players0, Map<PlayerID, Bonus> playerBonuses, | ||
144 | Set<Cell> bombedCells1, Board board1, Set<Cell> blastedCells1, | ||
145 | Map<PlayerID, Optional<Direction>> speedChangeEvents) { | ||
146 | |||
147 | return players0.stream() | ||
148 | .map(p -> GameState.nextPlayer( | ||
149 | p, playerBonuses.get(p.id()), | ||
150 | bombedCells1, board1, blastedCells1, | ||
151 | speedChangeEvents.get(p.id()))) | ||
152 | .collect(Collectors.toList()); | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * Computes and returns the next State of a Player from the given events. | ||
157 | * | ||
158 | * @param player0 the Player | ||
159 | * @param playerBonus the optional Bonus to apply to the Player | ||
160 | * @param bombedCells1 the Set of bombed Cells | ||
161 | * @param board1 the updated Board | ||
162 | * @param blastedCells1 the Set of blasted Cells | ||
163 | * @param requestedDirection the Player's new requested Direction | ||
164 | * @return the next state of the Player | ||
165 | */ | ||
166 | private static Player nextPlayer(Player player0, Bonus playerBonus, | ||
167 | Set<Cell> bombedCells1, Board board1, Set<Cell> blastedCells1, | ||
168 | Optional<Direction> requestedDirection) { | ||
169 | |||
170 | // 1. Compute the new path to follow | ||
171 | Sq<Player.DirectedPosition> updatedPath = GameState.nextPath(player0.directedPositions(), requestedDirection); | ||
172 | |||
173 | // 2. Follow the path if the Player can (moving life state and no collision) | ||
174 | Sq<Player.DirectedPosition> directedPos1 = GameState.moveAhead(player0, updatedPath, board1, bombedCells1); | ||
175 | |||
176 | // 3. Apply damages and generate a new LifeState Sequence | ||
177 | Sq<Player.LifeState> lifeStates1 = GameState.nextLifeState(player0, directedPos1, blastedCells1); | ||
178 | |||
179 | // 4. Create the new player given the new parameters | ||
180 | Player p1 = new Player(player0.id(), lifeStates1, directedPos1, player0.maxBombs(), player0.bombRange()); | ||
181 | |||
182 | // 5. Update the capacities of the player given the possible bonus | ||
183 | if (!Objects.isNull(playerBonus)) | ||
184 | p1 = playerBonus.applyTo(p1); | ||
185 | |||
186 | return p1; | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * Computes the new path to follow according to the Player's wishes and the Board constraints. | ||
191 | * | ||
192 | * @param p0 the current path | ||
193 | * @param newDir the new requested Direction | ||
194 | * @return the new path | ||
195 | */ | ||
196 | private static Sq<Player.DirectedPosition> nextPath(Sq<Player.DirectedPosition> p0, Optional<Direction> newDir) { | ||
197 | if (Objects.isNull(newDir)) | ||
198 | return p0; | ||
199 | |||
200 | if (!newDir.isPresent()) | ||
201 | return GameState.stopPath(p0); | ||
202 | |||
203 | if (p0.head().direction().isPerpendicularTo(newDir.get())) | ||
204 | return GameState.pivotPath(Player.DirectedPosition.moving(p0.head()), newDir.get()); | ||
205 | |||
206 | return Player.DirectedPosition.moving(new Player.DirectedPosition(p0.head().position(), newDir.get())); | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * Builds and returns a path stopping at the next central SubCell. | ||
211 | * | ||
212 | * @param p0 the current path | ||
213 | * @return the stop path | ||
214 | */ | ||
215 | private static Sq<Player.DirectedPosition> stopPath(Sq<Player.DirectedPosition> p0) { | ||
216 | return GameState | ||
217 | .pathToNextCentralPosition(p0) | ||
218 | .concat(Player.DirectedPosition.stopped(GameState.nextCentral(p0))); | ||
219 | } | ||
220 | |||
221 | /** | ||
222 | * Builds and returns a pivot path reaching the next pivot SubCell and then rotating to the given Direction. | ||
223 | * | ||
224 | * @param p0 the initial path | ||
225 | * @param newDir the new Direction to follow when possible | ||
226 | * @return the pivot path | ||
227 | */ | ||
228 | private static Sq<Player.DirectedPosition> pivotPath(Sq<Player.DirectedPosition> p0, Direction newDir) { | ||
229 | SubCell nextCentralPos = GameState.nextCentral(p0).position(); | ||
230 | |||
231 | return GameState | ||
232 | .pathToNextCentralPosition(p0) | ||
233 | .concat(Player.DirectedPosition.moving(new Player.DirectedPosition(nextCentralPos, newDir))); | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * Returns the path to the next central SubCell. | ||
238 | * | ||
239 | * @param p the path to follow | ||
240 | * @return the truncated path | ||
241 | */ | ||
242 | private static Sq<Player.DirectedPosition> pathToNextCentralPosition(Sq<Player.DirectedPosition> p) { | ||
243 | return p.takeWhile(dp -> !dp.position().isCentral()); | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * Searches for and returns the next central SubCell reachable following the given path. | ||
248 | * | ||
249 | * @param p the path to follow | ||
250 | * @return the next central SubCell | ||
251 | */ | ||
252 | private static Player.DirectedPosition nextCentral(Sq<Player.DirectedPosition> p) { | ||
253 | return p.findFirst(dp -> dp.position().isCentral()); | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * Checks for possible collisions and update the path if necessary. | ||
258 | * | ||
259 | * @param path the current path projection | ||
260 | * @param board1 the updated Board | ||
261 | * @param bombedCells1 the Set of bombed Cell-s | ||
262 | * @return the corrected path | ||
263 | */ | ||
264 | private static Sq<Player.DirectedPosition> moveAhead(Player player0, | ||