Coverage Summary for Class: Card10 (it.polimi.ingsw.Model)
Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Card10 |
100%
(1/1)
|
100%
(3/3)
|
82,1%
(23/28)
|
98%
(48/49)
|
1 package it.polimi.ingsw.Model;
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.Misc.Pair;
8 import it.polimi.ingsw.Model.Enums.PawnColour;
9
10 import java.io.Serial;
11 import java.util.ArrayList;
12 import java.util.EnumMap;
13 import java.util.List;
14 import java.util.Map;
15
16 import static it.polimi.ingsw.Misc.Utils.canMapFit;
17
18 /**
19 * EFFECT: You may exchange up to 2 Students between your entrance and your Dining Room
20 */
21 public class Card10 extends StatelessEffect {
22 @Serial
23 private static final long serialVersionUID = 112L; // convention: 1 for model, (01 -> 99) for objects
24
25 public Card10(Model ctx) {
26 super(10, 1, ctx);
27 }
28
29 /**
30 * Refer to: {@link CharacterCard#overridableCheckInput(CharacterCardInput)} for further information
31 *
32 * @param input CharacterCardInput should contain:
33 * <ul>
34 * <li>A valid list of pair having following properties: </li>
35 * <ul>
36 * No more than two pairs<br>
37 * No null values inside pairs
38 * </ul>
39 * <li>Every pairs must follow this format:</li>
40 * <ul>
41 * first element from entrance and second from diningRoom
42 * </ul>
43 * <li>a valid PawnColour from card</li>
44 * </ul>
45 */
46 @Override
47 public OptionalValue<InputValidationException> overridableCheckInput(CharacterCardInput input) {
48 //convention of input.targetPawnPairs ---> array of pairs, first element is from entrance, second is from diningRoom
49 OptionalValue<List<Pair<PawnColour, PawnColour>>> optionalPawnPairs = input.getTargetPawnPairs();
50 // make sure that:
51 if (
52 optionalPawnPairs.isEmpty() || // target pawn pairs was set as parameter
53 //optionalPawnPair.get().size() == 0 || // target pawn pairs is not empty (technically allowed)
54 optionalPawnPairs.get().size() > 2 || // target pawn pairs are not over the pair limit of 2 swaps
55 optionalPawnPairs.get().stream().anyMatch(p -> p.first() == null || p.second() == null) // no null values in pair
56 ) {
57 // in case throw exception for invalid element in input
58 return OptionalValue.of(new InvalidElementException("Target Pawn Pairs"));
59 }
60 // explode pawnpairs into respective arrays of elements
61 List<Pair<PawnColour, PawnColour>> pawnPairs = optionalPawnPairs.get();
62 // first count how many students of each colour the user picked
63 Map<PawnColour, Integer> comingFromEntrance = new EnumMap<>(PawnColour.class); // counts user entrance selected colours
64 Map<PawnColour, Integer> comingFromDiningRoom = new EnumMap<>(PawnColour.class); // counts diningroom selected colours
65 for (Pair<PawnColour, PawnColour> pair : pawnPairs) {
66 comingFromEntrance.merge(pair.first(), 1, Integer::sum);
67 comingFromDiningRoom.merge(pair.second(), 1, Integer::sum);
68 }
69
70 // get user entrance counts per colour
71 PlayerBoard playerBoard = input.getCaller();
72 Map<PawnColour, Integer> entranceMap = new EnumMap<>(PawnColour.class); // counts user entrance total colours
73 for (PawnColour pawn : playerBoard.getEntranceStudents().stream()
74 .filter(OptionalValue::isPresent)
75 .map(OptionalValue::get)
76 .toList()) {
77 entranceMap.merge(pawn, 1, Integer::sum);
78 }
79
80 // make sure the elements coming from user (first) are also mapped to entrance
81 if (!canMapFit(entranceMap, comingFromEntrance)) {
82 return OptionalValue.of(new InvalidElementException("Target Pawn Pairs"));
83 }
84
85 Map<PawnColour, Integer> diningRoomMap = new EnumMap<>(PawnColour.class); // counts user diningRoom total colours
86 for (PawnColour pawn : PawnColour.values()) {
87 diningRoomMap.merge(pawn, playerBoard.getDiningRoomCount(pawn), Integer::sum);
88 }
89 // make sure the elements coming from diningRoom (second) are also mapped to the diningroom
90 if (!canMapFit(diningRoomMap, comingFromDiningRoom)) {
91 return OptionalValue.of(new InvalidElementException("Target Pawn Pairs"));
92 }
93
94 // validate size of dining room
95 for (PawnColour p : comingFromEntrance.keySet()) {
96 if (playerBoard.getDiningRoomCount(p) - comingFromDiningRoom.getOrDefault(p, 0) + comingFromEntrance.getOrDefault(p, 0) > 10) {
97 return OptionalValue.of(new GenericInputValidationException("Dining Room",
98 "can't contain " + pawnPairs.size()
99 + "elements without overflowing on one of its lanes."));
100 }
101 }
102
103 return OptionalValue.empty(); // all checks passed, return true
104 }
105
106 /**
107 * Refer to: {@link CharacterCard#unsafeApplyEffect(CharacterCardInput)} for further information
108 */
109 @Override
110 protected void unsafeApplyEffect(CharacterCardInput input) throws Exception {
111 // explode pawnpairs into respective arrays of elements
112 List<Pair<PawnColour, PawnColour>> pawnPairs = input.getTargetPawnPairs().get();
113 List<PawnColour> fromEntrance = new ArrayList<>(pawnPairs.size());
114 List<PawnColour> fromDiningRoom = new ArrayList<>(pawnPairs.size());
115 // get the playerboard to operate on
116 PlayerBoard playerBoard = input.getCaller();
117 for (Pair<PawnColour, PawnColour> p : pawnPairs) {
118 fromEntrance.add(p.first());
119 fromDiningRoom.add(p.second());
120 playerBoard.removeStudentFromEntrance(p.first());
121 this.context.removeStudentFromDiningRoom(p.second(), playerBoard);
122 }
123 // true effect happens here
124 playerBoard.addStudentsToEntrance(fromDiningRoom);
125 for (PawnColour student : fromEntrance) {
126 this.context.addStudentToDiningRoom(student, playerBoard);
127 }
128 }
129
130 //test purpose only
131 }