From 72c35f7d1115e0f62265644cfb951a6fcaf35605 Mon Sep 17 00:00:00 2001 From: Jerome Berclaz Date: Sun, 5 Apr 2020 17:34:07 -0700 Subject: [PATCH] 2.0 refactor (#2) * Cleanup and formatting * Started server refactoring * Further server refactoring * Changed directory structure to maven * Refactoring client * Compiling but not working * Added unit tests * modified gitignore * added back readme and license * split classes * Improved server rpc * Removed commented out code * Refactoring * Added unit tests * Bug fix * Refactoring * started class diagram * Refactored server * Big client refactor * More client functions implemented * More client functions implemented * Added atout selection * Added set atout * Implemented play * Implemented setCard * Implemented playNext * Implemented set plie Added unit tests * Implemented setScore * Implemented getAnoucement * Implemented setAnoucement * Implemented setGameResult * Implemented new game * Implemented Playerleft * Continued server refactor * Refactored server * Fixed bugs * Better naming Fixed bug when player disconnects * Clean up * Build jar --- .gitignore | 2 + client/CanvasBorder.java | 63 -- client/CanvasBorder1.java | 45 - client/CanvasCenter.java | 55 -- client/CanvasLastPlie.java | 45 - client/CanvasPlayer.java | 44 - client/CanvasTop.java | 60 -- client/ClientFrame.java | 520 ----------- client/ClientListener.java | 96 -- client/ClientNetwork.java | 63 -- client/DialogAtout.java | 111 --- client/DialogNewPart.java | 105 --- client/DialogPartnerChoice.java | 90 -- client/FlatJassClientSystem.java | 695 --------------- client/JassCanvas.java | 24 - client/Makefile | 21 - client/pics/0.gif | Bin 1340 -> 0 bytes client/pics/1.gif | Bin 1361 -> 0 bytes client/pics/10.gif | Bin 1418 -> 0 bytes client/pics/11.gif | Bin 1440 -> 0 bytes client/pics/12.gif | Bin 1453 -> 0 bytes client/pics/13.gif | Bin 1496 -> 0 bytes client/pics/14.gif | Bin 2030 -> 0 bytes client/pics/15.gif | Bin 2078 -> 0 bytes client/pics/16.gif | Bin 2037 -> 0 bytes client/pics/17.gif | Bin 1224 -> 0 bytes client/pics/18.gif | Bin 1346 -> 0 bytes client/pics/19.gif | Bin 1366 -> 0 bytes client/pics/2.gif | Bin 1388 -> 0 bytes client/pics/20.gif | Bin 1400 -> 0 bytes client/pics/21.gif | Bin 1413 -> 0 bytes client/pics/22.gif | Bin 1451 -> 0 bytes client/pics/23.gif | Bin 2010 -> 0 bytes client/pics/24.gif | Bin 2058 -> 0 bytes client/pics/25.gif | Bin 2014 -> 0 bytes client/pics/26.gif | Bin 1226 -> 0 bytes client/pics/27.gif | Bin 1355 -> 0 bytes client/pics/28.gif | Bin 1370 -> 0 bytes client/pics/29.gif | Bin 1397 -> 0 bytes client/pics/3.gif | Bin 1421 -> 0 bytes client/pics/30.gif | Bin 1422 -> 0 bytes client/pics/31.gif | Bin 1460 -> 0 bytes client/pics/32.gif | Bin 2015 -> 0 bytes client/pics/33.gif | Bin 2050 -> 0 bytes client/pics/34.gif | Bin 1944 -> 0 bytes client/pics/35.gif | Bin 1211 -> 0 bytes client/pics/4.gif | Bin 1446 -> 0 bytes client/pics/5.gif | Bin 1933 -> 0 bytes client/pics/6.gif | Bin 2019 -> 0 bytes client/pics/7.gif | Bin 1919 -> 0 bytes client/pics/8.gif | Bin 1353 -> 0 bytes client/pics/9.gif | Bin 1377 -> 0 bytes client/pics/c0.gif | Bin 860 -> 0 bytes client/pics/c1.gif | Bin 859 -> 0 bytes client/pics/c2.gif | Bin 860 -> 0 bytes client/pics/c3.gif | Bin 862 -> 0 bytes client/pics/dos.gif | Bin 7067 -> 0 bytes doc/class_diagram.xmi | 436 ++++++++++ jass.iml | 2 + pom.xml | 63 ++ server/ClientLeftException.java | 13 - server/FlatJassServerSystem.java | 821 ------------------ server/Makefile | 16 - server/ServerNetwork.java | 98 --- .../jass/client}/AbsoluteConstraints.java | 3 + .../leflat/jass/client}/AbsoluteLayout.java | 3 + .../com/leflat/jass/client/CanvasBorder.java | 37 + .../com/leflat/jass/client/CanvasCenter.java | 112 +++ .../leflat/jass/client/CanvasLastPlie.java | 69 ++ .../com/leflat/jass/client/CanvasPlayer.java | 67 ++ .../com/leflat/jass/client/CanvasTop.java | 37 + .../com/leflat/jass/client/CardImages.java | 48 + .../com/leflat/jass/client/ClientPlayer.java | 14 + .../com/leflat/jass/client/DialogAtout.java | 119 +++ .../leflat/jass/client}/DialogConnect.java | 55 +- .../com/leflat/jass/client}/DialogInfo.java | 5 +- .../com/leflat/jass/client/DialogNewPart.java | 101 +++ .../jass/client/DialogPartnerChoice.java | 93 ++ .../leflat/jass/client}/DialogTeamChoice.java | 6 +- .../com/leflat/jass/client/JassCanvas.java | 69 ++ .../com/leflat/jass/client/JassClient.java | 7 + .../com/leflat/jass/client/JassFrame.java | 516 +++++++++++ .../com/leflat/jass/client/JassPlayer.java | 223 +++++ .../leflat/jass/client/RemoteController.java | 341 ++++++++ .../client/ServerDisconnectedException.java | 4 + .../com/leflat/jass/common/Anouncement.java | 129 +++ .../com/leflat/jass/common/BasePlayer.java | 50 ++ .../jass/common/BrokenRuleException.java | 11 + .../java/com/leflat/jass/common/Card.java | 124 +++ .../jass/common/ClientConnectionInfo.java | 19 + .../leflat/jass/common/ConnectionError.java | 8 + .../java/com/leflat/jass/common/IJassUi.java | 51 ++ .../java/com/leflat/jass/common/IPlayer.java | 45 + .../com/leflat/jass/common/IRemotePlayer.java | 9 + .../java/com/leflat/jass/common/Plie.java | 87 ++ .../com/leflat/jass/common/RemoteCommand.java | 26 + .../java/com/leflat/jass/common/Rules.java | 68 ++ .../java/com/leflat/jass/common/Team.java | 57 ++ .../jass/common/TeamSelectionMethod.java | 5 + .../jass/server/ConnectionListener.java | 86 ++ .../leflat/jass/server/GameController.java | 386 ++++++++ .../com/leflat/jass/server/JassServer.java | 26 + .../jass/server/PlayerLeftExpection.java | 13 + .../com/leflat/jass/server/PlayerNetwork.java | 61 ++ .../com/leflat/jass/server/RemotePlayer.java | 209 +++++ .../java/com/leflat/jass/test/TestClient.java | 182 ++++ src/main/resources/pics/0.png | Bin 0 -> 541 bytes src/main/resources/pics/1.png | Bin 0 -> 564 bytes src/main/resources/pics/10.png | Bin 0 -> 585 bytes src/main/resources/pics/11.png | Bin 0 -> 561 bytes src/main/resources/pics/12.png | Bin 0 -> 613 bytes src/main/resources/pics/13.png | Bin 0 -> 622 bytes src/main/resources/pics/14.png | Bin 0 -> 1123 bytes src/main/resources/pics/15.png | Bin 0 -> 1230 bytes src/main/resources/pics/16.png | Bin 0 -> 1252 bytes src/main/resources/pics/17.png | Bin 0 -> 467 bytes src/main/resources/pics/18.png | Bin 0 -> 520 bytes src/main/resources/pics/19.png | Bin 0 -> 539 bytes src/main/resources/pics/2.png | Bin 0 -> 554 bytes src/main/resources/pics/20.png | Bin 0 -> 563 bytes src/main/resources/pics/21.png | Bin 0 -> 567 bytes src/main/resources/pics/22.png | Bin 0 -> 596 bytes src/main/resources/pics/23.png | Bin 0 -> 1080 bytes src/main/resources/pics/24.png | Bin 0 -> 1210 bytes src/main/resources/pics/25.png | Bin 0 -> 1173 bytes src/main/resources/pics/26.png | Bin 0 -> 454 bytes src/main/resources/pics/27.png | Bin 0 -> 527 bytes src/main/resources/pics/28.png | Bin 0 -> 545 bytes src/main/resources/pics/29.png | Bin 0 -> 536 bytes src/main/resources/pics/3.png | Bin 0 -> 592 bytes src/main/resources/pics/30.png | Bin 0 -> 589 bytes src/main/resources/pics/31.png | Bin 0 -> 600 bytes src/main/resources/pics/32.png | Bin 0 -> 1138 bytes src/main/resources/pics/33.png | Bin 0 -> 1244 bytes src/main/resources/pics/34.png | Bin 0 -> 1080 bytes src/main/resources/pics/35.png | Bin 0 -> 457 bytes src/main/resources/pics/4.png | Bin 0 -> 611 bytes src/main/resources/pics/5.png | Bin 0 -> 1106 bytes src/main/resources/pics/6.png | Bin 0 -> 1177 bytes src/main/resources/pics/7.png | Bin 0 -> 1099 bytes src/main/resources/pics/8.png | Bin 0 -> 614 bytes src/main/resources/pics/9.png | Bin 0 -> 559 bytes src/main/resources/pics/c0.png | Bin 0 -> 302 bytes src/main/resources/pics/c1.png | Bin 0 -> 320 bytes src/main/resources/pics/c2.png | Bin 0 -> 331 bytes src/main/resources/pics/c3.png | Bin 0 -> 298 bytes src/main/resources/pics/dos.png | Bin 0 -> 6239 bytes src/test/java/RulesTests.java | 34 + src/test/java/ServerTests.java | 74 ++ 149 files changed, 4163 insertions(+), 3014 deletions(-) delete mode 100644 client/CanvasBorder.java delete mode 100644 client/CanvasBorder1.java delete mode 100644 client/CanvasCenter.java delete mode 100644 client/CanvasLastPlie.java delete mode 100644 client/CanvasPlayer.java delete mode 100644 client/CanvasTop.java delete mode 100644 client/ClientFrame.java delete mode 100644 client/ClientListener.java delete mode 100644 client/ClientNetwork.java delete mode 100644 client/DialogAtout.java delete mode 100644 client/DialogNewPart.java delete mode 100644 client/DialogPartnerChoice.java delete mode 100644 client/FlatJassClientSystem.java delete mode 100644 client/JassCanvas.java delete mode 100644 client/Makefile delete mode 100644 client/pics/0.gif delete mode 100644 client/pics/1.gif delete mode 100644 client/pics/10.gif delete mode 100644 client/pics/11.gif delete mode 100644 client/pics/12.gif delete mode 100644 client/pics/13.gif delete mode 100644 client/pics/14.gif delete mode 100644 client/pics/15.gif delete mode 100644 client/pics/16.gif delete mode 100644 client/pics/17.gif delete mode 100644 client/pics/18.gif delete mode 100644 client/pics/19.gif delete mode 100644 client/pics/2.gif delete mode 100644 client/pics/20.gif delete mode 100644 client/pics/21.gif delete mode 100644 client/pics/22.gif delete mode 100644 client/pics/23.gif delete mode 100644 client/pics/24.gif delete mode 100644 client/pics/25.gif delete mode 100644 client/pics/26.gif delete mode 100644 client/pics/27.gif delete mode 100644 client/pics/28.gif delete mode 100644 client/pics/29.gif delete mode 100644 client/pics/3.gif delete mode 100644 client/pics/30.gif delete mode 100644 client/pics/31.gif delete mode 100644 client/pics/32.gif delete mode 100644 client/pics/33.gif delete mode 100644 client/pics/34.gif delete mode 100644 client/pics/35.gif delete mode 100644 client/pics/4.gif delete mode 100644 client/pics/5.gif delete mode 100644 client/pics/6.gif delete mode 100644 client/pics/7.gif delete mode 100644 client/pics/8.gif delete mode 100644 client/pics/9.gif delete mode 100644 client/pics/c0.gif delete mode 100644 client/pics/c1.gif delete mode 100644 client/pics/c2.gif delete mode 100644 client/pics/c3.gif delete mode 100644 client/pics/dos.gif create mode 100644 doc/class_diagram.xmi create mode 100644 jass.iml create mode 100644 pom.xml delete mode 100644 server/ClientLeftException.java delete mode 100644 server/FlatJassServerSystem.java delete mode 100644 server/Makefile delete mode 100644 server/ServerNetwork.java rename {client => src/main/java/com/leflat/jass/client}/AbsoluteConstraints.java (99%) rename {client => src/main/java/com/leflat/jass/client}/AbsoluteLayout.java (99%) create mode 100644 src/main/java/com/leflat/jass/client/CanvasBorder.java create mode 100644 src/main/java/com/leflat/jass/client/CanvasCenter.java create mode 100644 src/main/java/com/leflat/jass/client/CanvasLastPlie.java create mode 100644 src/main/java/com/leflat/jass/client/CanvasPlayer.java create mode 100644 src/main/java/com/leflat/jass/client/CanvasTop.java create mode 100644 src/main/java/com/leflat/jass/client/CardImages.java create mode 100644 src/main/java/com/leflat/jass/client/ClientPlayer.java create mode 100644 src/main/java/com/leflat/jass/client/DialogAtout.java rename {client => src/main/java/com/leflat/jass/client}/DialogConnect.java (84%) rename {client => src/main/java/com/leflat/jass/client}/DialogInfo.java (97%) create mode 100644 src/main/java/com/leflat/jass/client/DialogNewPart.java create mode 100644 src/main/java/com/leflat/jass/client/DialogPartnerChoice.java rename {client => src/main/java/com/leflat/jass/client}/DialogTeamChoice.java (98%) create mode 100644 src/main/java/com/leflat/jass/client/JassCanvas.java create mode 100644 src/main/java/com/leflat/jass/client/JassClient.java create mode 100644 src/main/java/com/leflat/jass/client/JassFrame.java create mode 100644 src/main/java/com/leflat/jass/client/JassPlayer.java create mode 100644 src/main/java/com/leflat/jass/client/RemoteController.java create mode 100644 src/main/java/com/leflat/jass/client/ServerDisconnectedException.java create mode 100644 src/main/java/com/leflat/jass/common/Anouncement.java create mode 100644 src/main/java/com/leflat/jass/common/BasePlayer.java create mode 100644 src/main/java/com/leflat/jass/common/BrokenRuleException.java create mode 100644 src/main/java/com/leflat/jass/common/Card.java create mode 100644 src/main/java/com/leflat/jass/common/ClientConnectionInfo.java create mode 100644 src/main/java/com/leflat/jass/common/ConnectionError.java create mode 100644 src/main/java/com/leflat/jass/common/IJassUi.java create mode 100644 src/main/java/com/leflat/jass/common/IPlayer.java create mode 100644 src/main/java/com/leflat/jass/common/IRemotePlayer.java create mode 100644 src/main/java/com/leflat/jass/common/Plie.java create mode 100644 src/main/java/com/leflat/jass/common/RemoteCommand.java create mode 100644 src/main/java/com/leflat/jass/common/Rules.java create mode 100644 src/main/java/com/leflat/jass/common/Team.java create mode 100644 src/main/java/com/leflat/jass/common/TeamSelectionMethod.java create mode 100644 src/main/java/com/leflat/jass/server/ConnectionListener.java create mode 100644 src/main/java/com/leflat/jass/server/GameController.java create mode 100644 src/main/java/com/leflat/jass/server/JassServer.java create mode 100644 src/main/java/com/leflat/jass/server/PlayerLeftExpection.java create mode 100644 src/main/java/com/leflat/jass/server/PlayerNetwork.java create mode 100644 src/main/java/com/leflat/jass/server/RemotePlayer.java create mode 100644 src/main/java/com/leflat/jass/test/TestClient.java create mode 100644 src/main/resources/pics/0.png create mode 100644 src/main/resources/pics/1.png create mode 100644 src/main/resources/pics/10.png create mode 100644 src/main/resources/pics/11.png create mode 100644 src/main/resources/pics/12.png create mode 100644 src/main/resources/pics/13.png create mode 100644 src/main/resources/pics/14.png create mode 100644 src/main/resources/pics/15.png create mode 100644 src/main/resources/pics/16.png create mode 100644 src/main/resources/pics/17.png create mode 100644 src/main/resources/pics/18.png create mode 100644 src/main/resources/pics/19.png create mode 100644 src/main/resources/pics/2.png create mode 100644 src/main/resources/pics/20.png create mode 100644 src/main/resources/pics/21.png create mode 100644 src/main/resources/pics/22.png create mode 100644 src/main/resources/pics/23.png create mode 100644 src/main/resources/pics/24.png create mode 100644 src/main/resources/pics/25.png create mode 100644 src/main/resources/pics/26.png create mode 100644 src/main/resources/pics/27.png create mode 100644 src/main/resources/pics/28.png create mode 100644 src/main/resources/pics/29.png create mode 100644 src/main/resources/pics/3.png create mode 100644 src/main/resources/pics/30.png create mode 100644 src/main/resources/pics/31.png create mode 100644 src/main/resources/pics/32.png create mode 100644 src/main/resources/pics/33.png create mode 100644 src/main/resources/pics/34.png create mode 100644 src/main/resources/pics/35.png create mode 100644 src/main/resources/pics/4.png create mode 100644 src/main/resources/pics/5.png create mode 100644 src/main/resources/pics/6.png create mode 100644 src/main/resources/pics/7.png create mode 100644 src/main/resources/pics/8.png create mode 100644 src/main/resources/pics/9.png create mode 100644 src/main/resources/pics/c0.png create mode 100644 src/main/resources/pics/c1.png create mode 100644 src/main/resources/pics/c2.png create mode 100644 src/main/resources/pics/c3.png create mode 100644 src/main/resources/pics/dos.png create mode 100644 src/test/java/RulesTests.java create mode 100644 src/test/java/ServerTests.java diff --git a/.gitignore b/.gitignore index d7eac8b..3124173 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ dist/ release/ */TAGS *.zip +.idea/ +target/ diff --git a/client/CanvasBorder.java b/client/CanvasBorder.java deleted file mode 100644 index beff21a..0000000 --- a/client/CanvasBorder.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * CanvasBorder.java - * - * Created on 18. avril 2000, 16:49 - */ - - - -/** - * - * @author Berclaz Jérôme - * @version - */ - -import java.awt.*; - -public class CanvasBorder extends JassCanvas { - Image cardBack; - Image card; - int nbrCards; - // mode:: 0 : tirage des équipes, 1 : jouer normalement - - - /** Creates new CanvasBorder */ - public CanvasBorder(String imgPath) { - mode = 1; - nbrCards = 0; - name = ""; - Toolkit tk = Toolkit.getDefaultToolkit(); - - cardBack = tk.getImage(imgPath+"dos.gif"); - } - - public void setNbrCards(int nbrCards) { - this.nbrCards = nbrCards; - } - - public void setCard(Image card) { - this.card = card; - } - - public void removeCard() { - nbrCards--; - } - - public void paint (Graphics g) { - Dimension d = getSize(); - if (mode == 1) { - int w = (d.width - 71) / 2; - int h = (d.height - nbrCards * 30 - 66) / 2 + 20; - for (int i = 0; i 19)&&(e.getY() < 116)) && ((e.getX() > 19)&&(e.getX() - < 371))) { - System.out.println("Click"); // supprimer - int x = e.getX(); - int i = 9; - boolean found = false; - while (!found && (i > 0)) { - i--; - if ((x > (19 + i * 35)) && (x < (91 + i * 35))) - if (app.cards[i] < 36) - switch (app.playCard(playerCanvas.getMode(), app.cards[i])) { - case 0 : // jouer la carte - int playedCard = app.cards[i]; - jButtonAnounce.setEnabled(false); - setStatusBar(""); // remove "a vous de..." - centerCanvas.cardsChoosen[0] = playedCard; - playerCanvas.setMode(0); - playerCanvas.hand[i] = 37; - app.cards[i] = 37; - found = true; - repaint(17); - - // sends the card to the server - if (app.atout == Card.getColor(playedCard)) - app.sendCard(playedCard, Card.valueAtout[Card.getHeight(playedCard)]); // modifier !!!! - else - app.sendCard(playedCard, Card.value[Card.getHeight(playedCard)]); - break; - case -1 : // suivre - System.out.println("Il faut suivre !!"); - found = true; - break; - case -2 : // sous-couper - System.out.println("Vous ne pouvez pas sous-couper!!"); - found = true; - } - } - } - } - - public void setName(int player, String name) { - int diff = player - app.myPlayer; - if (diff < 0) - diff += 4; - switch (diff) { - case 0 : playerCanvas.setName(name); - playerCanvas.repaint(); - break; - case 1 : rightCanvas.setName(name); - rightCanvas.repaint(); - break; - case 2 : topCanvas.setName(name); - topCanvas.repaint(); - break; - case 3 : leftCanvas.setName(name); - leftCanvas.repaint(); - } - } - - public void teamChoiceShowCard(int player, int position, int cardNumber) { - int diff = player - app.myPlayer; - if (diff < 0) - diff += 4; - switch (diff) { - case 0 : for (int i=1; i<9; i++) - playerCanvas.hand[i] = 37; - playerCanvas.hand[0] = cardNumber; - playerCanvas.repaint(); - break; - case 1 : centerCanvas.cardsChoosen[position] = app.myPlayer; - centerCanvas.repaint(); - rightCanvas.setCard(playerCanvas.cards[cardNumber]); - rightCanvas.setMode(0); // tirage des équipes - rightCanvas.repaint(); - break; - case 2 : centerCanvas.cardsChoosen[position] = app.myPlayer; - centerCanvas.repaint(); - topCanvas.setCard(playerCanvas.cards[cardNumber]); - topCanvas.setMode(0); // tirage des équipes - topCanvas.repaint(); - break; - case 3 : centerCanvas.cardsChoosen[position] = app.myPlayer; - centerCanvas.repaint(); - leftCanvas.setCard(playerCanvas.cards[cardNumber]); - leftCanvas.setMode(0); // tirage des équipes - leftCanvas.repaint(); - } - } - - public void prepareTeamChoice() { - for (int i=0; i<36; i++) - centerCanvas.cardsChoosen[i] = 10; // carte présente - for (int i=0; i<9; i++) - playerCanvas.hand[i] = 37; // effacer les cartes - leftCanvas.setNbrCards(0); - leftCanvas.setMode(1); - rightCanvas.setNbrCards(0); - rightCanvas.setMode(1); - topCanvas.setNbrCards(0); - topCanvas.setMode(1); - centerCanvas.mode = 1; - centerCanvas.repaint(); - removeLastPlie(); - lastPlieCanvas.atout = 4; - setScore(0,0); - repaint(31); - } - - void centerCanvas_mouseClicked(MouseEvent e) { - System.out.println("Click 1"); - if (centerCanvas.mode == 2) { // à ton tour de tirer une carte - System.out.println("Click Center"); - if (((e.getY() < 137) && (e.getY() > 39)) && ((e.getX() > 9) && (e.getX() < 361))) { - int nbr = (e.getX() - 10) / 8; - if (nbr > 35) { - if (centerCanvas.cardsChoosen[35] == 10) - nbr = 35; - else if ((centerCanvas.cardsChoosen[34] == 10) && (e.getX() < 353)) - nbr = 34; - else if ((centerCanvas.cardsChoosen[33] == 10) && (e.getX() < 345)) - nbr = 33; - else if ((centerCanvas.cardsChoosen[32] == 10) && (e.getX() < 337)) - nbr = 32; - } - - if (centerCanvas.cardsChoosen[nbr] == 10) { - centerCanvas.cardsChoosen[nbr] = app.myPlayer; - centerCanvas.repaint(); - } - else if (centerCanvas.cardsChoosen[nbr-1] == 10) { - nbr--; - centerCanvas.cardsChoosen[nbr] = app.myPlayer; - centerCanvas.repaint(); - } - else if (centerCanvas.cardsChoosen[nbr-2] == 10) { - nbr-=2; - centerCanvas.cardsChoosen[nbr] = app.myPlayer; - centerCanvas.repaint(); - } - else if (centerCanvas.cardsChoosen[nbr-3] == 10) { - nbr-=3; - centerCanvas.cardsChoosen[nbr] = app.myPlayer; - centerCanvas.repaint(); - } - centerCanvas.mode = 1; // on ne peut plus choisir une carte - setStatusBar(""); // remove "veuillez tirer..." - app.sendCard(nbr, 0); - } - } - } - - // prépare l'écran pour une nouvelle partie - void prepareMatch() { - for (int i=0; i<4; i++) - centerCanvas.cardsChoosen[i] = 36; - centerCanvas.mode = 3; // mode de jeu - lastPlieCanvas.atout = 4; - lastPlieCanvas.repaint(); - repaint(31); // repaint les 5 canvas - setStatusBar(""); - removeLastPlie(); - } - - // affiche une carte jouée - void showPlayedCard(int player, int number) { - int diff = player - app.myPlayer; - if (diff < 0) - diff += 4; - switch (diff) { - case 1 : centerCanvas.cardsChoosen[1] = number; - rightCanvas.removeCard(); - rightCanvas.repaint(); - break; - case 2 : centerCanvas.cardsChoosen[2] = number; - topCanvas.removeCard(); - topCanvas.repaint(); - break; - case 3 : centerCanvas.cardsChoosen[3] = number; - leftCanvas.removeCard(); - leftCanvas.repaint(); - } - centerCanvas.repaint(); - statusBar.setText(app.players[player].getFirstName() + " a joué..."); - } - - // ramasse la plie - void pickUpPlie(String player) { - statusBar.setText(player + " a pris la plie"); - for (int i=0; i<4; i++) { - System.out.println(i); - lastPlieCanvas.cards[i] = playerCanvas.cards[centerCanvas.cardsChoosen[i]]; - centerCanvas.cardsChoosen[i] = 36; - } - lastPlieCanvas.display = true; - lastPlieCanvas.repaint(); - centerCanvas.repaint(); - } - - void removeLastPlie() { - lastPlieCanvas.display = false; - lastPlieCanvas.repaint(); - } - - void setScore(int ourScore, int theirScore) { - lastPlieCanvas.ourScore = ourScore; - lastPlieCanvas.theirScore = theirScore; - lastPlieCanvas.repaint(); - } - - - void setStatusBar(String text) { - statusBar.setText(text); - } - - void setAnounceEnabled(boolean b) { - jButtonAnounce.setEnabled(b); - } - - void setAtout(int player) { - int diff = player - app.myPlayer; - if (diff < 0) - diff += 4; - - playerCanvas.setAtout(false); - rightCanvas.setAtout(false); - topCanvas.setAtout(false); - leftCanvas.setAtout(false); - - switch (diff) { - case 0 : - playerCanvas.setAtout(true); - break; - case 1 : - rightCanvas.setAtout(true); - break; - case 2 : - topCanvas.setAtout(true); - break; - case 3 : - leftCanvas.setAtout(true); - } - repaint(31); - } -} diff --git a/client/ClientListener.java b/client/ClientListener.java deleted file mode 100644 index 9827c43..0000000 --- a/client/ClientListener.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * ClientListener.java - * - * Created on 18. avril 2000, 18:15 - */ - - - -/** - * - * @author Berclaz Jérôme - * @version 1.2 - */ - -import java.io.*; -import java.net.*; - -public class ClientListener extends Thread{ - FlatJassClientSystem app; - Socket threadSocket; - InputStreamReader isr; - BufferedReader is; - public boolean stop = false; - - public ClientListener(FlatJassClientSystem app, Socket cs) { - this.app = app; - threadSocket=cs; - stop = false; - try { - isr = new InputStreamReader(threadSocket.getInputStream()); - is = new BufferedReader(isr); - } - catch (IOException e) { - System.out.println("LISTENER:Impossible stream"); - } - } - -/* public ClientListener(FlatJassClientSystem app) { - this.app = app; - }*/ - - public void setSocket(Socket cs) { - threadSocket = cs; - try { - isr = new InputStreamReader(threadSocket.getInputStream()); - is = new BufferedReader(isr); - } - catch (IOException e) { - System.out.println("LISTENER:Impossible stream"); - } - } - - public void run() { - /* int c; - StringBuffer s = new StringBuffer(50); - String answer; - int len; - System.out.println("Client Listener : prêt"); - try { - while (true) { - s.setLength(0); - try { - while ((c = System.in.read()) != '\n') - s.append((char)c); - } - catch (IOException e) { - } - answer = s.toString(); - len = answer.length(); - app.execute(answer.substring(0, len-1)); - // app.execute(answer); - } - } - catch (Exception e) { - }*/ - System.out.println("Starting Listener..."); - - while (!stop) { - String rcvTemp = null; - - //implementer timeout + exc - - try { - rcvTemp=is.readLine(); - System.out.println("LISTENER:Reception:"+rcvTemp); - app.execute(rcvTemp); - } - catch (IOException e) { - System.out.println("LISTENER:Erreur reception"); - //System.exit(1); - } - } - - //this.stop(); - } -} diff --git a/client/ClientNetwork.java b/client/ClientNetwork.java deleted file mode 100644 index fbd1e7e..0000000 --- a/client/ClientNetwork.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * ClientNetwork.java - * - * Created on 18. avril 2000, 18:17 - */ - - - -/** - * - * @author Berclaz Jérôme - * @version 1.2 - */ - -import java.net.*; -import java.io.*; - -public class ClientNetwork { - - public static final int PORT_NUM=32107; - Socket myClientSocket=null; - - PrintWriter os; - - public ClientNetwork() { - - } - - public Socket connect(String ipAddress) { - - //streams - try { - myClientSocket = new Socket(ipAddress, PORT_NUM); - - os = new PrintWriter(myClientSocket.getOutputStream(),false); - System.out.println("Connection successful"); - } - catch (IOException e) { - System.out.println("Unable to create socket"); - System.out.println(e); - //System.exit(1); - return null; - } - - return this.myClientSocket; - } - - public void sendTo(String msg) { - os.println(msg); - os.flush(); - System.out.println("Envoi au serveur : " + msg); - } - - public void disconnect() { - try { - myClientSocket.close(); - } - catch (IOException e) { - System.out.println("Error while closing socket"); - //System.exit(1); - } - } -} diff --git a/client/DialogAtout.java b/client/DialogAtout.java deleted file mode 100644 index af33aa6..0000000 --- a/client/DialogAtout.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * DialogAtout.java - * - * Created on 19. avril 2000, 11:52 - */ - - - -/** - * - * @author Berclaz Jérôme - * @version - */ -public class DialogAtout extends javax.swing.JDialog { - int number = 0; - - /** Creates new form DialogAtout */ - public DialogAtout(java.awt.Frame parent,boolean modal, boolean pass) { - super (parent, modal); - initComponents (); - pack (); - if (pass) - jButtonPass.setEnabled(false); - jComboBoxAtout.addItem("Pique"); - jComboBoxAtout.addItem("Coeur"); - jComboBoxAtout.addItem("Carreau"); - jComboBoxAtout.addItem("Trèfle"); - } - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the FormEditor. - */ - private void initComponents () {//GEN-BEGIN:initComponents - jLabel1 = new javax.swing.JLabel (); - jComboBoxAtout = new javax.swing.JComboBox (); - jButtonOk = new javax.swing.JButton (); - jButtonPass = new javax.swing.JButton (); - getContentPane ().setLayout (new AbsoluteLayout ()); - setSize(300, 180); - setResizable (false); - setTitle ("Choix de l'atout"); - addWindowListener (new java.awt.event.WindowAdapter () { - public void windowClosing (java.awt.event.WindowEvent evt) { - closeDialog (evt); - } - } - ); - - jLabel1.setText ("Veuillez choisir l'atout"); - - - getContentPane ().add (jLabel1, new AbsoluteConstraints (20, 20, -1, -1)); - - - - getContentPane ().add (jComboBoxAtout, new AbsoluteConstraints (120, 60, -1, -1)); - - jButtonOk.setText ("Ok"); - jButtonOk.addActionListener (new java.awt.event.ActionListener () { - public void actionPerformed (java.awt.event.ActionEvent evt) { - jButtonOkActionPerformed (evt); - } - } - ); - - - getContentPane ().add (jButtonOk, new AbsoluteConstraints (40, 110, -1, -1)); - - jButtonPass.setText ("Passer"); - jButtonPass.addActionListener (new java.awt.event.ActionListener () { - public void actionPerformed (java.awt.event.ActionEvent evt) { - jButtonPassActionPerformed (evt); - } - } - ); - - - getContentPane ().add (jButtonPass, new AbsoluteConstraints (180, 110, -1, -1)); - - }//GEN-END:initComponents - - private void jButtonPassActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonPassActionPerformed -// Add your handling code here: - number = 4; - this.dispose(); - }//GEN-LAST:event_jButtonPassActionPerformed - - private void jButtonOkActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonOkActionPerformed -// Add your handling code here: - number = jComboBoxAtout.getSelectedIndex(); - this.dispose(); - }//GEN-LAST:event_jButtonOkActionPerformed - - /** Closes the dialog */ - private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog - setVisible (false); - dispose (); - }//GEN-LAST:event_closeDialog - - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel jLabel1; - private javax.swing.JComboBox jComboBoxAtout; - private javax.swing.JButton jButtonOk; - private javax.swing.JButton jButtonPass; - // End of variables declaration//GEN-END:variables - -} diff --git a/client/DialogNewPart.java b/client/DialogNewPart.java deleted file mode 100644 index 347a3f1..0000000 --- a/client/DialogNewPart.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * DialogNewPart.java - * - * Created on 11. avril 2002, 22:17 - */ - - - -/** - * - * @author Berclaz Jérôme - * @version - */ -public class DialogNewPart extends javax.swing.JDialog { - boolean newPart = false; - - /** Creates new form DialogNewPart */ - public DialogNewPart(java.awt.Frame parent,boolean modal) { - super (parent, modal); - initComponents (); - pack (); - } - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the FormEditor. - */ - private void initComponents () {//GEN-BEGIN:initComponents - jLabel1 = new javax.swing.JLabel (); - jButtonYes = new javax.swing.JButton (); - jButtonNo = new javax.swing.JButton (); - - getContentPane ().setLayout (new AbsoluteLayout ()); - setSize(320, 180); - setResizable (false); - setTitle ("Nouvelle partie"); - addWindowListener (new java.awt.event.WindowAdapter () { - public void windowClosing (java.awt.event.WindowEvent evt) { - closeDialog (evt); - } - } - ); - - jLabel1.setText ("Voulez vous faire une nouvelle partie?"); - - - getContentPane ().add (jLabel1, new AbsoluteConstraints (30, 20, -1, -1)); - - jButtonYes.setText ("Oui"); - jButtonYes.addActionListener (new java.awt.event.ActionListener () { - public void actionPerformed (java.awt.event.ActionEvent evt) { - jButtonYesActionPerformed (evt); - } - } - ); - - - getContentPane ().add (jButtonYes, new AbsoluteConstraints (40, 100, -1, -1)); - - jButtonNo.setText ("Non"); - jButtonNo.addActionListener (new java.awt.event.ActionListener () { - public void actionPerformed (java.awt.event.ActionEvent evt) { - jButtonNoActionPerformed (evt); - } - } - ); - - - getContentPane ().add (jButtonNo, new AbsoluteConstraints (180, 100, -1, -1)); - - - - - - - }//GEN-END:initComponents - - private void jButtonNoActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonManualActionPerformed -// Add your handling code here: - newPart = false; - this.dispose(); - }//GEN-LAST:event_jButtonManualActionPerformed - - private void jButtonYesActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonHasardActionPerformed -// Add your handling code here: - newPart = true; - this.dispose(); - }//GEN-LAST:event_jButtonHasardActionPerformed - - /** Closes the dialog */ - private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog - setVisible (false); - dispose (); - }//GEN-LAST:event_closeDialog - - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel jLabel1; - private javax.swing.JButton jButtonYes; - private javax.swing.JButton jButtonNo; - // End of variables declaration//GEN-END:variables - -} diff --git a/client/DialogPartnerChoice.java b/client/DialogPartnerChoice.java deleted file mode 100644 index 4f487ff..0000000 --- a/client/DialogPartnerChoice.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * DialogPartnerChoice.java - * - * Created on 19. avril 2000, 11:44 - */ - - - -/** - * - * @author Berclaz Jérôme - * @version - */ -public class DialogPartnerChoice extends javax.swing.JDialog { - int number = 0; - - /** Creates new form DialogPartnerChoice */ - public DialogPartnerChoice(java.awt.Frame parent,boolean modal) { - super (parent, modal); - initComponents (); - pack (); - } - - /** This method is called from within the constructor to - * initialize the form. - * WARNING: Do NOT modify this code. The content of this method is - * always regenerated by the FormEditor. - */ - private void initComponents () {//GEN-BEGIN:initComponents - jComboBoxPartner = new javax.swing.JComboBox (); - jButtonOk = new javax.swing.JButton (); - jLabel1 = new javax.swing.JLabel (); - getContentPane ().setLayout (new AbsoluteLayout ()); - setSize(300, 200); - setResizable (false); - setTitle ("Choix du partenaire"); - addWindowListener (new java.awt.event.WindowAdapter () { - public void windowClosing (java.awt.event.WindowEvent evt) { - closeDialog (evt); - } - } - ); - - - - getContentPane ().add (jComboBoxPartner, new AbsoluteConstraints (100, 50, -1, -1)); - - jButtonOk.setText ("Ok"); - jButtonOk.addActionListener (new java.awt.event.ActionListener () { - public void actionPerformed (java.awt.event.ActionEvent evt) { - jButtonOkActionPerformed (evt); - } - } - ); - - - getContentPane ().add (jButtonOk, new AbsoluteConstraints (120, 120, -1, -1)); - - jLabel1.setText ("Veuillez choisir votre partenaire"); - - - getContentPane ().add (jLabel1, new AbsoluteConstraints (20, 20, -1, -1)); - - }//GEN-END:initComponents - - private void jButtonOkActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonOkActionPerformed -// Add your handling code here: - number = jComboBoxPartner.getSelectedIndex(); - this.dispose(); - }//GEN-LAST:event_jButtonOkActionPerformed - - /** Closes the dialog */ - private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog - setVisible (false); - dispose (); - }//GEN-LAST:event_closeDialog - - - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JComboBox jComboBoxPartner; - private javax.swing.JButton jButtonOk; - private javax.swing.JLabel jLabel1; - // End of variables declaration//GEN-END:variables - - public void addPlayer(String name) { - jComboBoxPartner.addItem(name); - } -} diff --git a/client/FlatJassClientSystem.java b/client/FlatJassClientSystem.java deleted file mode 100644 index 35edaa2..0000000 --- a/client/FlatJassClientSystem.java +++ /dev/null @@ -1,695 +0,0 @@ -/* - * FlatJassClientSystem.java - * - * Created on 18. avril 2000, 16:09 - */ - - - -/** - * - * @author Berclaz Jérôme - * @version 1.2 - */ -import java.net.*; - -public class FlatJassClientSystem extends Object { - // Variables - ClientFrame frame = new ClientFrame(this); - - // Variables du moteur de jeu - ClientListener listener; // classe qui écoute - ClientNetwork network = new ClientNetwork(); // classe qui implémente socket - public int myPlayer; // id du joueur - Player[] players = new Player[4]; // les 4 joueurs - int[] cards = new int[9]; // main du joueur - int atout; // atout - Plie currentPlie = new Plie(); // plie en cours - Anounce[] myAnounces = new Anounce[3]; // annonces - int nbrAnounces; // nombre d'annonces - int stock; // 0:rien, 1:stöck, 2: joué un, 3:joué les deux - int plieNbr; // numéro de la plie - - /** Creates new FlatJassClientSystem */ - public FlatJassClientSystem() { - for (int i=0; i<4; i++) // construire les objets player - players[i] = new Player(); - for (int i=0; i<3; i++) // construire les objets Anounce - myAnounces[i] = new Anounce(); - frame.show(); - } - - /** - * @param args the command line arguments - */ - public static void main (String args[]) { - new FlatJassClientSystem(); - } - - public int connect(String firstName, String lastName, String IP) { - // renvoie -1 en cas d'échec et 0 si réussi - System.out.println("Connected to " + IP); - Socket cs=network.connect(IP); - if (cs != null) { // connexion réussie - players[0].setFirstName(firstName); - players[0].setLastName(lastName); - if (listener == null) { - listener = new ClientListener(this,cs); - System.out.println("Création d'un nouveau listener"); // débuggage - } - else { - // listener.setSocket(cs); - // listener.stop = false; - listener = new ClientListener(this,cs); - } - listener.start(); - } - else { - return -1; - } - - //players[0].setFirstName(firstName); - //players[0].setLastName(lastName); - // listener = new ClientListener(this); - // listener.start(); - return 0; - } - - // Procédure de décodage des instructions - private String[] decode(String instr) { - int cmpt = 0; - int cursor = 0; - String[] table = new String[10]; - for (int i=1; i demande d'envoi d'informations sur le joueur - // Syntaxe : 1 + id du joueur - temp = Integer.valueOf(tableInstr[1]); - myPlayer = temp.intValue(); - if (myPlayer != 0) { - players[myPlayer].setFirstName(players[0].getFirstName()); - players[myPlayer].setLastName(players[0].getLastName()); - } - players[myPlayer].setID(myPlayer); - frame.setName(myPlayer, players[myPlayer].getFirstName()); - frame.setStatusBar("Connexion réussie"); - answer = tableInstr[1] + " " + players[myPlayer].getFirstName() + " " + players[myPlayer].getLastName(); - break; - case 2 : // envoie les infos des autres joueurs - // Syntaxe : 2 + id du joueur + prenom du joueur + nom du joueur - temp = Integer.valueOf(tableInstr[1]); - int i = temp.intValue(); - players[i].setFirstName(tableInstr[2]); - players[i].setLastName(tableInstr[3]); - players[i].setID(i); - frame.setStatusBar(tableInstr[2] + " " + tableInstr[3] + " s'est connecté"); - frame.setName(i, tableInstr[2]); - answer = String.valueOf(myPlayer) + " 1"; // signifie que l'opération s'est bien déroulée - break; - case 3 : // demande de choisir le mode de choix des équipes - // Syntaxe : 3 - DialogTeamChoice dtc = new DialogTeamChoice(frame, true); - dtc.show(); - if (dtc.hasard) { - answer = String.valueOf(myPlayer) + " 1"; // choisir au hasard - } - else { - answer = String.valueOf(myPlayer) + " 0"; // choisir manuellement - } - break; - case 4 : // demande de préparer le mode de tirage des équipes - // Syntaxe : 4 - frame.prepareTeamChoice(); - frame.setStatusBar("Tirage des équipes..."); - answer = String.valueOf(myPlayer) + " 1"; - break; - case 5 : // demande de choisir une carte - // Syntaxe : 5 - frame.centerCanvas.mode = 2; - frame.setStatusBar("Veuillez choisir une carte"); - break; - case 6 : // donne la carte choisie par un joueur - // Syntaxe : 6 + id joueur + position de la carte + numéro de la carte - temp = Integer.valueOf(tableInstr[1]); - int id = temp.intValue(); - temp = Integer.valueOf(tableInstr[2]); - int pos = temp.intValue(); - temp = Integer.valueOf(tableInstr[3]); - int nbr = temp.intValue(); - frame.teamChoiceShowCard(id, pos, nbr); - frame.setStatusBar(players[id].getFirstName() + " a tiré une carte"); - answer = String.valueOf(myPlayer) + " 1"; // signifie que l'opération s'est bien déroulée - break; - case 7 : // le tirage n'est pas bon, on recommence - // Syntaxe : 7 - frame.prepareTeamChoice(); - frame.setStatusBar("Le tirage n'est pas bon..."); - answer = String.valueOf(myPlayer) + " 1"; - break; - case 8 : // transmet le nouvel ordre des joueurs - // Syntaxe : 9 + id1 + id2 + id3 + id4 - temp = Integer.valueOf(tableInstr[1]); - int id1 = temp.intValue(); - temp = Integer.valueOf(tableInstr[2]); - int id2 = temp.intValue(); - temp = Integer.valueOf(tableInstr[3]); - int id3 = temp.intValue(); - temp = Integer.valueOf(tableInstr[4]); - int id4 = temp.intValue(); - organisePlayers(id1, id2, id3, id4); - frame.setScore(0, 0); // initialize scores - frame.setStatusBar("Equipes réorganisées"); - for (i=0; i<4; i++) - frame.setName(i, players[i].getFirstName()); - answer = String.valueOf(myPlayer) + " 1"; // signifie que l'opération s'est bien déroulée - break; - case 9 : // demande de choisir son partenaire - //Syntaxe : 9 - DialogPartnerChoice dpc = new DialogPartnerChoice(frame, true); - for (i=0; i<4; i++) - if (i != myPlayer) - dpc.addPlayer(players[i].getFirstName()); - dpc.show(); - answer = String.valueOf(myPlayer) + " " + String.valueOf(dpc.number + 1); - break; - case 10 : // donne les cartes - // Syntaxe : 10 + carte1 + ... + carte9 - for (i=1; i<10; i++) { - temp = Integer.valueOf(tableInstr[i]); - cards[i-1] = temp.intValue(); - } - quickSort(cards, 0, 8); - for (i=0; i<4; i++) - if (i == myPlayer) - frame.setPlayerCards(cards); - else - frame.setOpponentCards(i, 9); - frame.prepareMatch(); // prépare l'écran pour une nouvelle partie - nbrAnounces = 0; - plieNbr = 0; - answer = String.valueOf(myPlayer) + " 1"; // signifie que l'opération s'est bien déroulée - break; - case 11 : // demande de faire atout en premier - // Syntaxe : 11 - DialogAtout da = new DialogAtout(frame, true, false); - da.show(); - if (da.number == 4) - answer = String.valueOf(myPlayer) + " 4"; - else - answer = String.valueOf(myPlayer) + " " + String.valueOf(da.number); - break; - case 12 : // demande de faire atout en second - // Syntaxe : 12 - da = new DialogAtout(frame, true, true); - da.show(); - answer = String.valueOf(myPlayer) + " " + String.valueOf(da.number); - break; - case 13 : // communique l'atout - // Syntaxe : 13 + numéro de l'atout + numero du joueur a faire - // atout - temp = Integer.valueOf(tableInstr[1]); - atout = temp.intValue(); - stock = findStock(); - frame.lastPlieCanvas.atout = atout; - frame.lastPlieCanvas.repaint(); - temp = Integer.valueOf(tableInstr[2]); - frame.setAtout(temp.intValue()); - answer = String.valueOf(myPlayer) + " 1"; // signifie que l'opération s'est bien déroulée - break; - case 14 : // demande de jouer en premier - // Syntaxe : 14 - frame.setStatusBar("A vous de jouer ..."); - frame.setAnounceEnabled(true); - frame.playerCanvas.setMode(1); - plieNbr++; - break; - case 15 : // communique la carte jouée - // Syntaxe : 15 + id du joueur + numéro de la carte - temp = Integer.valueOf(tableInstr[1]); - id = temp.intValue(); - temp = Integer.valueOf(tableInstr[2]); - frame.showPlayedCard(id, temp.intValue()); // affiche la carte jouée - answer = String.valueOf(myPlayer) + " 1"; // signifie que l'opération s'est bien déroulée - break; - case 16 : // demande de jouer ensuite - // Syntaxe : 16 + highest + color + coupe (coupe : 1 = true, 0 = false) - temp = Integer.valueOf(tableInstr[1]); - currentPlie.highest = temp.intValue(); - temp = Integer.valueOf(tableInstr[2]); - currentPlie.color = temp.intValue(); - temp = Integer.valueOf(tableInstr[3]); - if (temp.intValue() == 0) - currentPlie.coupe = false; - else - currentPlie.coupe = true; - frame.setStatusBar("A vous de jouer ..."); - frame.setAnounceEnabled(true); - plieNbr++; - frame.playerCanvas.setMode(2); // let the player play - break; - case 17 : // communique par qui la plie a été prise - // Syntaxe : 17 + id du joueur - temp = Integer.valueOf(tableInstr[1]); - id = temp.intValue(); - frame.pickUpPlie(players[id].getFirstName()); - answer = String.valueOf(myPlayer) + " 1"; // signifie que l'opération s'est bien déroulée - break; - case 18 : // communique le résultat de la partie - // Syntaxe : 18 + points de l'équipe + points de l'équipe adverse - Integer sc1 = Integer.valueOf(tableInstr[1]); - Integer sc2 = Integer.valueOf(tableInstr[2]); - frame.setScore(sc1.intValue(), sc2.intValue()); - answer = String.valueOf(myPlayer) + " 1"; // signifie que l'opération s'est bien déroulée - break; - case 19 : // demande de déclarer ses annonces - // Syntaxe : 19 - answer = String.valueOf(myPlayer) + " " + String.valueOf(nbrAnounces); - System.out.println("nbrAnounces : " + nbrAnounces); - - for (i=0; i 0)) { - sendAnounces = 2; // annonce le stöck lorsqu'on pose la dernière carte - stock = 0; - System.out.println("Annonce : stock"); - nbrAnounces = 0; - } - } - if ((nbrAnounces > 0) && (plieNbr == 1)) { // on annonce à la première plie - if ((stock > 0) && (nbrAnounces > 1)) { - sendAnounces = 3; - stock = 0; - System.out.println("Annonce : annonce + stock"); - nbrAnounces--; // le stöck peut être décomptabilisé - } - else if ((nbrAnounces > 0) && (stock == 0)) { - sendAnounces = 1; - System.out.println("Annonce : annonce"); - } - } - - network.sendTo(String.valueOf(myPlayer) + " " + String.valueOf(cardNumber) + " " + String.valueOf(score) + " " + String.valueOf(sendAnounces)); - nbrAnounces = 0; // reset anounces number - } - - - // détermine si on peut jouer la carte choisie - public int playCard(int mode, int cardChoosen) { - int answer = 0; // joue - if (mode == 2) { - if (Card.getColor(cardChoosen) != currentPlie.color) { - if (Card.getColor(cardChoosen) == atout) { - if (currentPlie.coupe) - switch (Card.getHeight(cardChoosen)) { - case 5 : answer = 0; // bourg : ok! - break; - case 3 : if (currentPlie.highest != 5) - answer = 0; // nell sans bourg : ok! - else - answer = -2; // on ne peut pas sous-couper - break; - default : if (((currentPlie.highest == 5) || (currentPlie.highest == 3)) || - (currentPlie.highest > Card.getHeight(cardChoosen))) - answer = -2; // on ne peut pas sous-couper - else - answer = 0; // sur-coupe : ok ! - } - else - answer = 0; // coupe : ok! - } - else { // carte jouée pas d'atout - if (checkColor(currentPlie.color)) { - // The player owns cards from the required color - if ((currentPlie.color == atout) && (bourgSec())) - answer = 0; // bourg sec -> ok - else - answer = -1; // il faut suivre - } - else - answer = 0; // pas de cette couleur : ok! - } - } - else - answer = 0; // suivi : ok! - } - return answer; - } - - - // vérifie si le joueur possède la couleur demandée - boolean checkColor(int colorChecked) { - boolean present = false; - for (int i=0; i<9; i++) - if (Card.getColor(cards[i]) == colorChecked) - present = true; - return present; - } - - // cherche si on a le stöck - int findStock() { - int queen = atout * 9 + 6; - int king = atout * 9 + 7; - int stock = 0; - for (int i=0; i<9; i++) - if (cards[i] == queen) - for (int j=0; j<9; j++) - if (cards[j] == king) - stock = 1; - if (stock == 1) - System.out.println("Stock !!"); - return stock; - } - - void findAnounce() { - // type : 0: stock, 1: 3cartes, 2: cinquante, 3: cent, 4: cent(carré), 5: centcinquante, 6: deux cent - nbrAnounces = 0; - // cherche les carrés - int i=0; - int nbrCards; - while ((i<9) && ((cards[i] / 9) == 0)) { // tant que c'est du pique (couleur la + à gauche) - nbrCards = 1; - for (int j=i+1; j<9; j++) { - if ((cards[j] % 9) == (cards[i] % 9)) - nbrCards++; - } - if ((nbrCards == 4) && ((cards[i] % 9) > 2)) { // carré trouvé - if ((cards[i] % 9) == 3) // cent-cinquante - myAnounces[nbrAnounces].type = 5; - else if ((cards[i] % 9) == 5) // deux cents - myAnounces[nbrAnounces].type = 6; - else // cent - myAnounces[nbrAnounces].type = 4; - myAnounces[nbrAnounces].height = cards[i] % 9; - nbrAnounces++; - System.out.println("Carré trouvé : " + cards[i] % 9); - } - i++; - } - - // cherche les suites - for (i=0; i<7; i++) { - int color = Card.getColor(cards[i]); - int j = i + 1; - nbrCards = 1; - while ((j < 9) && ((cards[j] / 9) == color)) { - if (cards[j] == (cards[i] + j - i)) // si les cartes se suivent - nbrCards++; - j++; - } - if (nbrCards > 2) { // on a trouvé une suite - if (nbrCards > 5) - nbrCards = 5; - myAnounces[nbrAnounces].type = nbrCards - 2; - myAnounces[nbrAnounces].height = cards[i + nbrCards - 1]; - nbrAnounces++; - System.out.println("Suite trouvée : " + cards[i + nbrCards - 1] + " type : " + (nbrCards - 2)); - i = j-1; - } - } - - // Stöck - if (stock > 0) - nbrAnounces++; - } - - - /** - * Checks if we have "bourg sec" - */ - boolean bourgSec() { - boolean bourg = false; - int atoutNbr = 0; - for (int i=0; i<9; i++) - if (Card.getColor(cards[i]) == atout) { - atoutNbr++; - if (Card.getHeight(cards[i]) == 5) // bourg - bourg = true; - } - if (bourg && (atoutNbr == 1)) - return true; - else - return false; - } - - /** - * Disconect from the server - */ - public void disconnect() { - listener.stop = true; - network.disconnect(); - } -} - -// ********************** CLASS TMember **************************************** -class TMember { - // Variables - private String firstName; - private String lastName; - private String function; - - // Constructeur - public TMember() { - } - - public TMember(String firstName, String lastName, String function) { - this(); - this.firstName = firstName; - this.lastName = lastName; - this.function = function; - } - - // Getters - public String getFirstName() { - return firstName; - } - - public String getLastName() { - return lastName; - } - - public String getFunction() { - return function; - } - - // Setters - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public void setFunction(String function) { - this.function = function; - } -} - -// **************************** CLASS Player *********************************** -class Player extends TMember { - // Variables - private int iD; // numéro d'identification du joueur - - public Player() { - super(); - } - - public Player(Player copyPlayer) { - super(copyPlayer.getFirstName(), copyPlayer.getLastName(), - copyPlayer.getFunction()); - this.iD = copyPlayer.getID(); - } - - // Getter - public int getID() { - return iD; - } - - // Setter - public void setID(int iD) { - this.iD = iD; - } -} - - -// **************************** CLASS Card ************************************* -abstract class Card { - static final int[] value = {0,0,0,0,10,2,3,4,11}; // valeurs des cartes - static final int[] valueAtout = {0,0,0,14,10,20,3,4,11}; - static final String[] color = {"pique", "coeur", "carreau", "trèfle"}; - static final String[] name = {"six", "sept", "huit", "neuf", "dix", "bourg", "dame", "roi", "as"}; - static final String[] anounce = {"stöck", "3 cartes", "cinquante", "cent", "cent", "cent cinquante", "deux cents"}; - - public static int getColor(int card) { - return card / 9; - } - - public static int getHeight(int card) { - return card % 9; - } -} - - -// **************************** CLASS Plie ************************************* -class Plie { - public int highest; // plus haute carte (si coupé, plus haut atout) - public int color; // couleur demandée - public boolean coupe; // coupé ou pas -} - - -// **************************** CLASS Anounce ********************************** -class Anounce { - int type; // 0: stöck, 1: 3 cartes, 2: cinquante, 3: cent, 4: carré - int height; // hauteur de l'annonce -} diff --git a/client/JassCanvas.java b/client/JassCanvas.java deleted file mode 100644 index 98843be..0000000 --- a/client/JassCanvas.java +++ /dev/null @@ -1,24 +0,0 @@ -import java.awt.*; - -public class JassCanvas extends Canvas{ - int mode; // 0 : rien, 1 : jouer - String name; - boolean atout; - - public void setMode(int mode) { - this.mode = mode; - } - - public void setAtout(boolean atout) { - this.atout = atout; - } - - public void setName(String name) { - this.name = name; - } - - public int getMode() { - return mode; - } - -} diff --git a/client/Makefile b/client/Makefile deleted file mode 100644 index e6d116d..0000000 --- a/client/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -SOURCES = AbsoluteConstraints.java CanvasPlayer.java DialogConnect.java \ - AbsoluteLayout.java CanvasTop.java DialogInfo.java \ - CanvasBorder.java ClientFrame.java \ - DialogPartnerChoice.java DialogNewPart.java ClientListener.java \ - DialogTeamChoice.java CanvasCenter.java ClientNetwork.java \ - FlatJassClientSystem.java CanvasLastPlie.java DialogAtout.java \ - JassCanvas.java - -JAVA = java -JAVAC = javac - -all: $(SOURCES:.java=.class) - -%.class: %.java - $(JAVAC) $< - -run: $(SOURCES:.java=.class) - $(JAVA) FlatJassClientSystem - -clean: - $(RM) *.class *~ diff --git a/client/pics/0.gif b/client/pics/0.gif deleted file mode 100644 index 2a7dcaff0c57594622ba4dfea0c491145ee23054..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1340 zcmZ?wbhEHbbZ1Cl_|5<2{etfP@Em!Qx&B})sdX)RMbZ&g%v)r^>>ukkm&EUB~n}c|}g3`R!8ZOkc{dHCQ z+Qzg_wVqwybZ)Iq6rYsxLTriiM)CLO=H9-r?(p8auUUJzx37%Y)ADiE-`Crdt{$yr z7v?)@vNmv<{K>qdH9tSPEr@yiN#?8;r`5^-cg?l@@@{MVKehdFVLQI9 z-Pg%(H~;(Y!yIlP;L5-R9J)#m1{NJgvJ*fYt z$dOpxWT=stHaD~UO5Uf1?Fl|B41Yv*{GFOUwR+=1j+uq8WI5#g-t3iF*b{a!VsY=f zl^x1~*1xVPm)lZ9+a+WtmTslRw-)!V(6%b%xum%9hxf22a diff --git a/client/pics/1.gif b/client/pics/1.gif deleted file mode 100644 index 1c534ab1e9e865d29a5679bc4e87cd30e50a059b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1361 zcmZ?wbhEHbbZ1Cl_|5s;P ztCH4g^<;@9J8$JaeJJ(C#fOpG%k^)qeI2mYV|V3trhmJ)ynN8pz}&1`Wmb6Ou;tuI zyjc?-pFY*G^H$5r@DEQ;J2}Un?Frg)?P%fGd3|dvj8o5f8_xdYBVqD(No;T5ojX$7 zV~*KY+nZ|7+4}yfZmO`WUD!_lXQ`L%^zFi}{9e@D_mkgMUR`j)eB(d+cXe~EUhliV zJp9?)8jJ6J%g(yn>HYhjb1CYn{M=ai+@gPrH;N|YUfeo;Zeu?u$L;uQXT$F{Jzo1% zyt!oV^r&=^xQ_X4oN_B<z diff --git a/client/pics/10.gif b/client/pics/10.gif deleted file mode 100644 index 6df8add0db7b5c64d13bcf704016a68bc6bb1368..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmZ?wbhEHbbZ1Cp_|5L zCpIiR(8R3dHOFJ)qNCjs##u59g^Q2(E7%M1cy3BLIZ=>xMU3WwCEn9D*f+%d+>~}= zhL-BHEhmGM&vaT>b8Y<*oPK(thq~C*N|TkBmpW>0GLZ^>d}WG>qf;(diQ47WVSCR# zc_rKsvMJ(dAMfgGtB)@Vz1$Z&yKr6X#I)T}Tdb$+-d`jAaM9mi=I?GEYU_MQY1B<3+#gbZU&>hinLWnYJ(5%H-9R1@5cgy|F5LF+I<7 z(UU(Go8BDEw>^LFws6IVb=!2A>&^ym_;^;g{dUW%P3vFGo4ndtZq{9uC(n0@|NH#s zTyLH0#m%4QWeI@Kn%DJ9>& z8k(G-x9@~>iPE+R#afe77Rg;}1aB_xSA6?pc1h~q<0%uQ(vB?b+u^F2+9xs3U18Ea z(eIb0a35+(onhMOnO2c5m60}U#zx5nv-1zloImGfujlg#<#n0q^TI#PT#{1#iSyR1 zA|{Uvuee(?ZZ8yGCh<75C2q!&wmoYrRr<9rr7oM~W;Fd_U;-EC(vJD8rmEdDi*7xi zv}#$DN?+j`rk@$BMasOib2hzNI%Te^702RrLK7<%uU{nPI&nj8?{n{0D*Wq761SM diff --git a/client/pics/11.gif b/client/pics/11.gif deleted file mode 100644 index ca32442e0a12ebbc44e82786c12d2a161be95634..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1440 zcmZ?wbhEHbbZ1Cl_|5L zCpIWDG%~Y$%<D+2Z{`R3eH_3o&`%zPFDB!6FD$R_0)8OP`(UL;bmth z8pV2DsocE$*c_%Xwy&OFlmZsIP7a!4xp~>8A|< z-qT}lv@a?mC1p+L`KWU}l^dR|4-;p;w?UvYX@%-EF@-_Y&*CpGxW?6hGTH2p(mu|Or*O3?3CMTWuE3tleY|H+CpLzN( z?#{UK2Kjv?duQo~fJGIe7vZ%w?OeUM{-*d_M%|Dkt z$YodBu^@$kO+>>6dt7^UJ?&58GsfZbY`b?VPSytK79?ey94qmPM@xWyOWN?|eNH z->Y%ALpk5%-H%Cq`%nHz?w_#J<7T?rTQQkVRlSud9h!obaygntJEu(fe$;(o^VU|6 zrR~CHm5S3pO`JKgk2x`V%cm1yNtAH1@k(W#utB^p6;44Y3b7`X@5_q&90j( z@nUk}s+G5Lx|Loo_II$;TDHXOo~LH$*T|}s#lo&$D<{oMx;43Ly{VX1hS|mIw^nwT z{a6;=zK|TlkD`Hi7QG5EPTL zCpIiR(8R3dHOFJ)qNCjs##u59g^Q2(E7%M1cy3BLIZ=ppMM&nRrQTDR!%mfW8mstB z6D+)TXXeLc4O8qZxwgI#esN*3aQ8Ewt}iS6XZke0n)7pF)|shhj6N+=UtjsGlRMZX zy7KBGzxDCQlb-yVtQ)yG{pP8w8rRpl&&b$)Ews8&|H}5D-OuiNU)R63JJnpuZco|9 z2i+Cizj?DIQl9j5Yx=Xj*=l_B?5yWedb7X13EHjjdXme^T}rPetY_R_mb>-I{mDyQ z57+YC{_^5L&+ku9dMmQuE^|_5cb{id==IE3-?{%?jaAXhYwbzzPyeuZ_U^Rlzr5wK zR)6x=)Eo`Z|6^|U^V74=bP+yLRIU->%=Cy49>vpM4ysCYE6Yne5d6}|lS1%PMEqx)Gr#JIPT-A|Y ziz&&b>)al-_hoKR&i{RNiu?q@cP3Aw*o|gf_O`$DJ#wP{tBgtAOuK$8Xgi*05z+VQ zqr~m$Ubkk06T^SBl-H{radxng&qiPp+$x6)VX zhj~w3Sv4i-s>uCm76+rAbxqjDwt7yV-LmY-E1gcwTlCbm60!+=xw+e zCpBwdt5=Hlir~Fn`m4MpQ{Nn5VSl54XoXgd!9f?JZ~6xw81ZaeAin)ez%hN+2OFIg O+I5Uh$g#69SOWk}wM&rz diff --git a/client/pics/13.gif b/client/pics/13.gif deleted file mode 100644 index 841553094fcd399bb13def4841f3b22b3d4fb1c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1496 zcmZ?wbhEHbbZ1Cl_|5L zCpIWDG%~Y$%<D+2Z{`R3eH_3o&`%zPFDBk6Hsta@}8!Vd@O|X^HcAc z7M^u6H-*&B&v7un^ycQL^b3o{+NHjBZpl18*Rt#ZVYHGGAJ>BS3-c++^rs0hVbJVNZ*8Eg^(%H6?PtMZhRpkB^irn|IH<>1$-v090 zzQ2;+jvg-ceQ$Sn_P3Zb(=~FP&XV zZbkX0)wAch|G)n7SI=hg&wc%tpMD&wd4BpT?;X30e}CtG3$OlH8WvZ3C-qx=WnZA% zt)|5XpDQE@ENhc*me~6}x=tV#O=<%OIJK9(y=PPUaQk| zeMYzP!5NEtRPw|_YS&!!xH`dKFjK`rN(5*N&51JY8s3M{;}CvX={IDn&^=pPjhLGrd_N?#R-(B02LUw`#NNYV+#T zG*nBJl`ItJ3J*cnABDEdgrr8QmhgE?yN0(}r`Lq#NxJ4OiHy4~voZ1M>~(QBon{o~h>B0unJn`^ z#qfk#S&qR;L%}y6j%oz!Y&x#9eb1XyM)Ow{O*GY?SzIpow&$`llcma+3ohKc0S*id F)&MSFSvCLw diff --git a/client/pics/14.gif b/client/pics/14.gif deleted file mode 100644 index 2d40d52724bc71b2000699c1fccd45dfe68f6de9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2030 zcmeH``!^E`0LK?yR@4=d8!a=Byl-tQ(gW>*$W?bFkIb0aQfGI%tWJ(fI9|$;qGvJ#G0N^rU6%gPht7yLnG;xg3$SPGA^sN1PzH)v?wmFmt z1CXoKm#p?18~Q0?JM&HSuUJMaga_Hip^79ws7((CN}3u?f%3b`yr7G)H_$7=j04%A zNG?o!xGKL6qwrsw%cqaZqHY-@#jG@h&hba( z?;MWIq$vPv*@@zTguWZ$pB{EN9jUw9o-15V4EK_~Xyah=dMumV)YpCNDYbi=rzU!M z`o}F917n14wr)4wrO=JoU*i(9Mb=D+kyRZu0J*l#gtT0>&`MJtP7gmNt}?Ojn_G(4 z%_*2~>w|Bf>L0qetS)ccql%pQQ(3oI{_u44gZk&wd7Le$KfaX64 z+0@{dr8jL!Y$-|$68rvi4DWx1KQ$$!dkcu=5!hd};=;BEPo?e>cx|N*nQEs%fHKS* zJIG58>G6>tqUzcglU?dcW<4e`o6XWV(sWFBd_c)iCzv$4Spz9M-*hmxp3BW9G3QS* z?qdHb;xgmr%Sm;G*!4<_RKie7?A5W(>8P4w>7kL~xI?R&62G}Fn+ zlE^{jn={B=0o#rnZMCRY(@=?y*H$~Rg__s`p0w(tS#4Mq0`z!MvV<>TEw^S!oSIZ? zpBp}xk1xYd)=L?+aJhKSk{ABMuGzVX;kMo-)GahaQrhj4*LJi{3W)CP3BRNtuui>@abH&nFE!Xg> zP-N2NDrWAp?M2p;-iW-s)xYufeZJr4`}^m4K8^?nb12Rca2c=$_?Z~MHsE`=w}0;Y z{mL%_{}TfGKMeTc0zi5TutN%9Q&ZZOD1AiNtE)y8aBGJQya-;);HD}V6URNLRRMph zoTUO0zn8zx(sDWne31#}1O&w^EDQ88MbuiJq*;h2C(mQ(*>R!Ou@jR84sqSV%MrJ7bbdxswy*QeP25oa=&2+P{t~W_5-(>o0MFtnCP` zQLAGarQ2H|O?VtTpJDmn@iw;JbhG;Ru^T};@P_{xuvZH-dABSyf>Vxzo=feE|mAbNtotaJ``s_2XgE$XC`%~dU1-ze>6q<%{@}rF6QdB$r zQ*Cw*qg~L-NmvP|x5<|RwUwcu^bph&rfpgo%QXW+*Lq)w&M_y@$SeGrE8LdGsl;Pd zDfpt`FN2Ey{GAFq#U=PgtdfhO*Pn4IH8DS=vNt9-$d-5D@)!WhGdnhi6AL6H>mn7NElDCaA<#bSj(y$QtUQ@ z5mk`vbXOr5sjCP`v(GwRGkR)65Rh{4QgYGD8O>CK3T1iuIVOI#VEi)BMbp2-dZUZU z?daVN>4s!*JxlghuRo3*f$m%AX_7?aSSl{fq-TTpDEX_O`AUmz9e0?a?;<7$|UV5_I;%<&qUtz$7nkSmAu zaE`xkvBs$z)inzKNd5Nr$Y4Bs{CT4F$?3HE#_a3f+NOevv|yr@7pKTgTsjw=SOOh0 zr^0GQKEq}oBkpI#o85NfVfejYo{lc#j-4vt+>Ov|#J0BQMUyKMXVWd0URZarKeq8= zVi`z}o0DIN7Yi1}!)og>xwGdePclv$GFuwFS0}Y;2E;|}iskK~ngw4z@}^z5B4&l) zq2At->h{RZC+GIA7n@MAQ**%uPIh2P=l&k!gGti^UkBArqFGm1dsWi*1wq;cl`j9l zhGRY5%U4LQzagNwl>^ddT?=@}`5gmE=BL?O1B=#M>OfgBGeA`-9AR#SEyD*~gs4v( zvI35y+7hm3OK~ZupqrVuBc=82is9F2OD-TVhufwg?YXDpkV2n&=UGH~nu($7c==aW zDaz22K~h)hQP8LxAmSb7+@e&BJIrQYX(H^0$;u7SmM1>ZaKYKTUxMJaV(oZJk_JvO zr+VnxBWlgvNlL&=`-o^{oX70+fmedUO;1B3BJ9C@xPVvFSys^l#Vz diff --git a/client/pics/16.gif b/client/pics/16.gif deleted file mode 100644 index aec86853eafb213d73e8ab9434a886e853d65b48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2037 zcmeH``!^E`0LN!}izAQaF{!bcnMtACRYJp-I-Z$Lt1(Spmn6^2Jfcy;jv4b9i+Pkh z+Ax(Uwx;P4WlB14|3-b!_j|s-f6n>1W8DrqlHCDk0h@pyNdbHXeCyY* zKlbf@K>QEyqhSFy7eat&Ca`dCUx*U0O)}Af3bEU!%Pf=bm%v~ zMqj!o`-s29&%!Sk-vJxLk0abG6am@>R?OvzR_aqAzY7!+&>ZEjRd|qduCG;bE$<$? zGYZoinE}BF1$DIU_@WD*LwIW$-_Zgnw@QSO%X4h4l+SiSL&Oxzlc2+d_W9v5N0i-m zf{kBG-5G9`Nt@lnR{oxcT6u|KbF#*$bR~7+rvPs6PzhOyJ!ZL z^fJ}q;^G^3xleC?ryyyTXR~vmU@L{#`z|;4h$(t=YhyyDBGO9#w03L4+8WAGHVxJoUOUXv;r0tZA~_@8maiN5`-cAN zh>k|wA~Ym7ab2Z8jXtfxTE{`vBe4rXF0Yt%kkFuv6!;w^*}jDwp0ur?UJE-wn_Dy%(9nab2G~NlWhzhd=Q&Fsm=caIsH$=`*z%`3m;H9 zfA;~O<%2~#I@I<13RX&xLT5+|6aYSP0co_^OzQyb?2_78#ctOyOw-^zroD`x_62(%lQH4`@k-R*l;GBpsq;9}{MVkWe0$U0U{hq&GjKeQ%A wrI!`es4`d%jL zCpIWDG%~Y$%<D+2Z{`R3eH_3o&`%zPFDAh5^319^mLzm_`W%oL8`8d z7MZW^WE3m;O|x!}dSj`O?zhk*G0cR^B=f>N_inRNFX30`=ZBQv+H=Y@`|6CK>3(0k zf}V!Xk2tx_WOi6q$X2z^L$zFG>jHL`?O&97(=7k&jP&!r{%TKL{otVDO)0tVh{Z{V zdxe{0Wg%`M%&^UuItLjq>-4eg19T`RVK2|NDu* z{J#Ch(f;Xm;eV?>Je?aCK2Nl+dVTHt$LD9&met?*zf(*hn|W2(gG}~m(-vfKH(g6e z=T8$$Ocg$NG@>=@X}eL zCpIWDG%~Y$%<D+2Z{`R3eH_3o&`%zPFD905;+jWaB8|is8`NOp=G|4 zjiT?}u{2ulJFu!zprvGKh^^}(#Jv1X; zE^qCW<;i#U=34Q|)mRiRJ=QPoyG};q^E2!D&f@>ds{&qL3rsqHZtCx^ukNk<*Iv$7 zx98iZXS<)XaoIdB>)zvgth_r;Idk@R|Mji9^Y>?;oX@BsmBI9jJt&=}&17a%%fyiT zx!ii!6mr@9cE~sVkIh)n@-r~vVFPQ~jD>BV6}K&{=1)>l%HDSIczCt^yzfFiT(71j zRVhExSlnm!>Bi!I&AS{+T;KCr22OM3+w7-eZ&+uznkrt7p)~eE`awzgNjp=Ll7+k$ z&lfZDSl(Sd?dOzvTRo>PpWXG#JYlvv)2eHs6>U3j`erV=COSj^RNBoIzE!(|0(KN~ zg-+AF5WDKt)Sm}eJzc$d*7m7u&z7E8T~zi@YTkx>l}DBY2v{z8v+0bMc7Ou|gEav9 CsR!Wz diff --git a/client/pics/19.gif b/client/pics/19.gif deleted file mode 100644 index febc4126e12c1bcb78490353f4b01eaaac42e148..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1366 zcmZ?wbhEHbbZ1Cl_|5L zCpIiR(8R3dHOFJ)qNCjs##u59g^Q2(E7%M1cy3BLIZ=>xMU3WwCEn9D*f+%d+>~}= zhL-BHEhmGM&vaT>b8Y<*oPK(thq~C*N|TkBmpU4nJ+=I**3cEGduhq6&?lh_LN{*O zV)^+=_+rWQGS-!5YtC=hyn8G+d(+#f9@qCqOQSEXyVLEx^w;0&LW9Jv#;sC(nGyO) zJB(O=tM0s^ePjRR-?z4Am8_1~H^sc!g>zQY%L}QN?`5NBd_8?-mH+IrGtLg#@zCD{RyqdNCU;X)Y ze|P$RczD%LHGbaDfG?$Y_ottqYT@%Mq|v*jzx4axAKx-t=A^E;skWd-`lGpcll;u< z(#e+{Unm6rIBT{rrX4<&xxzeNdDB4IwN%M&8PVh-!u}s*7~!nTZ=im}afnu*`}3 ZvcsiGDgg}4mP_7lyJOXo>%hQZ4FJm;9OD20 diff --git a/client/pics/2.gif b/client/pics/2.gif deleted file mode 100644 index b7930a073db34d22a66067603b50550c51f78881..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1388 zcmZ?wbhEHbbZ1Cl_|5>kg9u&PI6m`r*OlW+0xm| zwsdY@?mAa6_Lt1BqD5YdJhJ7Eo(N96JlVG~?#+#$7iU&P%#>Q=_4U=c_4bKQv0Onf zuXS;IF8jJ`b9QuVs^q4xFFvfi)>ioV(b<;<`geDgU1SoIwD@p(mlUHkpZCSL5BEum z3Y%4Zd+RYf>AlktA2XwC=h7>~zV!rcNnY#t&~K|%S!(9hCV$~+b8nP5EpaX_IyU#V zUhchhwVT=GiY-3gJKib1yu0}0{RijL+0AeMe3gGC%Q#xzwzB%`t?keM{fo7h`}X-} z(a(3b@$+n6etC1y`P!a+X1{(V3LdZTxBpi2`)%U48$~N~k4l!`m?|F9cyaI62g!9? z!(6_a1sRxGU7o5eA$YBui;1t0cP z5g`rlo_>#SwVCxixn7D*Qm*y`W5$#2-oG4ACKCMin|SIPA8Z4+RC*pUv}NIZCb*2Rm&wSIrCONUAcNyWajGN zghjJft(xt$ODnRV?ANOTcQ!8V`eUA<88hChYJ^T}mzq5_JBMj^&it@6uD6qq`<;H& qcO$Agck8WNr*gMX^W@Gef81pfu(O19!Mdb3+g`78U}Rxoum%7uP9YWm diff --git a/client/pics/20.gif b/client/pics/20.gif deleted file mode 100644 index e991341679ea4a9c33d4871e8efec85aff5948f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1400 zcmZ?wbhEHbbZ1Cl_|5L zCpIiR(8R3dHOFJ)qNCjs##u59g^Q2(E7%M1cy3BLIZ=ppMM&nRrQTDR!%mfW8mstB z6D+)TXXeLc4O8qZxwgI#esN*3aN@B!om*D=&vevxyK8w>jbX9nYQLD1lV6`)9l0^A zC2F$H$wi6Bovv~f>E51~Fn!kDSKrni+f~+iZ0hRC>!K%Dtlf5W_I0I)hdjTh+1&ZA zmprlSt(=;r?OLHRY-@Ur^`0}k~ z?MnMrh?HGk=#?yY_ixb0f;%?NuhU*#v3R@FeQL01Z%9?-tBZ3lui=TQ_C9vR@}K^F zoh!e}-rSu%N&o#{n=fDX-#^^nU$gw@Zn^UI_lbWP> zUxa6^ckFoJ_FA>$o*Q3TnPO{8;}pdT?xO1n)% zmAi8CG>d&@iz0*XWv)mOu;Y9-z2sO&@WkYxUyoyETw0O2bS`h!vn9^QW?q`VxGG9( zWze*h&lblo`*bXAS@$fi*yU^1y}Fe->Da8~rBL zCpIWDG%~Y$%<D+2Z{`R3eH_3o&`%zPFDB!6FD$R_0)8OP`(UL;bmth z8pV2DsocE$*c_%Xwy&OFlmZsIP7bo!RlMTT5{FEyy%t~7LMO?$UORH*Qu_5Nk-DGO zXk2|2HYZWDPF4HssuSDt>X+$ii{;(vEqgCDclR{im?@PP_nlomT`O^oBIoy`t9N{G znQ3jTWoBuq^5kgNTc&G0J2cbI=bHb2YqLu)_F}qjx^8ZjPS$a;&`oAt1u?_sbJbGRgB*?)doVSfjH4x)RG1_hlC9`&vg>UB4qUH{0g#q~C8RZa(<0T`Z>7 zCiUEX;l<+ead|(#-R57O9`m#E@y{>UZ}a!q?=S!QA#usvM8b)FUOX-WWu|d9Z!55^lsj+7TP6|Iw{Di@^0^C$DPR&BfLVA zlJ_@uKA5Vmwj(viWm}or^x&`=&swr4Nv2Qp3d&gKsoRt$JLh0$hI)eq)3)gTGpf_m z7d}z^b!A~!P^jSI-d}FZJDPuezcHKT+Oq2z?dNW)%yxcyQ?)U-s7o_%-l51>k-Fz1 zRNHbUyKR58+9T}i)7mZDI+sn`bt?1TgwiO_SA8qDMlDL zCpIiR(8R3dHOFJ)qNCjs##u59g^Q2(E7%M1cy3BLIgy!#qhm$T(v#Em19#=z6k6sx z!68leO)pA-O&X$kH48(M(CW_S8c5HEn4vD zv7?=Poy07^=^i_(Vi-KHHjgkj7i%a7~AYAuVhj@7^2 zx9)7jx12jW?_bJ0TfO7@jH%4`b!<&5y*8|wX{EPn=g*)M2ewyvh!%|zj#-^ zS$f6iwi(tv&z9L+@%^Rly6X~mi_?F6znNDaYp?(E-LI3Z3h58GF27i_pVvb^X_2kj zP4^oMkKL=}3tDlnj`f-O!#tt9A95{RaT&MT+Y4_fc1Ur}NQ!;>cZ*VYi{#X~H4g!0Io+@XtyuO-A zN`0QP)6evpTrJM!b3IR;Na%0gwezX3+b_u#zB99`G~)fdO4YlBOReJCj(l7`W#zk- zw`S)~y13Em*6J;8V%bYKAA0qwe$z&)SBrz(o@meST6bgS%BzuCIV-9&uzFqY@?6>=M?fSy**SS}{z9mKmoUFQC6t+k^ulvH;Z&!9328G|=W4qE@ zrRqo2?)<3Gms8&F_|J7cKbW<-Y{Nd*cI$#ePg&pSAGsFz=B|U-Zjlgr99%UtFFgW zvH@m5(Qf8DJ})@OAlMjO(J}+d?ed96wnL`^o6%kPv0;hraCN+Z9@n)9i|C4tY3;vF ziy7|J?STheADl<$MJ>Tkrs`Vv3-%eFpj)J31{MW% z)6c>VK@-cK_&eVW<0?i!%9vX@E}$x*9Wq88mu3g=Ck@W^ze*NOnB>mb8KWn0Wp#m`yDg7M{+3693YwS+ z!X;T~WaxOnN8>Zk!4ev#I+cVWj-`Up;nN$eUq>8FI*bYIw1LnGO5Mr^ImXm7XvSw5 zHX7Ho5E6=OA9kL^k1R#_+3i`FLb z4)$^hLB)<#cuH5Q0%jd(RURvS>T)rX3}0%u0Cg;<^s3@4Ph3s*Y4y2ZX|j0pH8L4? z*f7t3nH%AE#*19Rh$2B_Jdly8n9GVvpQjm&c5+=oWd^#Q8zt2p$$kS4LoKcpH8XCFBRJoKQkY{tYX@%F2C~|hK z<(*4#nadV>>7QzKEQkSLyEeI8@>BTL4MLpftKBWFC5Lt+_pA%jpVgKmp#8PRh=`_ znB}6U!&{Jg!nzf+9Ijmqb1cA! zdp9kct8bNT<>)kL||gB zq71itc85u6_(`^2Eo{rZv&AL59N%gL4sQ;Af4S5w;cXelMjJigREjwA)-I6f9q~TM Zx_K4!g~9wW?usQse@hQVZA}1h{U2+gNf7`5 diff --git a/client/pics/24.gif b/client/pics/24.gif deleted file mode 100644 index f2e927f1b82d77d84ff579bbc2daa270710f337c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2058 zcmeH``#aMM0LH%^cb-F}%t}l#)>Mczxh=-X!Qn8Ixtwf_iJ~@_P$zRQ6SGap9=9zq zVOYr}D}+LBb2%JFG8H|-(Zi|!jnn(Q@ALltd7h6W8f9@N$PowxHh>?A2X=vP-QE4M zZ}&4l3H(n8z`q;t-335?2au8mYn`&2h~puEMc#)gm6&iananYPrN%eyQ^B)PfR15~8CV+8pg%$|DMOaEzp1FPB>c zeoAh=b1aoaY$jpfXswDKjm7bjN10~agnt=)-yxz4_-#vk@TF1LKULOU1l21;8Fhc- zRwl0CXCsSuq;{uQEkv{JS4;_$Q>Q6eu;Myt9C>xBVF21#uicy&w;4e38N)4xpnN^I z&3uv*jIybIL^Wfl019dN)>xFxQlme4>I{fQ-ueSC3j3Joy)Q`>Ne@81Ll@xe3in$u zE{`tpMKSLAAZCOR;>130({HAm;U=z}BdAo9i=#D77zcAZtz-|ogAdyW`E!lSu{5j4 zU1ceaA^H9x(m9;K;N-+~T;PUycs>gRyS5M<+iqSKrNP|NXR=BN^fXy#4G<2CH>g0$ zs-YKfQHo%2l6Q_F{;Jp`^`GJ#-7ue$9+~&$CDOX+S*GO;svaQ~jNo8G%F{W_9U?Zg z_&$|e&nXHOF~f+9q`3D;Q!JEIwnz{x`*50 z___HOEUY}#Jb;%2Qh4ZiGydE zIvG46NZmrx0{j1r}o$5^1ka>Z&`urstmqA2oYbdLxPIqKYwB5DvS|^-B z_4;DqrS{L#^o&NL*~;lnPjb)$#w%Q1n!UN*;wgFRyXPL)$Yhpek8!3P?02OTT@Mbf z@qDO*Zu@+sKyuTO4zXYR2p5I=aQ)*sC}g($?a-I42Fl28n%{MkG2tO=RcncoayJv? zXvWS>f!F(R6TF(i*2Zwud~VB5zQ#EiJP>IomT_IAUoz0^UOLv1y7=3gyl{NKM!TdRcmUd?ds-oI; PVzm3WNAgE?0KoD;l3HC} diff --git a/client/pics/25.gif b/client/pics/25.gif deleted file mode 100644 index 79823c9fdb8d259704698bd1190bab163041e1b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2014 zcmeH``#aMM0LH(ZWL=0&VltPpx#rT26G`E?Y@_Hlqa&i2%fvZtjnIQ3*D1_c8JZck zjE8W#9F=L)g$SEcip4TLk2o$zcB=DdoZjbspZE9A^L%{J-us-xeSk1v8TceSiubOg0~-~jpGaK$1NoyW&^nc0zm{c|0em4KW<_(#oe8*qUprbuvyOh#pjJ@6?UJHN_*lnL!=K_>6Ny zMe*YfoDi08iBQ93S4@97`Qtnl<79eSCDU3wWm)kqJa3!!_*49-LnOb%;fUG!3P6^f zvs$^dyOJ2&07e_Z!k~W>ib9D&SJ zsh;k;alYSh-*^f@!@*KjNZxNrk5o7>$cgPf;{1Dxz31Wn~-?<5RU(h})!e`luV>KDanNNOkn z-m=k>xIjw=?UdW<*^_@zC?RRS@5(1mfHb0wNGo?c&917O*V8JOC(A;kFx^YL^Ea`x zaPAq0qnLQZ`IES6Wum%%64hgEtS;Cece&zp5;Y`-%ypiPNS3olJ>aqmXcE~gxS??7 zHGvhfVN1p6;xiE05MHs%s{AOmv0cK7>&}yNgbvbaJW17{wz*EOYr)@K7Cap~s2O8h z8xIQ}1qPh(=D1-ZLD6|_L@Muijj?9nOG3+n&a~OaMzvbXKzkY#$r$4G4`7TnPnc$@ z8>!gHv*t*NLPZ47lmfl2rsc=Ix2XDY?j``AI?vd&Q5&7!~y!J0%)R&_bZ6c^}7 z%VbY1G4yWkjuD(m;U$NzJA8lg5u?ORm0Rnp4?Z`t3wzS5>vC#B?4UH8@+j@%_x(0d+f73^Etg;fx&-TDL4VJke%Mw%Vx49M%SueX40PhM+e zOoKT_gPXE*U97a!K4tStdz(xHEL zCpIWDG%~Y$%<D+2Z{`R3eH_3o&`%zPFD906KUAA)Votlb5o6Haf(kT zQ_;0MCqF)Gm}6YYwY4I6`MHH2iFI=_OfrHN8utGy`L!i2WP!tKr`WEotFO(~TKZ_o zFVR%bH9niCl~#tm4cVCO`>SX7)OVM6ro25BJIieSy$P-t|M{NWu6^gAEw{8=PVmO? zV+}v|+45K}_TN5LIQq-%pO5aGQ&DZsv$?hA#U%y%#n)C;nI5~gu5(`B-zu}*o7+vF zr{z{}54u0C`ty^V&i_3t zU;cRVaAkP@yf~YZZ=v@O_v=^K{eAo3^rQ3qcZz@CxWV{iem0|!#e*zXr3!^CPNN+O znY>Ov9%KjxStw?R^0|ezRVf}>ciQY4rFU(Q+6!AxBy|_;y`hx%_hN*y zt5MnWX$h9Iwl9vd{k0<~z>#n1T^CoW$VHu7JXOOcdHID3PH{3decGXt*P+@I#WY=f es@t#f$5WFZNd(U1=X`QuR?fDa%N!UP8LR=8;m^<2{etfP@Em!Qx&B})sdX)RMbbfs1z09gx>#T-w>eX36n}ck-zG``{HSCV^nK3E* z%F?vs%XmG-v;&t%KP+oK6{d4ycEw9G-I>?lx$P~K{8#cLbZvZl5wGyR?CYxok4OnJ zheco9n6f`#oPV8+$>-ze=Q7vV*~~1uaoo-Fb4={7+%p?yY(EsX!@Ml^c73G&o)Xmab{4<4ol*a1(?8d3*D4v>%4RpRh-JjrrIxndD%$f= zbz$vO$7#{dt+E{pGn!Oy%uf@H+A*_D_8QxwhQNoeQRPOBY>6#=QspuA>(9PO?D?wO z5!Ux(;a8=u50TnEO zj%Qsqa@QWth+Zb4lpFIa>~a6PlN#4sa>Gtc>OQ~KL#^3*(hlkNdbZ;;^VRQdU%0?U zu8*KMLv@;^_?(Hy)MREH(&V}klDn+qLA$`BT`yM| z_3VAIs?RQ}{q~xuKCjmmX$w2$Rz;nAkv(G}Z};5BW80>`sgh$oE$uShRr~E0epUei H1_o;Y_O%h+ diff --git a/client/pics/28.gif b/client/pics/28.gif deleted file mode 100644 index c0bf665c3321dbc1afc17d666aa5411e480bf3f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1370 zcmZ?wbhEHbbZ1Cl_|57ljOKR-U3Uj8xf>6w`YrxtWF&u7aO+j6LT;mu{=w6_#JyE!lUvfE#) zmuF{{M*jX6Talk~N5@pzeYN$nk2~hp+8v*@?fI9cj%*B&DGN!lHNovNr?b{I^ z<`}z9HFJgFsZ$x{8rKIZeL#w=UHg>6Yui4bS>*kY)b9$V*s}8ww ZKXX34O7y_Cj?&j}w==N{2rw{M0{}Jd8eaea diff --git a/client/pics/29.gif b/client/pics/29.gif deleted file mode 100644 index 15fb4937cbbbec7e1dd7bb6b82cbc76825dc6104..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1397 zcmZ?wbhEHbbZ1Cl_|5>kg9u&PI6m`r*OlW+0xm| zwsdY@?mAa6_Lt1BqD5YdJhJ8X-q^VG!c3cPw%(}1m!8dPjb3ZHKCcR$7`QdaXU10T zD|6#7CVk!YZB5jwnAw|Jxy4rbb`+UDlARf@AKSzC|5z%=rM-_|a#zptk9-7|~%$So0zuxkFl6&>9y^jw~+%CLK$Fjcc|MQP!H|zJYu841Z$GAf-qQcQ^emdVH z_t{x1IbTGz?zws`u2O1~d3>exEH>Ff#XgONou0KFi=yu}riFK|@O?3*>(RsOx3hWT zGUj(MeG+}xcxdO1yN$mEBbGG&RkWBf>BH9(OH#S`JfD<%YLz7QeYzQOzs+D3`;*WY zttS`tI{9&?b;n70J`RyPcjM`-FGsgLPSfUNO`aR{Xl8P6Ro2P$`Iqm8KAd~u?zd-i zy!CEgo!hZ%`h)VATWyP5MNS>PzSM@va#pE8-P4!tGedU@OzCJm6|sP!?o`5*9f~)? zt7DiKS!vbZ&Rv-_twYXh#q_YL9#6Xu2fkb#dEnf#s~a+#Byu9oY+Jdo==dSamzy_r zv1UyA9vdy&-!e;L?Y7&SH1&35rFp$ud~@FIMZ45_kK`pX9+`b}&zEc30St_c4AuY= CnlT*! diff --git a/client/pics/3.gif b/client/pics/3.gif deleted file mode 100644 index 97514646ea29d32ad74165635123e2982cf781c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1421 zcmZ?wbhEHbbZ1Cl_|54rB@5*mW(s=JbI<}o(x)iex|>#(A1e56?|7~bOv47b@hSwddG<~k+3_Oo?=inCG#(w$Ds1BInNf0Q^+M(Cac8G(yM3cJ-9K#A zWcBppdmirNiwk+3_sFtP*`IGu*8BCk{q4DVGpll6R&3PXW_Rmb!u8wv|C-BJ1^oE_ z>G}OfXU_?gR-bu!(R~|ty>-H;?}v9U-~XxV>7Czu!^6^;PKhPBFZh`<&1Zk*bIIyu zQH|tG;v3H}mfrL~wefg-myXo3MV+;kR~M8i zottqh(>ST)ale?9MQrcFhZ;+2SADEVYIVCNCf_tSG(EZL>)aoUrl^F8%XdV?aVkuT z`z5|?TD05Cn=?|_R?M6F>1BA@WR|}2h24(xGM`P1{uS|jPUb;Rm0n#hPU&8E*Y7gz zr;o0PT(noxU1M>wO8$(EDT5}a#LNYdiTYP zypqOsKQyNoTdqu-9lJ;(F+BgCr24w}WxLc?A7DHEY{TYXv!1P;t7ajWJN=c|+Ra64 zP1kO&%9^FPpn6kQPLT7oiiq^<-=^Q$_`pd@Y{%18(JywtUDchnXTmw}%%vF@qxH9Z b-=#fkzm^+IL6LxI+J}Q2_96}r3=Gx)IBhyr diff --git a/client/pics/30.gif b/client/pics/30.gif deleted file mode 100644 index 00ca35db2061060ef403ef4ac638ed826a1876f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1422 zcmZ?wbhEHbbZ1Cl_|5>kg9u&PI6m`r*OlW+0xm| zwsdY@?mAa6_Lt1BqUC{$T(Vakxv_ESg_$i>cE;U+deuI{p0}&3|#rZRXj0 ze7AZ3a(?@3DzOLO+&(YoH(&SfkH?~~zv=$mTzBQ!K{bi!X5Qr?jav_z-^gVPGgD}+ zFkNx2QE(r7Wcc!{>Cwrn898ovE!rzSzvk@9>){!XR$jmF``@wrUKNk(ccm6XCiBHj ze7j!UE1%Z;P8)2F57vZpnDn`<$DmV8=xT$i%f)3iCf`yxX+#fl`C&*gcvGAY*jQs(VG$9oa7 z3onVLr!Uer>U!SVC&rbzBs*(I#v(qwS<7ekr)Iq<3_GfpP$?^zDL=XQu6Sr&P@Yy| zMyY?U+2hoIiW=H8Yujc%o>Ek|bLqPDbw^*VtK#xdtyyJt^7^K>tY7LaTPJZmUM_t~ z?CG3ymzIjpPxX5(RrNaZYShf>T&q$xt`73n3@8uElG**vi!Fao!?#;{dqbCX=kIWT c`+CZLLtB;&)r=xFHxF{C>jW?`GBQ{L0F7ZZZU6uP diff --git a/client/pics/31.gif b/client/pics/31.gif deleted file mode 100644 index 8d7ee17ad1a263c3e42b6625796fd3c406330425..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1460 zcmZ?wbhEHbbZ1Cl_|5coI=aIW}6ge z9q9~Se!j~lblD%zkfaM!ox1veVY znaSzSY)7-cDcsm%x>HHl`{LWHD+;gX?e#QQKC-{|Z`s|K7Zx8r*tD9@PG(1L;^FR_ zMs6|FH=Z~Vs4i?*v(qSUdyV~N*11CYk2W^kUN`4;rHRkAN^k8g8C81GSMzh*OMNQM z-{09(8^0}j%B%aA%B7#biIy(Pc;YfsTl|iJ)zc?eYyHljiv0bxcc$(?>AQUsZE{cQ zI8RqyC$9UuFJ?Da`-U8N+`7O2YsJW$CnoVuua_QL|}o^s_pTq?CaR(7Hb)GENU>>`Ql2a@u`U9Mt!ZN z$<5Z^ET;9_J>pO)xWjo{TrEkf`?+<-d%GwfJq^cwyr76ry zf0!xUwsOWy<6}>sPTibod8aAl9jj{lSH;T3b1he8#!quMdYNAT{qE27xv%%KsWzm@ zMZ73{|JHMP^`2A?=_v2c<-(J5iYhhdMKhgBnm1dipZf(r$ypZ!LLrk5uKE8Xo0a#%*xyY0;lc4sypO4xE@g PHRsbwCRPCf1_o;YMh!)C diff --git a/client/pics/32.gif b/client/pics/32.gif deleted file mode 100644 index 2ea3293be1e3f122c4424400ee9f7687d216a9d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2015 zcmeH`{Xf$Q0LQ-^4UIzbe3#|azMEBqX*rTLn};;b=4oUaZ61o`@G7p%L$1U;KMymJ zr{vj$!|3M1oy|4XN(+VPCCSygtAFGAygskb=lA#P^~T{(pl#3KfI#3q@HGhl@cF-f z{yJa&TfPzapAbNPG2n{}fQlSYPy!s9D+aEqXoG&E&7A(L+hBwe=a$Oh3_VN6Vcy~uA&i|dq&q@`T^pp_&nhJ_QW(_2HZWkxYuMn%L>w@T-|ipoXn zFNqwDDgvoilS``ARn^)91R}&u0X-4RjC%}HDLK>UFSWd-nRdTK3v&c{aUlPK?7I1A zZc_l?<)Ns$E0zZ+1o1(t(DIr4fWEe&a?;PBzW{DRbK1Xl(DSA;kgTEJIp;EdY@aSg zecIBWTZO&f_Yd0Qbipz29UAH_v0+o!JhzkU3avvQ!{a1N=9rDZl%a6#H{rS;T%<;9 z8yv!_XPqTzk63fm zxC)#?9T&!ww+)La=8Yi^0dP^QS6W1iRbH@4*2`(X=MAK}i&z%6pK&>B*f`PN9q-_w z{nHpX9MmbK_-#k=Bsr&RYR<4@u-1VdL}I!qX*27DjKL5s%0eKZj;KJd2)?&SIV{d2 zqc$dVPf5g>?UeIDST~82Vvrnq%N^Y01#NsvD<@>g;%>*&_4m`sH>sVK)jnQ66SZ>3 zM=4bqn7%fnU-cckE9;pnT_Kq&oOC9o9v07IrTXZ;zG1jAayzUOglKX|Z)@+cNjI+N zHOKGnsdHoZv1-cF<;e@-4;cl_f;N;YqCM*!dT$*|w7A?MQDd4Ho6_qgQRh)`gT`f! z+H?@9Ph!@ZNf(4?#9dG_rJh#AqiSBtU8Jq^)+JgrC2%&f7fs!4Zl!T~xg~Yfp7UP) zZ)FQ5*|OaR9r*5}7QY!rgB6kJE$HfSb+)U4FH*egkoQiW?cvGa2ah5KJ*Mkt$9wHC zO{0nirIcaPQ(q)MxLp`2hI&4p2%Tcs8H=q6Khj!BP>w(f^7VVV9$au=fr6sLcHM+G_;Ie;*}#Qr{ouJ!br3CWg=xMT2YX^5-8U8OROx*t^+;HPwCw>5lWP}=E|1Ub+NCm(!X0*t zQEI);L(j|t`OSHt<42oqz%+WTHsl?vO1qK3QCNivCf^}SI(y%fWjay+Z^5q zSDmCSCf^mZhfEw#UA0%$&}o}90%1)_zYwKmglM#8_Nb-!lqj`~vTH5}l7_VvM$^$F z*3aoTHI4}d#JoP?%-Zqc<9f7-PTZ>U1Ei`Sp$$rG02|Jp1L|msiHiCB{MKo*gl@V5E?EM?>9%@*Z bfh>GZ>dw}EimLq%u46(v%BfI60l@qVX{SH< diff --git a/client/pics/33.gif b/client/pics/33.gif deleted file mode 100644 index f4a74839d30079615379eb94cf902e2f111961a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2050 zcmeH``!^E`0LSN*<&hgp-sQ1*goNEwDN6E=TMF9{LfZ;AYRslQ$}^KJ8aB)}#ymH3 z8l94i(cFtmm{*T^OekHPw7A#(H*TLFzTe+J=X~5eT#ug!a|46|HUa;E0|0h*c6Mj? zfAZ77|HpvkZUMV30D#*78F|2&ri#vVpoT?IZ&T&VGzBBiGWTX`cdnKl+7fW9swe-j zle&sWON5|czgkI<;IVNE5s2uM&sL}Rla512@q6v6eRCba6H*U*q%YMyZ`uV;tQL~@ zRe9!-E}|~T2C~umXMP9tG#X!AL>U>1QcpRBIvJ9)~96&ZhtQT6Fzc!Hcsf2zl>M#*j%ogKx zd0%PfXG4>% z9)w`cWj03<#d{N`(iMv!1^*iSH@Co;{c%80uo0%G!mGZ%$mq6cM0^m(Uj*r< z1TT(Eq2zsdb*<6z(zLB$>S#e;2!$4HI-$Zm^S>eU- zbqWDJ2tnXaLtxXHh?2QTe+}FDIhbqC330O1O29~%>lQ>rREM;^PYCBdiGsz)bw}NE z>Ejm^U^}6VVn=InU}c7_l4iMC(lh@YHHfKMOyMP^L$ugmb!Hd}`kt^tf;QHob~`hV zM-9o_Vj}Ob_+m6+jZUwD^aAy1DMKS6KcH7@fkNgrfOs~8hSurS;eRHiY$ z!`dN+u80NCyga6fkHO*@o^zd9KX4Mv!gQTq{Dy~pW4mUL10r2U6jhB{pXMyr>DfuG zZsI7o$c8iH^G#G+5q!17H1kShkI#VOWIr&q?hD39_X6iy)b^xZhnC%&$v+)FNZ&qm z(4{e7F?1wMffZVn^qZk?#~g-jATp+H@lf0%)c}@x$0!56zWoC16g(P#vQBtQbew>< zDXkZhty^UN7~dFsF=Q1-&KYJN|G&J?BH-N@t`)L~h7 zZQ<54Ifa_B?bo+?(VswVtgQ@Sf+T6lLp|$#`-83L+NE31$nzAAWRbl(NI*{AnE3(6 zSL9F!q^nMROVgR_&~^#+Mx2r(`V`^l20b_i8nigp=zEBK8nX|=y}S}qyM|ytBK7TI zL`>DVzr+f<0EyguGSe#@p;ZqGblQW?KDR!e`<5DS89kEkI9MF=3|_PXQe(1XTzX;E6;DGsaqUVa%Yz55~-`w*<`(q3Gxl)%Di7Z++F^zw>L}9 zMFT~=Z?TxXn0@I)CFq57_iFG8OwuPzFthqgWg4$td)oX)Pxnp*)#KAn^npu(_*x zYShv1;gnpnF2TVeI!=jI(D>AhsYl2Hzk;cl1&{>QWn!YA0GtTNdNHK0i`>3M`lNWc zg!SEjim{J!f23_5nInI?^ESki`PdtkM6(fc7#_7uVWA4G)N#K%U=OuFu4LHog9;D; GaQFwOt#0}N diff --git a/client/pics/34.gif b/client/pics/34.gif deleted file mode 100644 index 30bf9ad2119b4b2f2dd70eac9aa06902bbfaf1e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1944 zcmeH``CHNl0EfRC<-Iv75*n5WiI`<86i6Lvny~E24zE0?lu}Ev#IpiR!z{#^qUNK> z5uj;`#iYwwnMxj&3V1&rq_WDTY2}pmxPN24KYTvFzt8jTC;8y;QTu^N;1lp4=>YKc z>(|v;{cn6T@c%KeeYJqq764;CU;qM$b-d5bePp$KA6jp=V1> zwAWYsQV|r-F8e6~O@&wUC~0w%;oC4)H#ki9=f}z~L&(B>#R3T>U=XS>VaO3uQMulK z$9_J)ls}*?V9PBc>uPgqY6mcF8jkFsgRTa@h#X}5f>pi#>xS_vj*{B8^&tqXF82}`N@p|gEh)Bm z3dbj<9`%Oe(q`Y+C#Ie-wk>lx^N-CwNL>nX)MG76FKA6IiNd#qk?#oc7yke+_p@`X zbup5$G}qb}H~TT+`RKuFS?A*x6sqYrlWQ}reGt>?kZHv6_rZqUR2er~v^*d8O!eii zzfbx@C&}fm&=2aaflXMlYr$ewH;WCL8C0BcJCR~)YoQ)yQ2XumVv4wLq2=fiYLckR z6Xx=Y4_#3eByLnS2Bu$R;d)`p4hP9;)0NkQ`7$hC{XInbMq}m@K2yGd9r`iPp8!p* z#CVLvW^TS?y_ehg6Upj1qNg@(Tu_thsEB;R-JDm^ZGmIE(=$dg7TaqQ{^lC`PMmEL zS>GPjbS_0*Zyg&7nd{kdZ6)i9NOfaYA~g~wICRP5vnEdMyzs@QXS!l)*)R5j_Wkbi zOT@_?MI^guGlTCwy-ap9g*)wa=}{Ysd*H?vcFZ4+^4_7*L^Fb&7xsQcB2H7kV8y}x zGw%;T8~M)|-OdPQ_tSk=HVZ3atTQr5f1ttMK%dKG!ftjh&CNgPYBY}_ha4JBn086s zh0MvdFQjqQ5Z8B(N1B^0%~3&34L2x<5>0GlmVYlyepPq%)Ef08rEH_MPgeHU+-Q1= z1R~x*q0lqP@cGZ{UFYiafaQsU#YJu&nJ3*~dnND-d*;UEG26 L+77h@fW7|$Z~QbL diff --git a/client/pics/35.gif b/client/pics/35.gif deleted file mode 100644 index 848e4c48c9027a04b3e6a3420aaf01d973d906c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1211 zcmZ?wbhEHbbZ1Cl_|5#cn&T|TD{a2?c5`X;xz^d{$zgM9erMJ04%iU0sg<`lsvbXW&(7@hE%O7j_-tovIeBr}{blQ3mTh&rvNCb@G~Zce8MoV`ldtZ* z{psCm&iRjy>4d)gaQ*1YPkFC*ul=y_!u0NGc6&eOFXroS4xRV6D!={S{)Ng@@5JbT zePzq39lqN3#m|jTGq;7?+%Kp-`2F{@^Sk%w{onC_qi8}D^Sn@n>UEh{Z#7!}Jhq_n z@zk&LYI#;Zn^nqxYidMW*~H^BTRA6{hjg^`dML$-6mmTD5{|p^C`)USiej?coI=aIW}6ge z9q9~Se!j~lblD%zkfaM!ox1$5ezNwytiPyH6?o`sX4o{5PUijYV%Zm-GgZGR7{!}`ne8YpD+L!-Wqf9^g9dEv^ zi|5TQE%_u$F_wQ8b$48fQq?y}xOXkF$ckn%YELO_k z<=tI|?^V4_(=JbFuf24%YtDzD)B6PD&%Kn&DSCDBzSZ@}x$o0D7R~&ZK0WX6?^9P+ z9cK@lKfChR{&&jR{pWX=f4^S!ntQo!{F8#iWuj~fas8$$59C%=sJ6cGR8y&1>*%f$74P&iW&Tdv z?N7VAsw_3iy5BudUmR(cE?L+&>AGa^jIgRi_uRV3rF9uh>{^T7FWtF(T2AY!S(AGA z?2SsQO57?LHKq6>S5oc9P41D=tyNm{)?Z#~xh%4?u4>K33bSeVH?65Ul{)=WAJ^M0 zSN2Kf&Rlacb>+62S7Nia-)HJvx;cAd^t=A6zOAp)ZOWd%3%wJ$<7$Mp(JZ}vKNqQn y`~RKyW&JKj@v`@YtoGj5c5{}W&Oda=kf$(#+1DqQK_YnB$73?wA`T7=4AuaO96fIU diff --git a/client/pics/5.gif b/client/pics/5.gif deleted file mode 100644 index fd2057f5c89fcb104bea17c744ec9001cb7fab42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1933 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 diff --git a/client/pics/7.gif b/client/pics/7.gif deleted file mode 100644 index 9eb02fd2cba92503d7cc5d6f42703c46eb9460b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1919 zcmeH`{Xf$Q0LQ=bP&(=2kcT>!keqTImf~D@Gh5y3b)GV6p7-EsZpJce2xY09S=($) z#u$d9^*Ta|b(*b`2ziJ`o5l#`u~&01_ixnuhtKEt_v`hB!hFETBcVV9unPPS82~mn zH@9Z%zw*_055X+*hE=C3YmvUQn zp#TEwjo!XaokHs^Lo5kFeXmd$D}`?*9pSYSRGg*NqM!@wp+GT8tm1QZQ+4g=L|ToK zn%lC@XQXZ@l)*XTVRTI1sbO;-MNAT$irN;-y^=fE;t-DfeYo^y(V&WfVR8;)OEtOT zgEj$4O(dmI%vK2K6)Ox>p4EU*h^$n$nhNgg3DqPpPKKdB*6YTE6a3dlac5?#Lt>rm zTJ7uDVdl;xBO*DrAVY*gHzX#8S~UOqHw!INCCd(%4UJ%@(!q>6C27J> zduY~yJy8@lbNPj6Wt=waY}?4zrJ1|LLi(=a5Va2!4W$vdr%olZ)^lj4%#J-3@vW@w z&yHppRNa1nuU9RtA6QPfE9!w`&tGC%5inhbClReCM4|JGh9#?n8icdzZ5QTVwVW2hkSj>1so!81rLuY>Y`H4N(eIEwf`*38X;^;z#NsDsB{0|E3tn88 zR{ZSoy8}+!XM*IB(2_OEh~9X^NQj$8fmS>|y)M39d;bET74U8q0~;(!#!tn0+o@M)GZ?vdQ>?Bi!lb!RX_&(K8*~Q<|y&`%~hP8!$FcinP zpGhkyp#)MrlU^qqo22nN`yb+tsxZ%J-rGL>eNhBv3`i7`vr3Vx3Fi@=ZsxS*HTZAnXm=uEwoD<_0cWviao$$Hy zO)vo#6*wvRx9*ZUpD+Ty-SgukVVd6wp}ahRdaBXI3vwMj1~czpGqw>H-T+Bu-^sB? lhC68msRomo3%))J3yZee>(Q3l-rYwmmI?!GV!Q$1#D7)4Jx%}s diff --git a/client/pics/8.gif b/client/pics/8.gif deleted file mode 100644 index 33b3fd647f198e1c7a36b68b75a3cc950e6dd6f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1353 zcmZ?wbhEHbbZ1Cl_|5{xcKQE=8ooS(e z@6C+O=|^WN=l^=c8M5@kLW_8<-p(l+ms@;m7sW`JJ`Y)Dwpz{f)a3b>bj%r2$*N=Ks2)k61G<~p&y`S|L=w4AwKJf{o`uX_iJy^s7;{crPCHia@4E%E5G*Qwv)>-Xh~$2Ca{ zs>>z|Jqy24vq|^Gf;N7)j)k>+S|*9@$4`pO)iKY@xZnE5aQlPW|D0|~Zd)I24{zgD ze397AvsZjktMx37_*(m8;!8S>ezhjM&w5#rk~YaOGhzzkx#>$fnl4UXT=Vp==;D6g zqROZlp3Am~SNh5>i=*)6Q$%jcGxO<&$V{cU7Kd0kX`NJ%8?ue&pu zE`1GORG^!s8rN>PYDLu~zgx=}USsT9Ib-&w8!MO3_WK^WG`>^o>GGCK%U>;zo8&cZ zmchDdtHgc!{bpq?T#@9iy(%i})a%%d^QJ~@SRdu06Mx*vN?`pp)(2~xFSu#zZaK%s H!e9*m-Ek3B diff --git a/client/pics/9.gif b/client/pics/9.gif deleted file mode 100644 index ab45c7ce915dafdac3e5292b22750089ee60f50b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1377 zcmZ?wbhEHbbZ1Cl_|5L zCpIiR(8R3dHOFJ)qNCjs##u59g^Q2(E7%M1cy3BLIZ>E(g-F7trQTBn!&jAfPF6WH z)wH;6&&i=`kr`CvnO4&crQ2v^9*xR@Xv&=Wk-VM<^xywv&@wYu!m)xDZ$~QXa?xeD;wd*6V z$J9uhKb&DD+x<;fM$>o3j$UO~zL?6O)90od_wSRj`uhC(T>ta$imkuTxwrqH{C2&4 zX+J)xonQQZubj=#Z|e6Cx61};<@bL3AI5gKO7GvlZH&`q2JAdrHmzQ2Wq3q}xM^5S z;HRm}Zqy6EGgoM43)6UzI>q$HjRKiV_?C>zcv@C=2mIf{8OC$Z2f7iml<1L icg{*}i?iH4Z`%volDDypEw3-^crwj8ZwDhIgEasJ8x}(V diff --git a/client/pics/c0.gif b/client/pics/c0.gif deleted file mode 100644 index 8135304fa77b8b56e22d5be3732d3a548f9c468f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 860 zcmZ?wbhEHbCMz{H>f$_b!6!N6h3z{(+`!N72^ znT1iqr6ORVa~qSaSBl2Qq{Cfe%1K976h3z8S8(o<$=tByXeX<+l*@z+u6_%);Ox&~TuU zm0!$f#{`8#ZT!++b38UKI@&Fw9469nQNg{N-QJI9Wq{Jj2?G9$N>)r-;y#Iyk--`O Dl#mlS diff --git a/client/pics/c2.gif b/client/pics/c2.gif deleted file mode 100644 index 499a0cd43aa08481d75e779ff7350261aa2aae6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 860 zcmZ?wbhEHbmGEI!&HC!Teu;-a!^kAStE&&o+p9hy0q HnHa1AcQFlt diff --git a/client/pics/c3.gif b/client/pics/c3.gif deleted file mode 100644 index 636ba468db38a81450424e46a1fd1c271b574e07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmZ?wbhEHbCMz{H>f$_b!6!N6h7z{(-RvEadh zW_E5Zml**Mo!XdW>{2u?DjezHQ`EbYA(-siDQDj$;~A9V!64`_w58+H<{q( diff --git a/client/pics/dos.gif b/client/pics/dos.gif deleted file mode 100644 index e6dcc4839b920af3e4375b94b4c506d86bc32873..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7067 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 TxNyt2q>i{f2k3SL2LAs8TJ%Ig diff --git a/doc/class_diagram.xmi b/doc/class_diagram.xmi new file mode 100644 index 0000000..2f7470a --- /dev/null +++ b/doc/class_diagram.xmi @@ -0,0 +1,436 @@ + + + + + umbrello uml modeller http://umbrello.kde.org + 1.6.13 + UnicodeUTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jass.iml b/jass.iml new file mode 100644 index 0000000..78b2cc5 --- /dev/null +++ b/jass.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..573e1a3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.leflat + jass + 2.0-SNAPSHOT + + + + org.junit.jupiter + junit-jupiter-api + 5.4.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.4.2 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + + + org.apache.maven.plugins + maven-jar-plugin + + + + **/log4j.properties + + + + + com.leflat.jass.client.JassClient + + + + + + + + + \ No newline at end of file diff --git a/server/ClientLeftException.java b/server/ClientLeftException.java deleted file mode 100644 index 7eb3407..0000000 --- a/server/ClientLeftException.java +++ /dev/null @@ -1,13 +0,0 @@ - - -class ClientLeftException extends Exception { - int clientID; - - public ClientLeftException(int clientID) { - this.clientID = clientID; - } - - public int getClientId() { - return clientID; - } -} diff --git a/server/FlatJassServerSystem.java b/server/FlatJassServerSystem.java deleted file mode 100644 index f44187b..0000000 --- a/server/FlatJassServerSystem.java +++ /dev/null @@ -1,821 +0,0 @@ -//Title: FlatJassServer -//Version: 1.2 -//Copyright: Copyright (c) 1998 -//Author: Pierre Métrailler & Jérome Berclaz -//Company: Flat(r) -//Description: This is the server for the Jass Card Game made by and -// for the proud members of the FLAT(r) -// -// Long life to the FLAT(r)! - - -// package FlatJassServerProject; -import java.net.*; -import java.io.*; -import java.util.*; - -public class FlatJassServerSystem { - private int atout; - private int firstToPlay; // celui qui commence la partie et fait atout - private int playersConnected; // nombre de joueurs connectés - private Plie currentPlie; // plie en cours - private Player[] players = new Player[4]; // les 4 joueurs - private Team[] teams = new Team[2]; // les 2 équipes - - // ***************** PROVISOIRE **************************************** - // static final int[] cards = {0,1,2,3,8,10,15,16,18, 11,12,13,17,19,20,21,30,34, - // 14,22,23,25,28,29,31,35,32, 4,5,6,7,33,9,24,26,27}; - //********************************************************************** - - int port_num; - ServerSocket myServerSocket=null; - ServerNetwork[] myServerNetwork = new ServerNetwork[4]; - - - public FlatJassServerSystem() { - this(32107); - } - - public FlatJassServerSystem(int port) { - port_num = port; - - for (int i=0; i<4; i++) // création des joueurs - players[i] = new Player(); - for (int i=0; i<2; i++) // création des équipes - teams[i] = new Team(); - currentPlie = new Plie(); - - System.out.println("Flat Jass System Server"); - System.out.println("Version 1.2"); - System.out.println("(c) 2000-2002 by FLAT(r)");System.out.println(); - playersConnected = 0; - - // Create server socket - try { - myServerSocket = new ServerSocket(port_num); - - System.out.println("Server socket created on port "+ port_num); - } - catch (IOException e) { - System.out.println("ERROR: cannot create server socket"); - System.exit(1); - } - - do { - - try { - while (playersConnected < 4) { // attend 4 connexions - playersConnected = waitConnect(); - } - - Integer temp; - String[] instr = new String[10]; - - do { - chooseTeam(); // détermine les équipes - - // Play one game (until 1500) - playPart(); - - // ask whether they want to play another part - myServerNetwork[0].sendStr("22"); - - instr = decode(myServerNetwork[0].rcvStr()); // réponse - temp = Integer.valueOf(instr[1]); - - teams[0].resetScore(); - teams[1].resetScore(); - } while (temp.intValue() != 0); - - } catch (ClientLeftException e) { - int id = e.getClientId(); - for (int i=0; (i<=playersConnected) && (i!=4); i++) - if (i != id) - myServerNetwork[i].sendStr("23 "+String.valueOf(id)); - - System.out.println("sleep..."); - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - } - System.out.println("Wakeup..."); - } - - // close sockets and remove players - for(int i=0; (i<=playersConnected) && (i!=4); i++) - myServerNetwork[i].close(); - playersConnected = 0; - } while(true); // loop forever - } - - // attend la connexion d'un joueur - public int waitConnect() throws ClientLeftException{ - int newPlayers = playersConnected; - if (myServerNetwork[playersConnected] == null) - myServerNetwork[playersConnected]=new ServerNetwork(); - if (myServerNetwork[playersConnected].connect(myServerSocket)) { - // la connexion a réussi - - myServerNetwork[playersConnected].setClientId(playersConnected); - myServerNetwork[playersConnected].sendStr("1 " + String.valueOf(playersConnected)); // donne son id et demande des infos - String[] instr = new String[10]; - instr = decode(myServerNetwork[playersConnected].rcvStr()); // attend les infos - - players[playersConnected].firstName = instr[1]; - players[playersConnected].lastName = instr[2]; - players[playersConnected].iD = playersConnected; - - System.out.println(instr[1] + " " +instr[2] + " is connected"); - - for (int i=0; i 1) - if (args[0].compareTo("-p") == 0) - temp = Integer.valueOf(args[1]); - else { - System.out.println("Syntax : java FlatJassServerSystem -p "); - System.exit(-1); - } - FlatJassServerSystem flatJassServerSystem; - if (temp == null) - flatJassServerSystem = new FlatJassServerSystem(); - else - flatJassServerSystem = new FlatJassServerSystem(temp.intValue()); -// flatJassServerSystem.invokedStandalone = true; - } - private boolean invokedStandalone = false; - - - // Procédure de décodage des instructions - private String[] decode(String instr) { - int cmpt = 0; - int cursor = 0; - String[] table = new String[10]; - for (int i=1; i (choosenCards[highest] % 9)) - highest = i; - } - for (int i=0; i<4; i++) { - if (i != lowest) - if ((choosenCards[i] % 9) == (choosenCards[lowest] % 9)) - ok = false; - if (i != highest) - if ((choosenCards[i] % 9) == (choosenCards[highest] % 9)) - ok = false; - } - if (ok) { - int j = 0; - for (int k=0; k<4; k++) - if (k == lowest) { - teams[0].players[0] = players[lowest]; - players[lowest].myTeam = 0; - } - else - if (k == highest) { - teams[0].players[1] = players[highest]; - players[highest].myTeam = 0; - } - else { - teams[1].players[j] = players[k]; - players[k].myTeam = 1; - j++; - } - - } - return ok; - } - - - // distribue les cartes - int[] chooseCards() { - final int[] cards = new int[36]; - boolean[] usedCards = new boolean[36]; - Random rand = new Random(); - for (int i=0; i<36; i++) - usedCards[i] = false; - int j; - for (int i=0; i<35; i++) { - do { - j = (int)(rand.nextDouble() * 36); - } while (usedCards[j]); - cards[i] = j; - usedCards[j] = true; - } - j = 0; - while (usedCards[j]) - j++; - cards[35] = j; - return cards; - } - - - void playPart() throws ClientLeftException{ - /* randomly choose the cards and send them */ - firstToPlay = distribute(); - String answer; - int nextPlayer; - do { - chooseAtout(); // choisit l'atout - nextPlayer = firstToPlay; - - /* for (int i=0; i<9; i++) // fait jouer les 9 plies - nextPlayer = playPlie(nextPlayer); */ - - int j=0; - while ((j<9) && (nextPlayer != -1)) { // fait jouer les 9 plies - nextPlayer = playPlie(nextPlayer); - j++; - } - - if (nextPlayer != -1) { // si personne n'a gagné : on continue normalement - // 5 de der - if (atout == 0) - teams[players[currentPlie.owner].myTeam].addScore(10); - else - teams[players[currentPlie.owner].myTeam].addScore(5); - - for (int i=0; i<4; i++) { // envoie le score - myServerNetwork[i].sendStr("18 " + String.valueOf(teams[players[i].myTeam].getScore()) - + " " + String.valueOf(teams[(players[i].myTeam + 1)%2].getScore())); - answer = myServerNetwork[i].rcvStr(); // réponse - } - - /* waits a few seconds so that the players can see the last - * cards and the score */ - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - } - - firstToPlay = (firstToPlay + 1) % 4; - distribute(); - } - else { // si une équipe a gagné - for (int i=0; i<4; i++) { // envoie le score - myServerNetwork[i].sendStr("18 " + String.valueOf(teams[players[i].myTeam].getScore()) - + " " + String.valueOf(teams[(players[i].myTeam + 1)%2].getScore())); - answer = myServerNetwork[i].rcvStr(); // réponse - } - } - - // répète jusqu'à ce qu'on gagne - } while ((!teams[0].won) && (!teams[1].won)); - - // Sends the winner to all player - int winner = teams[0].won ? 0 : 1; - for (int i=0; i<4; i++) { - myServerNetwork[i].sendStr("21 " + String.valueOf(winner) + " " + - String.valueOf(teams[winner].players[0].iD) + " " + - String.valueOf(teams[winner].players[1].iD)); - answer = myServerNetwork[i].rcvStr(); - } - - /* waits a few seconds so that the players can see all the cards */ - try { - Thread.sleep(4000); - } catch (InterruptedException e) { - } - - } - - - void chooseAtout() throws ClientLeftException { - myServerNetwork[firstToPlay].sendStr("11"); // demande de faire atout en premier - String[] instr = new String[10]; - instr = decode(myServerNetwork[firstToPlay].rcvStr()); // réponse - Integer temp = Integer.valueOf(instr[1]); - if (temp.intValue() == 4) { // si on passe - int second = (firstToPlay + 2) % 4; - myServerNetwork[second].sendStr("12"); // demande de faire atout en second - instr = decode(myServerNetwork[second].rcvStr()); // réponse - } - temp = Integer.valueOf(instr[1]); - atout = temp.intValue(); - for (int i=0; i<4; i++) { - // envoie l'atout choisi - myServerNetwork[i].sendStr("13 " + instr[1] + " " + firstToPlay); - instr[2] = myServerNetwork[i].rcvStr(); // réponse - } - } - - - - int distribute() throws ClientLeftException { - int seven = 0; // 7 de carreau - int[] cards = new int[36]; - cards = chooseCards(); // choisir les cartes au hasard - String s; // chaîne à envoyer - for (int i=0; i<4; i++) { - s = "10"; - for (int j=0; j<9; j++) { - s += " " + String.valueOf(cards[i*9+j]); - if (cards[i*9+j] == 19) // 7 de carreau - seven = i; - } - myServerNetwork[i].sendStr(s); - s = myServerNetwork[i].rcvStr(); - } - return seven; - } - - - int playPlie(int player) throws ClientLeftException { - currentPlie.owner = player; - currentPlie.coupe = 0; - myServerNetwork[player].sendStr("14"); // demande de jouer en premier - String[] instr = new String[10]; - instr = decode(myServerNetwork[player].rcvStr()); //réponse - Integer temp = Integer.valueOf(instr[1]); - currentPlie.color = temp.intValue() / 9; - currentPlie.highest = temp.intValue() % 9; - temp = Integer.valueOf(instr[2]); - currentPlie.score = temp.intValue(); - - temp = Integer.valueOf(instr[3]); // Annonces ? - int[] instr2 = new int[10]; // tableau d'instructions en integer - switch (temp.intValue()) { - case 1 : // Annonces - System.out.println("Annonces"); - myServerNetwork[player].sendStr("19"); // demande des précisions sur l'annonce - instr2 = decodint(myServerNetwork[player].rcvStr()); //réponse - for (int i=0; i currentPlie.highest)) { - currentPlie.highest = playedCard % 9; - currentPlie.owner = (player + i + 1) % 4; - } - } - // else souscoupe => nothing to do - } - else { // first to cut - currentPlie.coupe = 1; - currentPlie.highest = playedCard % 9; - currentPlie.owner = (player + i + 1) % 4; - } - } - else // si c'est joué atout - switch (playedCard % 9) { - case 3 : if (currentPlie.highest != 5) { // si on joue le nell - currentPlie.highest = playedCard % 9; - currentPlie.owner = (player + i + 1) % 4; - } - break; - case 5 : // si on joue le bourg - currentPlie.highest = playedCard % 9; - currentPlie.owner = (player + i + 1) % 4; - break; - default : // sinon - if (((currentPlie.highest!=5) && (currentPlie.highest!=3)) && ((playedCard % 9) > currentPlie.highest)) { - currentPlie.highest = playedCard % 9; - currentPlie.owner = (player + i + 1) % 4; - } - } - } - else if ((playedCard / 9) == currentPlie.color) - if (((playedCard % 9) > currentPlie.highest) && (currentPlie.coupe == 0)) { - currentPlie.owner = (player + i + 1) % 4; - currentPlie.highest = playedCard % 9; - } - temp = Integer.valueOf(instr[2]); - currentPlie.score = currentPlie.score + temp.intValue(); // augmente le score de la plie - } - - /* now everybody has played ... */ - - /* waits a few seconds so that the players can see all the cards */ - try { - Thread.sleep(1500); - } catch (InterruptedException e) { - } - - // communique qui a pris la plie - for (int i=0; i<4; i++) { - myServerNetwork[i].sendStr("17 " + String.valueOf(currentPlie.owner)); - instr[9] = myServerNetwork[i].rcvStr(); // réponse - } - - // choix et comptabilisation des annonces - int stock = -1; - int maxAnounce = 1; - int maxHeight = 0; - int anouncingTeam = -1; // joueur qui a la plus grosse annonce - for (int j=0; j<4; j++) - for (int i=0; i maxAnounce) { - // plus grosse annonce - anouncingTeam = j; - maxAnounce = players[j].anounces[i].type; - maxHeight = Card.getHeight(players[j].anounces[i].card); - } - else if ((players[j].anounces[i].type == maxAnounce) && - (Card.getHeight(players[j].anounces[i].card) > - maxHeight)) { - // même annonce plus haute - anouncingTeam = j; - maxHeight = Card.getHeight(players[j].anounces[i].card); - } - else if ((players[j].anounces[i].type == maxAnounce) && - (Card.getHeight(players[j].anounces[i].card) - == maxHeight)) { - // meme annonce, meme hauteur - } - if (players[j].anounces[i].type == 0) - stock = j; - } - String info; - System.out.println("Bigger 'annonce' : "+ anouncingTeam); - - if (anouncingTeam != -1) { // there are announces - for (int i=0; i<4; i++) { - if (((i == anouncingTeam) || (i == ((anouncingTeam + 2) % 4))) - && (players[i].nbrAnounces > 0)){ - // annonceur - - info = "20 " + i + " " + players[i].nbrAnounces; - for (int j=0; j 1499) - won = true; - } - - public int getScore() { - return currentScore; - } -} - -class Plie { - public int highest; // la plus haute carte de la plie (celle qui tient la plie) - public int color; // la couleur demandée - public int score; // la valeur de la plie - public int coupe; // 0 : pas coupé, 1 : coupé - public int owner; // iD de celui qui tient la plie -} - -/* ************************** REMARQUE ****************************** - Les cartes sont en fait représentées par des int de 0 à 35 - où "carte div 9" donne sa couleur et "carte mod 9" sa - hauteur. La classe Card est donc utilisée ici que pour - les méthodes statiques getColor et getHeight. - *************************************************************** */ - -abstract class Card { - static final int[] anounceValue = {20, 20, 50, 100, 100, 150, 200}; - - public static int getColor(int card) { - return card / 9; - } - - public static int getHeight(int card) { - return card % 9; - } -} - -class Anounce { - int type; // 0: stöck, 1: 3 cartes, 2: cinquante, 3: cent, 4: carré - int card; // plus haute carte de l'annonce - int player; -} diff --git a/server/Makefile b/server/Makefile deleted file mode 100644 index 07c7d86..0000000 --- a/server/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -SOURCES = FlatJassServerSystem.java ServerNetwork.java \ - ClientLeftException.java - -JAVA = java -JAVAC = javac - -all: $(SOURCES:.java=.class) - -%.class: %.java - $(JAVAC) $< - -run: $(SOURCES:.java=.class) - $(JAVA) FlatJassServerSystem - -clean: - $(RM) *.class *~ diff --git a/server/ServerNetwork.java b/server/ServerNetwork.java deleted file mode 100644 index 06cd50b..0000000 --- a/server/ServerNetwork.java +++ /dev/null @@ -1,98 +0,0 @@ -//Title: FlatJassServer -//Version: -//Copyright: Copyright (c) 1998 -//Author: Pierre Métrailler & Jérome Berclaz -//Company: Flat -//Description: Your description - - -//package FlatJassServerProject; - -import java.net.*; -import java.io.*; - -public class ServerNetwork { - - public static final int PORT_NUM = 1500; - - //ServerSocket myServerSocket; - Socket myClientSocket = null; - InputStreamReader isr; - BufferedReader is; - PrintWriter os; - int clientId; - - - public ServerNetwork() { - } - - public boolean connect(ServerSocket myServerSocket) { - // waiting and bind - - System.out.println("Waiting for connections"); - try { - - myClientSocket=myServerSocket.accept(); - - //streams - // a verifier le autoflush - isr = new InputStreamReader(myClientSocket.getInputStream()); - is = new BufferedReader(isr); - os = new PrintWriter(new BufferedOutputStream(myClientSocket.getOutputStream()),false); - - - } - catch (IOException e) { - System.out.println("Error unable to bind socket"); - //System.exit(1); - } - System.out.println("Connection successful."); - return true; - - } - - public void setClientId(int clientId) { - this.clientId = clientId; - } - - public boolean sendStr(String strToSend) { - - os.println(strToSend); - os.flush(); - System.out.println("SERVER sent : "+strToSend); - return true; - } - - public String rcvStr() throws ClientLeftException{ - String rcvTemp = null; - - //implementer timeout + exc - - try { - rcvTemp=is.readLine(); - } - catch (IOException e) { - System.out.println("Error during reception"); - //System.exit(1); - } - if (rcvTemp != null) - System.out.println("Received : " + rcvTemp); - else { - System.out.println("Client has left unexpectedly"); - throw new ClientLeftException(clientId); - } - - return rcvTemp; - - } - - public void close() { - try { - myClientSocket.close(); - } - catch (IOException e) { - System.out.println("Error while closing socket"); - System.exit(1); - } - } -} diff --git a/client/AbsoluteConstraints.java b/src/main/java/com/leflat/jass/client/AbsoluteConstraints.java similarity index 99% rename from client/AbsoluteConstraints.java rename to src/main/java/com/leflat/jass/client/AbsoluteConstraints.java index bc8c7f9..c00c163 100644 --- a/client/AbsoluteConstraints.java +++ b/src/main/java/com/leflat/jass/client/AbsoluteConstraints.java @@ -13,6 +13,9 @@ //package org.netbeans.lib.awtextra; +package com.leflat.jass.client; + + import java.awt.Dimension; import java.awt.Point; diff --git a/client/AbsoluteLayout.java b/src/main/java/com/leflat/jass/client/AbsoluteLayout.java similarity index 99% rename from client/AbsoluteLayout.java rename to src/main/java/com/leflat/jass/client/AbsoluteLayout.java index 83a9434..3fc0fd1 100644 --- a/client/AbsoluteLayout.java +++ b/src/main/java/com/leflat/jass/client/AbsoluteLayout.java @@ -13,6 +13,9 @@ //package org.netbeans.lib.awtextra; +package com.leflat.jass.client; + + import java.awt.*; /** AbsoluteLayout is a LayoutManager that works as a replacement for "null" layout to diff --git a/src/main/java/com/leflat/jass/client/CanvasBorder.java b/src/main/java/com/leflat/jass/client/CanvasBorder.java new file mode 100644 index 0000000..47e5bf1 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/CanvasBorder.java @@ -0,0 +1,37 @@ +/* + * CanvasBorder.java + * + * Created on 18. avril 2000, 16:49 + */ + + +/** + * @author Berclaz Jérôme + * @version + */ + +package com.leflat.jass.client; + +import java.awt.*; + +public class CanvasBorder extends JassCanvas { + public static final int Y_STEP = 30; + + public CanvasBorder() { + } + + public void paint(Graphics g) { + Dimension d = getSize(); + + int w = (d.width - CardImages.IMG_WIDTH) / 2; + int h = (d.height - hand.size() * Y_STEP - 66) / 2 + 20; + for (int i = 0; i < hand.size(); i++) { + g.drawImage(CardImages.getInstance().getImage(hand.get(i)), w, h + i * 30, this); + } + + g.drawString(name, 20, 30); + if (atout) { + g.drawString("atout", 20, 60); + } + } +} diff --git a/src/main/java/com/leflat/jass/client/CanvasCenter.java b/src/main/java/com/leflat/jass/client/CanvasCenter.java new file mode 100644 index 0000000..a162ba2 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/CanvasCenter.java @@ -0,0 +1,112 @@ +/* + * CanvasCenter.java + * + * Created on 18. avril 2000, 18:04 + */ + + +/** + * @author Berclaz Jérôme + * @version 1.1 + */ + +package com.leflat.jass.client; + +import com.leflat.jass.common.Card; + +import java.awt.*; +import java.util.*; +import java.util.List; + +import static java.lang.Integer.min; + +public class CanvasCenter extends Canvas { + private static final int X_OFFSET = 10; + private static final int Y_OFFSET = 40; + private static final int X_STEP = 8; + private static final int[] CARD_POS_X = {-35, 25, -35, -95}; + private static final int[] CARD_POS_Y = {2, -48, -98, -48}; + public static final int MODE_PASSIVE = 0; + public static final int MODE_DRAW_TEAMS = 1; + public static final int MODE_PICK_CARD = 2; + public static final int MODE_GAME = 3; + + + private int mode; // 0 : rien, 1 : tirer les équipes + // 2 : tirer les équipes et choisir une carte + // 3 : jouer + private List drawnCards = new ArrayList<>(); + private Map shownCards = new HashMap<>(); + + public CanvasCenter() { + mode = MODE_PASSIVE; + } + + public void setMode(int mode) { + this.mode = mode; + repaint(); + } + + public void drawCard(int i) { + drawnCards.add(i); + repaint(); + } + + public void resetCards() { + drawnCards.clear(); + shownCards.clear(); + repaint(); + } + + public void showCard(Card card, int player) { + shownCards.put(player, card); + repaint(); + } + + public Collection getShownCards() { + return shownCards.values(); + } + + public void paint(Graphics g) { + System.out.println("Repaint"); + Dimension d = getSize(); + switch (mode) { + case MODE_DRAW_TEAMS: + case MODE_PICK_CARD: + for (int i = 0; i < 36; i++) { + if (!drawnCards.contains(i)) { + g.drawImage(CardImages.getInstance().getBackImage(), X_OFFSET + i * X_STEP, Y_OFFSET, this); + } + } + break; + case MODE_GAME: + int w = d.width / 2; + int h = d.height / 2; + for (int i = 0; i < 4; i++) { + if (shownCards.containsKey(i)) { + g.drawImage(CardImages.getInstance().getImage(shownCards.get(i)), w + CARD_POS_X[i], h + CARD_POS_Y[i], this); + } + } + } + } + + public int getCard(int x, int y) { + if (mode != MODE_PICK_CARD) { + return -1; + } + if (y < Y_OFFSET || y > Y_OFFSET + CardImages.IMG_HEIGHT) { + return -1; + } + int highCardNbr = (x - X_OFFSET) / X_STEP; + int lowCardNbr = (x - X_OFFSET - CardImages.IMG_WIDTH) / X_STEP; + if (highCardNbr < 0 || lowCardNbr > 35) { + return -1; + } + for (int nbr = min(highCardNbr, 35); nbr >= lowCardNbr; nbr--) { + if (!drawnCards.contains(nbr)) { + return nbr; + } + } + return -1; + } +} diff --git a/src/main/java/com/leflat/jass/client/CanvasLastPlie.java b/src/main/java/com/leflat/jass/client/CanvasLastPlie.java new file mode 100644 index 0000000..447cc0d --- /dev/null +++ b/src/main/java/com/leflat/jass/client/CanvasLastPlie.java @@ -0,0 +1,69 @@ +/* + * CanvasLastPlie.java + * + * Created on 19. avril 2000, 11:23 + */ + + +/** + * @author Berclaz Jérôme + * @version + */ + +package com.leflat.jass.client; + +import com.leflat.jass.common.Card; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class CanvasLastPlie extends Canvas { + private List lastPlie = new ArrayList<>(); + private int atout; + private int ourScore, theirScore; + + public CanvasLastPlie() { + ourScore = 0; + theirScore = 0; + atout = 4; // ne rien afficher + } + + public void setLastPlie(Collection plie) { + lastPlie.clear(); + lastPlie.addAll(plie); + repaint(); + } + + public void setScores(int ourScore, int theirScore) { + this.ourScore = ourScore; + this.theirScore = theirScore; + repaint(); + } + + public void setAtout(int atout) { + this.atout = atout; + repaint(); + } + + public void hideAtout() { + this.atout = 4; + repaint(); + } + + public void paint(Graphics g) { + for (int i = 0; i < lastPlie.size(); i++) { + g.drawImage(CardImages.getInstance().getImage(lastPlie.get(i)), 120 + 30 * i, 5, this); + } + + if (atout < 4) { + g.drawImage(CardImages.getInstance().getColorImage(atout), 380, 8, this); + } + + g.drawString("Dernière plie:", 20, 20); + g.drawString("Atout:", 340, 20); + g.drawString("Nous: " + String.valueOf(ourScore), 420, 13); + g.drawString("Eux : " + String.valueOf(theirScore), 420, 27); + } +} diff --git a/src/main/java/com/leflat/jass/client/CanvasPlayer.java b/src/main/java/com/leflat/jass/client/CanvasPlayer.java new file mode 100644 index 0000000..61b5492 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/CanvasPlayer.java @@ -0,0 +1,67 @@ +/* + * CanvasPlayer.java + * + * Created on 18. avril 2000, 17:31 + */ + +/** + * @author Berclaz Jérôme + * @version + */ + +package com.leflat.jass.client; + +import com.leflat.jass.common.Card; + +import java.awt.*; + +import java.util.List; + +public class CanvasPlayer extends JassCanvas { + private static final int X_STEP = 35; + + public CanvasPlayer() { + } + + public void paint(Graphics g) { + if (!hand.isEmpty()) { + Dimension d = getSize(); + int cardsWidth = getCardsWidth(); + int xOffset = (d.width - cardsWidth) / 2; + for (int i = 0; i < hand.size(); i++) { + g.drawImage(CardImages.getInstance().getImage(hand.get(i)), + xOffset + i * X_STEP, 20, this); + } + } + g.drawString(name, 30, 15); + if (atout) { + g.drawString("atout", name.length() * 7 + 60, 15); + } + } + + public Card getCard(int x, int y) { + if (mode != JassCanvas.MODE_PLAY) { + return null; + } + if (y < 20) { + return null; + } + Dimension d = getSize(); + int cardsWidth = getCardsWidth(); + int xOffset = (d.width - cardsWidth) / 2; + for (int i = hand.size() - 1; i >= 0; i--) { + if (x >= xOffset + i * X_STEP && x < xOffset + i * X_STEP + CardImages.IMG_WIDTH) { + return hand.get(i); + } + } + return null; + } + + private int getCardsWidth() { + return CardImages.IMG_WIDTH + (hand.size() - 1) * X_STEP; + } + + public List getHand() { + return hand; + } +} diff --git a/src/main/java/com/leflat/jass/client/CanvasTop.java b/src/main/java/com/leflat/jass/client/CanvasTop.java new file mode 100644 index 0000000..685fe76 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/CanvasTop.java @@ -0,0 +1,37 @@ +/* + * CanvasTop.java + * + * Created on 18. avril 2000, 17:31 + */ + + +/** + * @author Berclaz Jérôme + * @version + */ + +package com.leflat.jass.client; + +import java.awt.*; + +public class CanvasTop extends JassCanvas { + private static final int X_STEP = 35; + + // Constructeur + public CanvasTop() { + } + + public void paint(Graphics g) { + Dimension d = getSize(); + + int w = (d.width - 36 - hand.size() * X_STEP) / 2; + for (int i = 0; i < hand.size(); i++) { + g.drawImage(CardImages.getInstance().getImage(hand.get(i)), w + X_STEP * i, 20, this); + } + + g.drawString(name, 30, 15); + if (atout) { + g.drawString("atout", name.length() * 7 + 60, 15); + } + } +} diff --git a/src/main/java/com/leflat/jass/client/CardImages.java b/src/main/java/com/leflat/jass/client/CardImages.java new file mode 100644 index 0000000..2290b84 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/CardImages.java @@ -0,0 +1,48 @@ +package com.leflat.jass.client; + +import com.leflat.jass.common.Card; + +import java.awt.*; + +public class CardImages { + public static final int IMG_WIDTH = 71; + public static final int IMG_HEIGHT = 96; + private static final String IMG_PATH = "pics/"; + private static final CardImages singleton = new CardImages(); + private Image[] images = new Image[36]; + private Image backImage; + private Image[] colorImages = new Image[4]; + + private CardImages() { + Toolkit tk = Toolkit.getDefaultToolkit(); + for (int i = 0; i < 36; i++) { + var imagePath = getClass().getClassLoader().getResource(IMG_PATH + i + ".png"); + images[i] = tk.getImage(imagePath); + } + var backImagePath = getClass().getClassLoader().getResource(IMG_PATH + "dos.png"); + backImage = tk.getImage(backImagePath); + for (int i = 0; i < 4; i++) { + var colorImagePath = getClass().getClassLoader().getResource(IMG_PATH + "c" + i + ".png"); + colorImages[i] = tk.getImage(colorImagePath); + } + } + + public static CardImages getInstance() { + return singleton; + } + + public Image getImage(Card card) { + if (!card.isBack()) { + return images[card.getNumber()]; + } + return backImage; + } + + public Image getBackImage() { + return backImage; + } + + public Image getColorImage(int color) { + return colorImages[color]; + } +} diff --git a/src/main/java/com/leflat/jass/client/ClientPlayer.java b/src/main/java/com/leflat/jass/client/ClientPlayer.java new file mode 100644 index 0000000..b8c72de --- /dev/null +++ b/src/main/java/com/leflat/jass/client/ClientPlayer.java @@ -0,0 +1,14 @@ +package com.leflat.jass.client; + +import com.leflat.jass.common.BasePlayer; + +public class ClientPlayer extends BasePlayer { + public ClientPlayer(int id, String name) { + super(id); + this.name = name; + } + + public ClientPlayer(int id) { + super(id); + } +} diff --git a/src/main/java/com/leflat/jass/client/DialogAtout.java b/src/main/java/com/leflat/jass/client/DialogAtout.java new file mode 100644 index 0000000..eca19a7 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/DialogAtout.java @@ -0,0 +1,119 @@ +/* + * DialogAtout.java + * + * Created on 19. avril 2000, 11:52 + */ + +/** + * @author Berclaz Jérôme + * @version + */ + +package com.leflat.jass.client; + + +public class DialogAtout extends javax.swing.JDialog { + private boolean pass = false; + + /** + * Creates new form DialogAtout + */ + public DialogAtout(java.awt.Frame parent, boolean modal, boolean allowedToPass) { + super(parent, modal); + initComponents(); + pack(); + jButtonPass.setEnabled(allowedToPass); + jComboBoxAtout.addItem("Pique"); + jComboBoxAtout.addItem("Cœur"); + jComboBoxAtout.addItem("Carreau"); + jComboBoxAtout.addItem("Trèfle"); + } + + /** + * This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the FormEditor. + */ + private void initComponents() {//GEN-BEGIN:initComponents + jLabel1 = new javax.swing.JLabel(); + jComboBoxAtout = new javax.swing.JComboBox(); + jButtonOk = new javax.swing.JButton(); + jButtonPass = new javax.swing.JButton(); + getContentPane().setLayout(new AbsoluteLayout()); + setSize(300, 180); + setResizable(false); + setTitle("Choix de l'atout"); + addWindowListener(new java.awt.event.WindowAdapter() { + public void windowClosing(java.awt.event.WindowEvent evt) { + closeDialog(evt); + } + } + ); + + jLabel1.setText("Veuillez choisir l'atout"); + + + getContentPane().add(jLabel1, new AbsoluteConstraints(20, 20, -1, -1)); + + + getContentPane().add(jComboBoxAtout, new AbsoluteConstraints(120, 60, -1, -1)); + + jButtonOk.setText("Ok"); + jButtonOk.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButtonOkActionPerformed(evt); + } + } + ); + + + getContentPane().add(jButtonOk, new AbsoluteConstraints(40, 110, -1, -1)); + + jButtonPass.setText("Passer"); + jButtonPass.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButtonPassActionPerformed(evt); + } + } + ); + + + getContentPane().add(jButtonPass, new AbsoluteConstraints(180, 110, -1, -1)); + + }//GEN-END:initComponents + + private void jButtonPassActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonPassActionPerformed +// Add your handling code here: + pass = true; + this.dispose(); + }//GEN-LAST:event_jButtonPassActionPerformed + + private void jButtonOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonOkActionPerformed +// Add your handling code here: + this.dispose(); + }//GEN-LAST:event_jButtonOkActionPerformed + + /** + * Closes the dialog + */ + private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog + setVisible(false); + dispose(); + }//GEN-LAST:event_closeDialog + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JComboBox jComboBoxAtout; + private javax.swing.JButton jButtonOk; + private javax.swing.JButton jButtonPass; + // End of variables declaration//GEN-END:variables + + public int getSelectedColor() { + if (pass) { + return 4; + } + return jComboBoxAtout.getSelectedIndex(); + } +} diff --git a/client/DialogConnect.java b/src/main/java/com/leflat/jass/client/DialogConnect.java similarity index 84% rename from client/DialogConnect.java rename to src/main/java/com/leflat/jass/client/DialogConnect.java index 1fb7e86..aaf4f4b 100644 --- a/client/DialogConnect.java +++ b/src/main/java/com/leflat/jass/client/DialogConnect.java @@ -4,16 +4,19 @@ * Created on 18. avril 2000, 21:34 */ - - -/** +/* * * @author Berclaz Jérôme * @version */ + +package com.leflat.jass.client; + + public class DialogConnect extends javax.swing.JDialog { boolean ok = false; - String lastName, firstName, iP; + String name, host; + int gameId; /** Creates new form DialogConnect */ public DialogConnect(java.awt.Frame parent,boolean modal) { @@ -30,12 +33,12 @@ public DialogConnect(java.awt.Frame parent,boolean modal) { */ private void initComponents () {//GEN-BEGIN:initComponents jPanel1 = new javax.swing.JPanel (); - jLabelFirstName = new javax.swing.JLabel (); - jLabelLastName = new javax.swing.JLabel (); + jLabelName = new javax.swing.JLabel (); + jLabelGame = new javax.swing.JLabel (); jLabel3 = new javax.swing.JLabel (); - jTextFieldFirstName = new javax.swing.JTextField (); - jTextFieldLastName = new javax.swing.JTextField (); - jTextFieldIP = new javax.swing.JTextField (); + jTextFieldName = new javax.swing.JTextField (); + jTextFieldGame = new javax.swing.JTextField (); + jTextFieldHost = new javax.swing.JTextField (); jButtonOk = new javax.swing.JButton (); jButtonCancel = new javax.swing.JButton (); getContentPane ().setLayout (new java.awt.GridBagLayout ()); @@ -54,16 +57,16 @@ public void windowClosing (java.awt.event.WindowEvent evt) { jPanel1.setBorder (new javax.swing.border.TitledBorder("Paramètres de connexion")); jPanel1.setName (""); - jLabelFirstName.setText ("Nom"); + jLabelName.setText ("Nom"); gridBagConstraints2 = new java.awt.GridBagConstraints (); gridBagConstraints2.gridx = 0; gridBagConstraints2.gridy = 0; gridBagConstraints2.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints2.insets = new java.awt.Insets (14, 20, 0, 0); - jPanel1.add (jLabelFirstName, gridBagConstraints2); + jPanel1.add (jLabelName, gridBagConstraints2); - jLabelLastName.setText ("Prénom"); + jLabelGame.setText ("Jeu"); gridBagConstraints2 = new java.awt.GridBagConstraints (); gridBagConstraints2.gridx = 0; @@ -71,7 +74,7 @@ public void windowClosing (java.awt.event.WindowEvent evt) { gridBagConstraints2.gridwidth = 2; gridBagConstraints2.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints2.insets = new java.awt.Insets (10, 20, 0, 0); - jPanel1.add (jLabelLastName, gridBagConstraints2); + jPanel1.add (jLabelGame, gridBagConstraints2); jLabel3.setText ("Serveur"); @@ -83,6 +86,7 @@ public void windowClosing (java.awt.event.WindowEvent evt) { gridBagConstraints2.insets = new java.awt.Insets (10, 21, 0, 0); jPanel1.add (jLabel3, gridBagConstraints2); + jTextFieldName.setText("Joueur"); gridBagConstraints2 = new java.awt.GridBagConstraints (); gridBagConstraints2.gridx = 3; @@ -91,8 +95,7 @@ public void windowClosing (java.awt.event.WindowEvent evt) { gridBagConstraints2.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints2.ipadx = 146; gridBagConstraints2.insets = new java.awt.Insets (11, 61, 8, 0); - jPanel1.add (jTextFieldFirstName, gridBagConstraints2); - + jPanel1.add (jTextFieldName, gridBagConstraints2); gridBagConstraints2 = new java.awt.GridBagConstraints (); gridBagConstraints2.gridx = 3; @@ -101,9 +104,9 @@ public void windowClosing (java.awt.event.WindowEvent evt) { gridBagConstraints2.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints2.ipadx = 146; gridBagConstraints2.insets = new java.awt.Insets (9, 61, 8, 0); - jPanel1.add (jTextFieldLastName, gridBagConstraints2); + jPanel1.add (jTextFieldGame, gridBagConstraints2); - jTextFieldIP.setText ("localhost"); + jTextFieldHost.setText ("localhost"); gridBagConstraints2 = new java.awt.GridBagConstraints (); gridBagConstraints2.gridx = 3; @@ -112,7 +115,7 @@ public void windowClosing (java.awt.event.WindowEvent evt) { gridBagConstraints2.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints2.ipadx = 77; gridBagConstraints2.insets = new java.awt.Insets (9, 61, 8, 0); - jPanel1.add (jTextFieldIP, gridBagConstraints2); + jPanel1.add (jTextFieldHost, gridBagConstraints2); gridBagConstraints1 = new java.awt.GridBagConstraints (); @@ -167,9 +170,9 @@ private void jButtonCancelActionPerformed (java.awt.event.ActionEvent evt) {//GE private void jButtonOkActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonOkActionPerformed // Add your handling code here: ok = true; - lastName = jTextFieldLastName.getText(); - firstName = jTextFieldFirstName.getText(); - iP = jTextFieldIP.getText(); + gameId = jTextFieldGame.getText().isEmpty() ? -1 : Integer.parseInt(jTextFieldGame.getText()); + name = jTextFieldName.getText(); + host = jTextFieldHost.getText(); this.dispose(); }//GEN-LAST:event_jButtonOkActionPerformed @@ -183,12 +186,12 @@ private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_clos // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel jPanel1; - private javax.swing.JLabel jLabelFirstName; - private javax.swing.JLabel jLabelLastName; + private javax.swing.JLabel jLabelName; + private javax.swing.JLabel jLabelGame; private javax.swing.JLabel jLabel3; - private javax.swing.JTextField jTextFieldFirstName; - private javax.swing.JTextField jTextFieldLastName; - private javax.swing.JTextField jTextFieldIP; + private javax.swing.JTextField jTextFieldName; + private javax.swing.JTextField jTextFieldGame; + private javax.swing.JTextField jTextFieldHost; private javax.swing.JButton jButtonOk; private javax.swing.JButton jButtonCancel; // End of variables declaration//GEN-END:variables diff --git a/client/DialogInfo.java b/src/main/java/com/leflat/jass/client/DialogInfo.java similarity index 97% rename from client/DialogInfo.java rename to src/main/java/com/leflat/jass/client/DialogInfo.java index ba9fed6..12040f4 100644 --- a/client/DialogInfo.java +++ b/src/main/java/com/leflat/jass/client/DialogInfo.java @@ -11,7 +11,10 @@ * @author Berclaz Jérôme * @version */ - import javax.swing.JLabel; + +package com.leflat.jass.client; + +import javax.swing.JLabel; public class DialogInfo extends javax.swing.JDialog { JLabel[] labelLine = new javax.swing.JLabel[5]; diff --git a/src/main/java/com/leflat/jass/client/DialogNewPart.java b/src/main/java/com/leflat/jass/client/DialogNewPart.java new file mode 100644 index 0000000..90bf446 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/DialogNewPart.java @@ -0,0 +1,101 @@ +/* + * DialogNewPart.java + * + * Created on 11. avril 2002, 22:17 + */ + +/** + * @author Berclaz Jérôme + * @version + */ + +package com.leflat.jass.client; + +public class DialogNewPart extends javax.swing.JDialog { + private boolean newPart = false; + + /** Creates new form DialogNewPart */ + public DialogNewPart(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + pack(); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the FormEditor. + */ + private void initComponents() {//GEN-BEGIN:initComponents + jLabel1 = new javax.swing.JLabel(); + jButtonYes = new javax.swing.JButton(); + jButtonNo = new javax.swing.JButton(); + + getContentPane().setLayout(new AbsoluteLayout()); + setSize(320, 180); + setResizable(false); + setTitle("Nouvelle partie"); + addWindowListener(new java.awt.event.WindowAdapter() { + public void windowClosing(java.awt.event.WindowEvent evt) { + closeDialog(evt); + } + } + ); + + jLabel1.setText("Voulez vous faire une nouvelle partie?"); + + + getContentPane().add(jLabel1, new AbsoluteConstraints(30, 20, -1, -1)); + + jButtonYes.setText("Oui"); + jButtonYes.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButtonYesActionPerformed(evt); + } + } + ); + + + getContentPane().add(jButtonYes, new AbsoluteConstraints(40, 100, -1, -1)); + + jButtonNo.setText("Non"); + jButtonNo.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButtonNoActionPerformed(evt); + } + } + ); + + + getContentPane().add(jButtonNo, new AbsoluteConstraints(180, 100, -1, -1)); + + + }//GEN-END:initComponents + + private void jButtonNoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonManualActionPerformed +// Add your handling code here: + newPart = false; + this.dispose(); + }//GEN-LAST:event_jButtonManualActionPerformed + + private void jButtonYesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonHasardActionPerformed +// Add your handling code here: + newPart = true; + this.dispose(); + }//GEN-LAST:event_jButtonHasardActionPerformed + + /** Closes the dialog */ + private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog + setVisible(false); + dispose(); + }//GEN-LAST:event_closeDialog + + public boolean getNewPart() { return newPart; } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JButton jButtonYes; + private javax.swing.JButton jButtonNo; + // End of variables declaration//GEN-END:variables + +} diff --git a/src/main/java/com/leflat/jass/client/DialogPartnerChoice.java b/src/main/java/com/leflat/jass/client/DialogPartnerChoice.java new file mode 100644 index 0000000..12aa34d --- /dev/null +++ b/src/main/java/com/leflat/jass/client/DialogPartnerChoice.java @@ -0,0 +1,93 @@ +/* + * DialogPartnerChoice.java + * + * Created on 19. avril 2000, 11:44 + */ + +/** + * @author Berclaz Jérôme + * @version + */ + +package com.leflat.jass.client; + + +import com.leflat.jass.common.BasePlayer; + +public class DialogPartnerChoice extends javax.swing.JDialog { + + /** + * Creates new form DialogPartnerChoice + */ + public DialogPartnerChoice(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + pack(); + } + + /** + * This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the FormEditor. + */ + private void initComponents() {//GEN-BEGIN:initComponents + jComboBoxPartner = new javax.swing.JComboBox(); + jButtonOk = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); + getContentPane().setLayout(new AbsoluteLayout()); + setSize(300, 200); + setResizable(false); + setTitle("Choix du partenaire"); + addWindowListener(new java.awt.event.WindowAdapter() { + public void windowClosing(java.awt.event.WindowEvent evt) { + closeDialog(evt); + } + } + ); + + getContentPane().add(jComboBoxPartner, new AbsoluteConstraints(100, 50, -1, -1)); + + jButtonOk.setText("Ok"); + jButtonOk.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButtonOkActionPerformed(evt); + } + } + ); + + getContentPane().add(jButtonOk, new AbsoluteConstraints(120, 120, -1, -1)); + + jLabel1.setText("Veuillez choisir votre partenaire"); + + + getContentPane().add(jLabel1, new AbsoluteConstraints(20, 20, -1, -1)); + + }//GEN-END:initComponents + + private void jButtonOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonOkActionPerformed +// Add your handling code here: + this.dispose(); + }//GEN-LAST:event_jButtonOkActionPerformed + + /** + * Closes the dialog + */ + private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog + setVisible(false); + dispose(); + }//GEN-LAST:event_closeDialog + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JComboBox jComboBoxPartner; + private javax.swing.JButton jButtonOk; + private javax.swing.JLabel jLabel1; + // End of variables declaration//GEN-END:variables + + public void addPlayer(BasePlayer player) { + jComboBoxPartner.addItem(player); + } + + public BasePlayer getSelectedPlayer() { return (BasePlayer) jComboBoxPartner.getSelectedItem(); } +} diff --git a/client/DialogTeamChoice.java b/src/main/java/com/leflat/jass/client/DialogTeamChoice.java similarity index 98% rename from client/DialogTeamChoice.java rename to src/main/java/com/leflat/jass/client/DialogTeamChoice.java index e6a4c0a..72424b4 100644 --- a/client/DialogTeamChoice.java +++ b/src/main/java/com/leflat/jass/client/DialogTeamChoice.java @@ -4,13 +4,15 @@ * Created on 19. avril 2000, 11:37 */ - - /** * * @author Berclaz Jérôme * @version */ + + +package com.leflat.jass.client; + public class DialogTeamChoice extends javax.swing.JDialog { boolean hasard = true; diff --git a/src/main/java/com/leflat/jass/client/JassCanvas.java b/src/main/java/com/leflat/jass/client/JassCanvas.java new file mode 100644 index 0000000..f07fc91 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/JassCanvas.java @@ -0,0 +1,69 @@ +package com.leflat.jass.client; + +import com.leflat.jass.common.Card; + +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +public abstract class JassCanvas extends Canvas { + public static final int MODE_STATIC = 0; + public static final int MODE_PLAY = 1; + + protected int mode; // 0 : rien, 1 : jouer + protected String name = ""; + protected boolean atout = false; + protected List hand = new ArrayList<>(); + + protected JassCanvas() { + mode = MODE_STATIC; + } + + public void setMode(int mode) { + this.mode = mode; + } + + public void setAtout(boolean atout) { + this.atout = atout; + repaint(); + } + + public void setName(String name) { + this.name = name; + } + + public void setHand(List hand) { + clearHand(); + this.hand.addAll(hand); + repaint(); + } + + public void clearHand() { + hand.clear(); + } + + public void setBackCards(int number) { + clearHand(); + for (int i=0; i jButtonConnectActionPerformed()); + + getContentPane().add(jButtonConnect, new AbsoluteConstraints(410, 500, -1, -1)); + + }//GEN-END:initComponents + + private void jButtonConnectActionPerformed() { + //GEN-FIRST:event_jButtonConnectActionPerformed + if (!myself.isConnected()) { + var dc = new DialogConnect(this, true); + dc.setLocationRelativeTo(this); + dc.setVisible(true); + if (!dc.ok) { + return; + } + int gameId = myself.connect(dc.name, dc.host, dc.gameId); + if (gameId >= 0) { + jButtonConnect.setText("Déconnexion"); + setGameId(gameId); + } else { + // TODO: error message + } + } else { + // TODO: DISPLAY A CONFIRMATION MSG + if (myself.disconnect()) { + disconnect(); + } + } + }//GEN-LAST:event_jButtonConnectActionPerformed + + + private void jButtonAnounceActionPerformed(java.awt.event.ActionEvent evt) { +//GEN-FIRST:event_jButtonAnounceActionPerformed +// Add your handling code here: + anoucementPressed = true; + setAnouncementEnabled(false); + }//GEN-LAST:event_jButtonAnounceActionPerformed + + /** + * Exit the Application + */ + private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm + System.exit(0); + }//GEN-LAST:event_exitForm + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel rightPanel; + private javax.swing.JPanel leftPanel; + private javax.swing.JPanel playerPanel; + private javax.swing.JPanel topPanel; + private javax.swing.JPanel centerPanel; + private javax.swing.JPanel lastPliePanel; + private javax.swing.JPanel jPanel1; + private javax.swing.JLabel statusBar; + private javax.swing.JLabel infoBar; + private javax.swing.JButton jButtonAnounce; + private javax.swing.JButton jButtonConnect; + // End of variables declaration//GEN-END:variables + + + void playerCanvas_mouseClicked(MouseEvent e) { + playedCard = playerCanvas.getCard(e.getX(), e.getY()); + if (playedCard == null) { + return; + } + + int decision = Rules.canPlay(playedCard, currentPlie, playerCanvas.getHand(), atoutColor); + switch (decision) { + case Rules.RULES_MUST_FOLLOW: + playedCard = null; + statusBar.setText("Il faut suivre!"); + return; + case Rules.RULES_CANNOT_UNDERCUT: + playedCard = null; + statusBar.setText("Vous ne pouvez pas sous-couper!"); + return; + } + + jButtonAnounce.setEnabled(false); + setStatusBar(""); + centerCanvas.showCard(playedCard, 0); + playerCanvas.setMode(JassCanvas.MODE_STATIC); + playerCanvas.removeCard(playedCard); + assert threadToSignal != null; + synchronized (threadToSignal) { + threadToSignal.notify(); + } + } + + void centerCanvas_mouseClicked(MouseEvent e) { + System.out.println("Center click: " + e.getX() + " " + e.getY()); + drawnCardPosition = centerCanvas.getCard(e.getX(), e.getY()); + if (drawnCardPosition < 0) { + return; + } + centerCanvas.setMode(CanvasCenter.MODE_DRAW_TEAMS); // on ne peut plus choisir une carte + setStatusBar(""); // remove "veuillez tirer..." + assert threadToSignal != null; + synchronized (threadToSignal) { + threadToSignal.notify(); + } + } + + //Fait un repaint des différents canvas + public void repaint(int nbr) { + if ((nbr & 1) == 1) + playerCanvas.repaint(); + if ((nbr & 2) == 2) + leftCanvas.repaint(); + if ((nbr & 4) == 4) + topCanvas.repaint(); + if ((nbr & 8) == 8) + rightCanvas.repaint(); + if ((nbr & 16) == 16) + centerCanvas.repaint(); + } + + public void disconnect() { + for (int i = 0; i < 4; i++) { + var canvas = getPlayerCanvas(i); + canvas.clearHand(); + canvas.setName(""); + canvas.setAtout(false); + canvas.setMode(JassCanvas.MODE_STATIC); + } + centerCanvas.resetCards(); + centerCanvas.setMode(CanvasCenter.MODE_PASSIVE); + removeLastPlie(); + lastPlieCanvas.hideAtout(); + setScore(0, 0); + + statusBar.setText("Déconnexion"); + jButtonConnect.setText("Connexion"); + } + + // attribue ses cartes aux joueur + public void setPlayerHand(List hand) { + playerCanvas.setHand(hand); + } + + @Override + public int chooseAtout(boolean allowedToPass) { + DialogAtout da = new DialogAtout(this, true, allowedToPass); + da.setLocationRelativeTo(this); + da.setVisible(true); + return da.getSelectedColor(); + } + + @Override + public void setAtout(int atout, int positionOfPlayerToChooseAtout) { + atoutColor = atout; + lastPlieCanvas.setAtout(atout); + for (int i = 0; i < 4; i++) { + var canvas = getPlayerCanvas(i); + canvas.setAtout(i == positionOfPlayerToChooseAtout); + } + } + + @Override + public void play(Plie currentPlie, Thread threadToSignal) { + this.currentPlie = currentPlie; + this.threadToSignal = threadToSignal; + setStatusBar("A vous de jouer ..."); + anoucementPressed = false; + setAnouncementEnabled(true); + playerCanvas.setMode(JassCanvas.MODE_PLAY); + } + + @Override + public Card getPlayedCard() { + return playedCard; + } + + @Override + public void setPlayedCard(Card card, int playerPosition) { + var canvas = getPlayerCanvas(playerPosition); + canvas.removeCard(); + centerCanvas.showCard(card, playerPosition); + } + + @Override + public void setOtherPlayersHands(int numberOfCards) { + var hand = new ArrayList(); + for (int i = 0; i < numberOfCards; i++) { + hand.add(new Card(Card.BACK_NUMBER)); + } + leftCanvas.setHand(hand); + topCanvas.setHand(hand); + rightCanvas.setHand(hand); + } + + // prépare l'écran pour une nouvelle partie + @Override + public void prepareGame() { + centerCanvas.resetCards(); + centerCanvas.setMode(CanvasCenter.MODE_GAME); // mode de jeu + lastPlieCanvas.hideAtout(); + setStatusBar(""); + removeLastPlie(); + } + + // ramasse la plie + @Override + public void setPlieOwner(int playerPosition) { + if (playerPosition == 0) { + setStatusBar("Vous avez pris la plie"); + } else { + statusBar.setText(getPlayerCanvas(playerPosition).getName() + " a pris la plie"); + } + lastPlieCanvas.setLastPlie(centerCanvas.getShownCards()); + centerCanvas.resetCards(); + } + + void removeLastPlie() { + lastPlieCanvas.setLastPlie(Collections.emptyList()); + } + + public void setScore(int ourScore, int opponentScore) { + lastPlieCanvas.setScores(ourScore, opponentScore); + } + + @Override + public boolean hasPlayerAnounced() { + return anoucementPressed; + } + + @Override + public void displayStatusMessage(String message) { + setStatusBar(message); + } + + @Override + public void displayGameResult(Team winningTeam) { + centerCanvas.setMode(CanvasCenter.MODE_PASSIVE); + playerCanvas.setMode(JassCanvas.MODE_STATIC); + DialogInfo diw = new DialogInfo(this, false); + diw.setLocationRelativeTo(this); + diw.setText(0, "L'équipe de"); + diw.setText(1, winningTeam.getPlayer(0).getName() + " & " + winningTeam.getPlayer(1).getName()); + diw.setText(2, "a gagné la partie!"); + setStatusBar("Partie terminée"); + diw.setVisible(true); + } + + @Override + public boolean getNewGame() { + DialogNewPart dnp = new DialogNewPart(this, true); + dnp.setLocationRelativeTo(this); + dnp.setVisible(true); + return dnp.getNewPart(); + } + + @Override + public void canceledGame(int leavingPlayerPosition) { + var di = new DialogInfo(this, false); + di.setLocationRelativeTo(this); + var canvas = getPlayerCanvas(leavingPlayerPosition); + di.setText(0, canvas.getName() + " a quitté le jeu."); + di.setText(1, "La partie est interrompue."); + setStatusBar("Partie interrompue"); + disconnect(); + di.setVisible(true); + } + + void setStatusBar(String text) { + statusBar.setText(text); + } + + void setAnouncementEnabled(boolean b) { + jButtonAnounce.setEnabled(b); + } + + void setGameId(int gameId) { + String title = APP_TITLE + " - Jeu " + gameId; + setTitle(title); + } + + @Override + public void setPlayer(BasePlayer player, int relativePosition) { + JassCanvas canvas = getPlayerCanvas(relativePosition); + canvas.setName(player.getName()); + canvas.repaint(); + } + + @Override + public void showUi(boolean enable) { + setLocationRelativeTo(null); + setVisible(enable); + } + + @Override + public TeamSelectionMethod chooseTeamSelectionMethod() { + DialogTeamChoice dtc = new DialogTeamChoice(this, true); + dtc.setLocationRelativeTo(this); + dtc.setVisible(true); + return dtc.hasard ? TeamSelectionMethod.RANDOM : TeamSelectionMethod.MANUAL; + } + + @Override + public void prepareTeamDrawing(boolean firstAttempt) { + System.out.println("frame: prepare team drawing"); + playerCanvas.clearHand(); + leftCanvas.clearHand(); + rightCanvas.clearHand(); + topCanvas.clearHand(); + centerCanvas.resetCards(); + centerCanvas.setMode(CanvasCenter.MODE_DRAW_TEAMS); + removeLastPlie(); + lastPlieCanvas.setAtout(4); + setScore(0, 0); + repaint(31); + } + + @Override + public void drawCard(Thread thread) { + centerCanvas.setMode(CanvasCenter.MODE_PICK_CARD); + setStatusBar("Veuillez choisir une carte"); + this.threadToSignal = thread; + } + + @Override + public int getDrawnCardPosition() { + return drawnCardPosition; + } + + @Override + public void setDrawnCard(int playerPosition, int cardPosition, Card card) { + centerCanvas.drawCard(cardPosition); + var canvas = getPlayerCanvas(playerPosition); + canvas.setHand(Collections.singletonList(card)); + } + + @Override + public BasePlayer choosePartner(List partners) { + DialogPartnerChoice dpc = new DialogPartnerChoice(this, true); + dpc.setLocationRelativeTo(this); + partners.forEach(dpc::addPlayer); + dpc.setVisible(true); + return dpc.getSelectedPlayer(); + } + + private JassCanvas getPlayerCanvas(int relativePosition) { + switch (relativePosition) { + case 0: + return playerCanvas; + case 1: + return rightCanvas; + case 2: + return topCanvas; + case 3: + return leftCanvas; + default: + throw new IndexOutOfBoundsException("Unknown canvas " + relativePosition); + } + } +} diff --git a/src/main/java/com/leflat/jass/client/JassPlayer.java b/src/main/java/com/leflat/jass/client/JassPlayer.java new file mode 100644 index 0000000..84e5194 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/JassPlayer.java @@ -0,0 +1,223 @@ +/* + * Created for and by the FLAT(r) + */ +package com.leflat.jass.client; + +import com.leflat.jass.common.*; + +import java.util.*; +import java.util.stream.Collectors; + +public class JassPlayer implements IPlayer, IRemotePlayer { + private RemoteController controller; + private IJassUi frame; + private int id; + private Map playersPositions = new HashMap<>(); + private Map players = new HashMap<>(); + private List playerHand = new ArrayList<>(); + + + public JassPlayer() { + controller = new RemoteController(this); + frame = new JassFrame(this); + frame.showUi(true); + + // TODO: remove (DEBUG) + Random rnd = new Random(); + connect(String.valueOf(rnd.nextInt(100)), "localhost", 0); + } + + @Override + public void setPlayerInfo(BasePlayer player) { + try { + var relativePosition = getInitialRelativePosition(player); + playersPositions.put(player.getId(), relativePosition); + players.put(player.getId(), player); + frame.setPlayer(player, relativePosition); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public TeamSelectionMethod chooseTeamSelectionMethod() { + return frame.chooseTeamSelectionMethod(); + } + + @Override + public void prepareTeamDrawing(boolean firstAttempt) { + frame.prepareTeamDrawing(firstAttempt); + } + + @Override + public int drawCard() { + synchronized (controller) { + frame.drawCard(controller); + try { + controller.wait(); + + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return frame.getDrawnCardPosition(); + } + + @Override + public void setCard(BasePlayer player, int cardPosition, Card card) { + try { + var relativePosition = playersPositions.get(player.getId()); + frame.setDrawnCard(relativePosition, cardPosition, card); + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + } + + @Override + public void setPlayersOrder(List playerIds) { + int ownPosition = playerIds.indexOf(id); + playersPositions.clear(); + for (int i = 0; i < playerIds.size(); i++) { + int playerId = playerIds.get(i); + playersPositions.put(playerId, (i - ownPosition + 4) % 4); + } + for (var player : players.values()) { + try { + frame.setPlayer(player, playersPositions.get(player.getId())); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public int choosePartner() { + var partners = players.values().stream().filter(p -> p.getId() != id).collect(Collectors.toList()); + return frame.choosePartner(partners).getId(); + } + + @Override + public void setHand(List cards) { + playerHand.clear(); + Card.sort(cards); + playerHand.addAll(cards); + frame.prepareGame(); + frame.setPlayerHand(cards); + frame.setOtherPlayersHands(9); + } + + @Override + public int chooseAtout(boolean first) { + return frame.chooseAtout(first); + } + + @Override + public void setAtout(int color, BasePlayer firstToPlay) { + frame.setAtout(color, playersPositions.get(firstToPlay.getId())); + } + + @Override + public Card play(int currentColor, int highestRank, boolean cut) { + synchronized (controller) { + frame.play(new Plie(currentColor, highestRank, cut), controller); + try { + controller.wait(); + + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return frame.getPlayedCard(); + } + + @Override + public void setPlayedCard(BasePlayer player, Card card) { + frame.setPlayedCard(card, playersPositions.get(player.getId())); + } + + @Override + public void setPlieOwner(BasePlayer player) { + frame.setPlieOwner(playersPositions.get(player.getId())); + } + + @Override + public void setScores(int score, int opponentScore) { + frame.setScore(score, opponentScore); + } + + @Override + public List getAnoucement() { + if (!frame.hasPlayerAnounced()) { + return Collections.emptyList(); + } + // TODO: handle stoeck properly (only communicated when playing the second card) + return Anouncement.findAnouncements(playerHand); + } + + @Override + public void setAnouncement(BasePlayer player, List anouncements) { + StringBuilder sb; + if (player.getId() == id) { + sb = new StringBuilder("Vous annoncez "); + } else { + sb = new StringBuilder(players.get(player.getId()).getName()).append(" annonce "); + } + sb.append(anouncements.get(0)); + for (int i = 1; i < anouncements.size(); i++) { + sb.append(" et ").append(anouncements.get(i)); + } + frame.displayStatusMessage(sb.toString()); + } + + @Override + public void setGameResult(Team winningTeam) { + var p0 = winningTeam.getPlayer(0); + p0.setName(players.get(p0.getId()).getName()); + var p1 = winningTeam.getPlayer(1); + p1.setName(players.get(p1.getId()).getName()); + frame.displayGameResult(winningTeam); + } + + @Override + public boolean getNewGame() { + return frame.getNewGame(); + } + + @Override + public void playerLeft(BasePlayer player) { + frame.canceledGame(playersPositions.get(player.getId())); + controller.disconnect(); + } + + @Override + public int connect(String name, String host, int gameId) { + var connectionInfo = controller.connect(host, gameId, name); + if (connectionInfo.error != ConnectionError.CONNECTION_SUCCESSFUL) { + return connectionInfo.error; + } + id = connectionInfo.playerId; + playersPositions.put(id, 0); + controller.start(); + try { + frame.setPlayer(new ClientPlayer(id, name), 0); + } catch (Exception e) { + e.printStackTrace(); + } + return connectionInfo.gameId; + } + + @Override + public boolean disconnect() { + controller.disconnect(); + return true; + } + + @Override + public boolean isConnected() { + return controller.isConnected(); + } + + private int getInitialRelativePosition(BasePlayer player) { + return (player.getId() - id + 4) % 4; + } +} diff --git a/src/main/java/com/leflat/jass/client/RemoteController.java b/src/main/java/com/leflat/jass/client/RemoteController.java new file mode 100644 index 0000000..7af44a8 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/RemoteController.java @@ -0,0 +1,341 @@ +package com.leflat.jass.client; + +import com.leflat.jass.common.*; +import com.leflat.jass.server.PlayerLeftExpection; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class RemoteController extends Thread { + private static final int PORT_NUM = 23107; + private static final int CONNECTION_TIMEOUT_MS = 10000; + private boolean running = false; + private int playerId; + private IPlayer player; + private Socket clientSocket = new Socket(); + private PrintWriter os; + private BufferedReader is; + + public RemoteController(IPlayer player) { + this.player = player; + } + + public ClientConnectionInfo connect(String host, int requestedGameId, String name) { + try { + clientSocket.connect(new InetSocketAddress(host, PORT_NUM), CONNECTION_TIMEOUT_MS); + + os = new PrintWriter(clientSocket.getOutputStream(), false); + is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + System.out.println("Connection successful"); + } catch (SocketTimeoutException e) { + System.out.println("Server does not answer"); + return new ClientConnectionInfo(ConnectionError.SERVER_UNREACHABLE); + } catch (IOException e) { + System.out.println("Unable to create socket: " + e); + return new ClientConnectionInfo(ConnectionError.SERVER_UNREACHABLE); + } + + sendRawMessage(String.valueOf(requestedGameId)); + + try { + int receivedGameId = Integer.parseInt(receiveRawMessage()); + if (receivedGameId < 0) { + return new ClientConnectionInfo(receivedGameId); + } + playerId = Integer.parseInt(receiveRawMessage()); + sendMessage(Collections.singletonList(name)); + return new ClientConnectionInfo(playerId, receivedGameId, ConnectionError.CONNECTION_SUCCESSFUL); + } catch (ServerDisconnectedException e) { + e.printStackTrace(); + } + return new ClientConnectionInfo(ConnectionError.SERVER_UNREACHABLE); + } + + public boolean isConnected() { + return clientSocket != null && !clientSocket.isClosed(); + } + + public void sendRawMessage(String message) { + os.println(message); + os.flush(); + System.out.println("Envoi au serveur : " + message); + } + + public void sendMessage(List message) { + String stringMessage = playerId + " " + String.join(" ", message); + sendRawMessage(stringMessage); + } + + public String receiveRawMessage() throws ServerDisconnectedException { + String message = null; + + // TODO: implementer timeout + exc + try { + message = is.readLine(); + } catch (IOException e) { + System.out.println("Error during reception"); + } + if (message != null) + System.out.println("Received : " + message); + else { + System.out.println("Server has left unexpectedly"); + throw new ServerDisconnectedException(); + } + return message; + } + + public void disconnect() { + try { + running = false; + clientSocket.close(); + } catch (IOException e) { + System.out.println("Error while closing socket"); + //System.exit(1); + } + } + + @Override + public void run() { + running = true; + System.out.println("Starting Listener..."); + while (running) { + try { + String message = receiveRawMessage(); + handleControllerMessage(message.split(" ")); + } catch (ServerDisconnectedException e) { + e.printStackTrace(); + running = false; + // TODO: player.serverDisconnected + } + } + System.out.println("Exiting listener"); + } + + private void handleControllerMessage(String[] message) { + int command = Integer.parseInt(message[0]); + List answer = new ArrayList<>(); + switch (command) { + case RemoteCommand.SET_PLAYER_INFO: + int playerId = Integer.parseInt(message[1]); + String name = message[2]; + try { + player.setPlayerInfo(new ClientPlayer(playerId, name)); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.CHOOSE_TEAM_SELECTION_METHOD: + try { + var choice = player.chooseTeamSelectionMethod(); + answer = Collections.singletonList(String.valueOf(choice)); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.PREPARE_TEAM_DRAWING: + try { + player.prepareTeamDrawing(true); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.DRAW_CARD: + try { + int position = player.drawCard(); + answer = Collections.singletonList(String.valueOf(position)); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_CARD: + int pId = Integer.parseInt(message[1]); + int cardPosition = Integer.parseInt(message[2]); + int cardNumber = Integer.parseInt(message[3]); + try { + player.setCard(new ClientPlayer(pId), cardPosition, new Card(cardNumber)); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.RESTART_TEAM_DRAWING: + try { + player.prepareTeamDrawing(false); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.CHOOSE_PARTNER: + try { + int partnerId = player.choosePartner(); + answer = Collections.singletonList(String.valueOf(partnerId)); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_PLAYERS_ORDER: + var order = Arrays.stream(message).skip(1).map(Integer::parseInt).collect(Collectors.toList()); + try { + player.setPlayersOrder(order); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_HAND: + var hand = Arrays.stream(message).skip(1).map(Integer::parseInt).map(Card::new).collect(Collectors.toList()); + try { + player.setHand(hand); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.CHOOSE_ATOUT: + case RemoteCommand.CHOOSE_ATOUT_SECOND: + try { + int atout = player.chooseAtout(command == RemoteCommand.CHOOSE_ATOUT); + answer = Collections.singletonList(String.valueOf(atout)); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_ATOUT: + try { + int atout = Integer.parseInt(message[1]); + var firstToPlay = new ClientPlayer(Integer.parseInt(message[2])); + player.setAtout(atout, firstToPlay); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.PLAY: + try { + var card = player.play(-1, -1, false); + answer = Collections.singletonList(String.valueOf(card.getNumber())); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_PLAYED_CARD: + try { + var p = new ClientPlayer(Integer.parseInt(message[1])); + var card = new Card(Integer.parseInt(message[2])); + player.setPlayedCard(p, card); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.PLAY_NEXT: + try { + int highestRank = Integer.parseInt(message[1]); + int currentColor = Integer.parseInt(message[2]); + boolean cut = message[3].equals("1"); + var playedCard = player.play(currentColor, highestRank, cut); + answer = Collections.singletonList(String.valueOf(playedCard.getNumber())); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_PLIE_OWNER: + try { + var pl = new ClientPlayer(Integer.parseInt(message[1])); + player.setPlieOwner(pl); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_SCORES: + try { + int ourScore = Integer.parseInt(message[1]); + int opponentScore = Integer.parseInt(message[2]); + player.setScores(ourScore, opponentScore); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + case RemoteCommand.GET_ANOUNCEMENTS: + try { + var anouncements = player.getAnoucement(); + answer.add(String.valueOf(anouncements.size())); + for (var an : anouncements) { + answer.add(String.valueOf(an.getType())); + answer.add(String.valueOf(an.getCard().getNumber())); + } + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_ANOUNCEMENTS: + try { + var p = new ClientPlayer(Integer.parseInt(message[1])); + int numberAnoucements = Integer.parseInt(message[2]); + List anouncements = new ArrayList<>(); + for (int i = 0; i < numberAnoucements; i++) { + var c = new Card(Integer.parseInt(message[4 + i * 2])); + anouncements.add(new Anouncement(Integer.parseInt(message[3 + i * 2]), c)); + } + player.setAnouncement(p, anouncements); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.SET_GAME_RESULT: + try { + var winningTeam = new Team(Integer.parseInt(message[1])); + winningTeam.addPlayer(new ClientPlayer(Integer.parseInt(message[2]))); + winningTeam.addPlayer(new ClientPlayer(Integer.parseInt(message[3]))); + player.setGameResult(winningTeam); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.GET_NEW_GAME: + try { + boolean newGame = player.getNewGame(); + answer = Collections.singletonList(newGame ? "1" : "0"); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + case RemoteCommand.PLAYER_LEFT: + try { + var p = new ClientPlayer(Integer.parseInt(message[1])); + player.playerLeft(p); + } catch (PlayerLeftExpection playerLeftExpection) { + playerLeftExpection.printStackTrace(); + return; + } + break; + default: + System.err.println("Unknown command " + command); + } + sendMessage(answer); + } +} diff --git a/src/main/java/com/leflat/jass/client/ServerDisconnectedException.java b/src/main/java/com/leflat/jass/client/ServerDisconnectedException.java new file mode 100644 index 0000000..2c59837 --- /dev/null +++ b/src/main/java/com/leflat/jass/client/ServerDisconnectedException.java @@ -0,0 +1,4 @@ +package com.leflat.jass.client; + +public class ServerDisconnectedException extends Exception { +} diff --git a/src/main/java/com/leflat/jass/common/Anouncement.java b/src/main/java/com/leflat/jass/common/Anouncement.java new file mode 100644 index 0000000..c441c0c --- /dev/null +++ b/src/main/java/com/leflat/jass/common/Anouncement.java @@ -0,0 +1,129 @@ +package com.leflat.jass.common; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class Anouncement { + public static final int STOECK = 0; + public static final int THREE_CARDS = 1; + public static final int FIFTY = 2; + public static final int HUNDRED = 3; + public static final int SQUARE = 4; + public static final int NELL_SQUARE = 5; + public static final int BOURG_SQUARE = 6; + + public static final int[] VALUES = {20, 20, 50, 100, 100, 150, 200}; + public static final String[] NAMES = {"stoeck", "3 cartes", "cinquante", "cent", "cent", "cent cinquante", "deux cents"}; + + public Anouncement(int type, Card card) { + this.type = type; + this.card = card; + } + + private int type; // 0: stöck, 1: 3 cartes, 2: cinquante, 3: cent, 4: carré + private Card card; // plus haute carte de l'annonce + + public int getType() { + return type; + } + + public Card getCard() { + return card; + } + + public int getValue() { + if (type == SQUARE) { + switch (card.getRank()) { + case Card.RANK_BOURG: + return 200; + case Card.RANK_NELL: + return 150; + default: + return 100; + } + } + return VALUES[type]; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(NAMES[type]); + if (type == STOECK) { + return sb.toString(); + } + if (type >= SQUARE) { + sb.append(" des ").append(Card.RANK_NAMES[card.getRank()]).append("s"); + } else { + switch (card.getRank()) { + case Card.RANK_DAME: + sb.append(" à la "); + break; + case Card.RANK_AS: + sb.append(" à l'"); + break; + default: + sb.append(" au "); + break; + } + sb.append(card.toString()); + } + return sb.toString(); + } + + public static List findAnouncements(List hand) { + var announcements = findSquares(hand); + announcements.addAll(findSuits(hand)); + return new ArrayList<>(announcements); + } + + public static boolean findStoeck(List hand, int atout) { + int queen = atout * 9 + Card.RANK_DAME; + int king = atout * 9 + Card.RANK_ROI; + var numbers = hand.stream().map(Card::getNumber).collect(Collectors.toList()); + return numbers.contains(queen) && numbers.contains(king); + } + + private static Collection findSquares(List hand) { + int[] rankCount = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + hand.forEach(c -> rankCount[c.getRank()]++); + var announcements = new ArrayList(); + for (int rank = Card.RANK_NELL; rank <= Card.RANK_AS; rank++) { + if (rankCount[rank] == 4) { + int type = SQUARE; + if (rank == Card.RANK_NELL) { + type = NELL_SQUARE; + } else if (rank == Card.RANK_BOURG) { + type = BOURG_SQUARE; + } + announcements.add(new Anouncement(type, new Card(rank, Card.COLOR_SPADE))); + } + } + return announcements; + } + + private static Collection findSuits(List hand) { + var announcements = new ArrayList(); + for (int i = 0; i < hand.size() - 2; i++) { + var firstCard = hand.get(i); + int color = firstCard.getColor(); + int j = i + 1; + int nbrCards = 1; + while ((j < hand.size()) && (hand.get(j).getColor() == color)) { + if (hand.get(j).getNumber() == (firstCard.getNumber() + j - i)) // si les cartes se suivent + nbrCards++; + j++; + } + if (nbrCards > 2) { // on a trouvé une suite + if (nbrCards > 5) { + nbrCards = 5; + } + announcements.add(new Anouncement(nbrCards - 2, hand.get(i + nbrCards - 1))); + System.out.println("Found suit: " + announcements.get(announcements.size() - 1)); + i = j - 1; + } + } + return announcements; + } +} diff --git a/src/main/java/com/leflat/jass/common/BasePlayer.java b/src/main/java/com/leflat/jass/common/BasePlayer.java new file mode 100644 index 0000000..5a55d53 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/BasePlayer.java @@ -0,0 +1,50 @@ +package com.leflat.jass.common; + +import java.util.ArrayList; +import java.util.List; + +public abstract class BasePlayer { + // Variables + protected String name; + protected int id; + protected Team team; + protected List anoucements = new ArrayList<>(); // annonces + + // Constructeur + public BasePlayer(int id) { + this.id = id; + } + + // Méthodes + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { this.name = name; } + + public Team getTeam() { + return team; + } + + public void setTeam(Team team) { + this.team = team; + } + + public void addAnouncement(Anouncement a) { + anoucements.add(a); + } + + public List getAnouncements() { + return anoucements; + } + + public void clearAnouncement() { + anoucements.clear(); + } + + public String toString() { return name; } +} diff --git a/src/main/java/com/leflat/jass/common/BrokenRuleException.java b/src/main/java/com/leflat/jass/common/BrokenRuleException.java new file mode 100644 index 0000000..6abdf62 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/BrokenRuleException.java @@ -0,0 +1,11 @@ +package com.leflat.jass.common; + +public class BrokenRuleException extends Exception { + int brokenRule; + + public BrokenRuleException(int brokenRule) { + this.brokenRule = brokenRule; + } + + public int getBrokenRule() { return brokenRule; } +} diff --git a/src/main/java/com/leflat/jass/common/Card.java b/src/main/java/com/leflat/jass/common/Card.java new file mode 100644 index 0000000..e763693 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/Card.java @@ -0,0 +1,124 @@ +package com.leflat.jass.common; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +/** + * ************************** REMARQUE ****************************** + * Les cartes sont en fait représentées par des int de 0 à 35 + * où "carte div 9" donne sa couleur et "carte mod 9" sa + * hauteur. La classe Card est donc utilisée ici que pour + * les méthodes statiques getColor et getHeight. + * *************************************************************** + */ + +public class Card { + public static final int DIAMOND_SEVEN = 19; + public static final int RANK_6 = 0; + public static final int RANK_7 = 1; + public static final int RANK_8 = 2; + public static final int RANK_NELL = 3; + public static final int RANK_10 = 4; + public static final int RANK_BOURG = 5; + public static final int RANK_DAME = 6; + public static final int RANK_ROI = 7; + public static final int RANK_AS = 8; + + public static final int COLOR_SPADE = 0; + public static final int COLOR_HEART = 1; + public static final int COLOR_DIAMOND = 2; + public static final int COLOR_CLUB = 3; + + public static final String[] RANK_NAMES = {"six", "sept", "huit", "nell", "dix", "bourg", "dame", "roi", "as"}; + public static final String[] COLOR_NAMES = {"pique", "coeur", "carreau", "trèfle"}; + + public static final int[] VALUES = {0, 0, 0, 0, 10, 2, 3, 4, 11}; + public static final int[] VALUES_ATOUT = {0, 0, 0, 14, 10, 20, 3, 4, 11}; + + public static final int BACK_NUMBER = 200; + private static final Card backCard = new Card(BACK_NUMBER); + + private int number; + + public Card(int cardNumber) { + number = cardNumber; + } + + public Card(int rank, int color) { + number = color * 9 + rank; + } + + public int getColor() { + return number / 9; + } + + public int getRank() { + return number % 9; + } + + public int getNumber() { + return number; + } + + public String toString() { + return RANK_NAMES[getRank()] + " de " + COLOR_NAMES[getColor()]; + } + + public int getValue() { + return getValue(-1); + } + + public int getValue(int atout) { + return atout == getColor() ? VALUES_ATOUT[getRank()] : VALUES[getRank()]; + } + + public boolean isBack() { + return number == BACK_NUMBER; + } + + public static Card getBack() { + return backCard; + } + + public static void sort(List cards) { + quickSortCards(cards, 0, cards.size() - 1); + } + + private static void quickSortCards(List cards, int min, int max) { + int i = min; + int j = max; + int x = cards.get((min + max) / 2).getNumber(); + do { + while (cards.get(i).getNumber() < x) + i++; + while (x < cards.get(j).getNumber()) + j--; + if (i <= j) { + Collections.swap(cards, i, j); + i++; + j--; + } + } while (i <= j); + if (min < j) + quickSortCards(cards, min, j); + if (i < max) + quickSortCards(cards, i, max); + } + + public static List shuffle(int number) { + List cards = new ArrayList<>(); + for (int i = 0; i < number; i++) { + cards.add(new Card(i)); + } + Random rand = new Random(); + for (int i = 35; i > 0; i--) { + int j = rand.nextInt(i + 1); + if (i != j) { + Collections.swap(cards, j, i); + } + } + return cards; + } +} diff --git a/src/main/java/com/leflat/jass/common/ClientConnectionInfo.java b/src/main/java/com/leflat/jass/common/ClientConnectionInfo.java new file mode 100644 index 0000000..9b18d5d --- /dev/null +++ b/src/main/java/com/leflat/jass/common/ClientConnectionInfo.java @@ -0,0 +1,19 @@ +package com.leflat.jass.common; + +public class ClientConnectionInfo { + public int playerId; + public int gameId; + public int error; + + public ClientConnectionInfo(int playerId, int gameId, int error) { + this.playerId = playerId; + this.gameId = gameId; + this.error = error; + } + + public ClientConnectionInfo(int error) { + this.error = error; + this.gameId = -1; + this.playerId = -1; + } +} diff --git a/src/main/java/com/leflat/jass/common/ConnectionError.java b/src/main/java/com/leflat/jass/common/ConnectionError.java new file mode 100644 index 0000000..4228dfa --- /dev/null +++ b/src/main/java/com/leflat/jass/common/ConnectionError.java @@ -0,0 +1,8 @@ +package com.leflat.jass.common; + +public abstract class ConnectionError { + public static final int CONNECTION_SUCCESSFUL = 0; + public static final int UNKNOWN_GAME = -1; + public static final int GAME_FULL = -2; + public static final int SERVER_UNREACHABLE = -3; +} diff --git a/src/main/java/com/leflat/jass/common/IJassUi.java b/src/main/java/com/leflat/jass/common/IJassUi.java new file mode 100644 index 0000000..488cef3 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/IJassUi.java @@ -0,0 +1,51 @@ +package com.leflat.jass.common; + +import java.util.List; + +public interface IJassUi { + void setPlayer(BasePlayer player, int relativePosition) throws Exception; + + void showUi(boolean enable); + + TeamSelectionMethod chooseTeamSelectionMethod(); + + void prepareTeamDrawing(boolean firstAttempt); + + void drawCard(Thread thread); + + int getDrawnCardPosition(); + + void setDrawnCard(int playerPosition, int cardPosition, Card card) throws IndexOutOfBoundsException; + + BasePlayer choosePartner(List players); + + void setPlayerHand(List hand); + + int chooseAtout(boolean allowedToPass); + + void setAtout(int atout, int positionOfPlayerToChooseAtout); + + void play(Plie currentPlie, Thread threadToSignal); + + Card getPlayedCard(); + + void setPlayedCard(Card card, int playerPosition); + + void setOtherPlayersHands(int numberOfCards); + + void prepareGame(); + + void setPlieOwner(int playerPosition); + + void setScore(int ourScore, int opponentScore); + + boolean hasPlayerAnounced(); + + void displayStatusMessage(String message); + + void displayGameResult(Team winningTeam); + + boolean getNewGame(); + + void canceledGame(int leavingPlayerPosition); +} diff --git a/src/main/java/com/leflat/jass/common/IPlayer.java b/src/main/java/com/leflat/jass/common/IPlayer.java new file mode 100644 index 0000000..5be8e07 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/IPlayer.java @@ -0,0 +1,45 @@ +package com.leflat.jass.common; + +import com.leflat.jass.server.PlayerLeftExpection; + +import java.util.List; + +public interface IPlayer { + void setPlayerInfo(BasePlayer player) throws PlayerLeftExpection; + + TeamSelectionMethod chooseTeamSelectionMethod() throws PlayerLeftExpection; + + void prepareTeamDrawing(boolean firstAttempt) throws PlayerLeftExpection; + + int drawCard() throws PlayerLeftExpection; + + void setCard(BasePlayer player, int cardPosition, Card card) throws PlayerLeftExpection; + + void setPlayersOrder(List playerIds) throws PlayerLeftExpection; + + int choosePartner() throws PlayerLeftExpection; + + void setHand(List cards) throws PlayerLeftExpection; + + int chooseAtout(boolean first) throws PlayerLeftExpection; + + void setAtout(int color, BasePlayer firstToPlay) throws PlayerLeftExpection; + + Card play(int currentColor, int highestRank, boolean cut) throws PlayerLeftExpection; + + void setPlayedCard(BasePlayer player, Card card) throws PlayerLeftExpection; + + void setPlieOwner(BasePlayer player) throws PlayerLeftExpection; + + void setScores(int score, int opponentScore) throws PlayerLeftExpection; + + List getAnoucement() throws PlayerLeftExpection; + + void setAnouncement(BasePlayer player, List anouncements) throws PlayerLeftExpection; + + void setGameResult(Team winningTeam) throws PlayerLeftExpection; + + boolean getNewGame() throws PlayerLeftExpection; + + void playerLeft(BasePlayer player) throws PlayerLeftExpection; +} diff --git a/src/main/java/com/leflat/jass/common/IRemotePlayer.java b/src/main/java/com/leflat/jass/common/IRemotePlayer.java new file mode 100644 index 0000000..3ca9513 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/IRemotePlayer.java @@ -0,0 +1,9 @@ +package com.leflat.jass.common; + +public interface IRemotePlayer { + int connect(String name, String host, int gameId); + + boolean disconnect(); + + boolean isConnected(); +} diff --git a/src/main/java/com/leflat/jass/common/Plie.java b/src/main/java/com/leflat/jass/common/Plie.java new file mode 100644 index 0000000..a76a8a2 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/Plie.java @@ -0,0 +1,87 @@ +package com.leflat.jass.common; + + +public class Plie { + public int highest; // la plus haute carte de la plie (celle qui tient la plie) + public int color; // la couleur demandée + public int score; // la valeur de la plie + public boolean cut; // 0 : pas coupé, 1 : coupé + public BasePlayer owner = null; // iD de celui qui tient la plie + + public Plie(Card card, int atout, BasePlayer owner) { + color = card.getColor(); + highest = card.getRank(); + score = card.getValue(atout); + this.owner = owner; + cut = false; + } + + public Plie(int color, int highestRank, boolean cut) { + this.color = color; + this.highest = highestRank; + this.cut = cut; + } + + public void playCard(Card card, int atout, BasePlayer player) throws BrokenRuleException { + if (card.getColor() == atout) { + if (color != atout) { // si on coupe + if (cut) { // already cut + switch (card.getRank()) { // surcoupe + case Card.RANK_NELL: // si on joue le nell + if (highest == Card.RANK_BOURG) { + throw new BrokenRuleException(Rules.RULES_CANNOT_UNDERCUT); + } + highest = card.getRank(); + owner = player; + break; + case Card.RANK_BOURG: // si on joue le bourg + highest = card.getRank(); + owner = player; + break; + default: // sinon + if ((highest == Card.RANK_BOURG) || + (highest == Card.RANK_NELL) || + (card.getRank() < highest)) { + throw new BrokenRuleException(Rules.RULES_CANNOT_UNDERCUT); + } + highest = card.getRank(); + owner = player; + break; + } + // else souscoupe => nothing to do + } else { // first to cut + cut = true; + highest = card.getRank(); + owner = player; + } + } else { // si c'est joué atout + switch (card.getRank()) { + case Card.RANK_NELL: // si on joue le nell + if (highest != Card.RANK_BOURG) { + highest = card.getRank(); + owner = player; + } + break; + case Card.RANK_BOURG: // si on joue le bourg + highest = card.getRank(); + owner = player; + break; + default: // sinon + if ((highest != Card.RANK_BOURG) && + (highest != Card.RANK_NELL) && + (card.getRank() > highest)) { + highest = card.getRank(); + owner = player; + } + break; + } + } + } else if (card.getColor() == color) { + if ((card.getRank() > highest) && !cut) { + highest = card.getRank(); + owner = player; + } + } + score += card.getValue(atout); // augmente le score de la plie + } +} diff --git a/src/main/java/com/leflat/jass/common/RemoteCommand.java b/src/main/java/com/leflat/jass/common/RemoteCommand.java new file mode 100644 index 0000000..aaccf78 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/RemoteCommand.java @@ -0,0 +1,26 @@ +package com.leflat.jass.common; + +public abstract class RemoteCommand { + public static final int SET_PLAYER_INFO = 2; + public static final int CHOOSE_TEAM_SELECTION_METHOD = 3; + public static final int PREPARE_TEAM_DRAWING = 4; + public static final int DRAW_CARD = 5; + public static final int SET_CARD = 6; + public static final int RESTART_TEAM_DRAWING = 7; + public static final int SET_PLAYERS_ORDER = 8; + public static final int CHOOSE_PARTNER = 9; + public static final int SET_HAND = 10; + public static final int CHOOSE_ATOUT = 11; + public static final int CHOOSE_ATOUT_SECOND = 12; + public static final int SET_ATOUT = 13; + public static final int PLAY = 14; + public static final int SET_PLAYED_CARD = 15; + public static final int PLAY_NEXT = 16; + public static final int SET_PLIE_OWNER = 17; + public static final int SET_SCORES = 18; + public static final int GET_ANOUNCEMENTS = 19; + public static final int SET_ANOUNCEMENTS = 20; + public static final int SET_GAME_RESULT = 21; + public static final int GET_NEW_GAME = 22; + public static final int PLAYER_LEFT = 23; +} diff --git a/src/main/java/com/leflat/jass/common/Rules.java b/src/main/java/com/leflat/jass/common/Rules.java new file mode 100644 index 0000000..95283b9 --- /dev/null +++ b/src/main/java/com/leflat/jass/common/Rules.java @@ -0,0 +1,68 @@ +package com.leflat.jass.common; + +import java.util.List; + +public class Rules { + public static final int RULES_OK = 0; + public static final int RULES_MUST_FOLLOW = 1; + public static final int RULES_CANNOT_UNDERCUT = 2; + + + public static int canPlay(Card card, Plie currentPlie, List hand, int atoutColor) { + if (currentPlie.color < 0) { + return RULES_OK; + } + if (card.getColor() == currentPlie.color) { + return RULES_OK; + } + if (card.getColor() == atoutColor) { + if (!currentPlie.cut) { + return RULES_OK; + } + + switch (card.getRank()) { + case Card.RANK_BOURG: + return RULES_OK; + case Card.RANK_NELL: + if (currentPlie.highest == Card.RANK_BOURG) { + return RULES_CANNOT_UNDERCUT; // on ne peut pas sous-couper + } + return RULES_OK; // nell sans bourg : ok! + default: + if ((currentPlie.highest == Card.RANK_BOURG) || + (currentPlie.highest == Card.RANK_NELL) || + (currentPlie.highest > card.getRank())) { + return RULES_CANNOT_UNDERCUT; // on ne peut pas sous-couper + } + return RULES_OK; + } + } else { // carte jouée pas d'atout + if (!hasColor(hand, currentPlie.color)) { + return RULES_OK; + } + // The player owns cards from the required color + if ((currentPlie.color == atoutColor) && hasBourSec(hand, atoutColor)) { + return RULES_OK; // bourg sec -> ok + } + return RULES_MUST_FOLLOW; + } + } + + public static boolean hasColor(List hand, int color) { + return hand.stream().anyMatch(c -> c.getColor() == color); + } + + public static boolean hasBourSec(List hand, int atout) { + int numberAtouts = 0; + boolean bourg = false; + for (var card : hand) { + if (card.getColor() == atout) { + numberAtouts += 1; + if (card.getRank() == Card.RANK_BOURG) { + bourg = true; + } + } + } + return bourg && numberAtouts == 1; + } +} diff --git a/src/main/java/com/leflat/jass/common/Team.java b/src/main/java/com/leflat/jass/common/Team.java new file mode 100644 index 0000000..b00ea3a --- /dev/null +++ b/src/main/java/com/leflat/jass/common/Team.java @@ -0,0 +1,57 @@ +package com.leflat.jass.common; + +import java.util.ArrayList; +import java.util.List; + +public class Team { + // Variables + private int currentScore; + private List players = new ArrayList<>(); + private int id; + public static final int WINNING_SCORE = 1500; + + // Constructeur + public Team(int id) { + currentScore = 0; + this.id = id; + } + + // Méthodes + public void resetScore() { + currentScore = 0; + } + + public void reset() { + players.clear(); + resetScore(); + } + + public void addScore(int score) { + currentScore += score; + } + + public boolean hasWon() { + return currentScore >= WINNING_SCORE; + } + + public int getScore() { + return currentScore; + } + + public BasePlayer getPlayer(int i) { + return players.get(i); + } + + public void addPlayer(BasePlayer p) { + players.add(p); + p.setTeam(this); + } + + public void addAnnoucementScore(List anouncements, int atout) { + for (var a : anouncements) { + currentScore += atout == Card.COLOR_SPADE ? 2 * a.getValue() : a.getValue(); + } + } + + public int getId() { return id; } +} diff --git a/src/main/java/com/leflat/jass/common/TeamSelectionMethod.java b/src/main/java/com/leflat/jass/common/TeamSelectionMethod.java new file mode 100644 index 0000000..a97dd1a --- /dev/null +++ b/src/main/java/com/leflat/jass/common/TeamSelectionMethod.java @@ -0,0 +1,5 @@ +package com.leflat.jass.common; + +public enum TeamSelectionMethod { + RANDOM, MANUAL +} diff --git a/src/main/java/com/leflat/jass/server/ConnectionListener.java b/src/main/java/com/leflat/jass/server/ConnectionListener.java new file mode 100644 index 0000000..8cc174b --- /dev/null +++ b/src/main/java/com/leflat/jass/server/ConnectionListener.java @@ -0,0 +1,86 @@ +package com.leflat.jass.server; + +import com.leflat.jass.common.ConnectionError; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.*; + +public class ConnectionListener extends Thread { + private ServerSocket serverSocket; + private Map games = new HashMap<>(); + private boolean running = false; + + public ConnectionListener(int port) throws IOException { + serverSocket = new ServerSocket(port); + + // TODO: remove (DEBUG) + games.put(0, new GameController(0)); + } + + @Override + public void run() { + System.out.println("Jass server running on port " + serverSocket.getLocalPort()); + running = true; + while (running) { + try { + var clientSocket = serverSocket.accept(); + handleNewConnection(clientSocket); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private void handleNewConnection(Socket clientSocket) throws IOException { + var network = new PlayerNetwork(clientSocket); + + try { + var game = selectGame(network); + if (game == null) { + return; + } + + var newPlayer = new RemotePlayer(game.getNbrPlayers(), network); + game.addPlayer(newPlayer); + + if (game.isGameFull()) { + game.start(); + } + } catch (PlayerLeftExpection ignored) { + System.err.println("Error: client left while attempting to connect"); + } + } + + private GameController selectGame(PlayerNetwork network) throws PlayerLeftExpection { + GameController game; + String connectionMessage = network.receiveRawMessage(); + int gameId = Integer.parseInt(connectionMessage); + if (gameId < 0) { + int newGameId = getRandomGameId(); + network.sendMessage(String.valueOf(newGameId)); + game = new GameController(newGameId); + games.put(newGameId, game); + } else { + if (!games.containsKey(gameId)) { + network.sendMessage(String.valueOf(ConnectionError.UNKNOWN_GAME)); + System.err.println("Error: unknown game id " + gameId); + return null; + } + game = games.get(gameId); + if (game.isGameFull()) { + network.sendMessage(String.valueOf(ConnectionError.GAME_FULL)); + System.err.println("Error: attempting to enter full game " + gameId); + return null; + } + network.sendMessage(String.valueOf(gameId)); + } + return game; + } + + private int getRandomGameId() { + Random rnd = new Random(); + return rnd.nextInt(999999); + } +} diff --git a/src/main/java/com/leflat/jass/server/GameController.java b/src/main/java/com/leflat/jass/server/GameController.java new file mode 100644 index 0000000..f078ec7 --- /dev/null +++ b/src/main/java/com/leflat/jass/server/GameController.java @@ -0,0 +1,386 @@ +package com.leflat.jass.server; + +import com.leflat.jass.common.*; + +import java.util.*; +import java.util.stream.Collectors; + +public class GameController extends Thread { + private int gameId; + private List players = new ArrayList<>(); + private Team[] teams = new Team[2]; // les 2 équipes + private int atout; + + public GameController(int id) { + this.gameId = id; + for (int i = 0; i < 2; i++) { // création des équipes + teams[i] = new Team(i); + } + } + + public void addPlayer(RemotePlayer newPlayer) throws PlayerLeftExpection { + assert players.size() < 4; + for (var p : players) { + p.setPlayerInfo(newPlayer); + newPlayer.setPlayerInfo(p); + } + players.add(newPlayer); + } + + public int getNbrPlayers() { + return players.size(); + } + + public boolean isGameFull() { + return players.size() == 4; + } + + public int getGameId() { + return gameId; + } + + @Override + public void run() { + System.out.println("Starting game room " + gameId); + + try { + boolean playAnotherGame; + do { + chooseTeam(); // détermine les équipes + + playOneGame(); + + playAnotherGame = players.get(0).getNewGame(); + + for (Team team : teams) { + team.resetScore(); + } + } while (playAnotherGame); + + } catch (PlayerLeftExpection e) { + var disconnectedPlayer = getPlayerById(e.getPlayerId()); + players.remove(disconnectedPlayer); + for (var player : players) { + try { + player.playerLeft(disconnectedPlayer); + } catch (PlayerLeftExpection ee) { + System.err.println("Player " + ee.getPlayerId() + " also left."); + } + } + } catch (BrokenRuleException e) { + System.err.println("Error: Jass rule broken: " + e.getBrokenRule()); + } + + System.out.println("Game " + gameId + " ended"); + } + + private void playOneGame() throws PlayerLeftExpection, BrokenRuleException { + int firstToPlay = drawCards(); + int nextPlayer; + do { + atout = chooseAtout(firstToPlay); // choisit l'atout + nextPlayer = firstToPlay; + + int plieNumber = 0; + while ((plieNumber < 9) && (nextPlayer >= 0)) { // fait jouer les 9 plies + nextPlayer = playPlie(nextPlayer); + plieNumber++; + } + + if (nextPlayer >= 0) { // si personne n'a gagné : on continue normalement + // 5 de der + players.get(nextPlayer).getTeam().addScore(atout == Card.COLOR_SPADE ? 10 : 5); + + for (var p : players) { // envoie le score + var opponentTeam = teams[(p.getTeam().getId() + 1) % 2]; + p.setScores(p.getTeam().getScore(), opponentTeam.getScore()); + } + + /* waits a few seconds so that the players can see the last + * cards and the score */ + waitSec(2); + + firstToPlay = (firstToPlay + 1) % 4; + drawCards(); + } else { // si une équipe a gagné + for (var p : players) { // envoie le score + var opponentTeam = teams[(p.getTeam().getId() + 1) % 2]; + p.setScores(p.getTeam().getScore(), opponentTeam.getScore()); + } + } + + // répète jusqu'à ce qu'on gagne + } while (!teams[0].hasWon() && !teams[1].hasWon()); + + // Sends the winner to all player + var winners = teams[0].hasWon() ? teams[0] : teams[1]; + for (var p : players) { + p.setGameResult(winners); + } + + /* waits a few seconds so that the players can see all the cards */ + waitSec(4); + } + + int playPlie(int startingPlayer) throws PlayerLeftExpection, BrokenRuleException { + var player = players.get(startingPlayer); + var cardPlayed = player.play(-1, -1, false); + var currentPlie = new Plie(cardPlayed, atout, player); + + for (int i = 1; i < 4; i++) { // envoie la carte jouée aux autres + players.get((startingPlayer + i) % 4).setPlayedCard(player, cardPlayed); + } + + player.getAnoucement(); + + for (int i = 1; i < 4; i++) { // demande de jouer + player = players.get((startingPlayer + i) % 4); + // TODO: replace arguments with currentPlie + cardPlayed = player.play(currentPlie.color, currentPlie.highest, currentPlie.cut); + + player.getAnoucement(); + + for (int j = 1; j < 4; j++) { // envoie la carte jouée aux autres + players.get((startingPlayer + i + j) % 4).setPlayedCard(player, cardPlayed); + } + + currentPlie.playCard(cardPlayed, atout, player); + } + + /* now everybody has played ... */ + + /* waits a few seconds so that the players can see all the cards */ + waitSec(1.5f); + + // communique qui a pris la plie + for (var p : players) { + p.setPlieOwner(currentPlie.owner); + } + + // choix et comptabilisation des annonces + processAnoucements(); + + // comptabilisation des points + currentPlie.owner.getTeam().addScore(atout == Card.COLOR_SPADE ? currentPlie.score * 2 : currentPlie.score); + + if (teams[0].hasWon() || teams[1].hasWon()) { + return -1; + } + + return getPlayerPosition(currentPlie.owner); + } + + void processAnoucements() throws PlayerLeftExpection { + BasePlayer playerWithStoeck = null; + int highestAnouncement = -1; + int highestRank = Card.RANK_6; + Team anouncingTeam = null; // joueur qui a la plus grosse annonce + for (var p : players) { + for (var anouncement : p.getAnouncements()) { + System.out.println(p + " announce : " + anouncement); + if (anouncement.getType() == Anouncement.STOECK) { + playerWithStoeck = p; + continue; + } + if (anouncement.getType() > highestAnouncement) { + // plus grosse annonce + anouncingTeam = p.getTeam(); + highestAnouncement = anouncement.getType(); + highestRank = anouncement.getCard().getRank(); + } else if ((anouncement.getType() == highestAnouncement) && + (anouncement.getCard().getRank() > highestRank)) { + // même annonce plus haute + anouncingTeam = p.getTeam(); + highestRank = anouncement.getCard().getRank(); + } else if ((anouncement.getType() == highestAnouncement) && + (anouncement.getCard().getRank() == highestRank) && + (anouncement.getCard().getColor() == atout)) { + // meme annonce, meme hauteur, mais atout + anouncingTeam = p.getTeam(); + } + } + } + + if (anouncingTeam != null) { // there are announces + for (var p : players) { + if (p.getAnoucement().isEmpty()) { + continue; + } + if ((p.getTeam() == anouncingTeam)) { + anouncingTeam.addAnnoucementScore(p.getAnouncements(), atout); + for (var p2 : players) { + p2.setAnouncement(p, p.getAnouncements()); + } + } + p.clearAnouncement(); + } + } else if (playerWithStoeck != null) { // no announce but stock + for (var p : players) { + p.setAnouncement(playerWithStoeck, Collections.singletonList(new Anouncement(Anouncement.STOECK, null))); + } + int stoeckScore = atout == Card.COLOR_SPADE ? Anouncement.VALUES[Anouncement.STOECK] * 2 : Anouncement.VALUES[Anouncement.STOECK]; + // add stock points + playerWithStoeck.getTeam().addScore(stoeckScore); + playerWithStoeck.clearAnouncement(); + } + } + + int chooseAtout(int playerNumber) throws PlayerLeftExpection { + var choice = players.get(playerNumber).chooseAtout(true); // demande de faire atout en premier + if (choice == 4) { // si on passe + var second = players.get((playerNumber + 2) % 4); + choice = second.chooseAtout(false); // demande de faire atout en second + } + for (var p : players) { + p.setAtout(choice, players.get(playerNumber)); + } + return choice; + } + + int drawCards() throws PlayerLeftExpection { + int playerWithDiamondSeven = 0; // 7 de carreau + var cards = Card.shuffle(36); // choisir les cartes au hasard + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 9; j++) { + if (cards.get(i * 9 + j).getNumber() == Card.DIAMOND_SEVEN) { // 7 de carreau + playerWithDiamondSeven = i; + break; + } + } + players.get(i).setHand(cards.subList(i * 9, (i + 1) * 9)); + } + return playerWithDiamondSeven; + } + + private void chooseTeam() throws PlayerLeftExpection { + Arrays.stream(teams).forEach(Team::reset); + + var teamChoiceMethod = players.get(0).chooseTeamSelectionMethod(); + if (teamChoiceMethod == TeamSelectionMethod.RANDOM) { // choisir au hasard + chooseTeamsRandomly(); + } else { // choisir son partenaire + pickTeamMates(); + } + + reorderPlayers(); + + var order = players.stream().map(BasePlayer::getId).collect(Collectors.toList()); + for (var p : players) { + p.setPlayersOrder(order); + } + } + + private void chooseTeamsRandomly() throws PlayerLeftExpection { + // préparation du tirage des équipes + for (var player : players) { + player.prepareTeamDrawing(true); + } + + boolean drawingSuccessful; + do { + HashMap cardsDrawn = new HashMap<>(); // cartes tirées + var cards = Card.shuffle(36); + for (var p : players) { + int cardNumber = p.drawCard(); + + cardsDrawn.put(p, cards.get(cardNumber)); + + for (var p2 : players) { + p2.setCard(p, cardNumber, cards.get(cardNumber)); + } + } + // delay to allow players to watch cards + waitSec(2); + + // détermine les équipes + drawingSuccessful = calculateTeam(cardsDrawn); + if (!drawingSuccessful) { + for (var p : players) { + p.prepareTeamDrawing(false); + } + } + } while (!drawingSuccessful); + } + + private boolean calculateTeam(Map choosenCards) { + var lowest = players.get(0); + var highest = players.get(0); + for (int i = 1; i < 4; i++) { + var player = players.get(i); + if (choosenCards.get(player).getRank() < choosenCards.get(lowest).getRank()) + lowest = player; + if (choosenCards.get(player).getRank() > choosenCards.get(highest).getRank()) + highest = player; + } + boolean ok = true; + for (BasePlayer player : players) { + if (player != lowest) { + if (choosenCards.get(player).getRank() == choosenCards.get(lowest).getRank()) { + ok = false; + } + } + if (player != highest) { + if (choosenCards.get(player).getRank() == choosenCards.get(highest).getRank()) { + ok = false; + } + } + } + if (!ok) return false; + + for (BasePlayer p : players) { + if (p == lowest || p == highest) { + teams[0].addPlayer(p); + } else { + teams[1].addPlayer(p); + } + } + + return true; + } + + private void pickTeamMates() throws PlayerLeftExpection { + int partnerId = players.get(0).choosePartner(); // demande de choisir le partenaire + for (var p : players) { + if (p.getId() == partnerId || p == players.get(0)) { + teams[0].addPlayer(p); + } else { + teams[1].addPlayer(p); + } + } + } + + private RemotePlayer getPlayerById(int id) { + for (var p : players) { + if (p.getId() == id) { + return p; + } + } + System.err.println("Error: unknown player id " + id); + return null; + } + + private int getPlayerPosition(BasePlayer player) { + for (int i = 0; i < players.size(); i++) { + if (players.get(i).getId() == player.getId()) { + return i; + } + } + throw new IndexOutOfBoundsException("Player " + player.getId() + " not found"); + } + + private void reorderPlayers() { + var tempList = List.copyOf(players); + players.clear(); + players.add(tempList.get(teams[0].getPlayer(0).getId())); + players.add(tempList.get(teams[1].getPlayer(0).getId())); + players.add(tempList.get(teams[0].getPlayer(1).getId())); + players.add(tempList.get(teams[1].getPlayer(1).getId())); + } + + private static void waitSec(float seconds) { + try { + Thread.sleep((long) (seconds * 1000)); + } catch (InterruptedException ignored) { + } + } +} diff --git a/src/main/java/com/leflat/jass/server/JassServer.java b/src/main/java/com/leflat/jass/server/JassServer.java new file mode 100644 index 0000000..cca6715 --- /dev/null +++ b/src/main/java/com/leflat/jass/server/JassServer.java @@ -0,0 +1,26 @@ +package com.leflat.jass.server; + +import java.io.IOException; + +public class JassServer { + public static final int DEFAULT_PORT = 23107; + + public static void main(String[] args) { + System.out.println("FLAT Jass System Server"); + System.out.println("Version 1.2"); + System.out.println("(c) 2000-2020 by FLAT(r)"); + System.out.println(); + + // TODO: add command line argument parsing (Apache commons) + ConnectionListener listener = null; + + try { + listener = new ConnectionListener(DEFAULT_PORT); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + + listener.start(); + } +} diff --git a/src/main/java/com/leflat/jass/server/PlayerLeftExpection.java b/src/main/java/com/leflat/jass/server/PlayerLeftExpection.java new file mode 100644 index 0000000..d82e412 --- /dev/null +++ b/src/main/java/com/leflat/jass/server/PlayerLeftExpection.java @@ -0,0 +1,13 @@ +package com.leflat.jass.server; + +public class PlayerLeftExpection extends Exception { + int playerId; + + public PlayerLeftExpection(int clientID) { + this.playerId = clientID; + } + + public int getPlayerId() { + return playerId; + } +} diff --git a/src/main/java/com/leflat/jass/server/PlayerNetwork.java b/src/main/java/com/leflat/jass/server/PlayerNetwork.java new file mode 100644 index 0000000..13e4f70 --- /dev/null +++ b/src/main/java/com/leflat/jass/server/PlayerNetwork.java @@ -0,0 +1,61 @@ +package com.leflat.jass.server; + +import java.io.*; +import java.net.Socket; +import java.util.Arrays; +import java.util.List; + +public class PlayerNetwork { + private BufferedReader is; + private PrintWriter os; + private int playerId = -1; + + public PlayerNetwork(Socket socket) throws IOException { + var isr = new InputStreamReader(socket.getInputStream()); + is = new BufferedReader(isr); + os = new PrintWriter(new BufferedOutputStream(socket.getOutputStream()), false); + } + + public int sendMessage(String message) { + os.println(message); + os.flush(); + System.out.println("Sent : " + message); // DEBUG + return 0; + } + + public int sendMessage(List message) { + return sendMessage(String.join(" ", message)); + } + + public String receiveRawMessage() throws PlayerLeftExpection { + String message = null; + + // TODO: implementer timeout + exc + try { + message = is.readLine(); + } catch (IOException e) { + System.out.println("Error during reception"); + } + if (message != null) + System.out.println("Received : " + message); // DEBUG + else { + System.out.println("Client has left unexpectedly"); + throw new PlayerLeftExpection(playerId); + } + return message; + } + + public String[] receiveMessage() throws PlayerLeftExpection { + var message = receiveRawMessage(); + var tokens = message.split(" "); + assert Integer.parseInt(tokens[0]) == playerId; + if (tokens.length == 1) { + return null; + } + return Arrays.copyOfRange(tokens, 1, tokens.length); + } + + void setPlayerId(int id) { + playerId = id; + } +} diff --git a/src/main/java/com/leflat/jass/server/RemotePlayer.java b/src/main/java/com/leflat/jass/server/RemotePlayer.java new file mode 100644 index 0000000..9b0aead --- /dev/null +++ b/src/main/java/com/leflat/jass/server/RemotePlayer.java @@ -0,0 +1,209 @@ +package com.leflat.jass.server; + +import com.leflat.jass.common.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class RemotePlayer extends BasePlayer implements IPlayer { + private PlayerNetwork network; + + public RemotePlayer(int id, PlayerNetwork network) throws PlayerLeftExpection { + super(id); + this.network = network; + this.network.setPlayerId(id); + updatePlayerInfo(); + } + + @Override + public void setPlayerInfo(BasePlayer player) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_PLAYER_INFO)); + message.add(String.valueOf(player.getId())); + message.add(player.getName()); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public TeamSelectionMethod chooseTeamSelectionMethod() throws PlayerLeftExpection { + network.sendMessage(String.valueOf(RemoteCommand.CHOOSE_TEAM_SELECTION_METHOD)); + var tokens = network.receiveMessage(); + return TeamSelectionMethod.valueOf(tokens[0]); + } + + @Override + public void prepareTeamDrawing(boolean firstAttempt) throws PlayerLeftExpection { + if (firstAttempt) { + network.sendMessage(String.valueOf(RemoteCommand.PREPARE_TEAM_DRAWING)); + } else { + network.sendMessage(String.valueOf(RemoteCommand.RESTART_TEAM_DRAWING)); + } + network.receiveMessage(); + } + + @Override + public int drawCard() throws PlayerLeftExpection { + network.sendMessage(String.valueOf(RemoteCommand.DRAW_CARD)); + return Integer.parseInt(network.receiveMessage()[0]); + } + + @Override + public void setCard(BasePlayer player, int cardPosition, Card card) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_CARD)); + message.add(String.valueOf(player.getId())); + message.add(String.valueOf(cardPosition)); + message.add(String.valueOf(card.getNumber())); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public void setPlayersOrder(List playerIds) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_PLAYERS_ORDER)); + message.addAll(playerIds.stream().map(String::valueOf).collect(Collectors.toList())); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public int choosePartner() throws PlayerLeftExpection { + network.sendMessage(String.valueOf(RemoteCommand.CHOOSE_PARTNER)); + return Integer.parseInt(network.receiveMessage()[0]); + } + + @Override + public void setHand(List cards) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_HAND)); + message.addAll(cards.stream().map(c -> String.valueOf(c.getNumber())).collect(Collectors.toList())); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public int chooseAtout(boolean first) throws PlayerLeftExpection { + if (first) { + network.sendMessage(String.valueOf(RemoteCommand.CHOOSE_ATOUT)); + } else { + network.sendMessage(String.valueOf(RemoteCommand.CHOOSE_ATOUT_SECOND)); + } + return Integer.parseInt(network.receiveMessage()[0]); + } + + @Override + public void setAtout(int color, BasePlayer firstToPlay) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_ATOUT)); + message.add(String.valueOf(color)); + message.add(String.valueOf(firstToPlay.getId())); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public Card play(int currentColor, int highestRank, boolean cut) throws PlayerLeftExpection { + if (currentColor < 0) { + network.sendMessage(String.valueOf(RemoteCommand.PLAY)); + } else { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.PLAY_NEXT)); + message.add(String.valueOf(highestRank)); + message.add(String.valueOf(currentColor)); + message.add(String.valueOf(cut ? 1 : 0)); + network.sendMessage(message); + } + var cardNumber = Integer.parseInt(network.receiveMessage()[0]); + return new Card(cardNumber); + } + + @Override + public void setPlayedCard(BasePlayer player, Card card) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_PLAYED_CARD)); + message.add(String.valueOf(player.getId())); + message.add(String.valueOf(card.getNumber())); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public void setPlieOwner(BasePlayer player) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_PLIE_OWNER)); + message.add(String.valueOf(player.getId())); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public void setScores(int score, int opponentScore) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_SCORES)); + message.add(String.valueOf(score)); + message.add(String.valueOf(opponentScore)); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public List getAnoucement() throws PlayerLeftExpection { + network.sendMessage(String.valueOf(RemoteCommand.GET_ANOUNCEMENTS)); + var tokens = network.receiveMessage(); + this.anoucements.clear(); + int nbrAnouncements = Integer.parseInt(tokens[0]); + for (int i = 0; i < nbrAnouncements; i++) { + var card = new Card(Integer.parseInt(tokens[i * 2 + 2])); + this.anoucements.add(new Anouncement(Integer.parseInt(tokens[i * 2 + 1]), card)); + } + return this.anoucements; + } + + @Override + public void setAnouncement(BasePlayer player, List anouncements) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_ANOUNCEMENTS)); + message.add(String.valueOf(player.getId())); + message.add(String.valueOf(anouncements.size())); + for (var anoucement : anouncements) { + message.add(String.valueOf(anoucement.getType())); + message.add(String.valueOf(anoucement.getCard().getNumber())); + } + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public void setGameResult(Team winningTeam) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.SET_GAME_RESULT)); + message.add(String.valueOf(winningTeam.getId())); + message.add(String.valueOf(winningTeam.getPlayer(0))); + message.add(String.valueOf(winningTeam.getPlayer(1))); + network.sendMessage(message); + network.receiveMessage(); + } + + @Override + public boolean getNewGame() throws PlayerLeftExpection { + network.sendMessage(String.valueOf(RemoteCommand.GET_NEW_GAME)); + return Integer.parseInt(network.receiveMessage()[0]) == 1; + } + + @Override + public void playerLeft(BasePlayer player) throws PlayerLeftExpection { + var message = new ArrayList(); + message.add(String.valueOf(RemoteCommand.PLAYER_LEFT)); + message.add(String.valueOf(player.getId())); + network.sendMessage(message); + network.receiveMessage(); + } + + private void updatePlayerInfo() throws PlayerLeftExpection { + network.sendMessage(String.valueOf(id)); + this.name = network.receiveMessage()[0]; + } +} diff --git a/src/main/java/com/leflat/jass/test/TestClient.java b/src/main/java/com/leflat/jass/test/TestClient.java new file mode 100644 index 0000000..c9e6ff7 --- /dev/null +++ b/src/main/java/com/leflat/jass/test/TestClient.java @@ -0,0 +1,182 @@ +package com.leflat.jass.test; + +import com.leflat.jass.common.Anouncement; +import com.leflat.jass.common.Card; +import com.leflat.jass.common.RemoteCommand; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TestClient { + public static final int PORT = 23107; + + private BufferedReader is; + private PrintWriter os; + + TestClient() { + ServerSocket serverSocket = null; + Socket clientSocket = null; + try { + serverSocket = new ServerSocket(PORT); + System.out.println("Started test server on port " + PORT); + clientSocket = serverSocket.accept(); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + + InputStreamReader isr = null; + try { + isr = new InputStreamReader(clientSocket.getInputStream()); + is = new BufferedReader(isr); + os = new PrintWriter(new BufferedOutputStream(clientSocket.getOutputStream()), false); + } catch (IOException e) { + e.printStackTrace(); + } + + String connectionMessage = receiveRawMessage(); + int gameId = Integer.parseInt(connectionMessage); + sendMessage(String.valueOf(gameId)); + int playerId = 1; + sendMessage(String.valueOf(playerId)); + String name = receiveMessage()[0]; + System.out.println("Client " + name + " connected"); + + sendMessage(RemoteCommand.SET_PLAYER_INFO + " " + 0 + " Berte" ); + receiveMessage(); + + sendMessage(RemoteCommand.SET_PLAYER_INFO + " " + 2 + " GC" ); + receiveMessage(); + + sendMessage(RemoteCommand.SET_PLAYER_INFO + " " + 3 + " Pischus" ); + receiveMessage(); + + /* + sendMessage(String.valueOf(RemoteCommand.DRAW_CARD)); + + int cardPosition = Integer.parseInt(receiveMessage()[0]); + System.out.println("Drawn card: " + cardPosition); + + var card = new Card(Card.RANK_BOURG, Card.COLOR_HEART); + sendMessage(RemoteCommand.SET_CARD + " " + playerId + " " + cardPosition + " " + card.getNumber()); + receiveMessage(); + + var otherCard = new Card(Card.RANK_DAME, Card.COLOR_DIAMOND); + sendMessage(RemoteCommand.SET_CARD + " " + (playerId+1) + " " + 20 + " " + otherCard.getNumber()); + receiveMessage(); + + sendMessage(RemoteCommand.SET_PLAYERS_ORDER + " 3 0 2 1"); + receiveMessage(); + + sendMessage(String.valueOf(RemoteCommand.CHOOSE_PARTNER)); + int partnerId = Integer.parseInt(receiveMessage()[0]); + + */ +/* + sendMessage(RemoteCommand.SET_HAND + " 10 12 14 16 18"); + receiveMessage(); + + sendMessage(String.valueOf(RemoteCommand.CHOOSE_ATOUT)); + int color = Integer.parseInt(receiveMessage()[0]); + System.out.println("Chosen : " + color); + + sendMessage(String.valueOf(RemoteCommand.CHOOSE_ATOUT_SECOND)); + color = Integer.parseInt(receiveMessage()[0]); + System.out.println("Chosen : " + color); + + sendMessage(String.valueOf(RemoteCommand.PLAY)); + card = new Card(Integer.parseInt(receiveMessage()[0])); + System.out.println(card); + + sendMessage(RemoteCommand.SET_PLAYED_CARD + " 0 35"); + receiveMessage(); + + sendMessage(RemoteCommand.PLAY_NEXT + " 3 1 0"); + receiveMessage(); + + sendMessage(RemoteCommand.SET_PLIE_OWNER + " 3"); + receiveMessage(); + + sendMessage(RemoteCommand.SET_SCORES + " 69 156"); + receiveMessage(); +*/ + sendMessage(RemoteCommand.SET_HAND + " 3 4 5 6 12 21 30"); + receiveMessage(); + + + sendMessage(RemoteCommand.SET_ATOUT + " " + Card.COLOR_DIAMOND + " 2"); + receiveMessage(); + + sendMessage(String.valueOf(RemoteCommand.PLAY)); + var card = new Card(Integer.parseInt(receiveMessage()[0])); + System.out.println(card); + + sendMessage(String.valueOf(RemoteCommand.GET_ANOUNCEMENTS)); + var answer = receiveMessage(); + int nbr = Integer.parseInt(answer[0]); + for (int i=0; i message) { + return sendMessage(String.join(" ", message)); + } +} diff --git a/src/main/resources/pics/0.png b/src/main/resources/pics/0.png new file mode 100644 index 0000000000000000000000000000000000000000..23b52135b6c43a9b9f8cd10926a42fddc97c5d4c GIT binary patch literal 541 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LDJR0CNI|Ns9l zdhR3wWHBarySp&{XVSd~2$a1@{(07&T z=PxYtnB?mx$?9^CsYvF9<_Vh>S6uU_U-~@f^7)tFHo7|=mt$R7c{e;}maJnk+mG}m zIZCVTdjckG^5oKqrv;%94^wsT&%wEKJ4E&J|!=gvK>{bTilUrP2SOL89-9AI85-*X83qRbyO?L8Xvob^$xN%nt)cJwZeW};FnGH9xvX9f literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/1.png b/src/main/resources/pics/1.png new file mode 100644 index 0000000000000000000000000000000000000000..cf22d54add511e02a00b8727cb090cf6c02a0107 GIT binary patch literal 564 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LCuX@+Ws|NsB< z-k0M6vKf=S-CYNntAH9f8n={$6Ss}39qc{-S&Ob?pL~--mUd-LA+3!w~krnVoK|L3Xy$C%SqQggUO_QmvAUQh^kMk%5tcu7R S?*>LS1B0ilpUXO@geCx_RMwjS literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/10.png b/src/main/resources/pics/10.png new file mode 100644 index 0000000000000000000000000000000000000000..eaae273c7f6473aa420a86f6e99a4b0fa4db99af GIT binary patch literal 585 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Q##0(^VYKJTbQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIfJw}w3VleV~EA+yH^eK4jb^W z2ArL1^hM;IuT^Qr`^yX2U-~dUr`wYS&UjIMgOs9TrW?Vnh{`D{lE zyTS4589KFjlRgNZ+WdcAO-s@(C9j}s6AjHL>1!@uBoOY@(R#{`(^Gw=%5siv9H*k* zYwYnpb6R!5_3Q4nNt(ZR2_^;AKw_Psy}i|KI1Lu$udn*tkPxSB+F>*DkhO z=vLb|@u=_YpDw=tR>b%$U-A8)+vD4f|6~vKa@d}2*Eru7bz%Rdb6e)?*0zZMPX8fs z@6E?wylPbocI=B+dI$_y)e_f;l9a@fRIB8oR3OD*WME{VYhbKvY!G5#Ze?gXq9HdwB{QuOw}!s!yMgh~z~JfX=d#Wzp$Py>qutg3 literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/11.png b/src/main/resources/pics/11.png new file mode 100644 index 0000000000000000000000000000000000000000..de0a49f6b702fe2e619bdf2f60ce5d262a2253b6 GIT binary patch literal 561 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZQ-$_=LDJR0CNI|AByk zVcGRPwLlJIlDE4H<9{aIdq56*iKnkC`x9n4c0S%YGGFBU6&`#vEnkAK^{Wt($&mpy;6 zbkPJBCjpIK^@(*g9W&<3{@9Xtgx@XOmMvn!X^rd5Y_HvAIxI`&P-CjLQ!hTKO7Ymz=qKJ>%Uz zw@eYMw2;>k0ZWs8ola&)pFTD}@b*XcZPtBk%Kv%x2Z)@zx~brC888421sKVg<*=i?2QdiM_~9p>rc7-DgH?bL(0hYfgK zYu{6mrTEWi7^+ ztCg7celzE?cRBNo%i#=I~`xhsbux+)7>}ac;OSF z04tHVi6+Mq5BC;2^f32H*2c-aD-dsAsMxpq`fiJw9L<>kY!=;n`1gmlZGTr~+&*+fsBG#_e(!%rUidI3 zy2w0UAkiMsvDj*(?5y+7)^D?A{4dwZU%dXs@So(qe@x$A?y$YzrD_cfbk!2qh?11V zl2ohYqEsNoU}Ruqple{PYitl=U~Xk-Vr6Ko3uG7={O@9(g`y!hKP5A*61RrF>$`zz PgMq=*)z4*}Q$iB}i7f8G literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/13.png b/src/main/resources/pics/13.png new file mode 100644 index 0000000000000000000000000000000000000000..b5a2f93713493602aac339a0ac840b35b95e9dee GIT binary patch literal 622 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZQ-$_=LDJR0CNI|AByk zVcGRPwLlJIlDE4H<9{aIdq56*iKnkC`x9n4c0S%61Z>-}Ykw=3$7d0Xr2*H(T(oVTaeP@2{KP=qj(WrSvwcNp_9{)6>MG^4 zWO7r~J#l^d3CG@RQW^zwls{?k8EcgVL?pR~HofVHe7izulC0uN*+r}Vd%pPm<^hYl z^*U3>gvLoq=e0UyO>@H>->lHsBvt89eodA4#m1Wd?LFtE9oH(PBZibPn=_1Qw_uap;_q~Ud+7i~IYsx%6E3S}T>#8ZXb+PHDMfcuy{aX5fQQVs0 z+-By(JvMUOxBBzg;}A`^&;_+OyJt zp|4uv8c~vxSdwa$T$Bo=7>o>z40H{Qb&U-|49u+zO{@%!b%6{6ga2L3vrshT=BH$) YRpQprcYQZ7kuWfLy85}Sb4q9e08@bL5dZ)H literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/14.png b/src/main/resources/pics/14.png new file mode 100644 index 0000000000000000000000000000000000000000..d5b4f4c36e3c71558e1e3a375877e6568d0e5130 GIT binary patch literal 1123 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt!VDz$ninksQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI?NMQuIx{k<=FXngQed6V_;zR_H=O!@i=~U zx?|B31s+$^fH%hG0gh?c^gQ$~>|_WHzVlz}^ZpIH4b`qjrU*}Zx8wGCry9=A6Z(~A z9!JuhUGD^I)X8c+-MwH+z1|m3mphD0c~02HN@-C@-w1rJnE}HE_U9|_(-|$w6Vsq{SkJ@qivk;E|=|k$8_<_+V$@m z%9iJ;8+ywq{?wirKdnOObHEafrwo!QY2ZU+zTTVEULC@Lc4=#)FvDX078maPiYw0yPlbrn@SmAA4+Tw1F7 zRddTVk85gs^u^~V9M{(5^PC{!IA5V9omtgIt4QXkuimr`>>jFV>(zq==5ijIJNJ=7 za@O317x=oSojAgqY2$cdeX#U2zhwtjN^_;rpSXSKQe>0b`_jZr zq08G_S>DQsNkpyT<9U?Nm?m`K!l@wH;MO*^f0gv}gUrQF;cQ*A#w<({4x z%)u3+4Bue$x%wH$$4Z!a7@KjGfOQ!C~#e7s|U#&X#r!{>FIb~qnD$n0a^ zb3jPJdU4Fc*bg^qt+eYL`Y+E}`;O`6Mz6A84VN^Y)j0%t zl&kxFOJGh@Epd$~Nl7e8wMs5Z1yT$~21W+D2FAL^1|bIKR)!{4hQ_);hJnHVF6LP% e8glbfGSez?Yv{Ya8(1}$^42Y$chzUK@q0+sWqZ4wntoyKOy}_8 z;3Ib#H%yxLg`?M+*LRji9nVT<4?V40jja##Hsqz92sz)Yy8EB#_tn|A9R+qK=)7H6 zS$I3D%u9_oNMZW9jXZ|e=1wZRQXjvz>_}4DhrNp*$twto%JbZ3nmOZR_-qBPQc0ck zFoD@G{Z2mbn;?B}dXv4ue&(fYYJ!`Z4ko;iv9N0vb9VCx+mdqH+Bo*44*R;*Gp^4% ze6KKY>7`jaofs2+3zo_qyTW~q+dX>W=H?@R1CL%RK9Oh{z|``!bB)N_*1Y34m$U8n zEk1uK`p&e>lh#W$96!(6`|g3-G;6lGvd1~JY`61W3^F{vDZJ|_bGWYPZbmKJX={X4 z=1lme)L>HPV`_A=|FB_0Lrw|Hy{uyI*)~o*4EpH{S=Vmq;uV`bbE=WLY<7##w_RN~ zOGEA^|8z?+J`$z4OxNtz{dCo54Z=(;);0xG=L${goDtT(;6#eNhN@ZfqiIS<&rZK# zvim@&?o@%(ew%JK3N6UtImsl#? z&%O2frw%$LyI8B9=`VZvX!fI}Mdg+SHXFaU2wHr5RIuUquc8F^54#SpXut4iwhdd( zno5!7_ICD886vmu9o0F?xN-R%$9qLDOodZ6@pasN$@l5mQL|_MB@XwV=UuqQHZyub zPEO4m+0Sg1%*#c(rtl^?{QDriYg?LJM4Zz!c9GRQ89STT9cRt)HJYYrFQML@|Dx){ z1=HV=Dpj@qeesHi$$xhFwVm$&y>%RaU&ybU|L65L ze%1OTDJBVq2Z8xowZt`|BqgyV)hf9t6-Y4{85kMp8W`&u8-y5`TN#>I85-*X83qRb iyO?L8Xvob^$xN%nt)cJwZeStAz~JfX=d#Wzp$PytdJYr- literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/16.png b/src/main/resources/pics/16.png new file mode 100644 index 0000000000000000000000000000000000000000..42313d88f4c7d8db0b3651407ae613f0fb8fa89a GIT binary patch literal 1252 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt!VDz$ninksQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI#39}qKA8)YKyMGJ}%;!B_978;guMN-5 zdu+w?#LLRzJ6lWBKk-9f_A|U@dHeVMr!)`sjZeR-9f)4qnOy0_r&$+TcXG9>OYPPW zmAx!i%N^6icmET<@U-fuwVK1XS(mj_wO{bQzBk#$PhEXd*|oc|QKvetzu0rPVb#NP z`(7=N4XxVab*u5t%!IW2_GzL<8-7jDa-T3W_Gehljoh9k%DE18swZSz_TA)Bu(4u3 zsu$LNNd4n_|Je?l-qpI9kG=>m$j^D8uc{earoiDbzwc*|=pLpiTjn*cYTI?`HJgj; z{gwY%ma=!G<#jw(d{?pY7enURYi4>44EJWn2OsS#`dBkxflcI{LYL7FZX-{rYsY3@ z|6u=^tF4gz*Mri@Cm&bD&shD(Ej2Xf_)9@yE!~Mbt!hu6>{FPxM&-HBR)?+39Q&`_ znX+llbQ8;hNwYpCa+odMK6%@l!mghFMpiX9f0nXOx)>rYw>5~RF!Jj(*HdyK zJZ)ROYGo7oo_XsTtFKU8x#XGx$0V=p$}!;qRtta|EvZSJ2cj*tVK`*58upEpu7oS3F^DQk!5=s8`V58NlvX$u#xhj8%0- z7moF6zA%dM<7b@0{its$&xyl~n~y2>F*NOQJXjlc{4l#wt5x|c3pd8O4(z7S{d{Ks zZP|NNOe#J6zSG*a#eCPEwl=mOdS%`5sD{CDf~}OJ-9(Wl6)yGf{}ooYENI{G>d17? zCrvYE^;p(4wr)4H+#Dca@ap1ei?++tZm(Il(N3dw=1l(HlcgsTIFjytJhOnO^Kqov z&&-KzFMX^Mzn<#(-YM*JZm0W#l546@_MFiSl}POsy!9~qi$)0lURmQEx;@G_?q{lT zU)<($e5<*Eqq+-ka^Iq=7kA#f*o1wI7K>|L@n&fif6`gM9y9&(mAT)8FT`%FUw`hD z!`HXA99O%y_DS4((4y}yu=&=FgLA}>{wSEVXSt1V4U9z`7 zMp(V9TK8`I){heR_!QWFS$#ce#dd85*LHO>GM-&=l;YmxZ0WDXcw3C#kp?(zw2)w+b@c#PWqSnA_17&RZCnWN>UO_QmvAU zQh^kMk%5tcu7R?*=5+TL}QE94aZl!eeX}ZtzvR-4Y09dTOY@RoPop^2}r7s?6>aR;n)ST@hD+scD$ zN~r(rjUB8VpINq;Dyuj)Jv<|{aFX&Du8aLDR{qI{&VS%_YAt$em|&m_^p9$ZYeY#( zVo9o1a#1RfVlXl=GSD?J)-^T=F)+6>G_f)?)&(*Q4E}d9&qC3Vo1c=IR*73f-}T+V Qm|$S=boFyt=akR{0AqEXu>b%7 literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/18.png b/src/main/resources/pics/18.png new file mode 100644 index 0000000000000000000000000000000000000000..d624da619bc5a71832056e63a5bc72bd589e4889 GIT binary patch literal 520 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZQ-$_=LDJR0CNI|AByk zVcGRPwLlJIlDE4H<9{aIdq56*iKnkC`x9n4c0S%mG*KEM! zy7`OHoMqErsNOnV(BbFSG`I1Ny88(M^MkxD|KtR`C%$4lJo&A~{b>c;+c)ig?GgEY zp447n6ITbhz(YqmIt(WYtLeFHt~ty5i*1=K?;fGTCN;jN&U2=GFIr{sGH&soluVa9 zU2K)-`&fG=o^obAoh(|Fy8f4zM``?oeaW3+UKJswHYUgI-MkJxnsUKqgAWK@dp0Lp z&G=P94Y~O#nQ4`{HS}HI R4U9ep22WQ%mvv4FO#sVow*&wH literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/19.png b/src/main/resources/pics/19.png new file mode 100644 index 0000000000000000000000000000000000000000..657811aa45c4b0fee8d0f60342cd86afced74b46 GIT binary patch literal 539 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZQ-$_=LCuX@+Ws|3JXN z(Dm*K50J~421sKVg<*=i?2QdiM_~&Ex6f7-Dhy=~P?3W&@s9 zui7 zE_%C~Y+YcIwC+k%OvuFp@y@N+7G3Vzv$ap`P~*hqs`Bh^jnh`iluVg_#JMnH!sG8U z9fAGZnVy}R%DU^B#;;iuU(~r3xjCsa<*j&b-d_DhFuTWQ`2K~n{P5mqjjm*3ZJ z`{ETJ_5D;&5U1k9Wgpx#y3;mgsI9hoK4a#U=-!u8_f2^>Rnk=T)#v#dOhueuJ_)ao z;@G0U;#Pp;RXOi1a^qj|;;Zc2HQ&@O zUcU46{D(hm6IN8#l|R*a1Pm(G64!{5l*E!$tK_0oAjM#0U}T_cV61Cw5Mp3%WoTk$ pXsiol7#RHTVxEPfAvZrIGp!Q0hQ8~&ficR!;OXk;vd$@?2>^0v$6EjZ literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/2.png b/src/main/resources/pics/2.png new file mode 100644 index 0000000000000000000000000000000000000000..23c3bb182f7a262d8b4bda47721117a7e1f753f0 GIT binary patch literal 554 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LCuX@+Ws|NsB< z-k0M6vKf=S-CYdV)xAkZ2g zW4gKMBC|=XWZ$LGeit8pr(j$<}QhRiRfzVF1O-lsW zS~KiA`A+G7JS`%vE9}qO)pU2l#QwQaPonfrvFi7!N3~S)$I0AgJ8%8rk@A*z9MRX_ zcAUt5(&IGEW?oMl`y@Rbvnk6pr_Dd~YL~lPX@5^ky!qQz#dN%PJ%TYmm`SVnJ#wD^Mc$FEKO zeLXb2>O$_cj9f3Tvl^bhD>|5)yADq>suB)Vs!x=;-I*#gdz}&AtWX)wrxrq|3g2w) z+BiRgXO{n>eV^FHAE;hr&;Mz>=RZSPM{w5$>-iUefu~yH8c~vxSdwa$T$Bo=7>o>z z40H{Qb&U-|49u+zO{@%!b%6{6ga2L3vrshT=BH$)RpQprcYQZ7f*BY*UHx3vIVCg! E05HnJy8r+H literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/20.png b/src/main/resources/pics/20.png new file mode 100644 index 0000000000000000000000000000000000000000..13ee8432966cf7105791fb1042598d7c90cfcf53 GIT binary patch literal 563 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZQ-$_=LCuX@+Ws|3JXN z(Dm*K50J~421sKVg<*=i?2QdiM_~t>Wq87-Dhy>D1kVhYWby zVuJ$>SaY0?dWi5i>E54c=enogy&!R-OT;zLM;u#rbWD1mV_T=`d4qlHw?y`=*u#N~ zcXtOFa;$bt{Ux}!(Eie84ZeL%*ZP#!weT!t{QSwGtwuG*dBVaDsk7lr7M)n5p`^57 z=EcMByb@~8P878|8}@Xo!qmS_CB3Kfmbz?`t-27qrB-H>wv*t(A%c{L+0(w_*_I~5-C;p{#*!lWtu_c;TPCc1& zf5M81f#HjO3axe(>E^HN-nO&;^LDH35^nLo#x^#LAG|x}ZuUOf3k*Wl64!{5l*E!$ ztK_0oAjM#0U}T_cV61Cw5Mp3%WoTk$Xsiol7#RHTVxEPfAvZrIGp!Q0hQ8~&fica% N;OXk;vd$@?2>_Ai%zpp? literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/21.png b/src/main/resources/pics/21.png new file mode 100644 index 0000000000000000000000000000000000000000..bc4faaa10f23dfa2f09413871d59a2ba680f6dc0 GIT binary patch literal 567 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZQ-$_=LDJR0CNI|AByk zVcGRPwLlJIlDE4H<9{aIdq56*iKnkC`x9n4c0S%nW&T2{iiCs)<{&iiplWJ_0_$VPMwEv9+yLez>iyX_R&yx@6%E!N}(Niya zzH5%{uX&4(G%R6w^tRUH^7^vq*&SVV-P6=c7b{qAT(BZw${KO*Wp*d;HL|)&2K?((mkHhqf0w!~_nsKM{>1kc>-P#D9NvGaU2x@s8SJrI)*lzd&7N%ZB|?`&Ye9JvPp_EU#%tD7j%=~5H$BOkVH%q! z)0rr<)!n}DgZwc^3%>1YlT&A{xszk~yI3UEp>bi7&H`=D75OV(Z(Mk;bJ}X>wMp-@ z>|WHSICMIOJHD6PD_-Kn_2cO6-xpMBga3ce@Ry4f|0DU2VVT*+1^)Z@@c@HSwZt`| zBqgyV)hf9t6-Y4{85kMp8W`&u8-y5`TN#>I85-*X83qRbyO?L8Xvob^$xN%nt)cJw SZeVOPFnGH9xvX21sKVg<*=i?2QdiM_~?ds{`7-DgH?^MIQ!v;LA zrsbz51g|iEc}2POg`-up!0+7^{}JMwFx^mZVxG7o`Fz1|tI_16>1SU1Ng~ v19K}w6DvbwT_D52;C~nMEEEm7`6-!cmAEzZUEd8%3JeUMu6{1-oD!M<6};eN literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/23.png b/src/main/resources/pics/23.png new file mode 100644 index 0000000000000000000000000000000000000000..f4cb820c06b1717e780ced9a134b048ae15d979c GIT binary patch literal 1080 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt!VDz$ninksQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI^mS!_!Ys$m#~Up5?jHjKv$Cg)V~EG`tJ4n_ zJ~rTS-7Cv-Wdno9Zq|Sp^9?H*9w+`^`{qAOs<)Et?+M-(owl3i&2wl_dUM(}uDAd6 z)9Zh-UOW|wbNYPzcEGv&th%qyx6TVu$+@tJX_fosNz=Ff^DO33C{2nL=D*H7rMUT7 zXZbN#5#37ut1Y|ZH}lVXywtkmu=5r7J^PFQ-FCdT;1}O}^_KtNF8peEBY8wXIweg@ zYvSgEHyr2hXbD&mFoEHO`i6T8rIZv(I|}k287lNvzmS z*wyqJW*re(bbhh9r_)Uj)fC>VGrwM&sp6-U>ze6Oo(z@IbiJ(fGNmbT)v71XzDW}&Yp#e2^)=_K*zjeI`O&>xQ#miq{=ica!pC{7 zuWVV*6`K$#kGZ~^j{N92Wir8P_6*UT_d*RK#f7t`pIP^P#gxS5dXKM`tew0@Ky03! zsc+AzA1!|VJ{oV|dQJ5>s3bc}qf1gtk1fW`u=`Td1^y)shZ9Lwop;^n=`u>d@x<6t9AKYi#o?iFYBuZuFO0B_T!RO@iTP_o*X-L ze2!q^mug3?8&kLOE?&i!Ei<|M``Y$Z1~=wk_`E1*wuIRl*WsKIJ{9ueE2* zyOAU};rlv?J4c?WKYdpFjb?t?^%qx)|G4}ySW)dy^#ZGVE%$yM z{CduR`~SGGyHbm*R{H~UkZOr*L`h0wNvc(HQ7VvPFfuSQ&^0jDH8uz_Ft;)^u`)E) n1u_f_{&z9YLeY?$pOTqYiCaV8_1(bi&cNX5>gTe~DWM4fr0v-k literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/24.png b/src/main/resources/pics/24.png new file mode 100644 index 0000000000000000000000000000000000000000..7a15f6e12921b09a17ab47af11e53988e9a800e3 GIT binary patch literal 1210 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt!VDz$ninksQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIlFz@i=~U`rXoG zMV^+uvMx<*E$77;Qn(+qaGCx6zRCEI{Z6I0-=;Py>ih1?b_zP3d#Q5%+U=KLR@+3E zS-lKiv1c#W*L(Avp5Oajyke?6kL`*MIfb*r+E*88JzgiMFuV9k2ygZ}_qPleeVo>% zXx)^b`#b!^=|!r$g)*W)?ujVx&=;Jgyi(*%W6=KU|HTcbno>#(j%qFRSNZr|`^m0= zNl`AYT8~{jG?}7i3s2_j*!{3B@51u^O-FU;uxj-Os_m2I-_Fe$vC=CfaJCBxFLbhQm4_Og#Q+BltHR&_K;eAklV)$&4+f2+2~ zvG+YYq!ygXIM*p}E%#hWaSqSt;}1lv9WJafW113sO1#ck-gzaHNy?8-<>uu)D#}l$ zEKPfN_wY>#i#I>CqZp>Em${xX5)P>nTrf*`&PO)~gLh3n8yGxI8}DYUP!ts~OAqAq zR1Isr%jc0<5hC=ZYs)LI;HYKuUTw49Q}1@FViqIs6&Ia(Od?yiP20N3Gy3Zrlcz+xF4M!6tM47%(7djn_fX}Tq5gbM96O)gk&Ox#Q0`LRdqDZ&*+=W^xE0k83%avjd$ZSZ`pKF` ziwSIpWvA*H+g+A*Xv;3kI`eXo3;%;{kh!b_Z~UcIY+5o3Vtb6M||C*+P$qlx3CZ~RdNt~N}{PErg zUK*zvtG=XuVX%#|7yp&@?C{sEI{#z!o%H`6{(5SE-hYOpNn30U{w-bu%)_cBt`Q|E zi6yC4$wjF^iowXh$UxV?Sl8Gf#K7Fj(8S8nSQp4JF!?NMQuIx{k<=FXngQed6V_;ye^>lFz@i@MA zdSKoi10I)1uNUQ2%#Pj9Zb#()?>wfS@veUQxe1%DPb+`TD#gyY=^VGEROb?<`%m5| z2A-d%v}k!it!UQUOY7p9V%JTtH)h;g?3;el>dT>5aosMy{$(!Zt6xuhcJss2{*Ry8 zR-Lt1%$}+0UFNeoBT~^^czzAz%B^L}X zL0#zY%GUQDSu;yFTZQj&y0GfigsZQ=@&y{|JZ&yv6_CFW{X$sq%FMFb^j8f%_Bs=^ z?-pbr{L_aoorS_KiSeI89%J*ph;}P4j?5es7SH|;-HS4V7HY9u7T{QV6QXT$u z-g-INaxeTZ?X6EMRo=7~-JbC#Q{^M;$0<|S{o(N3ap(Tg;ueX0DZD9V`%}w9 zoP_!3RCY{S%W&jf@9M+-nWuh;y_oIFm?m92Nt1cQ%Db!A{gDg5ek(=m+9a2|XNxYl zsHJCmEI+-_+JW;-&b3dsm9^5pXb{Js1BH9uJV*t2M{M7suOz1gzEw;LpQ<6Whkdi(t4W6C`B zWTH<5l(occJl!aKD&XgYzOrn$B_|hl1$?fU_sJw!=v>c^yEm5DIC0K8^Jhsa_tP6+ z3}u}ZXY=#K%gy*1P@ZY@nq9Bwkf!_@kyTNCdf#%5jNTr6RrUQ-Q9|gJmmE*+4i{wY z*>QlUG(C0gNfTd-sz`x!8~;PcdT+UX&pX6*0IuD$m=!lFSS?uoRQt~Wbr4ypKtC?sorM2 z<=orYC8fu#zr_9KO<3=_K0M;aY~z@ie1_Xs8MO?HUN9ah_qCqyH2Hj2K}~B}UvcS* zkEvI5?PL|Uy*N-(cqc!h`gq|>^?th(~e)d%MawY-mZ3hS$f<%ML&)8 zQrXd;AB;||e)aS0zP(Jb`?M~3&)T-SdR|WQvwFw7N2V{>9=vSN$^OsvcklgU((ldc z`q!NB8JIU!OI#yLQW8s2t&)pUffR$0fsui(fw8W!L5P95m7$51p|LKIVPNpTi+L7` dhTQy=%(P0}8v3s929_lZ44$rjF6*2UngAu830?pI literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/26.png b/src/main/resources/pics/26.png new file mode 100644 index 0000000000000000000000000000000000000000..eba6007fd82dc2527af71015b985d18ceb826ac9 GIT binary patch literal 454 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZQ-$_=LDJR0CNI|AByk zVcGRPwLlJIlDE4H<9{aIdq56*iKnkC`x9n4c0S%o>z z40H{Qb&U-|49u+zO{@%!b%6{6ga2L3vrshT=BH$)RpQprcYQZ7@EI69UHx3vIVCg! E01B>~hyVZp literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/27.png b/src/main/resources/pics/27.png new file mode 100644 index 0000000000000000000000000000000000000000..377d0f2d5444b8cc6964528cbcbbddb6ee27ac27 GIT binary patch literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LDJR0CNI|Ns9l zdhR3wWHBarySp&{XVSd~Kit_(>-!ICD|`P{Nt2Tp9?8*Oxpr7?n>Ctl)rD&v1?*Pxv9!VwWq38^A=?= zvbsny>na^`xFlh*T*O_%w)w4ugR7S?-ux-GHAd9bIP{8GrifJFp#vpC8G=9Kn0bVJx$4{WkD4$z zZuCEL6&OOQC9V-ADTyViR>?)FK#IZ0z{o(?z*yJVAjH7j%Fx8h&{!A9FfjPv#XJi| dLvDUbW?Cg~4Sm;l17nea!PC{xWt~$(69AknyC47n literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/28.png b/src/main/resources/pics/28.png new file mode 100644 index 0000000000000000000000000000000000000000..6a1b6eb723ea8456e019b99418d4c93f819f5aa0 GIT binary patch literal 545 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LCuX@+Ws|NsB< z-k0M6vKf=S-CY>XCFGE0N|TNcS3Q9h;F-S{cF<4KARbBF$^!*zQ!HZVwY&uHdlpQb!VDL^RY zTi|Jr3EP8P1gkGAOgGa~=JD1#axnGCmW;UT8!FcNI`W+`Yx!X(n#k8N`vb$KX+lcN zUwVXOU%Pbck(+PT=B2{sR%`RE7wx;!F(W=}tBh)^&K1QF(Wx4DTo#9YTkm>vzgTMe zy2Wqe%acB>U%filT5p<{ZlcvP#s`KI7e^;5X^IIOK1y7)lmGJiw`xA=xtpK-5iSyH z@jn`9F#C6CVByvUuWWbK?pXNY=jZAviskd>ceH@Hx7Ae(vhL`$8kBIZiuDE;_DWV9QrP(9j!k#Yr@71@CcI$v z|7pCZmf=cAyw{}Qd+orWQY~?fC`m~yNwrEYN(E93Mg~R(x(3F&#s(n<=2nI#R))s9 mK!$nC}Q!>*kack(iz8e^w3=E#GelF{r5}E*Eb-fD! literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/3.png b/src/main/resources/pics/3.png new file mode 100644 index 0000000000000000000000000000000000000000..92aae787b360e8dc60da8629681040aa596ff183 GIT binary patch literal 592 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LDJR0CNI|Ns9l zdhR3wWHBarySp&{XVSd~FfJXPop`U8a)q*P32>{zBv0tF6R!p2Ojd?F%Qm4R%$)3V!9V|i*23z zgR|)!eovH3WVpPfT-OI2V6pGHWB$hWkoA+-g__s(E5F}RUcAxW?a=i#qHgu9b9ovH z&fB&A_T7Dd%I_GlcROn)|L&UPcCo+X4!g~r=dVxN?zmg~b|T+tInBoR)jGTj0x}dD zzG?r|pVjP=*!lSDleT%qCtUSiHs4z={`soUm*9%Lul^-(FRr<6mUd`{O!B@pnkH#q zBQC}}e%2B^I#rQxOZ$yLb*VW*vH_ZF6sxA>iO2?au2ZbK=-2J0D;3bW&h{Jk_1_Z; zYrXAtgl$!-e1Dz)z$_zko83}vfj%&ZRZCnWN>UO_QmvAUQh^kMk%5tcu7R?*=9V1_n=8KbLh*2~7Ys%;1^; literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/30.png b/src/main/resources/pics/30.png new file mode 100644 index 0000000000000000000000000000000000000000..3a1086fa075bc8e06329cb3523b8d70ad0d00264 GIT binary patch literal 589 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LCuX@+Ws|NsB< z-k0M6vKf=S-CYy>N#?<}QCs=d7s7^;EETSrd9TYpWJ_> zH!j@OHQe#5LiN3DndgPm8(;R$bN-VjGq2}jj^Hhe6W5rG3L8FZU;1ZhqA~yKe_Mur zx!S{<|4Y{WW$OCl9dTbsSRNR@swJ)wB`Jv|saDBFsX&Us$iT=z*T7iU*dWBf+{)0z q%FtLB$S^SY-^Dx&MMG|WN@iLmZVi3ccLUP^1B0ilpUXO@geCw)CfOGN literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/31.png b/src/main/resources/pics/31.png new file mode 100644 index 0000000000000000000000000000000000000000..f687a9ad7b1bc737fa4dd06837ddf57c8c760483 GIT binary patch literal 600 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LCuX@+Ws|NsB< z-k0M6vKf=S-CY>vZ$XX#2Tj#}Gw!P_D!RF*_7Gk=fEk5q%A~xQ|Vh5x21)Stt zFXlGLy^% z+`@TXfxNEwHy_(o({rqg??|`Ihv~O3UYo0T=|wTe{akjIZ~Im(oE?)FK#IZ0z{o(? zz*yJVAjH7j%Fx8h&{!A9FfjPv#XJi|LvDUbW?Cg~4Sm;l1Cs*-gQu&X%Q~loCIDu^ B+j;;1 literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/32.png b/src/main/resources/pics/32.png new file mode 100644 index 0000000000000000000000000000000000000000..e8bb9a2c2631afd80bfe5d5acbd8fafc9611feb4 GIT binary patch literal 1138 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt!VDz$ninksQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIId(qYV5xWi7#NtNJY5_^JdR%t zKV5X&fXC(JhVf!(YUgeG_`8lP2UhsZlQMGoO!Ul$npNi?t z9W`blDs!4XyTI1NmE5cS)%b*ss;Sfpk+>FhfylS9 zovvLj4_jxo=PMLf969|S8kO*yxg0}aZ;0qQEmO7P~xh(m*e~8q~1=eXT>(4BV6v$kd z*0tnWLz^#eQN!V6m!b-VXMz3B$1_5Y`@H4a>TfKhv)gBjrm3uz+GLrve4*1M9o0_S z33rCg4%z0DV?*nwk5uN#BUZkdHcJr>AY7C=lXA0cWxUu%fsA_ zSGVeByqtPkPv!J=kCnZ~$HkuUow8ne$MN;I7sYR;$4uy{4Xb7Li0rbNeIa2+(baeE zx6R%$+nCkeVEokgmLqlb@-u&x9haVZG?V?p!?X}N9($SlqDvmPAFF53^{e45u{`?1 z_1^jVU9tZd->xpyH~A>i56pO~C9V-ADTyViR>?)FK#IZ0z{o(?z*yJVAjH7j%Fx8h p&{!A9FfjPv#XJi|LvDUbW?Cg~4Sm;l0}BcU22WQ%mvv4FO#n!%(?kFO literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/33.png b/src/main/resources/pics/33.png new file mode 100644 index 0000000000000000000000000000000000000000..404a96e19c8c55ec16411c092f0f237b7a1c8e5d GIT binary patch literal 1244 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt!VDz$ninksQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIYh7ML)4Y{wesQoIuz#h*5%XW~g`)CHwo6^vQ*~88avzsfQTMpxfBWDk-XsIz6)isV_$Iiw z{BL@sU%B>H=DELn99QKF>=RwE^`_eLxsoitoR=6ME&nIxvXa4ZQlr}v+pJSuo_A&I zwtVRAkNj~f<#SK_M4NTq95NrdC*;0l*~|1#ykt*s<5n4k8`b=ivJ!cd_I&+vfJN!H zQs2F`j+1};3Jc6Ms#-QnMQwrCy!?BA|I0H!+LXHDpl#;0V=VIzE-`(&TPP&iU~z%W z#4TGz0#~n<51j9y;I;32(6w6MYj0M(wB~Snu;l8!?_J?{Cza212;;tEb|u|C<*ZO^ z?}AlR zH5XhoGA`XBDkGxtDyXHeXI2RBc7|pvk)lTVg1irWyBrU=u^ql;B4yO;vt~xzkDh56 z9O|!2Q|0wS@+X(G@dRAuQ@grJ z*X-H37yLpQtS+}CHZ1k^=~j5Q;rfFG+n7oN?A>g(>pF8O{&42}tSs45UUH&ow=BXCj5|l`{v7}dk6WY9Wz$CD$QE4 zBh+*GnorUktBg*aD|5TI#U&>vPf1RIyM?+yhC!&|QwzZxuXKLQAI_^Av>w(%1qe9sdu7g(2+*>3r-qF0jAo=NpuIj6oE+s82vW_i2{`9D= z*sFxIQC8C*Zhi5iajRHaBJbJH3`HJ}Vu`%QPdYg_atW9f>HfU&DBO4B6aIh*}P9W6{&neS5B5W^@Yj57^s0k8iR0h09;xH?2EZv#0LM z?5C`M7+<_rKDPhK_viET;_DfYh7ML)4Pr}34L9eyU&j;S$f`|NxL1D?(zzA zZV&h+BD7KZQ}Bs`yLRUe-DTmtTUGv|`DX1h7k_bWp?hiDJ8w?;d+0$pyGCdIr~9rS zxf86MJ?XmKebCQJ9>ejSnV(DcXYwatWXRV5H*ynOhPWB6{;jG=uFDO_$*F4OB zF!j!&1}VpFR)X!;9GBc0#U43?zFtuFzIwv$R`q?-4s4S1=dNAva$j!x;Wz*O`Tb^^ z{l@E|*^HA<`7dPeT_tv4;CMAA9;XUJ-=wNcq} z4vSKSx-VTSZ(J&uRCHWtl4qx%>32ngS{pgGl{{Wb^V*hLI4|1jd6XkrvM)$n-Q`^; zWB9~`xU7wgPrOvcP6it3v?_jCpgFVEbX_TDP@9*MYvhxEjFn|=DU;Ga-a2jQl-c&) zPt$K^v6=2nuhTI(E0#D`vTsv&Yv?X%oAIJyOHm$&)sHP(pUSRUJNKf<)Mq&hvn8^X zZ$@$1D%PL(T6;lTeAfY=rISj$)-2h%?yom@tF`|GvDLvhZ?D^1dSAD2rO9+nIR}Cnu}NL_>9rA9FdqI?|p#p71G{eJX>0M2th;pGY^a z#+yz{R|MIVmMjpN_(a$zQ!MR4GQ+{6M%ABAZ4$fBuxZx7$8h)Zk~RevZw78>hn176I8sBdJ#xP9;NZ-E@=RUup3gbek?fgs zEz-Xk+!o&6p7zZ$f9Abc#|2cRm|rm+TBW$SN7m}i@lHFI-^Fp&)wenM1d1--+5Ei7 zTD7ILyw35N^!eLiSIU+vPcd71_}H_~d(J24=0`vIu5kaTwZ;$r?aU50&a>oSsM>S0 zPl(y$UGqulYgu*H_rCBZ-#z_JIEa1g-<{KppNQQrJ9pz7pMC4vc~%|jhs)k@|7QBV z@&m`hBiETz{HpCvGVZeJxuwXxuDyoo=JqLepGxN3{d$=1;2-@ARr{v8Jjve^?Z5xu z_4g06Ca=Snz8TZj19OmSiEBhjN@7W>RdP`(kYX@0Ff!0JFxE9T2r)3XGBmL=G}Z+& l3=IBvG0#HLkei>9nO2EgL*Mn?!0gVz;OXk;vd$@?2>?i^%3J^d literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/35.png b/src/main/resources/pics/35.png new file mode 100644 index 0000000000000000000000000000000000000000..d98e95dad70bb929f7513be5a3cf243897f0e06e GIT binary patch literal 457 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LDJR0CNI|Ns9l zdhR3wWHBarySp&{XVSd~1s4LDc> z^!K{>UG{EB`4+;p{G^j#fY#0BIcn}rAxn8AUw!1=_WyX`Dc&=?rmvj6RczZA8}+_` z#T+li7W&F^S}-MeU(wNB@nvq4?;O@cDpy~v<8shUniDI$vG7RJf7aeF?7b^xOy=EO z|Kxkt)=7%24$hnE=V%7ac^lU&R-I(G_f)?)&(*Q4E}d9&qC3Vo1c=IR*73f-}T+V@MmD~boFyt I=akR{0CfMIAOHXW literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/4.png b/src/main/resources/pics/4.png new file mode 100644 index 0000000000000000000000000000000000000000..28e7695a72cf4d8af4d443f19c2d2ffbd9c8f9ee GIT binary patch literal 611 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LCuX@+Ws|NsB< z-k0M6vKf=S-CYX>&#fyZ_K zCaI0HT=@mgaurTlSEXX|%5lvaf#qusCP*YK*}}W}iKNZH8tZ23w$k>_!=DP27m02v zn|+~A^5WJP6U?tG&i6Esoz#?>tve4=@`Lca+AiP^4ZMRO*Y?{}_Il70AC zKqSpYZ}A1id?q=ACx5nyAF(W~=$o){(!FJO*GyDiWvIBM##gy((<{;EyKG&HOfAJf zvN>M)k~iPc{m9w;t9+hvdsTzIr#{j9wEjv^?7k%7y@A$88pW;WT&e7peST3{d8_i$ zbn72GZv~os(+xK*(|!JE!@q8^m(P`+1&KHPtFe6kt}gY~pY9it)1uneE6N7C-c?A>E}K&Lyd$pUr^XbeU%UCmZ!t=}<+w3hwz8O~ zOg?&@V(!kiFU@x*tn(IBThbJFO{li-g>?Mm`45lt|J*0_hgx-k*5>pV+#AjaKl#@_GcRCDO2j{%O`LCoZhi_D&S{XJ@AqlV-noAD zpEfUB9$dlobo+G0iSwIG{=~iTb34=`vYtmLd*#wA64^SzhaGP@_g!2N^mBHca|o}d znTO{@4d)ZPq7;qqERb1k>XemvL*cS%#xgwJy$L^gSG&@pD_tPk}{~QhwHOMEW@15BDhL*F3g` zMb51}Hl*p<>WhDWN67M93NDh>E4QBipyKt0&9cw?(l$DA>$-=mn|I8-$M>o2^;t`{ zmaqpIi*8kVvF^02Do;lI-Qxm2Hw8j1n^q*dMFmSQGic6IKgW6|7;L|C?7sW_Ux z)^(G`(vBHTb6&T;XIiQhI_tfj;fgB1=fy^<277H zk_@kv<@||v9Hr)M6?f-aSi-BgdWZU(OS1x2ER|MmnR-iL(PLTvod+gf-oIz}EzX5? z%!#_0E6)ohel+#GSn9q|ZLQyVYv)z>3c0?n{b=8rRGjRpO zCauJ_keDc+e^~`D&RX*6OTLmaKQ>{Jm9et(t3;^<&L^gOZJx00AZKgdr6#KkpW_}p zEfKc&J}R0B-dZZdrK+9-D{NJbKh)!RB=C21I)ALX3wq08Px=zme zU|i?Fq&hRFm|yyuJ+`UQ$B$22e6mc_E_K3NzumdXIdRXPE%!Nb=7w`wvO}3kq7VN9 zK~>udo|h7H-#*%X;P^D7h3`{KVq*Fgw_cI^Vd5WG#uw;R_U%!1^EBl%HfwxTr%zKf zS)FQCH^Dk4y;pz2JLdQzc8eo%r@n7t#+re|Z(&Xb;mW37zf<6^(nsV1< zf3d{W*azje^IJ21oOieu*An;Z@YhrE+yC#9UedyMCQTHWu~bW3BT7;dOH!?pi&B9U zgOP!efv$nEuCYOgfw`5TiIt(TE|6hh@V|?B7K(=4{FKbJO57UyuI~mG1PlzGu6{1- HoD!MpUTRdGHLp+XOo$g)q z+JeXRqC)k&?aU&fw&|R446kSYjt{=i?bWw$sn6mgWu11reh4t7{B)nkC-n5CfnaUR zss4rT0u8pmTsflli|@KAc-OHoO)D*U+P&*XWEG^%xMIPgJw}h!J!IyUbDU+kwIlMJSAkZww*i~POs#OQ%c~hb?c_SYD9rET z!2op=*I1Ku!+D=no~>`2J0YYkU_q>mI9C2s1v9(jq&fNQR3&ZD65%`#Hcvo|wMxdMW z#k1@%DU*Lp}Jz%ckf;EYY(v-%@bU> zSaeThLlvvx2eGH!3JSUz??0)7qdjZ=OuE zxRV;#HFJ~G>l2RWSMIoD@o)d~%S>wzpRO^)Um3NgR75@fBbVr1r^kZF0vHU< zgTGYuT5di0>2L9U_V(lA`#Rb`m|wh}xKaP-=RcRYh7ML)45<=;a_*+u9DJyT~W^M z3;u|xEHXc_T;b?zvpH?Md4)<=-*wE5}WP0)>_q=Sn z?$BJ3bgF2Hio)v^CudGO#A3Z7W3H*_=Z1$3qCdp+OhW@07Ps0zsFa@(Gx>R6+t12* zho(xLOc9Ux(rjhG`KPxqz3WR&iWBd|R#8?>pMsm~@7xXf6PnEQf>kXoE#;KXmR;QL zIUX}+T$yyO^|oA+^h}QpT=lNTDtq13s@%_?^)Xo3bGe#f;hWi^OP0Ex`D`e&d1g~+ zqpXJ7?jwpZN4{B~@IGq78`^M7vs-kwcj1{L?V9Jr6@(O9%cLh+#5Xz|u@<-4@#T7B5{sg24)enMm;N)P?mxBncG~-0 z>H^kBubEa&{x+|OCDP($?y1`o%a=1Qxwq`ots8sJ^DdAvyKql?KYL2-AC{oF=tqZY z88~kHY%ng&l5kk;&$GOi@237ngN7WTOS$W2zSdlm-uB#fee)%TP3Mn%_FEV|r`lk+(3@pbVI)R7+eVN>UO_QmvAUQh^kM zk%5tcu7R?*^6t3=E#GelF{r G5}E)@BiK9u literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/8.png b/src/main/resources/pics/8.png new file mode 100644 index 0000000000000000000000000000000000000000..5d7226a94a66f63415a67ba4c2b6d56edb7d8062 GIT binary patch literal 614 zcmeAS@N?(olHy`uVBq!ia0vp^?m(Qt#0(^j%G;TM6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZMQN_=LCuX@+Ws|NsB< z-k0M6vKf=S-CYO{NE5AEq+J<>e%7Oy?OZGb0-_}@jh4b}$jfE@ciTOBQPhP&rzemW#&P16d z_0$2oL%GYpJSzM4{CDZLqV-#=*2=sxer5mPoqgrvOY@IzR$u#6>ZOU*q=Jp>R_DIn z#}g2@Yg=)Atz&qtXRY%bH;-w8zoZt)t9^X;Qds_9>W^={Pn!Ccojmie3K;OJC9V-A zDTyViR>?)FK#IZ0z{o(?z*yJVAjH7j%Fx8h&{!A9FfjPv#XJi|LvDUbW?Cg~4Sm;l R0}}@WgQu&X%Q~loCIG|A21sKVg<*=i?2QdiM_~t>Ee67-Dhy=~UZ3CPSXq z;NZXlt{t4B?zftn1FVI1GVb*6n=my;CcUobYz8~m79q{FMdv>rsjR!$l53DGZI*D% z|3-94WmizCMsd5%B$9bpK zk?2Ymo9VKg;(|%uj3?hOm78Fp*FI@0@5ini{d!aRI8=BzMNchqT;nw-`}$Iy?D<{J zg`wH|St5-;UY@nm^ummnE7kA0WuIY5ID2J+;pMnTx;8UC`~GIAIUe(Qn|kI;yww*= z5nHdL3uETSwS6#skmLuOCE_*6?=BT7;dOH!?pi&B9U zgOP!efv$nEuCYOgfw`5TiIt(TE|6hh@V|?B7K(=4{FKbJO57UyuI~oMG6RFBtDnm{ Hr-UW|$S;_Ik#Wl3bs%1n zx4R1i!>V3oBOs5x#M9T6{Ry)iJ0EYb)VqH`Aq`I##}JO|$v?jLKlGP9%x~SsU&O=z zLxOLEp@~7GfQ*5u!4}!36^sm?r+D5zQQR8^)T3JB8c~vxSdwa$T$Bo=7>o>z40H{Q zb&U-|49u+zO{@%!b%6{6ga2L3vrshT=BH$)RpQprcYQa|lMD==u6{1-oD!M14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a> zMHowh{DK)Ap4~_Tagw~D~i!*h@TpUD=;7%dzwE21~vB2Ncrsba4#fxSqUW zWx|Tzhd;cRKU}X8@}IrsO})X1`h*Ad2M*LVH12F{oXE()+$ub^B~CI0s7tlPHKHUX zu_Vlzz`7?@ibnphbc>jD`D2LHR5XQ61w%}>cptHiCL@A___ Qw;32bUHx3vIVCg!00EL)Z2$lO literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/c2.png b/src/main/resources/pics/c2.png new file mode 100644 index 0000000000000000000000000000000000000000..c78cf66d38c7a656e961f30aa84d464c8744afff GIT binary patch literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^{2!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8U)v6XN#39}qKA8)YKyMI6-BTpB{5RU7~ z2?;3-OkwN_ic$%>ntV)lMD{0XW`Fo z0ClUDxJHzuB$lLFB^RXvDF!10BLiInV_jo|5Cd~7LlY}QV_hJ_z~Fxu^DGn%x%nxX YX_dG&^j+T#3;_lPPgg&ebxsLQ0FLNZUjP6A literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/c3.png b/src/main/resources/pics/c3.png new file mode 100644 index 0000000000000000000000000000000000000000..556af63f25c68fa6b1f0874b4bb133853e9ed7b0 GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^{2$S;_Ik#Wl3bs%1n zx4R1i!>V3oBOs5x#M9T6{Ry)iJ0EYb)VqH`Ar(&-#}JO|$v=XRe>mLtuvtcg@uvG7 z38@(p4;HNKe0al-29Zxv`X9>`mXN=dXRy^)78&qol`;+0Eq@rR{#J2 literal 0 HcmV?d00001 diff --git a/src/main/resources/pics/dos.png b/src/main/resources/pics/dos.png new file mode 100644 index 0000000000000000000000000000000000000000..3200712c447ed153b4b050b36e300756c9d10a65 GIT binary patch literal 6239 zcmV-l7@+5gP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00093 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 JPDHLkV1g|Li!uNJ literal 0 HcmV?d00001 diff --git a/src/test/java/RulesTests.java b/src/test/java/RulesTests.java new file mode 100644 index 0000000..f2e870f --- /dev/null +++ b/src/test/java/RulesTests.java @@ -0,0 +1,34 @@ +import com.leflat.jass.common.Card; +import com.leflat.jass.common.Rules; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + + +public class RulesTests { + @Test + public void test_bourg_sec() { + int[] list = {Card.RANK_6, Card.RANK_8, Card.RANK_BOURG, 20, 21, 22}; + List hand = Arrays.stream(list).mapToObj(Card::new).collect(Collectors.toList()); + assertFalse(Rules.hasBourSec(hand, Card.COLOR_SPADE)); + + int[] list2 = {Card.RANK_BOURG, 20, 21, 22, 28, 29, 30}; + List hand2 = Arrays.stream(list2).mapToObj(Card::new).collect(Collectors.toList()); + assertTrue(Rules.hasBourSec(hand2, Card.COLOR_SPADE)); + } + + @Test + public void test_has_color() { + int[] list = {1, 3, 10, 12, 20, 30, 31}; + List hand = Arrays.stream(list).mapToObj(Card::new).collect(Collectors.toList()); + assertTrue(Rules.hasColor(hand, Card.COLOR_HEART)); + + int[] list2 = {1, 3, 4, 8, 20, 30, 31, 32}; + List hand2 = Arrays.stream(list2).mapToObj(Card::new).collect(Collectors.toList()); + assertFalse(Rules.hasColor(hand2, Card.COLOR_HEART)); + } +} diff --git a/src/test/java/ServerTests.java b/src/test/java/ServerTests.java new file mode 100644 index 0000000..913ba40 --- /dev/null +++ b/src/test/java/ServerTests.java @@ -0,0 +1,74 @@ +import com.leflat.jass.common.Anouncement; +import com.leflat.jass.common.Card; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +public class ServerTests { + @Test + public void card_test() { + var card = new Card(Card.RANK_BOURG, Card.COLOR_SPADE); + assertEquals(2, card.getValue()); + assertEquals(20, card.getValue(Card.COLOR_SPADE)); + assertEquals(Card.COLOR_SPADE, card.getColor()); + assertEquals(Card.RANK_BOURG, card.getRank()); + assertEquals("bourg de pique", card.toString()); + assertEquals(5, card.getNumber()); + var card2 = new Card(12); + assertEquals(Card.RANK_NELL, card2.getRank()); + assertEquals(Card.COLOR_HEART, card2.getColor()); + } + + @Test + public void anouncement_test() { + var card = new Card(Card.RANK_DAME, Card.COLOR_CLUB); + var anouncement = new Anouncement(Anouncement.FIFTY, card); + assertEquals("cinquante à la dame de trèfle", anouncement.toString()); + assertEquals(50, anouncement.getValue()); + } + + @Test + public void find_square_test() { + int[] list = {5, 9, 12, 14, 19, 23, 24, 32}; + var annoucements = Anouncement.findAnouncements(Arrays.stream(list).mapToObj(Card::new).collect(Collectors.toList())); + assertEquals(1, annoucements.size()); + var anouncement = annoucements.get(0); + assertEquals(anouncement.getType(), Anouncement.BOURG_SQUARE); + assertEquals(anouncement.getCard().getNumber(), 5); + } + + @Test + public void find_suit_test() { + int[] list = {5, 9, 12, 13, 14, 19, 20, 21, 22, 24, 32}; + var annoucements = Anouncement.findAnouncements(Arrays.stream(list).mapToObj(Card::new).collect(Collectors.toList())); + assertEquals(2, annoucements.size()); + var firstAnouncement = annoucements.get(0); + assertEquals(firstAnouncement.getType(), Anouncement.THREE_CARDS); + assertEquals(firstAnouncement.getCard().getNumber(), 14); + var secondAnouncement = annoucements.get(1); + assertEquals(secondAnouncement.getType(), Anouncement.FIFTY); + assertEquals(secondAnouncement.getCard().getNumber(), 22); + } + + @Test + public void find_stoeck_test() { + int[] list = {5, 9, 12, 13, 14, 19, 20, 21, 22, 24, 25}; + assertFalse(Anouncement.findStoeck(Arrays.stream(list).mapToObj(Card::new).collect(Collectors.toList()), Card.COLOR_HEART)); + assertTrue(Anouncement.findStoeck(Arrays.stream(list).mapToObj(Card::new).collect(Collectors.toList()), Card.COLOR_DIAMOND)); + } + + @Test + public void card_sort_test() { + int[] list = {20, 30, 1, 3, 15, 12, 8, 35, 34}; + List hand = Arrays.stream(list).mapToObj(Card::new).collect(Collectors.toList()); + Card.sort(hand); + for (int i=1; i