Coverage Summary for Class: CliWriter (it.polimi.ingsw.Client.CLI)
Class |
Method, %
|
Branch, %
|
Line, %
|
CliWriter |
0%
(0/20)
|
0%
(0/246)
|
0%
(0/373)
|
CliWriter$1 |
0%
(0/1)
|
0%
(0/1)
|
Total |
0%
(0/21)
|
0%
(0/246)
|
0%
(0/374)
|
1 package it.polimi.ingsw.Client.CLI;
2
3 import it.polimi.ingsw.Controller.Actions.*;
4 import it.polimi.ingsw.Controller.MoveDestination;
5 import it.polimi.ingsw.Misc.OptionalValue;
6 import it.polimi.ingsw.Misc.Pair;
7 import it.polimi.ingsw.Model.*;
8 import it.polimi.ingsw.Model.Enums.GameMode;
9 import it.polimi.ingsw.Model.Enums.GamePhase;
10 import it.polimi.ingsw.Model.Enums.PawnColour;
11 import it.polimi.ingsw.Network.SocketWrapper;
12 import it.polimi.ingsw.Server.Messages.Events.Requests.*;
13
14 import java.io.BufferedReader;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.UUID;
19 import java.util.concurrent.BrokenBarrierException;
20 import java.util.concurrent.CyclicBarrier;
21 import java.util.stream.Collectors;
22 import java.util.stream.IntStream;
23
24 /**
25 * This class takes Client's commands from terminal, provides support to the user and sends the command to the Server by using the SocketWrapper
26 */
27 public class CliWriter implements Runnable {
28
29 /**
30 * used to synchronize CliWriter and CliReader, useful when the first one needs to wait the second one
31 */
32 final CyclicBarrier cyclicBarrier;
33 /**
34 * socket wrapper to connect the Client to the Server
35 */
36 private final SocketWrapper socketWrapper;
37 /**
38 * used to store the client's game data
39 */
40 private final ClientView clientView;
41 /**
42 * used to acquire text from command line
43 */
44 private final BufferedReader stdIn;
45
46
47 public CliWriter(SocketWrapper socketWrapper, ClientView clientView, BufferedReader bufferedReader, CyclicBarrier cyclicBarrier) {
48 this.socketWrapper = socketWrapper;
49 this.clientView = clientView;
50 this.stdIn = bufferedReader;
51 this.cyclicBarrier = cyclicBarrier;
52 }
53
54 @Override
55 public void run() {
56 try {
57 //wait for the CliReader to receive the confirmation of successful connection to the server
58 cyclicBarrier.await();
59 } catch (BrokenBarrierException | InterruptedException e) {
60 throw new RuntimeException(e);
61 }
62 //exit the Thread whether the client was not able to connect to the server
63 if (!this.clientView.isConnected()) {
64 return;
65 }
66 try {
67 System.out.println(
68 """
69 ERIANTYS
70 Welcome Player
71 """
72 );
73 String nickname;
74 while (true) {
75 System.out.println("Insert Username:");
76 //acquire Username from stdIn
77 nickname = stdIn.readLine();
78 if (!this.clientView.isConnected()) return;
79 //whether at least one between nickname and password is empty then repeat the login process
80 if (nickname.equals("")) {
81 System.out.println("Username not well formatted");
82 } else {
83 if (!this.clientView.isConnected()) return;
84 //create and initialize the DeclarePlayer request
85 DeclarePlayerRequest dp = new DeclarePlayerRequest(nickname);
86 //send the request to the server
87 socketWrapper.sendMessage(dp);
88 //wait for the CliReader to receive the confirmation of successful login
89 cyclicBarrier.await();
90 //check if this Client is connected
91 if (this.clientView.isLogged()) {
92 break;
93 }
94 }
95 //repeat until user has typed Username and password correctly (syntactically) or typed password is wrong
96 }
97 //set nickname in clientView object
98 this.clientView.setNickname(nickname);
99 String input;
100 if (!this.clientView.isConnected()) Thread.currentThread().interrupt();
101 while (true) {
102 //acquire command from stdIn
103 input = stdIn.readLine();
104 if (!this.clientView.isConnected()) return;
105 //execute command
106 elaborateInput(input);
107 }
108 } catch (IOException | BrokenBarrierException e) {
109 System.out.println("IO exception when reading from stdIn.");
110 e.printStackTrace();
111 } catch (InterruptedException e) {
112 throw new RuntimeException(e);
113 }
114 }
115
116 /**
117 * This method, given a String, executes the proper method according to User's request
118 *
119 * @param userInput: text containing the command
120 * @throws IOException the integer read from command line
121 */
122 private void elaborateInput(String userInput) throws IOException {
123 switch (userInput.toLowerCase()) {
124 case "showactions" -> printActions();
125 case "createlobby" -> createLobby();
126 case "joinlobby" -> joinLobby();
127 case "startgame" -> startGame();
128 case "charactercardinfo" -> {
129 if (checkActionRequest()) return;
130 characterCardInfo();
131 }
132 case "playassistantcard" -> {
133 if (checkActionRequest()) return;
134 playAssistantCard();
135 }
136 case "playcharactercard" -> {
137 if (checkActionRequest()) return;
138 playCharacterCard();
139 }
140 case "movestudent" -> {
141 if (checkActionRequest()) return;
142 moveStudent();
143 }
144 case "movemothernature" -> {
145 if (checkActionRequest()) return;
146 moveMotherNature();
147 }
148 case "choosecloud" -> {
149 if (checkActionRequest()) return;
150 chooseCloud();
151 }
152 case "endturn" -> {
153 if (checkActionRequest()) return;
154 endTurn();
155 }
156 default -> System.out.println("Command not valid");
157 }
158 }
159
160 /**
161 * This method, after checking the gameMode, takes CharacterCard's number and extracts the characteCard; basing on CharacterCard type
162 * the methods create dynamically the CharacterCardInput by requesting the user only the right input.
163 *
164 * @throws IOException input has gone wrong
165 */
166 private void playCharacterCard() throws IOException {
167 //check for GameMode's validity
168 if (this.clientView.getModel().getGameMode() == GameMode.SIMPLE) {
169 System.out.println("Command valid only in Advanced GameMode");
170 return;
171 }
172 //Get selected characterCard's index
173 int selected = getCharacterCardIndex();
174 int finalSelected = selected;
175 //get current player
176 PlayerBoard currentPlayer = this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer();
177 //extract CharacterCard from GameBoard
178 CharacterCard characterCard = this.clientView.getModel().getCharacterCards().stream().filter(characterCard1 -> characterCard1.getId() == finalSelected).findFirst().get();
179 //get selected characterCard's index from gameBoard
180 selected = IntStream.range(0, 3).filter(i -> this.clientView.getModel().getCharacterCards().get(i).getId() == characterCard.getId()).findFirst().orElse(selected);
181 PlayCharacterCard playCharacterCard;
182 PlayerActionRequest playerActionRequest;
183 //pattern matching switch responsible for redirecting user to right input creation
184 switch (characterCard) {
185 case Card01 card01 -> {
186 PawnColour pawnToMove;
187 System.out.println("Type student's position (from 0 to 3) to move");
188 //Get selected pawn's index from 0 to 3 (Card01 contains 4 students)
189 while (true) {
190 selected = getInt();
191 if (selected < 0 || selected > 3) {
192 System.out.println("Index not valid, try again");
193 } else {
194 break;
195 }
196 }
197 //get selected pawn having the index
198 pawnToMove = (PawnColour) card01.getState().get(selected);
199 System.out.println("Type target island's id");
200 //get Island's ID
201 Integer IslandId = getInt();
202 //create playCharacterCard's controller action
203 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.of(IslandId), OptionalValue.of(pawnToMove), OptionalValue.empty());
204 //create playerActionRequest message to send to the server
205 playerActionRequest = new PlayerActionRequest(playCharacterCard);
206 }
207 case Card02 ignored -> {
208 //create playCharacterCard's controller action
209 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.empty(), OptionalValue.empty());
210 //create playerActionRequest message to send to the server
211 playerActionRequest = new PlayerActionRequest(playCharacterCard);
212 }
213 case Card03 ignored1 -> {
214 System.out.println("Type target island's id");
215 //get Island's ID
216 Integer IslandId = getInt();
217 //create playCharacterCard's controller action
218 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.of(IslandId), OptionalValue.empty(), OptionalValue.empty());
219 //create playerActionRequest message to send to the server
220 playerActionRequest = new PlayerActionRequest(playCharacterCard);
221 }
222 case Card04 ignored2 -> {
223 //create playCharacterCard's controller action
224 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.empty(), OptionalValue.empty());
225 //create playerActionRequest message to send to the server
226 playerActionRequest = new PlayerActionRequest(playCharacterCard);
227 }
228 case Card05 ignored3 -> {
229 System.out.println("Type target island's id");
230 //get Island's ID
231 Integer IslandId = getInt();
232 //create playCharacterCard's controller action
233 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.of(IslandId), OptionalValue.empty(), OptionalValue.empty());
234 //create playerActionRequest message to send to the server
235 playerActionRequest = new PlayerActionRequest(playCharacterCard);
236 }
237 case Card06 ignored4 -> {
238 //create playCharacterCard's controller action
239 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.empty(), OptionalValue.empty());
240 //create playerActionRequest message to send to the server
241 playerActionRequest = new PlayerActionRequest(playCharacterCard);
242 }
243 case Card07 card07 -> {
244 System.out.println("You can now type up to 3 pairs to exchange, the first element coming from the entrance and the second one from the card\n" +
245 "Press 'enter' after a pair if you want to exchange less than 3 pairs");
246 //create List of Pairs which will contain up to 3 pairs of pawns
247 List<Pair<PawnColour, PawnColour>> pairs = new ArrayList<>();
248 //index containing pawn's index in currentPlayers' entrance
249 OptionalValue<Integer> PawnPosition;
250 //index containing pawn's index inside the card
251 int cardIndex;
252 outerWhile:
253 do {
254 System.out.println("Insert entrance's position containing the pawn to move, or 'enter' to conclude the choice");
255 do {
256 //get pawn's index (that one coming from entrance)
257 PawnPosition = getOptionalInt();
258 //if user pressed 'enter' the input will interrupt (the user can choose less than 3 pairs)
259 if (PawnPosition.isEmpty()) break outerWhile;
260 //check whether user typed an invalid index
261 if (PawnPosition.get() < 0 || PawnPosition.get() >= currentPlayer.getEntranceStudents().size())
262 System.out.println("Target Entrance Position invalid");
263 else break;
264 } while (true);
265 System.out.println("Select pawn's index from card");
266 do {
267 //get pawn's index (that one coming from the card)
268 cardIndex = getInt();
269 //check whether user typed an invalid index
270 if (cardIndex < 0 || cardIndex >= card07.getState().size())
271 System.out.println("Target Card Position invalid");
272 else {
273 break;
274 }
275 } while (true);
276 //add to the list containing the pairs the one just chosen by the user
277 pairs.add(new Pair<>(currentPlayer.getEntranceStudents().get(PawnPosition.get()).get(), (PawnColour) card07.getState().get(cardIndex)));
278 //repeat the loop until the user has selected 3 pairs or pressed 'enter'
279 } while (pairs.size() < 3);
280 //create playCharacterCard's controller action
281 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.empty(), OptionalValue.of(pairs));
282 //create playerActionRequest message to send to the server
283 playerActionRequest = new PlayerActionRequest(playCharacterCard);
284 }
285 case Card08 ignored5 -> {
286 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.empty(), OptionalValue.empty());
287 //create playerActionRequest message to send to the server
288 playerActionRequest = new PlayerActionRequest(playCharacterCard);
289 }
290 case Card09 ignored6 -> {
291 System.out.println("type the colour to make it irrelevant during this turn");
292 //create playerActionRequest message to send to the server
293 playerActionRequest = createPlayerActionByPawnColour(selected, currentPlayer);
294 }
295 case Card10 ignored7 -> {
296 System.out.println("You can now type up to 2 pairs to exchange, the first element coming from the entrance and the second one from the dining room\n" +
297 "Press 'enter' after a pair if you want to exchange less than 2 pairs");
298 //create List of Pairs which will contain up to 2 pairs of pawns
299 List<Pair<PawnColour, PawnColour>> pairs = new ArrayList<>();
300 //index containing pawn's index in currentPlayers' entrance
301 OptionalValue<Integer> PawnPosition;
302 outerWhile:
303 do {
304 System.out.println("Insert entrance's position containing the pawn to move, or 'enter' to conclude the choice");
305 do {
306 //get pawn's index (that one coming from entrance)
307 PawnPosition = getOptionalInt();
308 //if user pressed 'enter' the input will interrupt (the user can choose less than 3 pairs)
309 if (PawnPosition.isEmpty()) break outerWhile;
310 //check whether user typed an invalid index
311 if (PawnPosition.get() < 0 || PawnPosition.get() >= currentPlayer.getEntranceStudents().size())
312 System.out.println("Target Entrance Position invalid");
313 else {
314 break;
315 }
316 } while (true);
317 System.out.println("Select diningRoom's colour");
318 boolean repeat = true;
319 //get the selected PawnColour from stdIn and add the pair to the list
320 do {
321 switch (stdIn.readLine().toLowerCase()) {
322 case "red" -> {
323 pairs.add(new Pair<>(currentPlayer.getEntranceStudents().get(PawnPosition.get()).get(), PawnColour.RED));
324 repeat = false;
325 }
326 case "yellow" -> {
327 pairs.add(new Pair<>(currentPlayer.getEntranceStudents().get(PawnPosition.get()).get(), PawnColour.YELLOW));
328 repeat = false;
329 }
330 case "green" -> {
331 pairs.add(new Pair<>(currentPlayer.getEntranceStudents().get(PawnPosition.get()).get(), PawnColour.GREEN));
332 repeat = false;
333 }
334 case "pink" -> {
335 pairs.add(new Pair<>(currentPlayer.getEntranceStudents().get(PawnPosition.get()).get(), PawnColour.PINK));
336 repeat = false;
337 }
338 case "blue" -> {
339 pairs.add(new Pair<>(currentPlayer.getEntranceStudents().get(PawnPosition.get()).get(), PawnColour.BLUE));
340 repeat = false;
341 }
342 case default -> System.out.println("Colour not valid, try again");
343 }
344 //repeat whether the user typed an invalid PawnColour
345 } while (repeat);
346 //repeat the loop until the user has selected 2 pairs or pressed 'enter'
347 } while (pairs.size() < 2);
348 //create playCharacterCard's controller action
349 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(),
350 selected,
351 OptionalValue.empty(),
352 OptionalValue.empty(),
353 OptionalValue.of(pairs));
354 //create playerActionRequest message to send to the server
355 playerActionRequest = new PlayerActionRequest(playCharacterCard);
356 }
357 case Card11 card11 -> {
358 System.out.println("Select pawn's index from card");
359 int cardIndex;
360 do {
361 //get selected pawn's index from card
362 cardIndex = getInt();
363 //check whether user typed an invalid index
364 if (cardIndex < 0 || cardIndex >= card11.getState().size())
365 System.out.println("Target Card Position invalid");
366 else {
367 break;
368 }
369 } while (true);
370 //create playCharacterCard's controller action
371 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.of((PawnColour) card11.getState().get(cardIndex)), OptionalValue.empty());
372 //create playerActionRequest message to send to the server
373 playerActionRequest = new PlayerActionRequest(playCharacterCard);
374 }
375 case Card12 ignored8 -> {
376 System.out.println("Select Pawn's colour to remove from all Playerboards' diningrooms");
377 //create playerActionRequest message to send to the server
378 playerActionRequest = createPlayerActionByPawnColour(selected, currentPlayer);
379 }
380 case default -> {
381 System.out.println("CharacterCard not valid");
382 return;
383 }
384 }
385 //send playerActionRequest to the server
386 socketWrapper.sendMessage(playerActionRequest);
387 }
388
389 /**
390 * This method prints selected characterCard to the command line to explain card's effect to the player
391 *
392 * @throws IOException input has gone wrong
393 */
394 private void characterCardInfo() throws IOException {
395 //check for GameMode's validity
396 if (this.clientView.getModel().getGameMode() == GameMode.SIMPLE) {
397 System.out.println("Command valid only in Advanced GameMode");
398 return;
399 }
400 //get selected characterCard's index
401 int selected = getCharacterCardIndex();
402 //every switch's case print CharacterCard's info (every information is available on game's guide)
403 switch (selected) {
404 case 1 -> System.out.println("EFFECT: Take 1 Student from this card and place it on " +
405 "an Island of your choice. Then, draw a new Student from the Bag and place it on this card.");
406 case 2 -> System.out.println("EFFECT: During this turn, you take control of any\n" +
407 " number of Professors even if you have the same number of Students as the player who currently controls them.");
408 case 3 -> System.out.println("""
409 EFFECT: Choose an Island and resolve the Island as if
410 Mother Nature had ended her movement there. Mother
411 Nature will still move and the Island where she ends her movement will also be resolved.""");
412 case 4 -> System.out.println("EFFECT: You may move Mother Nature up to 2\n" +
413 " additional Islands than is indicated by the Assistant card you've played.");
414 case 5 -> System.out.println("""
415 EFFECT: Place a No Entrytile on an Island of your choice.
416 The first time Mother Nature ends her movement there, put the No Entry tile back onto this card
417 DO NOT calculate influence on that Island, or place any Towers.""");
418 case 6 ->
419 System.out.println("EFFECT: When resolving a Conquering on an Island, Towers do not count towards influence.");
420 case 7 ->
421 System.out.println("EFFECT: you may take up to 3 students from this card and replace them with the same number of Students\n" +
422 " from your Entrance");
423 case 8 ->
424 System.out.println("EFFECT: During the influence calculation this turn, you count as having 2 more influence");
425 case 9 ->
426 System.out.println("EFFECT: Choose a color of Student: during the influence calculation this turn, that color adds no influence");
427 case 10 ->
428 System.out.println("EFFECT: You may exchange up to 2 Students between your entrance and your Dining Room");
429 case 11 ->
430 System.out.println("EFFECT: Take 1 Student from this card and place it in your Dining Room. Then, draw a new Student from the\n" +
431 "Bag and place it on this card.");
432 case 12 -> System.out.println("""
433 EFFECT: Choose a type of Student: every player (including yourself) must return 3 Students of that type
434 from their Dining Room to the bag. If any player has fewer than 3 Students of that type, return as many Students as they have.
435 """);
436 }
437 System.out.println("You may now continue with the game, remember the 'showActions' command, it's your best friend during the game");
438 }
439
440 /**
441 * Support method responsible for checking whether the user has the rights to perform the requested action.
442 * This method will only be executed to check actions available only when the match has started
443 *
444 * @return true if the check fails
445 */
446 private boolean checkActionRequest() {
447 //check if the user is present inside a lobby
448 if (!this.clientView.isInLobby()) {
449 System.out.println("Action not valid, join or create a lobby");
450 return true;
451 }
452 //check if the game has started
453 if (!this.clientView.getGameStarted()) {
454 if (this.clientView.getAdmin().equals(this.clientView.getNickname())) {
455 System.out.println("Action not valid, you need to start the game");
456 } else {
457 System.out.println("Action not valid, you need to wait for the admin to start the game");
458 }
459 return true;
460 }
461 return false;
462 }
463
464 /**
465 * Method responsible for print actions during all the game
466 */
467 private void printActions() {
468 //actions available outside a lobby
469 if (!this.clientView.isInLobby()) {
470 System.out.println("Available commands:\n");
471 System.out.println("-- createLobby (create a open or private lobby)");
472 System.out.println("-- joinLobby (join a lobby with the UUID)");
473 return;
474 }
475 if (!this.clientView.getGameStarted()) {
476 //actions available when the game has not started
477 if (this.clientView.getNickname().equals(this.clientView.getAdmin())) {
478 //if the user is the admin
479 System.out.println("Available commands:\n");
480 System.out.println("-- startGame (start the game)");
481 } else {
482 //if the user is not the admin
483 System.out.println("No actions available, wait for the admin to start the game");
484 }
485 return;
486 }
487 //print action-phase commands
488 printGameActions();
489 }
490
491 /**
492 * Executes the createLobby command; during the creation the user is able to choose lobby's visibility (private or public) and
493 * lobby's dimension (this dimension will also be game's number of players).
494 * <p>
495 * Finally, it creates the createLobby request and then sends it to the server.
496 *
497 * @throws IOException if an I/O error occurs
498 */
499 private void createLobby() throws IOException {
500 //check if the user is already in a lobby
501 if (!clientView.isInLobby()) {
502 //create CreateLobbyRequest object
503 CreateLobbyRequest createLobbyRequest;
504 //print visibility request
505 System.out.println("Do you want to create an open or private lobby?");
506 System.out.println("O : open");
507 System.out.println("P : private");
508 boolean isPublic;
509 String input;
510 loop:
511 while (true) {
512 //get visibility choice
513 input = stdIn.readLine().toUpperCase();
514 //assign user's choice to boolean variable 'isPublic'
515 switch (input) {
516 case "O" -> {
517 isPublic = true;
518 break loop;
519 }
520 case "P" -> {
521 isPublic = false;
522 break loop;
523 }
524 case default -> System.out.println("input not correct, please try again");
525 }
526 }
527 //print lobby's dimension request
528 System.out.println("how many player will the lobby contain?");
529 int players;
530 while (true) {
531 //get lobby's dimension from stdIn
532 players = getInt();
533 //check input's validity (lobby's dimension must be between 2 and 4)
534 if (players >= 2 && players <= 4) {
535 break;
536 }
537 System.out.println("Amount of players not valid");
538 }
539 //initialize createLobbyRequest object to send to the server
540 createLobbyRequest = new CreateLobbyRequest(isPublic, players);
541 //send request to the server
542 socketWrapper.sendMessage(createLobbyRequest);
543 } else {
544 System.out.println("You are already in a lobby");
545 }
546 }
547
548 /**
549 * Executes the joinLobby command by knowing lobby's UUID, it allows to access both open and private lobbies.
550 * <p>
551 * Finally, it creates the joinLobby request and then sends it to the Server.
552 *
553 * @throws IOException if an I/O error occurs
554 */
555 private void joinLobby() throws IOException {
556 //check if the user is already in a lobby
557 if (!clientView.isInLobby()) {
558 //create ConnectLobbyRequest object
559 ConnectLobbyRequest connectLobbyRequest;
560 System.out.println("Insert lobby's UUID");
561 UUID id = null;
562 boolean repeat;
563 do {
564 try {
565 //get UUID from stdIn
566 id = UUID.fromString(stdIn.readLine());
567 repeat = false;
568 } catch (IllegalArgumentException e) {
569 System.out.println("UUID not valid, try again");
570 repeat = true;
571 }
572 //repeat until the user types a valid UUID (syntactically, the server will check if the UUID is associated with a valid lobby)
573 } while (repeat);
574 //initialize connectLobbyRequest object to send to the server
575 connectLobbyRequest = new ConnectLobbyRequest(id);
576 //send message to the server
577 socketWrapper.sendMessage(connectLobbyRequest);
578
579 } else {
580 System.out.println("You are already in a lobby");
581 }
582 }
583
584 /**
585 * Executes the startGame command, it can be executed only by lobby's admin and whether the lobby is full.
586 * The admin can choose the game mode between simple and advanced.
587 * <p>
588 * Finally, it creates the startGameRequest and then sends it to the Server.
589 *
590 * @throws IOException if an I/O error occurs
591 */
592 private void startGame() throws IOException {
593 if (this.clientView.isInLobby()) {
594 ////print gameMode request
595 System.out.println("Select the game mode:");
596 System.out.println("S: simple");
597 System.out.println("A: advanced");
598 //create GameMode object
599 GameMode gameMode;
600 String input;
601 //create startGameRequest object
602 StartGameRequest startGameRequest;
603 loop:
604 //repeat until the user types a valid gamemode
605 while (true) {
606 ////get gameMode from stdIn
607 input = stdIn.readLine().toUpperCase();
608 switch (input) {
609 case "S" -> {
610 gameMode = GameMode.SIMPLE;
611 break loop;
612 }
613 case "A" -> {
614 gameMode = GameMode.ADVANCED;
615 break loop;
616 }
617 case default -> System.out.println("input not correct, please try again");
618 }
619 }
620 //initialize startGameRequest object
621 startGameRequest = new StartGameRequest(gameMode);
622 //send message to the server
623 socketWrapper.sendMessage(startGameRequest);
624 } else {
625 System.out.println("You first need to join or create a lobby");
626 }
627 }
628
629
630 /**
631 * Executes the playAssistantCard command
632 * The method shows the available assistantCard at that moment (removing the used cards and cards picked by other players in the same turn to avoid
633 * playing cards with same value).
634 * <p>
635 * Finally, it creates the playAssistantCard request and then sends it to the Server.
636 *
637 * @throws IOException if an I/O error occurs
638 */
639 private void playAssistantCard() throws IOException {
640 System.out.println("select one of these available assistant card");
641 //get the unused cards
642 ArrayList<AssistantCard> availableAssistants = this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getMutableAssistantCards()
643 .stream().filter(assistantCard -> !assistantCard.getUsed())
644 .collect(Collectors.toCollection(ArrayList::new));
645 //from the unused cards, extract the card with a priority not selected before by other players
646 for (PlayerBoard pb : this.clientView.getModel().getMutableTurnOrder().getCurrentTurnOrder()) {
647 if (this.clientView.getModel().getMutableTurnOrder().getMutableSelectedCard(pb).isPresent()) {
648 availableAssistants.removeIf(assistantCard -> assistantCard.getPriority() == this.clientView.getModel().getMutableTurnOrder().getMutableSelectedCard(pb).get().getPriority());
649 }
650 }
651 //get the priority of the available cards
652 ArrayList<Integer> cardNumbers = availableAssistants.stream().map(AssistantCard::getPriority).collect(Collectors.toCollection(ArrayList::new));
653 availableAssistants.forEach(assistantCard -> System.out.println("priority:" + assistantCard.getPriority() + " maxMovement:" + assistantCard.getMaxMovement()));
654 //get the selected card
655 int selected;
656 do {
657 selected = getInt();
658 if (!cardNumbers.contains(selected)) {
659 System.out.println("Card not available");
660 } else {
661 break;
662 }
663 //repeat until the user pic
664 } while (true);
665 PlayAssistantCard playAssistantCard = new PlayAssistantCard(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getId(), selected);
666 PlayerActionRequest playerAction = new PlayerActionRequest(playAssistantCard);
667 socketWrapper.sendMessage(playerAction);
668 }
669
670 /**
671 * Executes the moveStudent command, the method requires from the user to type pawn's index inside entrance then it
672 * allows the user to choose the destination:
673 * type enter to move the pawn directly to the dining room
674 * type a number that will be interpreted as the island's id on which to move the pawn
675 * <p>
676 * Finally, it creates the moveStudent request and then sends it to the Server.
677 *
678 * @throws IOException if an I/O error occurs
679 */
680 private void moveStudent() throws IOException {
681 //get entrance size
682 int entranceSize = this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getEntranceSize() - 1;
683 System.out.println("Insert the number of entrance's position between 0 and " + entranceSize);
684 //get selected pawn's index from entrance
685 int selected = getInt();
686
687 System.out.println("Type the island id to move the selected student there or press 'Enter' to send it to the dining room");
688 //get destination ('enter' for dining room or island's ID)
689 OptionalValue<Integer> choice = getOptionalInt();
690 //create MoveStudent object
691 MoveStudent moveStudent;
692 if (choice.isEmpty()) {
693 //if the user has typed 'enter' then initialize the moveStudent action with dining room as MoveDestination
694 moveStudent = new MoveStudent(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getId(), selected, MoveDestination.toDiningRoom());
695 } else {
696 //if the user has typed a number then initialize the moveStudent with that number
697 moveStudent = new MoveStudent(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getId(), selected, MoveDestination.toIsland(choice.get()));
698 }
699 //create and initialize clientPlayerAction to send to the Server
700 PlayerActionRequest clientPlayerAction = new PlayerActionRequest(moveStudent);
701 //send the action request to the Server
702 socketWrapper.sendMessage(clientPlayerAction);
703
704 }
705
706 /**
707 * Executes the moveMotherNature command, this method asks the user to type the amount of steps motherNature should take
708 * <p>
709 * Finally, it creates the moveMotherNature request and then sends it to the Server.
710 *
711 * @throws IOException if an I/O error occurs
712 */
713 private void moveMotherNature() throws IOException {
714 int steps = this.clientView.getModel().getMutableEffects().isMotherNatureMovementIncreased() ?
715 this.clientView.getModel().getMutableTurnOrder().getMutableSelectedCard(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer()).get().getPriority() + 2 :
716 this.clientView.getModel().getMutableTurnOrder().getMutableSelectedCard(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer()).get().getPriority();
717 System.out.println("You can now move mother nature up to " + steps + " steps");
718 System.out.println("How many steps do you want mother nature to take?");
719 //get the amount of steps to perform
720 steps = getInt();
721 //create and initialize moveMotherNature action
722 MoveMotherNature moveMotherNature = new MoveMotherNature(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getId(), steps);
723 //create and initialize PlayerActionRequest to send to the server
724 PlayerActionRequest clientPlayerAction = new PlayerActionRequest(moveMotherNature);
725 //send action request to the server
726 socketWrapper.sendMessage(clientPlayerAction);
727 }
728
729 /**
730 * Executes the chooseCloud command, this method asks the user to type a number corresponding to Island's id.
731 * Finally, it creates the chooseCloudTile request and then sends it to the Server.
732 *
733 * @throws IOException if an I/O error occurs
734 */
735 private void chooseCloud() throws IOException {
736 System.out.println("Select one cloud from 0 to " + (this.clientView.getModel().getClouds().size() - 1));
737 //get selected Cloud's ID
738 int selectedCloud = getInt();
739 //create and initialize ChooseCloudTile Object with the selected Island's ID
740 ChooseCloudTile chooseCloudTile = new ChooseCloudTile(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getId(), selectedCloud);
741 //create and initialize clientPlayerAction request to send to the Server
742 PlayerActionRequest clientPlayerAction = new PlayerActionRequest(chooseCloudTile);
743 //send clientPlayerAction request to the Server
744 socketWrapper.sendMessage(clientPlayerAction);
745 }
746
747 /**
748 * Executes the endTurn command to end currentPlayer's action turn and move on to the next player
749 */
750 private void endTurn() throws IOException {
751 //create and initialize endTurnOfActionPhase action
752 EndTurnOfActionPhase endTurnOfActionPhase = new EndTurnOfActionPhase(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getId());
753 //create and initialize PlayerActionRequest to send to the server
754 PlayerActionRequest clientPlayerAction = new PlayerActionRequest(endTurnOfActionPhase);
755 //send action to the server
756 socketWrapper.sendMessage(clientPlayerAction);
757 }
758
759 /**
760 * print all the available actions basing on current game phase
761 */
762 private void printGameActions() {
763 //check whether the user is allowed to perform any action (i.e. if is the current player)
764 if (!this.clientView.getNickname().equals(this.clientView.getModel().getMutableTurnOrder().getMutableCurrentPlayer().getNickname())) {
765 System.out.println("No actions are allowed out of turn");
766 return;
767 }
768 //get current gamePhase
769 GamePhase gamePhase = this.clientView.getModel().getMutableTurnOrder().getGamePhase();
770 System.out.println("during the " + gamePhase + " phase these are the available commands:");
771 switch (gamePhase) {
772 //during SETUP phase the user can only play the assistant card
773 case SETUP ->
774 System.out.println("--playAssistantCard (play the assistant card to establish the turn order)");
775 //print all actions available during ACTION phase
776 case ACTION -> {
777 System.out.println("--moveStudent (move one student from entrance to dining room or one island)");
778 //only in advanced game mode the user can play a CharacterCard
779 if (this.clientView.getModel().getGameMode() == GameMode.ADVANCED) {
780 System.out.println("--playCharacterCard (activate the powerful effect of the character card)");
781 }
782 System.out.println("--moveMotherNature (move mother nature and calculate the influence");
783 System.out.println("--chooseCloud (fill your entrance after moving three students)");
784 //only in advanced game mode the user can get CharacterCard's info
785 if (this.clientView.getModel().getGameMode() == GameMode.ADVANCED)
786 System.out.println("--characterCardInfo (show the information about one characterCard)");
787 System.out.println("--endTurn (end your turn)");
788 }
789 default -> System.out.println("Gamephase is not valid");
790 }
791 }
792
793 /**
794 * Support method used to acquire chosen characterCard from command line; it requires user to type the characterCard number
795 * (not the index inside Game's list of 3 characterCards but the "id" o the card between 1 and 12). The methods also checks
796 * if the chosen characterCard's number is present among the 3 CharacterCards available during the game.
797 *
798 * @return int representing characterCard's number
799 * @throws IOException if an I/O error occurs
800 */
801 private int getCharacterCardIndex() throws IOException {
802 System.out.println("Pick one character card from the list above (type the card number)");
803 int selected;
804 //ArrayList containing the 3 characterCards' numbers available during the game
805 ArrayList<Integer> characterCardsNumbers =
806 this.clientView.getModel().getCharacterCards().stream().map(CharacterCard::getId).collect(Collectors.toCollection(ArrayList::new));
807 do {
808 //acquire number
809 selected = getInt();
810 //check if this number belongs to the list of available characterCards
811 if (!characterCardsNumbers.contains(selected)) {
812 System.out.println("CharacterCard not available");
813 } else {
814 break;
815 }
816 //repeat until the chosen number belongs to the list of available characterCards
817 } while (true);
818 //return the chosen number
819 return selected;
820 }
821
822 /**
823 * Support method to read an integer from command line, this method does not accept 'enter' as input
824 *
825 * @return the integer read from command line
826 * @throws IOException if an I/O error occurs
827 */
828 private int getInt() throws IOException {
829 int result;
830 while (true) {
831 try {
832 //get text from stdIn
833 String text = stdIn.readLine();
834 if (text == null)
835 throw new IOException();
836 //try to parse String from stdIn to Integer
837 result = Integer.parseInt(text);
838 break;
839 } catch (NumberFormatException ex) {
840 System.out.println("This string is not a number, retry.");
841 }
842 //repeat until the String is a valid number
843 }
844 return result;
845 }
846
847 /**
848 * Support method to read an integer from command line, this method accepts 'enter' as input
849 *
850 * @return the integer read from command line or Optional.empty if the user pressed "Enter"
851 * @throws IOException if an I/O error occurs
852 */
853 private OptionalValue<Integer> getOptionalInt() throws IOException {
854 int result;
855 while (true) {
856 try {
857 //get text from stdIn
858 String text = stdIn.readLine();
859 if (text == null)
860 throw new IOException();
861 //if user has typed 'enter' then return Optional.empty
862 if (text.equals(""))
863 return OptionalValue.empty();
864 //try to parse String from stdIn to Integer
865 result = Integer.parseInt(text);
866 break;
867 } catch (NumberFormatException ex) {
868 System.out.println("This string is not a number, retry.");
869 }
870 //repeat until the String is a valid number
871 }
872 return OptionalValue.of(result);
873 }
874
875 /**
876 * Support method that create the PlayerActionRequest after that user type a PawnColour.
877 *
878 * @param selected selected CharacterCard's index among the 3 available during the game
879 * @param currentPlayer current player in turn, necessary to create PlayCharacterCard action
880 * @return PlayerActionRequest with the selected PawnColour as parameter
881 * @throws IOException if an I/O error occurs
882 */
883 private PlayerActionRequest createPlayerActionByPawnColour(int selected, PlayerBoard currentPlayer) throws IOException {
884 //create PlayerActionRequest object
885 PlayerActionRequest playerActionRequest;
886 //create PlayCharacterCard object
887 PlayCharacterCard playCharacterCard = null;
888 boolean repeat = true;
889 do {
890 //get the input from stdIn, every switch's case create a playCharacterCard with selected pawnColour
891 switch (stdIn.readLine().toLowerCase()) {
892 case "red" -> {
893 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.of(PawnColour.RED), OptionalValue.empty());
894 repeat = false;
895 }
896 case "yellow" -> {
897 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.of(PawnColour.YELLOW), OptionalValue.empty());
898 repeat = false;
899 }
900 case "green" -> {
901 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.of(PawnColour.GREEN), OptionalValue.empty());
902 repeat = false;
903 }
904 case "pink" -> {
905 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.of(PawnColour.PINK), OptionalValue.empty());
906 repeat = false;
907 }
908 case "blue" -> {
909 playCharacterCard = new PlayCharacterCard(currentPlayer.getId(), selected, OptionalValue.empty(), OptionalValue.of(PawnColour.BLUE), OptionalValue.empty());
910 repeat = false;
911 }
912 case default -> System.out.println("Colour not valid, try again");
913 }
914 //repeat until the user types a valid pawnColour
915 } while (repeat);
916 //initialize playerActionRequest
917 playerActionRequest = new PlayerActionRequest(playCharacterCard);
918 //return the playerActionRequest
919 return playerActionRequest;
920 }
921 }