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 }