Coverage Summary for Class: PlayerBoard (it.polimi.ingsw.Model)
| Class | Class, % | Method, % | Branch, % | Line, % | 
|---|---|---|---|---|
| PlayerBoard | 100% (1/1) | 90% (18/20) | 82% (41/50) | 87,9% (58/66) | 
1 package it.polimi.ingsw.Model; 2 3 import it.polimi.ingsw.Exceptions.Container.EmptyContainerException; 4 import it.polimi.ingsw.Exceptions.Container.FullContainerException; 5 import it.polimi.ingsw.Exceptions.Container.InvalidContainerIndexException; 6 import it.polimi.ingsw.Exceptions.Input.InvalidElementException; 7 import it.polimi.ingsw.Exceptions.Operation.ForbiddenOperationException; 8 import it.polimi.ingsw.Logger; 9 import it.polimi.ingsw.Misc.OptionalValue; 10 import it.polimi.ingsw.Model.Enums.PawnColour; 11 12 import java.io.Serial; 13 import java.io.Serializable; 14 import java.util.*; 15 16 /** 17 * This class represents the Player's part of the board 18 */ 19 public class PlayerBoard implements Serializable { 20 @Serial 21 private static final long serialVersionUID = 126L; // convention: 1 for model, (01 -> 99) for objects 22 private final String nickname; 23 private final AssistantCard[] assistantCards; 24 private final Map<PawnColour, Integer> diningRoom; 25 private final List<OptionalValue<PawnColour>> entrance; 26 private final int id; 27 private int coinBalance; 28 29 /** 30 * Generates a board and initializes its structures 31 * 32 * @param id the ID of the player, used to reference the board through various classes. 33 * @param numOfPlayers when creating a board, the number of players becomes important during initialization of various 34 * internal structures 35 * @param nickname every player has a nickname and the board can store it. Nicknames are not guaranteed to be unique, so 36 * identifying a board through nicknames is highly insecure. 37 * @param studentBag used during the initialization of the board. 38 */ 39 public PlayerBoard(int id, int numOfPlayers, String nickname, StudentBag studentBag) { 40 this.nickname = nickname; 41 this.assistantCards = new AssistantCard[10]; 42 for (int i = 1; i <= 10; i++) { 43 assistantCards[i - 1] = new AssistantCard(i); 44 } 45 this.coinBalance = 1; 46 this.id = id; 47 this.diningRoom = new EnumMap<>(PawnColour.class); 48 for (PawnColour p : PawnColour.values()) { 49 diningRoom.put(p, 0); 50 } 51 this.entrance = new ArrayList<>(numOfPlayers == 3 ? 9 : 7); 52 if (numOfPlayers >= 2 && numOfPlayers <= 4) { 53 for (int i = 0; i < (numOfPlayers == 3 ? 9 : 7); i++) { 54 try { 55 entrance.add(OptionalValue.of(studentBag.extract())); 56 } catch (EmptyContainerException e) { 57 // should never happen 58 Logger.severe("student bag was found empty while adding a student to an student entrance. Critical, unrecoverable, error"); 59 throw new RuntimeException(e); 60 } 61 } 62 } else { 63 throw new RuntimeException("Inconsistent number of players"); 64 } 65 } 66 67 /** 68 * Get the {@link AssistantCard}s linked to the player 69 * 70 * @return an Unmodifiable {@link List} of {@link AssistantCard} linked to the player 71 */ 72 public List<AssistantCard> getMutableAssistantCards() { 73 return Arrays.stream(assistantCards).toList(); 74 } 75 76 /** 77 * Get the current coin balance of the player 78 * 79 * @return an integer representing the amount of coins owned by the player 80 */ 81 public int getCoinBalance() { 82 return coinBalance; 83 } 84 85 /** 86 * Sets the coin balance for the PlayerBoard. <br> 87 * NOTE: This method should only be called by tests 88 * 89 * @param balance the amount of coins you want to set the {@link PlayerBoard}'s balance to 90 */ 91 public void setCoinBalance(int balance) { 92 this.coinBalance = balance; 93 } 94 95 /** 96 * Get the mappings from {@link PawnColour} to number of students of that colour in the Dining room 97 * 98 * @return an Unmodifiable {@link Map} from {@link PawnColour} to {@link Integer} 99 */ 100 public Map<PawnColour, Integer> getDiningRoom() { 101 return Map.copyOf(diningRoom); 102 } 103 104 /** 105 * Get a list of the active slots usable in the student entrance 106 * 107 * @return an Unmodifiable {@link List} representing each slot of the Entrance 108 */ 109 public List<OptionalValue<PawnColour>> getEntranceStudents() { 110 return List.copyOf(entrance); 111 } 112 113 /** 114 * Get the ID of the player 115 * 116 * @return the ID of the PlayerBoard 117 */ 118 public int getId() { 119 return id; 120 } 121 122 /** 123 * Get the Nickname of the player 124 * 125 * @return the Nickname of the PlayerBoard 126 */ 127 public String getNickname() { 128 return nickname; 129 } 130 131 /** 132 * Unsafely add a student to the Dining room. The addition is unsafe because it doesn't track gained coins or teachers. 133 * This method is used by {@link Model#addStudentToDiningRoom(PawnColour, PlayerBoard)} which is the "safe" version. 134 * 135 * @param colour the colour of the student to add to the dining room 136 * @return true if a new coin is to be added to the player's balance 137 * @throws FullContainerException if the dining room is full on the lane of colour before the addition 138 */ 139 protected boolean unsafeAddStudentToDiningRoom(PawnColour colour) throws FullContainerException { 140 if (this.isDiningRoomFull(colour)) { 141 throw new FullContainerException("Dining Room"); 142 } 143 this.diningRoom.merge(colour, 1, Integer::sum); 144 return this.diningRoom.get(colour) % 3 == 0; // returns true when a coin should be added 145 } 146 147 /** 148 * Check to see if the dining room can accommodate more students on a lane 149 * 150 * @param student selects the lane of the dining room to inspect 151 * @return true if the dining room's lane is full, false otherwise 152 */ 153 public boolean isDiningRoomFull(PawnColour student) { 154 return getDiningRoomCount(student) >= 10; 155 } 156 157 /** 158 * Get the amount of students in a lane of the dining room 159 * 160 * @param colour selects the lane of the dining room to inspect 161 * @return the count of students in the lane selected by colour 162 */ 163 public int getDiningRoomCount(PawnColour colour) { 164 return diningRoom.get(colour); 165 } 166 167 /** 168 * Unsafely remove a student from the Dining room. The removal is unsafe because it doesn't track teachers. 169 * This method is used by {@link Model#removeStudentFromDiningRoom(PawnColour, PlayerBoard)} which is the "safe" version. 170 * 171 * @param colour the colour of the student to remove the dining room 172 * @throws EmptyContainerException if the dining room is empty on the lane of colour before the removal 173 */ 174 public void unsafeRemoveStudentFromDiningRoom(PawnColour colour) throws EmptyContainerException { 175 if (this.getDiningRoomCount(colour) == 0) { 176 throw new EmptyContainerException("Dining Room"); 177 } else { 178 this.diningRoom.merge(colour, -1, Integer::sum); 179 } 180 } 181 182 /** 183 * Add multiple students to slots in the entrance 184 * 185 * @param students a {@link List} of {@link PawnColour} containing the students to add to the entrance 186 * @throws FullContainerException if the entrance isn's capable of holding all the students, the students are not added and the 187 * exception is thrown 188 */ 189 public void addStudentsToEntrance(List<PawnColour> students) throws FullContainerException { 190 if (students.size() > 0) { 191 if (students.size() > this.getEntranceSpaceLeft()) { 192 // 2 & 4 players -> 7 students placed on entrance, 3 players -> 9 students placed on entrance 193 throw new FullContainerException("Entrance"); 194 } 195 int cont = 0; 196 for (int i = 0; i < this.getEntranceSize(); i++) { 197 if (this.entrance.get(i).isEmpty()) { 198 this.entrance.set(i, OptionalValue.of(students.get(cont++))); 199 if (cont == students.size()) { 200 break; 201 } 202 } 203 } 204 } 205 } 206 207 /** 208 * Get the amount of free slots in the student entrance 209 * 210 * @return the count of free slots in the entrance 211 */ 212 public int getEntranceSpaceLeft() { 213 return (int) entrance.stream() 214 .filter(OptionalValue::isEmpty) 215 .count(); 216 } 217 218 /** 219 * Get the size of the entrance (can change depending on the number of players) 220 * 221 * @return the size of the entrance 222 */ 223 public int getEntranceSize() { 224 return entrance.size(); 225 } 226 227 /** 228 * Add a single student to a slot in the entrance 229 * 230 * @param student a {@link PawnColour} representing the student to add to the entrance 231 * @throws FullContainerException if the entrance isn's capable of the student, the student is not added and the 232 * exception is thrown 233 */ 234 public void addStudentToEntrance(PawnColour student) throws FullContainerException { 235 if (this.getEntranceSpaceLeft() == 0) { 236 // 2 & 4 players -> 7 students placed on entrance, 3 players -> 9 students placed on entrance 237 throw new FullContainerException("Entrance"); 238 } 239 for (int i = 0; i < this.getEntranceSize(); i++) { 240 if (this.entrance.get(i).isEmpty()) { 241 this.entrance.set(i, OptionalValue.of(student)); 242 return; 243 } 244 } 245 } 246 247 /** 248 * Removes a single student from the entrance 249 * 250 * @param pos the index of the slot from which to remove the student 251 * @return the {@link PawnColour} of the removed student 252 * @throws InvalidContainerIndexException if the index is out of bounds or the slot was empty before removal 253 */ 254 public PawnColour removeStudentFromEntrance(int pos) throws InvalidContainerIndexException { 255 if (pos < 0 || pos >= this.getEntranceSize() || this.entrance.get(pos).isEmpty()) { 256 throw new InvalidContainerIndexException("Entrance"); 257 } 258 PawnColour student = this.entrance.get(pos).get(); 259 this.entrance.set(pos, OptionalValue.empty()); 260 return student; 261 } 262 263 /** 264 * Removes a single student from the entrance 265 * 266 * @param colour the {@link PawnColour} of the student to remove from the entrance 267 * @throws InvalidElementException if no students of the same colour could be found in the entrance 268 */ 269 public void removeStudentFromEntrance(PawnColour colour) throws InvalidElementException { 270 for (int i = 0; i < this.getEntranceSize(); i++) { 271 if (entrance.get(i).equals(OptionalValue.of(colour))) { 272 entrance.set(i, OptionalValue.empty()); 273 return; 274 } 275 } 276 throw new InvalidElementException("Target Pawn Colour"); 277 } 278 279 /** 280 * Adds a coin to the balance 281 */ 282 protected void addCoin() { 283 this.coinBalance += 1; 284 } 285 286 /** 287 * Removes as many coins from the balance as the cost of the paid card. 288 * 289 * @param card the {@link CharacterCard} to pay for 290 * @throws ForbiddenOperationException if the balance cannot accommodate for the cost of the card 291 */ 292 public void payCharacterEffect(CharacterCard card) throws ForbiddenOperationException { 293 if (this.coinBalance >= card.getCost()) { 294 this.coinBalance -= card.getCost(); 295 } else { 296 throw new ForbiddenOperationException("payCharacterEffect", "player coin balance too low"); 297 } 298 } 299 300 } 301