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 }