Coverage Summary for Class: PlayerAction (it.polimi.ingsw.Controller.Actions)
Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
PlayerAction |
100%
(1/1)
|
100%
(6/6)
|
85%
(17/20)
|
91,7%
(22/24)
|
1 package it.polimi.ingsw.Controller.Actions;
2
3 import it.polimi.ingsw.Exceptions.Input.GenericInputValidationException;
4 import it.polimi.ingsw.Exceptions.Input.InputValidationException;
5 import it.polimi.ingsw.Exceptions.Input.InvalidElementException;
6 import it.polimi.ingsw.Misc.OptionalValue;
7 import it.polimi.ingsw.Model.Model;
8
9 import java.io.Serial;
10 import java.io.Serializable;
11 import java.util.List;
12
13 /**
14 * The {@link PlayerAction} object is the definition of a user's intention. <br>
15 * The various Classes that extend it are the only possible way to interact safely with the model.
16 */
17 public abstract class PlayerAction implements Serializable {
18 @Serial
19 private static final long serialVersionUID = 200L; // convention: 2 for controller, (01 -> 99) for objects
20
21 private final int playerBoardID;
22 private final boolean uniquePerTurn;
23
24 /**
25 * Package protected constructor used to initialize the playerBoardID.
26 *
27 * @param playerBoardID the ID of the player who wishes to interact with the controller.
28 * @param uniquePerTurn if set to false, the user can submit multiple actions of the same type during his turn.
29 */
30 protected PlayerAction(int playerBoardID, boolean uniquePerTurn) {
31 this.playerBoardID = playerBoardID;
32 this.uniquePerTurn = uniquePerTurn;
33 }
34
35 /**
36 * The validate function is used to check whether or not the declared {@link PlayerAction} is possible.<br>
37 * The validate function will check for:
38 * <ul>
39 * <li>The game must not be over</li>
40 * <li>The action must be called during the correct turn</li>
41 * <li>If the action is unique, check that no duplicates have been played before</li>
42 * <li>Any additional constraint imposed by {@link #customValidation(List, Model)}</li>
43 * </ul>
44 *
45 * @param history the controller stores a {@link List} of previous {@link PlayerAction}s related to the player taking
46 * the current turn (at every new turn, the history is cleared).
47 * Some actions may use this {@link List} to check for duplicates.
48 * @param ctx a reference to {@link Model}. Some actions may use this reference to check for consistency between what
49 * the actions declares and what the Model offers.
50 * @return An empty {@link OptionalValue} in case of a successful validation. Otherwise the returned {@link OptionalValue}
51 * contains the related {@link InputValidationException}
52 */
53 public final OptionalValue<InputValidationException> validate(List<PlayerAction> history, Model ctx) {
54 OptionalValue<InputValidationException> gameRunningCheck = isGameRunning(ctx);
55 if (gameRunningCheck.isPresent()) return gameRunningCheck;
56
57 OptionalValue<InputValidationException> correctTurnCheck = isCorrectTurn(ctx);
58 if (correctTurnCheck.isPresent()) return correctTurnCheck;
59
60 OptionalValue<InputValidationException> duplicateCheck = isDuplicate(history);
61 if (duplicateCheck.isPresent()) return duplicateCheck;
62
63 OptionalValue<InputValidationException> customValidationCheck = customValidation(history, ctx);
64 if (customValidationCheck.isPresent()) return customValidationCheck;
65
66 return OptionalValue.empty();
67 }
68
69 /**
70 * SUB-VALIDATION FUNCTION: <br>
71 * if the game is not active anymore (i.e. the game is over and no actions can be made), this function will return a non-empty value.
72 *
73 * @param ctx the {@link Model} object, used during verification.
74 * @return an {@link OptionalValue} value, the value is empty if no issues are found during the validation of the function. Else the
75 * value will contain a {@link Throwable} {@link Exception} that can be used to propagate the error message.
76 */
77 private OptionalValue<InputValidationException> isGameRunning(Model ctx) {
78 if (ctx.isGameOver()) {
79 return OptionalValue.of(new GenericInputValidationException(this.getClass().getSimpleName(), "Game is over"));
80 }
81 return OptionalValue.empty();
82 }
83
84 /**
85 * SUB-VALIDATION FUNCTION: <br>
86 * if the {@link PlayerAction}'s declared player is not the current player that needs to play, this function will return a non-empty value.
87 *
88 * @param ctx the {@link Model} object, used during verification.
89 * @return an {@link OptionalValue} value, the value is empty if no issues are found during the validation of the function. Else the
90 * value will contain a {@link Throwable} {@link Exception} that can be used to propagate the error message.
91 */
92 private OptionalValue<InputValidationException> isCorrectTurn(Model ctx) {
93 if (!(ctx.getMutablePlayerBoards().size() > this.getPlayerBoardID())) {
94 return OptionalValue.of(new InvalidElementException("PlayerBoardID out of range"));
95 }
96 if (ctx.getMutableTurnOrder().getMutableCurrentPlayer().getId() != this.getPlayerBoardID()) {
97 return OptionalValue.of(new GenericInputValidationException(this.getClass().getSimpleName(), "It's not your turn yet"));
98 }
99 return OptionalValue.empty();
100 }
101
102 /**
103 * SUB-VALIDATION FUNCTION: <br>
104 * if the {@link PlayerAction} is marked as unique per turn, this function will return a non-empty value in case of a duplicate
105 * action being present in the history
106 *
107 * @param history a list of previous actions submitted by the player
108 * @return an {@link OptionalValue} value, the value is empty if no issues are found during the validation of the function. Else the
109 * value will contain a {@link Throwable} {@link Exception} that can be used to propagate the error message.
110 */
111 private OptionalValue<InputValidationException> isDuplicate(List<PlayerAction> history) {
112 if (!this.uniquePerTurn || history.stream().noneMatch(h -> h.getClass() == this.getClass())) {
113 return OptionalValue.empty();
114 }
115 return OptionalValue.of(new GenericInputValidationException(this.getClass().getSimpleName(), "Too many similar actions have been executed"));
116 }
117
118 /**
119 * This function is used by {@link #validate(List, Model)} to check whether or not the declared {@link PlayerAction} is possible.<br>
120 * This function will check for the following requirements:
121 *
122 * @param history the controller stores a {@link List} of previous {@link PlayerAction}s related to the player taking
123 * the current turn (at every new turn, the history is cleared).
124 * Some actions may use this {@link List} to check for duplicates.
125 * @param ctx a reference to {@link Model}. Some actions may use this reference to check for consistency between what
126 * the actions declares and what the Model offers.
127 * @return An empty {@link OptionalValue} in case of a successful validation. Otherwise the returned {@link OptionalValue}
128 * contains the related {@link InputValidationException}
129 */
130 protected abstract OptionalValue<InputValidationException> customValidation(List<PlayerAction> history, Model ctx);
131
132 /**
133 * @return the {@link it.polimi.ingsw.Model.PlayerBoard} id set during construction of the Action.
134 */
135 final public int getPlayerBoardID() {
136 return playerBoardID;
137 }
138
139 /**
140 * Every class extending {@link PlayerAction} must implement the following method, which takes a {@link Model} reference and
141 * applies the concrete effect of the action. <br>
142 * Warning: this function, as implied by the name, is unsafe. it should never be called by called outside the scope
143 * of the class {@link it.polimi.ingsw.Controller.Controller}, which takes adequate precautions in order to guarantee
144 * a coherent execution of the method.
145 *
146 * @param ctx the {@link Model} reference, once the method finishes running the game state will be altered.
147 * @throws Exception Should an error occur during the execution of the method, such error will be reported through the
148 * thrown {@link Exception}. Note that any {@link PlayerAction} inheritor should guarantee the absence of Exceptions
149 * for any positive return value yielded by {@link #validate(List, Model)}
150 */
151 public abstract void unsafeExecute(Model ctx) throws Exception;
152 }