aboutsummaryrefslogtreecommitdiff
path: root/src/ch/epfl/xblast/server/Player.java
blob: 7b5ac26974abe042943e828e4e28f2b8b93c04cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
package ch.epfl.xblast.server;

import ch.epfl.cs108.Sq;
import ch.epfl.xblast.*;

import java.util.Objects;

/**
 * A Player.
 *
 * @author Pacien TRAN-GIRARD (261948)
 * @author Timothée FLOURE (257420)
 */
public final class Player {

    /**
     * The life state of a player.
     */
    public static final class LifeState {

        /**
         * Enum containing all the possible life states.
         */
        public enum State {
            INVULNERABLE,
            VULNERABLE,
            DYING,
            DEAD
        }

        private final int lives;
        private final State state;

        /**
         * Instantiates a new LifeSate.
         *
         * @param lives the number of lives
         * @param state the state
         * @throws IllegalArgumentException if lives is negative
         */
        public LifeState(int lives, State state) {
            this.lives = ArgumentChecker.requireNonNegative(lives);
            this.state = state;
        }

        /**
         * @return the number of lives
         */
        public int lives() {
            return lives;
        }

        /**
         * @return the state
         */
        public State state() {
            return state;
        }

        /**
         * @return true if the actual state allow to move
         */
        public boolean canMove() {
            return (state() == State.INVULNERABLE || state() == State.VULNERABLE);
        }

    }

    /**
     * The "directed" position of a player.
     */
    public static final class DirectedPosition {

        private final SubCell position;
        private final Direction direction;

        /**
         * @return an infinite sequence of directed positions corresponding to a stopped player.
         */
        public static Sq<DirectedPosition> stopped(DirectedPosition p) {
            return Sq.constant(p);
        }

        /**
         * @return an infinite sequence of directed position corresponding to a moving player.
         */
        public static Sq<DirectedPosition> moving(DirectedPosition p) {
            return Sq.iterate(p, x -> x.withPosition(x.position().neighbor(x.direction())));
        }

        /**
         * Instantiates a new DirectedPos
         *
         * @param position  the position of the player
         * @param direction the direction of the player
         * @throws IllegalArgumentException if position or direction is null
         */
        public DirectedPosition(SubCell position, Direction direction) {
            this.position = Objects.requireNonNull(position);
            this.direction = Objects.requireNonNull(direction);
        }

        /**
         * @return the position
         */
        public SubCell position() {
            return position;
        }

        /**
         * @return a new directed position with the given position and the previous direction
         */
        public DirectedPosition withPosition(SubCell newPosition) {
            return new DirectedPosition(newPosition, direction);
        }

        /**
         * @return the direction
         */
        public Direction direction() {
            return direction;
        }

        /**
         * @return a new directed position with the previous position and the given direction
         */
        public DirectedPosition withDirection(Direction newDirection) {
            return new DirectedPosition(position, newDirection);
        }

    }

    /**
     * The default Direction of a new Player.
     */
    private static final Direction DEFAULT_DIRECTION = Direction.S;

    private final PlayerID id;
    private final Sq<LifeState> lifeStates;
    private final Sq<DirectedPosition> directedPos;
    private final int maxBombs;
    private final int bombRange;

    /**
     * Builds a default LifeState sequence with the given number of lives.
     *
     * @param lives number of lives of the desired sequence
     * @return the sequence
     */
    private static Sq<LifeState> buildDefaultLifeStateSequence(int lives) {
        LifeState invulnerability = new LifeState(
                ArgumentChecker.requireNonNegative(lives),
                LifeState.State.INVULNERABLE
        );
        LifeState vulnerability = new LifeState(
                ArgumentChecker.requireNonNegative(lives),
                LifeState.State.VULNERABLE
        );

        return Sq.repeat(Ticks.PLAYER_INVULNERABLE_TICKS, invulnerability).concat(Sq.constant(vulnerability));
    }

    /**
     * Builds a default DirectedPosition sequence at the given position.
     *
     * @param pos the position
     * @return the sequence
     */
    private static Sq<DirectedPosition> buildDefaultDirectedPositionSequence(Cell pos) {
        DirectedPosition dp = new DirectedPosition(
                SubCell.centralSubCellOf(Objects.requireNonNull(pos)),
                Player.DEFAULT_DIRECTION
        );

        return DirectedPosition.stopped(dp);
    }

    /**
     * Instantiates a new Player.
     *
     * @param id          the Player's id
     * @param lifeStates  a sequence of LifeState-s
     * @param directedPos a sequence of DirectedPosition-s
     * @param maxBombs    the maximum number of Bomb-s the Player can carry
     * @param bombRange   the range of the Bomb-s
     * @throws IllegalArgumentException
     * @throws NullPointerException
     */
    public Player(PlayerID id, Sq<LifeState> lifeStates, Sq<DirectedPosition> directedPos, int maxBombs, int bombRange) {
        this.id = Objects.requireNonNull(id);
        this.lifeStates = Objects.requireNonNull(lifeStates);
        this.directedPos = Objects.requireNonNull(directedPos);
        this.maxBombs = ArgumentChecker.requireNonNegative(maxBombs);
        this.bombRange = ArgumentChecker.requireNonNegative(bombRange);
    }

    /**
     * Instantiates a new Player.
     *
     * @param id        the Player's id
     * @param lives     the number of lives of the Player
     * @param position  the starting position of the Player
     * @param maxBombs  the maximum number of Bomb-s the Player can carry
     * @param bombRange the range of the Bomb-s
     * @throws IllegalArgumentException
     * @throws NullPointerException
     */
    public Player(PlayerID id, int lives, Cell position, int maxBombs, int bombRange) {
        this(
                id,
                Player.buildDefaultLifeStateSequence(lives),
                Player.buildDefaultDirectedPositionSequence(position),
                maxBombs,
                bombRange
        );
    }

    /**
     * @return the player's ID
     */
    public PlayerID id() {
        return id;
    }

    /**
     * @return the player's life states
     */
    public Sq<LifeState> lifeStates() {
        return lifeStates;
    }

    /**
     * @return the sequence related to the player's next life
     */
    public Sq<LifeState> statesForNextLife() {
        LifeState dying = new LifeState(lives(), LifeState.State.DYING);
        Sq<LifeState> nextLifeState = Sq.repeat(Ticks.PLAYER_DYING_TICKS, dying);

        int newLives = lives() - 1;

        if (newLives >= 0) {
            LifeState dead = new LifeState(newLives, LifeState.State.DEAD);
            nextLifeState = nextLifeState.concat(Sq.constant(dead));
        } else {
            LifeState invulnerable = new LifeState(newLives, LifeState.State.INVULNERABLE);
            LifeState vulnerable = new LifeState(newLives, LifeState.State.VULNERABLE);

            nextLifeState = nextLifeState.concat(Sq.repeat(Ticks.PLAYER_INVULNERABLE_TICKS, invulnerable));
            nextLifeState = nextLifeState.concat(Sq.constant(vulnerable));

        }
        return nextLifeState;
    }

    /**
     * @return the current life state of the player
     */
    public LifeState lifeState() {
        return lifeStates.head();
    }

    /**
     * @return the current number of lives of the player
     */
    public int lives() {
        return lifeStates.head().lives();
    }

    /**
     * @return true is the player has more than 0 lives
     */
    public boolean isAlive() {
        return lives() >= 0;
    }

    /**
     * @return the directed position sequence of the player
     */
    public Sq<DirectedPosition> directedPositions() {
        return directedPos;
    }

    /**
     * @return the position of the player
     */
    public SubCell position() {
        return directedPos.head().position();
    }

    /**
     * @return the current direction of the player
     */
    public Direction direction() {
        return directedPos.head().direction();
    }

    /**
     * @return the maximum number of bombs that the player can use
     */
    public int maxBombs() {
        return maxBombs;
    }

    /**
     * @return a new Player with the new maximum of bombs
     */
    public Player withMaxBombs(int newMaxBombs) {
        return new Player(id, lifeStates, directedPos, newMaxBombs, bombRange);
    }

    /**
     * @return the range of the player's bomb
     */
    public int bombRange() {
        return bombRange;
    }

    /**
     * @return a new Player with the new bomb range
     */
    public Player withBombRange(int newBombRange) {
        return new Player(id, lifeStates, directedPos, maxBombs, newBombRange);
    }

    /**
     * @return a new bomb posed by the Player
     */
    public Bomb newBomb() {
        return new Bomb(id, position().containingCell(), Ticks.BOMB_FUSE_TICKS, bombRange);
    }

}