diff --git a/build.gradle b/build.gradle index 6d5ac2c..4501f8b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'java' apply plugin: 'application' -mainClassName = "com.zuehlke.jasschallenge.Application" +mainClassName = "com.zuehlke.jasschallenge.RandomBot" compileJava { sourceCompatibility = '1.8' diff --git a/doc/images/chooseUsername.PNG b/doc/images/chooseUsername.PNG deleted file mode 100644 index 4f10c3e..0000000 Binary files a/doc/images/chooseUsername.PNG and /dev/null differ diff --git a/doc/images/createTournament.PNG b/doc/images/createTournament.PNG deleted file mode 100644 index 307d6ae..0000000 Binary files a/doc/images/createTournament.PNG and /dev/null differ diff --git a/doc/images/result.png b/doc/images/result.png new file mode 100644 index 0000000..7043f40 Binary files /dev/null and b/doc/images/result.png differ diff --git a/doc/images/tournament.PNG b/doc/images/tournament.PNG new file mode 100644 index 0000000..2c03604 Binary files /dev/null and b/doc/images/tournament.PNG differ diff --git a/doc/images/tournamentPage.PNG b/doc/images/tournamentPage.PNG deleted file mode 100644 index bd07d78..0000000 Binary files a/doc/images/tournamentPage.PNG and /dev/null differ diff --git a/doc/images/tournamentReady.PNG b/doc/images/tournamentReady.PNG new file mode 100644 index 0000000..676de33 Binary files /dev/null and b/doc/images/tournamentReady.PNG differ diff --git a/doc/images/tournament_page.PNG b/doc/images/tournament_page.PNG new file mode 100644 index 0000000..2de20b8 Binary files /dev/null and b/doc/images/tournament_page.PNG differ diff --git a/doc/images/username.PNG b/doc/images/username.PNG new file mode 100644 index 0000000..cdd5567 Binary files /dev/null and b/doc/images/username.PNG differ diff --git a/readme.md b/readme.md index 3b07d46..4197081 100644 --- a/readme.md +++ b/readme.md @@ -1,81 +1,63 @@ -# jass-challenge-client-java ![Build Status](https://travis-ci.org/webplatformz/challenge-client-java.svg?branch=master) +# Jass Challenge 2020 -This is a Java client (bot) for the [Jass challenge server](https://github.com/webplatformz/challenge). +This is a Java client (bot) for the [Jass challenge server](https://github.com/RFS-0/challenge). This client allows you to easily develop a bot for the Jass challenge. -### Wiki (Server): -https://github.com/webplatformz/challenge/wiki - -### JassChallenge2017 -If you are an enrolled student in switzerland, you are welcome to participate the **JassChallenge2017** competition in April '17 - -https://jass-challenge.zuehlke.io/ - +Note: This readme refers to the forked versions of the jass challenge server and the java client. +The original server and client can be found here: +* [original jass challenge server](https://github.com/webplatformz/challenge) +* [original java client](https://github.com/webplatformz/challenge-client-java) +The forked version of the server offers a description how to deploy it as a kubernetes deployment (on GCP). +The forked version of the client offers some utility classes that should help you speed up the development of a bot. ## Getting started -Clone this repository and start (`gradlew run`) the [Application](src/main/java/com/zuehlke/jasschallenge/Application.java) class: - -``` java -public class Application { - //CHALLENGE2017: Set your bot name - private static final String BOT_NAME = "awesomeJavaBot"; - //CHALLENGE2017: Set your own strategy - private static final RandomJassStrategy STRATEGY = new RandomJassStrategy(); - - private static final String LOCAL_URL = "ws://localhost:3000"; - - public static void main(String[] args) throws Exception { - String websocketUrl = parseWebsocketUrlOrDefault(args); - - Player myLocalPlayer = new Player(BOT_NAME, STRATEGY); - startGame(websocketUrl, myLocalPlayer, SessionType.TOURNAMENT); - } -} -``` - -The client needs the challenge server to connect to. Clone the challenge server and run npm start. For more information -go to [Jass challenge server](https://github.com/webplatformz/challenge) repository. - -## Implement your own bot - -To implement your own bot you need to provide an implementation of the -[JassStrategy](src/main/java/com/zuehlke/jasschallenge/client/game/strategy/JassStrategy.java) interface: - -``` java -public interface JassStrategy { - Mode chooseTrumpf(Set availableCards, GameSession session); - Card chooseCard(Set availableCards, GameSession session); - - default void onSessionStarted(GameSession session) {} - default void onGameStarted(GameSession session) {} - default void onMoveMade(Move move, GameSession session) {} - default void onGameFinished() {} - default void onSessionFinished() {} -} -``` - -## Start your own tournament -To test your bot against other bots, such das the random bot, you need to start your own tournament. - -1. start the challenge server: -`npm start` -2. Browse to http://localhosthost:3000 -3. Enter some user name: - -![Alt text](doc/images/chooseUsername.PNG?raw=true "Choose a user name") -4. Enter some tournament name and press **Enter** - -![Alt text](doc/images/createTournament.PNG?raw=true "Choose a user name") - -5. Join your bots, they should appear on the next page - -![Alt text](doc/images/tournamentPage.PNG?raw=true "Choose a user name") - -6. Join random strategy bots for testing. In your challenge server directory enter the command: -`npm run bot:start` -This will add 4 random bot teams to your tournament. +1. Clone the forked server: `git clone https://github.com/RFS-0/challenge.git` +2. Clone this repository (i.e. the java client): `https://github.com/RFS-0/challenge-client-java.git` + +## Running the server + +To run the server execute the following commands: +1. `npm run clean` +2. `npm run build` +3. `npm run start:tournament` + +This will start the jass challenge server on `localhost:3000`. + +## How to create a new jass bot + +1. Create an implementation of the [JassStrategy](src/main/java/com/zuehlke/jasschallenge/client/game/strategy/JassStrategy.java) interface. This implementation will be your bot. See [RandomJassStrategy](src/main/java/com/zuehlke/jasschallenge/client/game/strategy/RandomJassStrategy.java) or [StrongestOrWeakestStrategy](src/main/java/com/zuehlke/jasschallenge/client/game/strategy/StrongestOrWeakestStrategy.java) for reference. + * You might want to check out the [utils](src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils) package, it provides some helpful classes to implement a new bot. +2. Create a new application to run your bot. See [RandomBot](src/main/java/com/zuehlke/jasschallenge/RandomBot.java) or [StrongestOrWeakestBot](src/main/java/com/zuehlke/jasschallenge/StrongestOrWeakestBot.java) for reference. Just replace the values of the following attributes with your own values: + * `BOT_NAME` -> define the name of your bot; must be unique among all bots + * `STRATEGY` -> instantiate your implementation of the [JassStrategy](src/main/java/com/zuehlke/jasschallenge/client/game/strategy/JassStrategy.java) interface + * `SERVER_URL` -> once the tournament begins this has to be updated with the ip address and port of the remote jass challenge server. Set it to `ws://127.0.0.1:3000` during development. + +## How to run the client + +1. Run `./gradlew clean` +2. Run `./gradlew build` +3. Execute the `main` method of your bot. Or if you want to run the application with the `./gradlew run` command be sure to update the `mainClassName` in [build.gradle](build.gradle). +4. Execute step 3 again. Because to form a team, two instances of your bot must be connected to the jass challenge server. Teams are formed via the bot name. This is why each team should use a unique name for their bot and you have to run your client twice to form a team. + +### Example development scenario + +1. You implemented a first version of your bot and now you would like to test it. For this you first start the [jass challenge server](https://github.com/RFS-0/challenge). +2. Switch to your clone of the server repository and execute `npm run start:tournament` +3. Go to `localhost:3000` and enter some user name, e.g. `user`: +![Alt text](doc/images/username.PNG?raw=true "Choose a user name") +4. Enter some tournament name, e.g. `tournament` and click enter: +![Alt text](doc/images/tournament.PNG?raw=true "Choose a user name") +5. You should now see this screen: +![Alt text](doc/images/tournament_page.PNG?raw=true "Tournament page") +6. To test against bots that use the random strategy you have two options. Either run the main method of the [RandomBot](src/main/java/com/zuehlke/jasschallenge/RandomBot.java) twice or run `npm run bot:start` in the server repo to start 4 teams of random bots. The [StrongestOrWeakestBot](src/main/java/com/zuehlke/jasschallenge/StrongestOrWeakestBot.java) is only available in this version of the java client, so to evaluate your bot against it you have to run is main method twice. +7. Do not forget to execute the main method of your bot twice as well! +8. Once all the clients have connected to the server the tournament page it should look like this: +![Alt text](doc/images/tournamentReady.PNG?raw=true "Tournament ready") +9. Click start to run the tournament. +10. Click show rankings to see how well your bot performed: +![Alt text](doc/images/result.PNG?raw=true "Tournament result") ## Contributors ## Thanks to [fluescher](https://github.com/fluescher) for creating this skeleton. diff --git a/src/main/java/com/zuehlke/jasschallenge/Application.java b/src/main/java/com/zuehlke/jasschallenge/RandomBot.java similarity index 74% rename from src/main/java/com/zuehlke/jasschallenge/Application.java rename to src/main/java/com/zuehlke/jasschallenge/RandomBot.java index 269bc09..3ec8223 100644 --- a/src/main/java/com/zuehlke/jasschallenge/Application.java +++ b/src/main/java/com/zuehlke/jasschallenge/RandomBot.java @@ -4,6 +4,7 @@ import com.zuehlke.jasschallenge.client.game.Player; import com.zuehlke.jasschallenge.client.game.strategy.RandomJassStrategy; import com.zuehlke.jasschallenge.messages.type.SessionType; + import java.util.Arrays; /** @@ -14,21 +15,18 @@ * gradlew run [websocketUrl] * */ -public class Application { - //CHALLENGE2017: Set your bot name - private static final String BOT_NAME = "awesomeJavaBot"; - //CHALLENGE2017: Set your own strategy +public class RandomBot { + private static final String BOT_NAME = "random_bot"; private static final RandomJassStrategy STRATEGY = new RandomJassStrategy(); - - private static final String LOCAL_URL = "ws://127.0.0.1:3000"; + private static final String SERVER_URL = "ws://127.0.0.1:3000"; public static void main(String[] args) throws Exception { String websocketUrl = parseWebsocketUrlOrDefault(args); - Player myLocalPlayer = new Player(BOT_NAME, STRATEGY); + Player randomPlayer = new Player(BOT_NAME, STRATEGY); System.out.println("Connecting... Server socket URL: " + websocketUrl); - startGame(websocketUrl, myLocalPlayer, SessionType.TOURNAMENT); + startGame(websocketUrl, randomPlayer, SessionType.SINGLE_GAME); } @@ -37,11 +35,11 @@ private static String parseWebsocketUrlOrDefault(String[] args) { System.out.println("Arguments: " + Arrays.toString(args)); return args[0]; } - return LOCAL_URL; + return SERVER_URL; } private static void startGame(String targetUrl, Player myLocalPlayer, SessionType sessionType) throws Exception { - RemoteGame remoteGame = new RemoteGame(targetUrl, myLocalPlayer, sessionType); + RemoteGame remoteGame = new RemoteGame(targetUrl, myLocalPlayer, sessionType, "tournament"); remoteGame.start(); } } diff --git a/src/main/java/com/zuehlke/jasschallenge/StrongestOrWeakestBot.java b/src/main/java/com/zuehlke/jasschallenge/StrongestOrWeakestBot.java new file mode 100644 index 0000000..5aab7cb --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/StrongestOrWeakestBot.java @@ -0,0 +1,45 @@ +package com.zuehlke.jasschallenge; + +import com.zuehlke.jasschallenge.client.RemoteGame; +import com.zuehlke.jasschallenge.client.game.Player; +import com.zuehlke.jasschallenge.client.game.strategy.StrongestOrWeakestStrategy; +import com.zuehlke.jasschallenge.messages.type.SessionType; + +import java.util.Arrays; + +/** + * Starts one bot in tournament mode. Add your own strategy to compete in the Jass Challenge Tournament 2017! + *

+ * To start from CLI use + *
+ *     gradlew run [websocketUrl]
+ * 
+ */ +public class StrongestOrWeakestBot { + private static final String BOT_NAME = "strongest_or_weakest_bot"; + private static final StrongestOrWeakestStrategy STRATEGY = new StrongestOrWeakestStrategy(); + private static final String SERVER_URL = "ws://127.0.0.1:3000"; + + public static void main(String[] args) throws Exception { + String websocketUrl = parseWebsocketUrlOrDefault(args); + + Player strongestOrWeakestPlayer = new Player(BOT_NAME, STRATEGY); + + System.out.println("Connecting... Server socket URL: " + websocketUrl); + startGame(websocketUrl, strongestOrWeakestPlayer, SessionType.TOURNAMENT); + } + + + private static String parseWebsocketUrlOrDefault(String[] args) { + if (args.length > 0) { + System.out.println("Arguments: " + Arrays.toString(args)); + return args[0]; + } + return SERVER_URL; + } + + private static void startGame(String targetUrl, Player myLocalPlayer, SessionType sessionType) throws Exception { + RemoteGame remoteGame = new RemoteGame(targetUrl, myLocalPlayer, sessionType, "tournament"); + remoteGame.start(); + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/RemoteGame.java b/src/main/java/com/zuehlke/jasschallenge/client/RemoteGame.java index 95963f3..bfe62e8 100644 --- a/src/main/java/com/zuehlke/jasschallenge/client/RemoteGame.java +++ b/src/main/java/com/zuehlke/jasschallenge/client/RemoteGame.java @@ -19,18 +19,20 @@ public class RemoteGame implements Game { private final Player player; private final String targetUrl; private final SessionType sessionType; + private final String sessionName; - public RemoteGame(String targetUrl, Player player, SessionType sessionType) { + public RemoteGame(String targetUrl, Player player, SessionType sessionType, String sessionName) { this.targetUrl = targetUrl; this.player = player; this.sessionType = sessionType; + this.sessionName = sessionName; } @Override public void start() throws Exception { final WebSocketClient client = new WebSocketClient(); try { - RemoteGameSocket socket = new RemoteGameSocket(new GameHandler(player, sessionType)); + RemoteGameSocket socket = new RemoteGameSocket(new GameHandler(player, sessionType, sessionName)); client.start(); URI uri = new URI(targetUrl); diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/GameSessions.java b/src/main/java/com/zuehlke/jasschallenge/client/game/GameSessions.java new file mode 100644 index 0000000..6ce83cc --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/GameSessions.java @@ -0,0 +1,15 @@ +package com.zuehlke.jasschallenge.client.game; + +import com.zuehlke.jasschallenge.game.cards.Card; + +import java.util.List; +import java.util.stream.Collectors; + +public final class GameSessions { + + public static List getCardsPlayedInCurrentRound(GameSession session) { + return session.getCurrentRound().getMoves().stream() + .map(Move::getPlayedCard) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/Rounds.java b/src/main/java/com/zuehlke/jasschallenge/client/game/Rounds.java new file mode 100644 index 0000000..ece8db8 --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/Rounds.java @@ -0,0 +1,21 @@ +package com.zuehlke.jasschallenge.client.game; + +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.cards.Color; + +import java.util.List; + +public final class Rounds { + public static boolean isFirstCardOfRound(List playedCards) { + return playedCards.size() < 1; + } + + public static Color getColorOfRound(List playedCards) { + Card firstCardOfRound = playedCards.get(0); + return firstCardOfRound.getColor(); + } + + public static boolean isNewRound(GameSession session, int currentRoundNumber) { + return session.getCurrentRound().getRoundNumber() != currentRoundNumber; + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/StrongestOrWeakestStrategy.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/StrongestOrWeakestStrategy.java new file mode 100644 index 0000000..0be52ed --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/StrongestOrWeakestStrategy.java @@ -0,0 +1,50 @@ +package com.zuehlke.jasschallenge.client.game.strategy; + +import com.zuehlke.jasschallenge.client.game.GameSession; +import com.zuehlke.jasschallenge.client.game.Move; +import com.zuehlke.jasschallenge.client.game.strategy.utils.Strategy; +import com.zuehlke.jasschallenge.client.game.strategy.utils.StrategyBuilder; +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.mode.Mode; + +import java.util.*; + +import static com.zuehlke.jasschallenge.client.game.strategy.utils.rules.ChooseCardRules.*; +import static com.zuehlke.jasschallenge.client.game.strategy.utils.rules.ChooseTrumpfRules.CHOOSE_TRUMPF_WITH_HIGHEST_TOTAL_RANK; + +public class StrongestOrWeakestStrategy implements JassStrategy { + + private Strategy strategy; + private List allMoves = new ArrayList<>(); + + public StrongestOrWeakestStrategy() { + strategy = new StrategyBuilder() + .chooseTrumpfRules() + .addRule(CHOOSE_TRUMPF_WITH_HIGHEST_TOTAL_RANK) + .chooseCardRules() + .addRule(CHOOSE_STRONGEST_CARD_IF_ROUND_CAN_BE_WON) + .addRule(CHOOSE_WEAKEST_CARD_THAT_CAN_BE_APPLIED_TO_ROUND) + .addRule(CHOOSE_WEAKEST_CARD) + .build(); + } + + @Override + public Mode chooseTrumpf(Set availableCards, GameSession session, boolean isGschobe) { + return strategy.applyChooseTrumpfRules(availableCards, session, isGschobe); + } + + @Override + public Card chooseCard(Set availableCards, GameSession session) { + return strategy.applyChooseCardRules(availableCards, session, allMoves); + } + + @Override + public void onGameStarted(GameSession session) { + allMoves.clear(); + } + + @Override + public void onMoveMade(Move move, GameSession session) { + allMoves.add(move); + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/Strategy.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/Strategy.java new file mode 100644 index 0000000..caa968b --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/Strategy.java @@ -0,0 +1,60 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils; + +import com.zuehlke.jasschallenge.client.game.GameSession; +import com.zuehlke.jasschallenge.client.game.Move; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.CardOption; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.CardOptions; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.ModeOption; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.ModeOptions; +import com.zuehlke.jasschallenge.client.game.strategy.utils.rules.ChooseCardRule; +import com.zuehlke.jasschallenge.client.game.strategy.utils.rules.ChooseTrumpfRule; +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.mode.Mode; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class Strategy { + private List chooseCardRules; + private List chooseTrumpfRules; + + public Strategy(List chooseTrumpfRules, List chooseCardRules) { + this.chooseTrumpfRules = chooseTrumpfRules; + this.chooseCardRules = chooseCardRules; + } + + public Mode applyChooseTrumpfRules(Set availableCards, GameSession session, boolean isGschobe) { + List availableModeOptions = ModeOptions.forAvailableCards(new ArrayList<>(availableCards)); + List allModeOptions = createAllModeOptions(isGschobe); + for (ChooseTrumpfRule rule : chooseTrumpfRules) { + availableModeOptions = rule.apply(availableModeOptions, availableCards, allModeOptions); + } + if (availableModeOptions.size() != 1) { + throw new IllegalStateException("Invalid strategy! A single mode option must be selected."); + } + return availableModeOptions.get(0).getMode(); + } + + private List createAllModeOptions(boolean isGschobe) { + List allModeOptions = Mode.standardModes().stream() + .map(mode -> new ModeOption(mode, 0)) + .collect(Collectors.toList()); + if (!isGschobe) { + allModeOptions.add(new ModeOption(Mode.shift(), 0)); + } + return allModeOptions; + } + + public Card applyChooseCardRules(Set availableCards, GameSession session, List allMoves) { + List availableCardOptions = CardOptions.from(availableCards); + for (ChooseCardRule rule : chooseCardRules) { + availableCardOptions = rule.apply(availableCardOptions, session, allMoves); + } + if (availableCardOptions.size() != 1) { + throw new IllegalStateException("Invalid strategy! A single card option must be selected."); + } + return availableCardOptions.get(0).getCard(); + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/StrategyBuilder.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/StrategyBuilder.java new file mode 100644 index 0000000..acd5b9a --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/StrategyBuilder.java @@ -0,0 +1,50 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils; + +import com.zuehlke.jasschallenge.client.game.strategy.utils.rules.ChooseCardRule; +import com.zuehlke.jasschallenge.client.game.strategy.utils.rules.ChooseTrumpfRule; + +import java.util.ArrayList; +import java.util.List; + +public class StrategyBuilder { + + private List chooseCardRules; + private List chooseTrumpfRules; + + public ChooseTrumpfRuleBuilder chooseTrumpfRules() { + return new ChooseTrumpfRuleBuilder(); + } + + public class ChooseTrumpfRuleBuilder { + + public ChooseTrumpfRuleBuilder() { + chooseTrumpfRules = new ArrayList<>(); + } + + public ChooseTrumpfRuleBuilder addRule(ChooseTrumpfRule chooseTrumpfRule) { + chooseTrumpfRules.add(chooseTrumpfRule); + return this; + } + + public ChooseCardRuleBuilder chooseCardRules() { + return new ChooseCardRuleBuilder(); + } + } + + public class ChooseCardRuleBuilder { + + public ChooseCardRuleBuilder() { + chooseCardRules = new ArrayList<>(); + } + + public ChooseCardRuleBuilder addRule(ChooseCardRule chooseCardRule) { + chooseCardRules.add(chooseCardRule); + return this; + } + + public Strategy build() { + return new Strategy(chooseTrumpfRules, chooseCardRules); + } + } +} + diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOption.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOption.java new file mode 100644 index 0000000..83d5df9 --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOption.java @@ -0,0 +1,89 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.options; + +import com.zuehlke.jasschallenge.client.game.Rounds; +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.cards.Color; +import com.zuehlke.jasschallenge.game.mode.Mode; +import com.zuehlke.jasschallenge.game.mode.Modes; + +import java.util.List; +import java.util.stream.Collectors; + +public class CardOption { + protected Card card; + + public CardOption(Card card) { + this.card = card; + } + + public Card getCard() { + return card; + } + + public int getRank() { + return card.getValue().getRank(); + } + + public int getTrumpfRank() { + return card.getValue().getTrumpfRank(); + } + + public boolean canBeAppliedTo(Mode mode, List playedCards) { + if (Rounds.isFirstCardOfRound(playedCards)) { + return true; + } else { + Color colorOfRound = Rounds.getColorOfRound(playedCards); + Color colorOfCard = card.getColor(); + Color colorOfTrumpf = mode.getTrumpfColor(); + return colorOfCard == colorOfRound || colorOfCard == colorOfTrumpf; + } + } + + public boolean canWinRound(Mode mode, List playedCards) { + if (Rounds.isFirstCardOfRound(playedCards)) { + return true; + } else if (!Rounds.getColorOfRound(playedCards).equals(card.getColor()) + && !card.getColor().equals(mode.getTrumpfColor())) { + return false; + } else { + if (Modes.isModeObeabe(mode)) { + return playedCards.stream() + .filter(card -> card.isHigherThan(this.card)) + .count() < 1; + } else if (Modes.isModeUndeufe(mode)) { + return playedCards.stream() + .filter(card -> !card.isHigherThan(this.card)) + .count() < 1; + } else { // mode is trumpf + final List playedTrumpfs = playedCards.stream() + .filter(card -> card.getColor().equals(mode.getTrumpfColor())) + .collect(Collectors.toList()); + if (playedTrumpfs.size() > 0) { + return playedTrumpfs.stream() + .filter(card -> card.isHigherTrumpfThan(this.card)) + .count() < 1; + } else { + return playedCards.stream() + .filter(card -> card.isHigherThan(this.card)) + .count() < 1 + || this.card.getColor().equals(mode.getTrumpfColor()); + } + } + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CardOption that = (CardOption) o; + + return card == that.card; + } + + @Override + public int hashCode() { + return card.hashCode(); + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptions.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptions.java new file mode 100644 index 0000000..8d8360b --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptions.java @@ -0,0 +1,63 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.options; + +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.mode.Mode; + +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public final class CardOptions { + + public static List from(List availableCards) { + return availableCards.stream() + .map(CardOption::new) + .collect(Collectors.toList()); + } + + public static List from(Set availableCards) { + return availableCards.stream() + .map(CardOption::new) + .collect(Collectors.toList()); + } + + public static List canWinRound(List options, Mode mode, List playedCards) { + return filterCardOptions(options, option -> option.canWinRound(mode, playedCards)); + } + + public static List canBeAppliedTo(List options, Mode mode, List playedCards) { + return filterCardOptions(options, option -> option.canBeAppliedTo(mode, playedCards)); + } + + public static List filterCardOptions(List options, Predicate cardOptionPredicate) { + return options.stream() + .filter(cardOptionPredicate) + .collect(Collectors.toList()); + } + + public static CardOption getMaxOption(List cardOptions, Comparator comparing) { + if (cardOptions == null) { + return null; + } + if (cardOptions.size() == 1) { + return cardOptions.get(0); + } + return cardOptions.stream() + .max(comparing) + .get(); + } + + public static CardOption getMinOption(List cardOptions, Comparator comparing) { + if (cardOptions == null) { + return null; + } + if (cardOptions.size() == 1) { + return cardOptions.get(0); + } + return cardOptions.stream() + .min(comparing) + .get(); + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOption.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOption.java new file mode 100644 index 0000000..34960b7 --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOption.java @@ -0,0 +1,22 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.options; + +import com.zuehlke.jasschallenge.game.mode.Mode; + +public class ModeOption { + + private Mode mode; + private int totalRank; + + public ModeOption(Mode mode, int totalRank) { + this.mode = mode; + this.totalRank = totalRank; + } + + public Mode getMode() { + return mode; + } + + public int getTotalRank() { + return totalRank; + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptions.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptions.java new file mode 100644 index 0000000..7f0780c --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptions.java @@ -0,0 +1,31 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.options; + +import com.zuehlke.jasschallenge.game.Trumpf; +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.cards.Cards; +import com.zuehlke.jasschallenge.game.cards.Color; +import com.zuehlke.jasschallenge.game.mode.Mode; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public final class ModeOptions { + + public static List forAvailableCards(List availableCards) { + Map> colorToCards = Cards.groupCardsByColor(availableCards); + ArrayList modeOptions = new ArrayList<>(); + for (Color color: colorToCards.keySet() ) { + int totalNormalRank = colorToCards.get(color).stream() + .mapToInt(card -> card.getValue().getRank()) + .sum(); + int totalTrumpfRank = colorToCards.get(color).stream() + .mapToInt(card -> card.getValue().getTrumpfRank()) + .sum(); + modeOptions.add(new ModeOption(Mode.from(Trumpf.OBEABE, color), totalNormalRank)); + modeOptions.add(new ModeOption(Mode.from(Trumpf.UNDEUFE, color), totalNormalRank)); + modeOptions.add(new ModeOption(Mode.from(Trumpf.TRUMPF, color), totalTrumpfRank)); + } + return modeOptions; + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRule.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRule.java new file mode 100644 index 0000000..160e0a2 --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRule.java @@ -0,0 +1,12 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.rules; + +import com.zuehlke.jasschallenge.client.game.GameSession; +import com.zuehlke.jasschallenge.client.game.Move; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.CardOption; + +import java.util.List; + +@FunctionalInterface +public interface ChooseCardRule { + List apply(List inputOptions, GameSession gameSession, List allMoves); +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRules.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRules.java new file mode 100644 index 0000000..13f11c3 --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRules.java @@ -0,0 +1,98 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.rules; + +import com.zuehlke.jasschallenge.client.game.GameSessions; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.CardOption; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.CardOptions; +import com.zuehlke.jasschallenge.game.Trumpf; +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.cards.Color; +import com.zuehlke.jasschallenge.game.mode.Mode; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class ChooseCardRules { + + /** + * This rule returns the strongest card that can win the current round. If it + */ + public static ChooseCardRule CHOOSE_STRONGEST_CARD_IF_ROUND_CAN_BE_WON = (inputOptions, gameSession, allMoves) -> { + if (inputOptions.size() <= 1) { + return inputOptions; + } + Mode mode = gameSession.getCurrentRound().getMode(); + List playedCards = GameSessions.getCardsPlayedInCurrentRound(gameSession); + List canWinRound = CardOptions.canWinRound(inputOptions, mode, playedCards); + if (canWinRound.size() <= 1) { + return inputOptions; + } + boolean trumpfPlayed = playedCards.stream().anyMatch(card -> card.getColor().equals(mode.getTrumpfColor())); + List trumpfOptions = canWinRound.stream() + .filter(option -> option.getCard().getColor().equals(mode.getTrumpfColor())) + .collect(Collectors.toList()); + if (trumpfPlayed) { + return Collections.singletonList( + CardOptions.getMaxOption(inputOptions, Comparator.comparing(CardOption::getTrumpfRank)) + ); + } else if (trumpfOptions.size() > 0) { + return Collections.singletonList( + CardOptions.getMaxOption(trumpfOptions, Comparator.comparing(CardOption::getTrumpfRank)) + ); + } else { + return Collections.singletonList( + CardOptions.getMaxOption(inputOptions, Comparator.comparing(CardOption::getRank)) + ); + } + }; + public static ChooseCardRule CHOOSE_WEAKEST_CARD_THAT_CAN_BE_APPLIED_TO_ROUND = (inputOptions, gameSession, allMoves) -> { + if (inputOptions.size() <= 1) { + return inputOptions; + } + Mode mode = gameSession.getCurrentRound().getMode(); + List playedCards = GameSessions.getCardsPlayedInCurrentRound(gameSession); + List canBeApplied = CardOptions.canBeAppliedTo(inputOptions, mode, playedCards); + if (canBeApplied.size() <= 1) { + return inputOptions; + } + boolean trumpfPlayed = playedCards.stream() + .anyMatch(card -> card.getColor().equals(mode.getTrumpfColor())); + if (trumpfPlayed) { + return Collections.singletonList( + CardOptions.getMinOption(canBeApplied, Comparator.comparing(CardOption::getTrumpfRank)) + ); + } else { + return Collections.singletonList( + CardOptions.getMinOption(canBeApplied, Comparator.comparing(CardOption::getRank)) + ); + } + }; + public static ChooseCardRule CHOOSE_WEAKEST_CARD = (inputOptions, gameSession, allMoves) -> { + if (inputOptions.size() <= 1) { + return inputOptions; + } + Mode mode = gameSession.getCurrentRound().getMode(); + Color trumpfColor = mode.getTrumpfColor(); + if (mode.getTrumpfName().equals(Trumpf.TRUMPF) || mode.getTrumpfName().equals(Trumpf.SCHIEBE)) { + final Optional worstOptionWithoutTrumpfCards = inputOptions.stream() + .filter(option -> !option.getCard().getColor().equals(trumpfColor)) + .min(Comparator.comparing(CardOption::getRank)); + if (worstOptionWithoutTrumpfCards.isPresent()) { + CardOption worstOption = worstOptionWithoutTrumpfCards.get(); + return Collections.singletonList(worstOption); + } else { + CardOption worstOptionOfAllAvailableOptions = inputOptions.stream() + .min(Comparator.comparing(CardOption::getRank)) + .get(); + return Collections.singletonList(worstOptionOfAllAvailableOptions); + } + } else { + CardOption worstOptionOfAllAvailableOptions = inputOptions.stream() + .min(Comparator.comparing(CardOption::getRank)) + .get(); + return Collections.singletonList(worstOptionOfAllAvailableOptions); + } + }; +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseTrumpfRule.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseTrumpfRule.java new file mode 100644 index 0000000..ef0eafe --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseTrumpfRule.java @@ -0,0 +1,12 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.rules; + +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.ModeOption; +import com.zuehlke.jasschallenge.game.cards.Card; + +import java.util.List; +import java.util.Set; + +@FunctionalInterface +public interface ChooseTrumpfRule { + List apply(List inputOptions, Set availableCards, List allPossibleModes); +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseTrumpfRules.java b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseTrumpfRules.java new file mode 100644 index 0000000..5fe9c19 --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseTrumpfRules.java @@ -0,0 +1,15 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.rules; + +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.ModeOption; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Random; + +public class ChooseTrumpfRules { + public static ChooseTrumpfRule CHOOSE_TRUMPF_WITH_HIGHEST_TOTAL_RANK = (inputOptions, availableCards, allPossibleModes) -> Collections.singletonList( + inputOptions.stream() + .max(Comparator.comparing(ModeOption::getTotalRank)) + .orElse(allPossibleModes.get(new Random().nextInt(allPossibleModes.size()))) + ); +} diff --git a/src/main/java/com/zuehlke/jasschallenge/client/websocket/GameHandler.java b/src/main/java/com/zuehlke/jasschallenge/client/websocket/GameHandler.java index a7c2dc2..d13dd47 100644 --- a/src/main/java/com/zuehlke/jasschallenge/client/websocket/GameHandler.java +++ b/src/main/java/com/zuehlke/jasschallenge/client/websocket/GameHandler.java @@ -21,23 +21,30 @@ import static java.util.stream.Collectors.toList; public class GameHandler { + private static final String DEFAULT_SESSION_NAME = "tournament"; private final Player localPlayer; private final SessionType sessionType; private GameSession gameSession; private PlayerMapper playerMapper; private boolean shifted = false; + private String sessionName; private final static Logger logger = LoggerFactory.getLogger(GameHandler.class); - public GameHandler(Player localPlayer, SessionType sessionType) { + public GameHandler(Player localPlayer, SessionType sessionType, String sessionName) { this.localPlayer = localPlayer; resetPlayerMapper(localPlayer); this.sessionType = sessionType; + this.sessionName = sessionName; + } + + public GameHandler(Player localPlayer, SessionType sessionType) { + this(localPlayer, sessionType, DEFAULT_SESSION_NAME); } GameHandler(Player localPlayer, GameSession gameSession) { - this(localPlayer, SessionType.TOURNAMENT); + this(localPlayer, SessionType.TOURNAMENT, DEFAULT_SESSION_NAME); this.gameSession = gameSession; } @@ -51,11 +58,17 @@ List getTeams() { } public ChooseSession onRequestSessionChoice() { - return new ChooseSession(AUTOJOIN, "Java Client session", sessionType); + logger.debug("<< onRequestSessionChoice"); + ChooseSession chooseSessionResponse = new ChooseSession(AUTOJOIN, sessionName, sessionType); + logger.debug(">> " + chooseSessionResponse.getData()); + return chooseSessionResponse; } public ChoosePlayerName onRequestPlayerName() { - return new ChoosePlayerName(localPlayer.getName()); + logger.debug("<< onRequestPlayerName"); + ChoosePlayerName choosePlayerNameResponse = new ChoosePlayerName(localPlayer.getName()); + logger.debug(">> " + choosePlayerNameResponse); + return choosePlayerNameResponse; } public void onDealCards(List dealCard) { @@ -63,13 +76,15 @@ public void onDealCards(List dealCard) { } public void onPlayerJoined(PlayerJoinedSession joinedPlayer) { - if(localPlayer.getId() == null) { + logger.debug("<< on onPlayerJoined: " + joinedPlayer.getPlayer().getId()); + if (localPlayer.getId() == null) { localPlayer.setId(joinedPlayer.getPlayer().getId()); localPlayer.setSeatId(joinedPlayer.getPlayer().getSeatId()); } } public void onBroadCastTeams(List remoteTeams) { + logger.debug("<< onBroadCastTeams: " + remoteTeams); final List teams = mapTeams(remoteTeams); final List playersInPlayingOrder = getPlayersInPlayingOrder(remoteTeams); gameSession = new GameSession(teams, playersInPlayingOrder); @@ -82,6 +97,7 @@ private void resetPlayerMapper(Player localPlayer) { } public ChooseTrumpf onRequestTrumpf() { + logger.debug("<< onRequestTrumpf"); final Mode mode = localPlayer.chooseTrumpf(gameSession, shifted); return new ChooseTrumpf(mode.getTrumpfName(), Mapping.mapColor(mode.getTrumpfColor())); } @@ -175,7 +191,7 @@ private Team toTeam(RemoteTeam remoteTeam) { } private static void checkEquals(Object a, Object b, String errorMessage) { - if(!a.equals(b)) { + if (!a.equals(b)) { logger.warn("Expected {} to be equal to {}: {}", a, b, errorMessage); throw new RuntimeException(errorMessage); } diff --git a/src/main/java/com/zuehlke/jasschallenge/client/websocket/GameSocket.java b/src/main/java/com/zuehlke/jasschallenge/client/websocket/GameSocket.java index 4698fd6..230ce31 100644 --- a/src/main/java/com/zuehlke/jasschallenge/client/websocket/GameSocket.java +++ b/src/main/java/com/zuehlke/jasschallenge/client/websocket/GameSocket.java @@ -19,6 +19,7 @@ public GameSocket(GameHandler handler) { } public void onMessage(Message msg) { + logger.debug("<< onMessage: " + msg); Optional response = dispatchMessage(msg); response.ifPresent(responseChannel::respond); } diff --git a/src/main/java/com/zuehlke/jasschallenge/game/cards/Cards.java b/src/main/java/com/zuehlke/jasschallenge/game/cards/Cards.java new file mode 100644 index 0000000..67ab963 --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/game/cards/Cards.java @@ -0,0 +1,18 @@ +package com.zuehlke.jasschallenge.game.cards; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.stream.Collectors.groupingBy; + +public final class Cards { + + public static Map> groupCardsByColor(Set availableCards) { + return availableCards.stream().collect(groupingBy(Card::getColor)); + } + + public static Map> groupCardsByColor(List availableCards) { + return availableCards.stream().collect(groupingBy(Card::getColor)); + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/game/mode/BottomUpMode.java b/src/main/java/com/zuehlke/jasschallenge/game/mode/BottomUpMode.java index 9520e54..ed03e18 100644 --- a/src/main/java/com/zuehlke/jasschallenge/game/mode/BottomUpMode.java +++ b/src/main/java/com/zuehlke/jasschallenge/game/mode/BottomUpMode.java @@ -11,7 +11,7 @@ import static com.zuehlke.jasschallenge.game.mode.GeneralRules.calculateLastRoundBonus; import static java.lang.String.valueOf; -class BottomUpMode extends Mode{ +public class BottomUpMode extends Mode { private static final int FACTOR = 1; @Override @@ -26,7 +26,7 @@ public Color getTrumpfColor() { @Override public int calculateRoundScore(int roundNumber, Set playedCards) { - if(roundNumber == Game.LAST_ROUND_NUMBER) { + if (roundNumber == Game.LAST_ROUND_NUMBER) { return calculateLastRoundBonus(FACTOR) + calculateScore(playedCards); } return calculateScore(playedCards); @@ -40,7 +40,6 @@ public int calculateScore(Set playedCards) { } - @Override public boolean canPlayCard(Card card, Set alreadyPlayedCards, Color currentRoundColor, Set playerCards) { return GeneralRules.canPlayCard(card, alreadyPlayedCards, currentRoundColor, playerCards); @@ -60,4 +59,19 @@ public Comparator createRankComparator() { public String toString() { return valueOf(getTrumpfName()); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BottomUpMode that = (BottomUpMode) o; + + return getTrumpfName().equals(that.getTrumpfName()); + } + + @Override + public int hashCode() { + return getTrumpfName().hashCode(); + } } diff --git a/src/main/java/com/zuehlke/jasschallenge/game/mode/Modes.java b/src/main/java/com/zuehlke/jasschallenge/game/mode/Modes.java new file mode 100644 index 0000000..e0e7ba4 --- /dev/null +++ b/src/main/java/com/zuehlke/jasschallenge/game/mode/Modes.java @@ -0,0 +1,35 @@ +package com.zuehlke.jasschallenge.game.mode; + +import com.zuehlke.jasschallenge.game.Trumpf; +import com.zuehlke.jasschallenge.game.cards.Color; + +public final class Modes { + public static boolean isModeObeabe(Mode mode) { + return mode.getTrumpfName().equals(Trumpf.OBEABE); + } + + public static boolean isModeUndeufe(Mode mode) { + return mode.getTrumpfName().equals(Trumpf.UNDEUFE); + } + + public static boolean isModeTrumpf(Mode mode) { + Trumpf trumpfName = mode.getTrumpfName(); + return trumpfName.equals(Trumpf.SCHIEBE) || trumpfName.equals(Trumpf.TRUMPF); + } + + public static boolean isClubs(Mode mode) { + return mode.getTrumpfColor().equals(Color.CLUBS); + } + + public static boolean isDiamonds(Mode mode) { + return mode.getTrumpfColor().equals(Color.DIAMONDS); + } + + public static boolean isSpades(Mode mode) { + return mode.getTrumpfColor().equals(Color.SPADES); + } + + public static boolean isHearts(Mode mode) { + return mode.getTrumpfColor().equals(Color.HEARTS); + } +} diff --git a/src/main/java/com/zuehlke/jasschallenge/game/mode/ShiftMode.java b/src/main/java/com/zuehlke/jasschallenge/game/mode/ShiftMode.java index 7f556c7..d944e31 100644 --- a/src/main/java/com/zuehlke/jasschallenge/game/mode/ShiftMode.java +++ b/src/main/java/com/zuehlke/jasschallenge/game/mode/ShiftMode.java @@ -50,4 +50,19 @@ public int getFactor() { public Comparator createRankComparator() { return null; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ShiftMode that = (ShiftMode) o; + + return getTrumpfName().equals(that.getTrumpfName()); + } + + @Override + public int hashCode() { + return getTrumpfName().hashCode(); + } } diff --git a/src/main/java/com/zuehlke/jasschallenge/game/mode/TopDownMode.java b/src/main/java/com/zuehlke/jasschallenge/game/mode/TopDownMode.java index adb59a3..530289d 100644 --- a/src/main/java/com/zuehlke/jasschallenge/game/mode/TopDownMode.java +++ b/src/main/java/com/zuehlke/jasschallenge/game/mode/TopDownMode.java @@ -10,7 +10,7 @@ import static java.lang.String.valueOf; -class TopDownMode extends Mode { +public class TopDownMode extends Mode { private static final int FACTOR = 1; @Override @@ -58,4 +58,19 @@ public String toString() { return valueOf(getTrumpfName()); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TopDownMode that = (TopDownMode) o; + + return getTrumpfName().equals(that.getTrumpfName()); + } + + @Override + public int hashCode() { + return getTrumpfName().hashCode(); + } } diff --git a/src/main/java/com/zuehlke/jasschallenge/game/mode/TrumpfColorMode.java b/src/main/java/com/zuehlke/jasschallenge/game/mode/TrumpfColorMode.java index eb04961..286e661 100644 --- a/src/main/java/com/zuehlke/jasschallenge/game/mode/TrumpfColorMode.java +++ b/src/main/java/com/zuehlke/jasschallenge/game/mode/TrumpfColorMode.java @@ -12,7 +12,7 @@ import static java.lang.String.valueOf; -class TrumpfColorMode extends Mode { +public class TrumpfColorMode extends Mode { private final Color trumpfColor; @@ -128,4 +128,18 @@ private int compareMoves(Card card1, Card card2) { return card1.isHigherThan(card2) ? 1 : -1; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TrumpfColorMode that = (TrumpfColorMode) o; + + return trumpfColor == that.trumpfColor; + } + + @Override + public int hashCode() { + return trumpfColor.hashCode(); + } } diff --git a/src/main/java/com/zuehlke/jasschallenge/messages/responses/ChooseSession.java b/src/main/java/com/zuehlke/jasschallenge/messages/responses/ChooseSession.java index 063fcd0..2da1571 100644 --- a/src/main/java/com/zuehlke/jasschallenge/messages/responses/ChooseSession.java +++ b/src/main/java/com/zuehlke/jasschallenge/messages/responses/ChooseSession.java @@ -8,7 +8,7 @@ public class ChooseSession implements Response { private final ChooseSessionData data; public ChooseSession(SessionChoice sessionChoice) { - this(sessionChoice, "Java Client Session", SessionType.TOURNAMENT); + this(sessionChoice, "tournament", SessionType.TOURNAMENT); } public ChooseSession(SessionChoice sessionChoice, String sessionName, SessionType tournament) { diff --git a/src/test/java/com/zuehlke/jasschallenge/ApplicationBotTryout.java b/src/test/java/com/zuehlke/jasschallenge/ApplicationBotTryout.java index ba6ef12..18dd16d 100644 --- a/src/test/java/com/zuehlke/jasschallenge/ApplicationBotTryout.java +++ b/src/test/java/com/zuehlke/jasschallenge/ApplicationBotTryout.java @@ -62,7 +62,7 @@ private static void awaitFuture(Future future) { private static RemoteGame startGame(String targetUrl, Player myLocalPlayer, SessionType sessionType) throws Exception { - final RemoteGame remoteGame = new RemoteGame(targetUrl, myLocalPlayer, sessionType); + final RemoteGame remoteGame = new RemoteGame(targetUrl, myLocalPlayer, sessionType, "someSession"); remoteGame.start(); return remoteGame; } diff --git a/src/test/java/com/zuehlke/jasschallenge/ApplicationPerformance.java b/src/test/java/com/zuehlke/jasschallenge/ApplicationPerformance.java index 859b2f8..a1340a6 100644 --- a/src/test/java/com/zuehlke/jasschallenge/ApplicationPerformance.java +++ b/src/test/java/com/zuehlke/jasschallenge/ApplicationPerformance.java @@ -58,7 +58,7 @@ private static void awaitFuture(Future future) { private static RemoteGame startGame(String targetUrl, Player myLocalPlayer, SessionType sessionType) throws Exception { - final RemoteGame remoteGame = new RemoteGame(targetUrl, myLocalPlayer, sessionType); + final RemoteGame remoteGame = new RemoteGame(targetUrl, myLocalPlayer, sessionType, "tournament"); remoteGame.start(); return remoteGame; } diff --git a/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptionTest.java b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptionTest.java new file mode 100644 index 0000000..1f505af --- /dev/null +++ b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptionTest.java @@ -0,0 +1,334 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.options; + +import com.zuehlke.jasschallenge.game.Trumpf; +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.cards.Color; +import com.zuehlke.jasschallenge.game.mode.Mode; +import org.junit.Before; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +public class CardOptionTest { + private Mode clubGschobe; + private Mode club; + private Mode diamondGschobe; + private Mode diamond; + private Mode heartGschobe; + private Mode heart; + private Mode spadeGschobe; + private Mode spade; + private Mode obeabe; + private Mode undeufe; + + private CardOption clubSixOption; + private CardOption clubSevenOption; + private CardOption clubEightOption; + private CardOption clubNineOption; + private CardOption clubTenOption; + private CardOption clubJackOption; + private CardOption clubQueenOption; + private CardOption clubKingOption; + private CardOption clubAceOption; + + private Set clubSixNotAvailable; + private Set clubSevenNotAvailable; + private Set clubEightNotAvailable; + private Set clubNineNotAvailable; + private Set clubTenNotAvailable; + private Set clubJackNotAvailable; + private Set clubQueenNotAvailable; + private Set clubKingNotAvailable; + private Set clubAceNotAvailable; + + private HashSet clubSixAvailable; + private HashSet clubSevenAvailable; + private HashSet clubEightAvailable; + private HashSet clubNineAvailable; + private HashSet clubTenAvailable; + private HashSet clubJackAvailable; + private HashSet clubQueenAvailable; + private HashSet clubKingAvailable; + private HashSet clubAceAvailable; + + private List noPlayedCards; + private List highestClubPlayed; + private List lowestClubPlayed; + private List highestDiamondPlayed; + private List lowestDiamondPlayed; + private List highestHeartPlayed; + private List lowestHeartPlayed; + private List highestSpadePlayed; + private List lowestSpadePlayed; + + @Before + public void setUp() throws Exception { + clubGschobe = Mode.from(Trumpf.SCHIEBE, null); + club = Mode.from(Trumpf.TRUMPF, Color.CLUBS); + diamond = Mode.from(Trumpf.TRUMPF, null); + diamondGschobe = Mode.from(Trumpf.SCHIEBE, null); + heartGschobe = Mode.from(Trumpf.SCHIEBE, null); + heart = Mode.from(Trumpf.TRUMPF, Color.HEARTS); + spadeGschobe = Mode.from(Trumpf.SCHIEBE, null); + spade = Mode.from(Trumpf.TRUMPF, Color.SPADES); + obeabe = Mode.topDown(); + undeufe = Mode.bottomUp(); + + clubSixOption = new CardOption(Card.CLUB_SIX); + clubSevenOption = new CardOption(Card.CLUB_SEVEN); + clubEightOption = new CardOption(Card.CLUB_EIGHT); + clubNineOption = new CardOption(Card.CLUB_NINE); + clubTenOption = new CardOption(Card.CLUB_TEN); + clubJackOption = new CardOption(Card.CLUB_JACK); + clubQueenOption = new CardOption(Card.CLUB_QUEEN); + clubKingOption = new CardOption(Card.CLUB_KING); + clubAceOption = new CardOption(Card.CLUB_ACE); + + clubSixNotAvailable = Collections.emptySet(); + clubSevenNotAvailable = Collections.emptySet(); + clubEightNotAvailable = Collections.emptySet(); + clubNineNotAvailable = Collections.emptySet(); + clubTenNotAvailable = Collections.emptySet(); + clubJackNotAvailable = Collections.emptySet(); + clubQueenNotAvailable = Collections.emptySet(); + clubKingNotAvailable = Collections.emptySet(); + clubAceNotAvailable = Collections.emptySet(); + + clubSixAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_SIX)); + clubSevenAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_SEVEN)); + clubEightAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_EIGHT)); + clubNineAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_NINE)); + clubTenAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_TEN)); + clubJackAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_JACK)); + clubQueenAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_QUEEN)); + clubKingAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_KING)); + clubAceAvailable = new HashSet<>(Collections.singletonList(Card.CLUB_ACE)); + + noPlayedCards = Collections.emptyList(); + highestClubPlayed = Arrays.asList(Card.CLUB_ACE); + lowestClubPlayed = Arrays.asList(Card.CLUB_SIX); + highestDiamondPlayed = Arrays.asList(Card.DIAMOND_ACE); + lowestDiamondPlayed = Arrays.asList(Card.DIAMOND_SIX); + highestHeartPlayed = Arrays.asList(Card.HEART_ACE); + lowestHeartPlayed = Arrays.asList(Card.HEART_SIX); + highestSpadePlayed = Arrays.asList(Card.SPADE_ACE); + lowestSpadePlayed = Arrays.asList(Card.SPADE_SIX); + } + + @Test + public void getCard() { + assertEquals(Card.CLUB_SIX, clubSixOption.getCard()); + assertEquals(Card.CLUB_SEVEN, clubSevenOption.getCard()); + assertEquals(Card.CLUB_EIGHT, clubEightOption.getCard()); + assertEquals(Card.CLUB_NINE, clubNineOption.getCard()); + assertEquals(Card.CLUB_TEN, clubTenOption.getCard()); + assertEquals(Card.CLUB_JACK, clubJackOption.getCard()); + assertEquals(Card.CLUB_QUEEN, clubQueenOption.getCard()); + assertEquals(Card.CLUB_KING, clubKingOption.getCard()); + assertEquals(Card.CLUB_ACE, clubAceOption.getCard()); + } + + @Test + public void getRank() { + assertEquals(1, clubSixOption.getRank()); + assertEquals(2, clubSevenOption.getRank()); + assertEquals(3, clubEightOption.getRank()); + assertEquals(4, clubNineOption.getRank()); + assertEquals(5, clubTenOption.getRank()); + assertEquals(6, clubJackOption.getRank()); + assertEquals(7, clubQueenOption.getRank()); + assertEquals(8, clubKingOption.getRank()); + assertEquals(9, clubAceOption.getRank()); + } + + @Test + public void getTrumpfRank() { + assertEquals(1, clubSixOption.getTrumpfRank()); + assertEquals(2, clubSevenOption.getTrumpfRank()); + assertEquals(3, clubEightOption.getTrumpfRank()); + assertEquals(12, clubNineOption.getTrumpfRank()); + assertEquals(5, clubTenOption.getTrumpfRank()); + assertEquals(13, clubJackOption.getTrumpfRank()); + assertEquals(7, clubQueenOption.getTrumpfRank()); + assertEquals(8, clubKingOption.getTrumpfRank()); + assertEquals(9, clubAceOption.getTrumpfRank()); + } + + @Test + public void canBeAppliedTo_cardAvailableAndFirstCardOfRound_true() { + assertEquals(true, clubSixOption.canBeAppliedTo(club, noPlayedCards)); + assertEquals(true, clubSevenOption.canBeAppliedTo(diamond, noPlayedCards)); + assertEquals(true, clubEightOption.canBeAppliedTo(heart, noPlayedCards)); + assertEquals(true, clubNineOption.canBeAppliedTo(heart, noPlayedCards)); + assertEquals(true, clubTenOption.canBeAppliedTo(spade, noPlayedCards)); + assertEquals(true, clubJackOption.canBeAppliedTo(clubGschobe, noPlayedCards)); + assertEquals(true, clubQueenOption.canBeAppliedTo(diamondGschobe, noPlayedCards)); + assertEquals(true, clubKingOption.canBeAppliedTo(heartGschobe, noPlayedCards)); + assertEquals(true, clubAceOption.canBeAppliedTo(spadeGschobe, noPlayedCards)); + } + + @Test + public void canBeAppliedTo_cardAvailableAndClubNotTrumpfAndDiamondPlayed_false() { + assertEquals(false, clubSixOption.canBeAppliedTo(diamond, highestDiamondPlayed)); + assertEquals(false, clubSevenOption.canBeAppliedTo(heart, highestDiamondPlayed)); + assertEquals(false, clubEightOption.canBeAppliedTo(spade, highestDiamondPlayed)); + assertEquals(false, clubNineOption.canBeAppliedTo(diamondGschobe, highestDiamondPlayed)); + assertEquals(false, clubTenOption.canBeAppliedTo(heartGschobe, highestDiamondPlayed)); + assertEquals(false, clubJackOption.canBeAppliedTo(spadeGschobe, highestDiamondPlayed)); + assertEquals(false, clubQueenOption.canBeAppliedTo(diamond, highestDiamondPlayed)); + assertEquals(false, clubKingOption.canBeAppliedTo(heart, highestDiamondPlayed)); + assertEquals(false, clubAceOption.canBeAppliedTo(spade, highestDiamondPlayed)); + } + + @Test + public void canBeAppliedTo_cardAvailableAndClubNotTrumpfAndHeartPlayed_false() { + assertEquals(false, clubSixOption.canBeAppliedTo(diamond, lowestHeartPlayed)); + assertEquals(false, clubSevenOption.canBeAppliedTo(heart, lowestHeartPlayed)); + assertEquals(false, clubEightOption.canBeAppliedTo(spade, lowestHeartPlayed)); + assertEquals(false, clubNineOption.canBeAppliedTo(diamondGschobe, lowestHeartPlayed)); + assertEquals(false, clubTenOption.canBeAppliedTo(heartGschobe, lowestHeartPlayed)); + assertEquals(false, clubJackOption.canBeAppliedTo(spadeGschobe, lowestHeartPlayed)); + assertEquals(false, clubQueenOption.canBeAppliedTo(diamond, lowestHeartPlayed)); + assertEquals(false, clubKingOption.canBeAppliedTo(heart, lowestHeartPlayed)); + assertEquals(false, clubAceOption.canBeAppliedTo(spade, lowestHeartPlayed)); + } + + @Test + public void canBeAppliedTo_cardAvailableAndClubNotTrumpfAndSpadePlayed_false() { + assertEquals(false, clubSixOption.canBeAppliedTo(diamond, highestSpadePlayed)); + assertEquals(false, clubSevenOption.canBeAppliedTo(heart, highestSpadePlayed)); + assertEquals(false, clubEightOption.canBeAppliedTo(spade, highestSpadePlayed)); + assertEquals(false, clubNineOption.canBeAppliedTo(diamondGschobe, highestSpadePlayed)); + assertEquals(false, clubTenOption.canBeAppliedTo(heartGschobe, highestSpadePlayed)); + assertEquals(false, clubJackOption.canBeAppliedTo(spadeGschobe, highestSpadePlayed)); + assertEquals(false, clubQueenOption.canBeAppliedTo(diamond, highestSpadePlayed)); + assertEquals(false, clubKingOption.canBeAppliedTo(heart, highestSpadePlayed)); + assertEquals(false, clubAceOption.canBeAppliedTo(spade, highestSpadePlayed)); + } + + @Test + public void canBeAppliedTo_cardAvailableAndClubTrumpfAndDiamondPlayed_true() { + assertEquals(true, clubSixOption.canBeAppliedTo(club, highestDiamondPlayed)); + assertEquals(true, clubSevenOption.canBeAppliedTo(club, highestDiamondPlayed)); + assertEquals(true, clubEightOption.canBeAppliedTo(club, highestDiamondPlayed)); + assertEquals(true, clubNineOption.canBeAppliedTo(club, highestDiamondPlayed)); + assertEquals(true, clubTenOption.canBeAppliedTo(club, highestDiamondPlayed)); + assertEquals(true, clubJackOption.canBeAppliedTo(club, highestDiamondPlayed)); + assertEquals(true, clubQueenOption.canBeAppliedTo(club, highestDiamondPlayed)); + assertEquals(true, clubKingOption.canBeAppliedTo(club, highestDiamondPlayed)); + assertEquals(true, clubAceOption.canBeAppliedTo(club, highestDiamondPlayed)); + } + + @Test + public void canBeAppliedTo_cardAvailableAndClubTrumpfAndHeartPlayed_true() { + assertEquals(true, clubSixOption.canBeAppliedTo(club, lowestHeartPlayed)); + assertEquals(true, clubSevenOption.canBeAppliedTo(club, lowestHeartPlayed)); + assertEquals(true, clubEightOption.canBeAppliedTo(club, lowestHeartPlayed)); + assertEquals(true, clubNineOption.canBeAppliedTo(club, lowestHeartPlayed)); + assertEquals(true, clubTenOption.canBeAppliedTo(club, lowestHeartPlayed)); + assertEquals(true, clubJackOption.canBeAppliedTo(club, lowestHeartPlayed)); + assertEquals(true, clubQueenOption.canBeAppliedTo(club, lowestHeartPlayed)); + assertEquals(true, clubKingOption.canBeAppliedTo(club, lowestHeartPlayed)); + assertEquals(true, clubAceOption.canBeAppliedTo(club, lowestHeartPlayed)); + } + + @Test + public void canBeAppliedTo_cardAvailableAndClubTrumpfAndSpadePlayed_true() { + assertEquals(true, clubSixOption.canBeAppliedTo(club, highestSpadePlayed)); + assertEquals(true, clubSevenOption.canBeAppliedTo(club, highestSpadePlayed)); + assertEquals(true, clubEightOption.canBeAppliedTo(club, highestSpadePlayed)); + assertEquals(true, clubNineOption.canBeAppliedTo(club, highestSpadePlayed)); + assertEquals(true, clubTenOption.canBeAppliedTo(club, highestSpadePlayed)); + assertEquals(true, clubJackOption.canBeAppliedTo(club, highestSpadePlayed)); + assertEquals(true, clubQueenOption.canBeAppliedTo(club, highestSpadePlayed)); + assertEquals(true, clubKingOption.canBeAppliedTo(club, highestSpadePlayed)); + assertEquals(true, clubAceOption.canBeAppliedTo(club, highestSpadePlayed)); + } + + @Test + public void canBeAppliedTo_cardAvailableAndAndClubPlayed_true() { + assertEquals(true, clubSixOption.canBeAppliedTo(club, highestClubPlayed)); + assertEquals(true, clubSevenOption.canBeAppliedTo(club, highestClubPlayed)); + assertEquals(true, clubEightOption.canBeAppliedTo(club, highestClubPlayed)); + assertEquals(true, clubNineOption.canBeAppliedTo(club, highestClubPlayed)); + assertEquals(true, clubTenOption.canBeAppliedTo(club, highestClubPlayed)); + assertEquals(true, clubJackOption.canBeAppliedTo(club, highestClubPlayed)); + assertEquals(true, clubQueenOption.canBeAppliedTo(club, highestClubPlayed)); + assertEquals(true, clubKingOption.canBeAppliedTo(club, highestClubPlayed)); + assertEquals(true, clubAceOption.canBeAppliedTo(club, highestClubPlayed)); + } + + @Test + public void canWinRound_modeObeabeAndHigherClubPlayed_false() { + assertEquals(false, clubSixOption.canWinRound(obeabe, highestClubPlayed)); + assertEquals(false, clubSevenOption.canWinRound(obeabe, highestClubPlayed)); + assertEquals(false, clubEightOption.canWinRound(obeabe, highestClubPlayed)); + assertEquals(false, clubNineOption.canWinRound(obeabe, highestClubPlayed)); + assertEquals(false, clubTenOption.canWinRound(obeabe, highestClubPlayed)); + assertEquals(false, clubJackOption.canWinRound(obeabe, highestClubPlayed)); + assertEquals(false, clubQueenOption.canWinRound(obeabe, highestClubPlayed)); + assertEquals(false, clubKingOption.canWinRound(obeabe, highestClubPlayed)); + } + + @Test + public void canWinRound_modeObeabeAndLowerClubPlayed_true() { + assertEquals(true, clubSevenOption.canWinRound(obeabe, lowestClubPlayed)); + assertEquals(true, clubEightOption.canWinRound(obeabe, lowestClubPlayed)); + assertEquals(true, clubNineOption.canWinRound(obeabe, lowestClubPlayed)); + assertEquals(true, clubTenOption.canWinRound(obeabe, lowestClubPlayed)); + assertEquals(true, clubJackOption.canWinRound(obeabe, lowestClubPlayed)); + assertEquals(true, clubQueenOption.canWinRound(obeabe, lowestClubPlayed)); + assertEquals(true, clubKingOption.canWinRound(obeabe, lowestClubPlayed)); + assertEquals(true, clubAceOption.canWinRound(obeabe, lowestClubPlayed)); + } + + @Test + public void canWinRound_modeUndeufeAndLowerClubPlayed_false() { + assertEquals(false, clubSevenOption.canWinRound(undeufe, lowestClubPlayed)); + assertEquals(false, clubEightOption.canWinRound(undeufe, lowestClubPlayed)); + assertEquals(false, clubNineOption.canWinRound(undeufe, lowestClubPlayed)); + assertEquals(false, clubTenOption.canWinRound(undeufe, lowestClubPlayed)); + assertEquals(false, clubJackOption.canWinRound(undeufe, lowestClubPlayed)); + assertEquals(false, clubQueenOption.canWinRound(undeufe, lowestClubPlayed)); + assertEquals(false, clubKingOption.canWinRound(undeufe, lowestClubPlayed)); + assertEquals(false, clubAceOption.canWinRound(undeufe, lowestClubPlayed)); + } + + @Test + public void canWinRound_modeUndeufeAndHigherClubPlayed_true() { + assertEquals(true, clubSixOption.canWinRound(undeufe, highestClubPlayed)); + assertEquals(true, clubSevenOption.canWinRound(undeufe, highestClubPlayed)); + assertEquals(true, clubEightOption.canWinRound(undeufe, highestClubPlayed)); + assertEquals(true, clubNineOption.canWinRound(undeufe, highestClubPlayed)); + assertEquals(true, clubTenOption.canWinRound(undeufe, highestClubPlayed)); + assertEquals(true, clubJackOption.canWinRound(undeufe, highestClubPlayed)); + assertEquals(true, clubQueenOption.canWinRound(undeufe, highestClubPlayed)); + assertEquals(true, clubKingOption.canWinRound(undeufe, highestClubPlayed)); + } + + @Test + public void canWinRound_modeTrumpfClubPlayed_true() { + assertEquals(true, clubSixOption.canWinRound(club, highestDiamondPlayed)); + assertEquals(true, clubSevenOption.canWinRound(club, highestHeartPlayed)); + assertEquals(true, clubEightOption.canWinRound(club, highestSpadePlayed)); + assertEquals(true, clubNineOption.canWinRound(club, highestDiamondPlayed)); + assertEquals(true, clubTenOption.canWinRound(club, highestHeartPlayed)); + assertEquals(true, clubJackOption.canWinRound(club, highestSpadePlayed)); + assertEquals(true, clubQueenOption.canWinRound(club, highestDiamondPlayed)); + assertEquals(true, clubKingOption.canWinRound(club, highestHeartPlayed)); + } + + @Test + public void canWinRound_modeTrumpfOtherThanClubPlayed_false() { + assertEquals(false, clubSixOption.canWinRound(diamond, lowestDiamondPlayed)); + assertEquals(false, clubSevenOption.canWinRound(heart, lowestHeartPlayed)); + assertEquals(false, clubEightOption.canWinRound(spade, lowestSpadePlayed)); + assertEquals(false, clubNineOption.canWinRound(diamond, lowestDiamondPlayed)); + assertEquals(false, clubTenOption.canWinRound(heart, lowestHeartPlayed)); + assertEquals(false, clubJackOption.canWinRound(spade, lowestSpadePlayed)); + assertEquals(false, clubQueenOption.canWinRound(diamond, lowestDiamondPlayed)); + assertEquals(false, clubKingOption.canWinRound(heart, lowestHeartPlayed)); + } +} \ No newline at end of file diff --git a/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptionsTest.java b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptionsTest.java new file mode 100644 index 0000000..6e0908b --- /dev/null +++ b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/CardOptionsTest.java @@ -0,0 +1,47 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.options; + +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.CardOption; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.CardOptions; +import com.zuehlke.jasschallenge.game.cards.Card; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class CardOptionsTest { + + private List allCards; + private List allOptions; + + @Before + public void setUp() throws Exception { + allCards = Stream.of( + Card.DIAMOND_SIX, + Card.DIAMOND_SEVEN, + Card.DIAMOND_EIGHT, + Card.DIAMOND_NINE, + Card.DIAMOND_TEN, + Card.DIAMOND_JACK, + Card.DIAMOND_QUEEN, + Card.DIAMOND_KING, + Card.DIAMOND_ACE + ).collect(Collectors.toList()); + + allOptions = Stream.of( + new CardOption(Card.DIAMOND_SIX), + new CardOption(Card.DIAMOND_SEVEN), + new CardOption(Card.DIAMOND_EIGHT), + new CardOption(Card.DIAMOND_NINE), + new CardOption(Card.DIAMOND_TEN), + new CardOption(Card.DIAMOND_JACK), + new CardOption(Card.DIAMOND_QUEEN), + new CardOption(Card.DIAMOND_KING), + new CardOption(Card.DIAMOND_ACE) + ).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptionTest.java b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptionTest.java new file mode 100644 index 0000000..f1e29e9 --- /dev/null +++ b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptionTest.java @@ -0,0 +1,28 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.options; + +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.ModeOption; +import com.zuehlke.jasschallenge.game.mode.Mode; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ModeOptionTest { + + ModeOption modeOption; + + @Before + public void setUp() throws Exception { + modeOption = new ModeOption(Mode.bottomUp(), 10); + } + + @Test + public void getMode() { + assertEquals(Mode.bottomUp(), modeOption.getMode()); + } + + @Test + public void getTotalRank() { + assertEquals(10, modeOption.getTotalRank()); + } +} \ No newline at end of file diff --git a/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptionsTest.java b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptionsTest.java new file mode 100644 index 0000000..ad8e20f --- /dev/null +++ b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/options/ModeOptionsTest.java @@ -0,0 +1,99 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.options; + +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.ModeOption; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.ModeOptions; +import com.zuehlke.jasschallenge.game.Trumpf; +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.cards.Color; +import com.zuehlke.jasschallenge.game.mode.Mode; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.Assert.*; + +public class ModeOptionsTest { + + private List allDiamondCards; + private List allHeartCards; + private List allSpadeCards; + private List allClubCards; + private List allColors; + + @Before + public void setUp() throws Exception { + allDiamondCards = Stream.of( + Card.DIAMOND_SIX, + Card.DIAMOND_SEVEN, + Card.DIAMOND_EIGHT, + Card.DIAMOND_NINE, + Card.DIAMOND_TEN, + Card.DIAMOND_JACK, + Card.DIAMOND_QUEEN, + Card.DIAMOND_KING, + Card.DIAMOND_ACE + ).collect(Collectors.toList()); + allHeartCards = Stream.of( + Card.HEART_SIX, + Card.HEART_SEVEN, + Card.HEART_EIGHT, + Card.HEART_NINE, + Card.HEART_TEN, + Card.HEART_JACK, + Card.HEART_QUEEN, + Card.HEART_KING, + Card.HEART_ACE + ).collect(Collectors.toList()); + allHeartCards = Stream.of( + Card.SPADE_SIX, + Card.SPADE_SEVEN, + Card.SPADE_EIGHT, + Card.SPADE_NINE, + Card.SPADE_TEN, + Card.SPADE_JACK, + Card.SPADE_QUEEN, + Card.SPADE_KING, + Card.SPADE_ACE + ).collect(Collectors.toList()); + allHeartCards = Stream.of( + Card.CLUB_SIX, + Card.CLUB_SEVEN, + Card.CLUB_EIGHT, + Card.CLUB_NINE, + Card.CLUB_TEN, + Card.CLUB_JACK, + Card.CLUB_QUEEN, + Card.CLUB_KING, + Card.CLUB_ACE + ).collect(Collectors.toList()); + allColors = Stream.of( + Card.DIAMOND_ACE, + Card.HEART_ACE, + Card.SPADE_ACE, + Card.CLUB_ACE + ).collect(Collectors.toList()); + } + + @Test + public void forAvailableCards_allDiamondCards_onlyDiamondColorAndRankXXAndTrumpfRankZZ() { + List modeOptions = ModeOptions.forAvailableCards(allDiamondCards); + List possibleModes = modeOptions.stream() + .map(ModeOption::getMode) + .distinct() + .collect(Collectors.toList()); + List expectedModes = Stream.of( + Mode.topDown(), + Mode.bottomUp(), + Mode.from(Trumpf.TRUMPF, Color.DIAMONDS) + ).collect(Collectors.toList()); + assertEquals(expectedModes, possibleModes); + List totalRanks = modeOptions.stream() + .map(ModeOption::getTotalRank) + .collect(Collectors.toList()); + assertEquals(Arrays.asList(45,45,60), totalRanks); + } +} \ No newline at end of file diff --git a/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRulesTest.java b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRulesTest.java new file mode 100644 index 0000000..dc97476 --- /dev/null +++ b/src/test/java/com/zuehlke/jasschallenge/client/game/strategy/utils/rules/ChooseCardRulesTest.java @@ -0,0 +1,182 @@ +package com.zuehlke.jasschallenge.client.game.strategy.utils.rules; + +import com.zuehlke.jasschallenge.client.game.GameSession; +import com.zuehlke.jasschallenge.client.game.Move; +import com.zuehlke.jasschallenge.client.game.Player; +import com.zuehlke.jasschallenge.client.game.Team; +import com.zuehlke.jasschallenge.client.game.strategy.utils.options.CardOption; +import com.zuehlke.jasschallenge.game.Trumpf; +import com.zuehlke.jasschallenge.game.cards.Card; +import com.zuehlke.jasschallenge.game.cards.Color; +import com.zuehlke.jasschallenge.game.mode.Mode; +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.Array; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.zuehlke.jasschallenge.client.game.strategy.utils.rules.ChooseCardRules.*; +import static org.junit.Assert.assertEquals; + +public class ChooseCardRulesTest { + Mode trumpfClubs; + Mode trumpfSpades; + Mode obeabe; + Mode undeufe; + + Set singleCardHand; + Set weakClubHand; + Set strongClubHand; + Set weakClubAndStrongSpadeHand; + Set weakSpadeHand; + Set strongSpadeHand; + + List singleOptionInput; + List weakClubOptions; + List strongClubOptions; + List weakClubAndStrongSpadeOptions; + List weakSpadeOptions; + List strongSpadeOptions; + + Player weakClubPlayer; + Player strongClubPlayer; + Player weakSpadePlayer; + Player strongSpadePlayer; + + Team weakClubTeam; + Team strongClubTeam; + Team weakSpadeTeam; + Team strongSpadeTeam; + + List strongClubThenWeakSpadePlayers; + List strongClubThenStrongSpadePlayers; + List weakClubThenWeakSpadePlayers; + List weakClubThenStrongSpadePlayers; + + List strongSpadeThenWeakClubPlayers; + List strongSpadeThenStrongClubPlayers; + List weakSpadeThenWeakClubPlayers; + List weakSpadeThenStrongClubPlayers; + + List allMoves = new ArrayList<>(); + + @Before + public void setUp() { + trumpfClubs = Mode.from(Trumpf.TRUMPF, Color.CLUBS); + trumpfSpades = Mode.from(Trumpf.TRUMPF, Color.SPADES); + obeabe = Mode.topDown(); + undeufe = Mode.bottomUp(); + + singleCardHand = Stream.of(Card.CLUB_ACE).collect(Collectors.toSet()); + singleOptionInput = singleCardHand.stream() + .map(CardOption::new) + .collect(Collectors.toList()); + + weakClubHand = Stream.of(Card.CLUB_SIX, Card.CLUB_SEVEN).collect(Collectors.toSet()); + weakClubOptions = weakClubHand.stream().map(CardOption::new).collect(Collectors.toList()); + + strongClubHand = Stream.of(Card.CLUB_JACK, Card.CLUB_NINE).collect(Collectors.toSet()); + strongClubOptions = strongClubHand.stream().map(CardOption::new).collect(Collectors.toList()); + + weakClubAndStrongSpadeHand = Stream.of(Card.CLUB_SIX, Card.SPADE_ACE).collect(Collectors.toSet()); + weakClubAndStrongSpadeOptions = weakClubAndStrongSpadeHand.stream().map(CardOption::new).collect(Collectors.toList()); + + weakSpadeHand = Stream.of(Card.SPADE_SIX, Card.SPADE_SEVEN).collect(Collectors.toSet()); + weakSpadeOptions = weakSpadeHand.stream().map(CardOption::new).collect(Collectors.toList()); + + strongSpadeHand = Stream.of(Card.SPADE_JACK, Card.SPADE_NINE).collect(Collectors.toSet()); + strongSpadeOptions = strongSpadeHand.stream().map(CardOption::new).collect(Collectors.toList()); + + weakClubPlayer = new Player("weakClubPlayer"); + weakClubPlayer.setCards(weakClubHand); + + strongClubPlayer = new Player("strongClubPlayer"); + strongClubPlayer.setCards(strongClubHand); + + weakSpadePlayer = new Player("weakSpadePlayer"); + weakSpadePlayer.setCards(weakSpadeHand); + + strongSpadePlayer = new Player("strongSpadePlayer"); + strongSpadePlayer.setCards(strongSpadeHand); + + weakClubTeam = new Team("weakClubTeam", Collections.singletonList(weakClubPlayer)); + strongClubTeam = new Team("strongClubTeam", Collections.singletonList(strongClubPlayer)); + + weakSpadeTeam = new Team("weakSpadeTeam", Collections.singletonList(weakSpadePlayer)); + strongSpadeTeam = new Team("strongSpadeTeam", Collections.singletonList(strongSpadePlayer)); + + strongClubThenWeakSpadePlayers = Arrays.asList(strongClubPlayer, weakSpadePlayer); + strongClubThenStrongSpadePlayers = Arrays.asList(strongClubPlayer, strongSpadePlayer); + weakClubThenWeakSpadePlayers = Arrays.asList(weakClubPlayer, weakSpadePlayer); + weakClubThenStrongSpadePlayers = Arrays.asList(weakClubPlayer, strongSpadePlayer); + + strongSpadeThenWeakClubPlayers = Arrays.asList(strongSpadePlayer, weakClubPlayer); + strongSpadeThenStrongClubPlayers = Arrays.asList(strongSpadePlayer, strongClubPlayer); + weakSpadeThenWeakClubPlayers = Arrays.asList(weakSpadePlayer, weakClubPlayer); + weakSpadeThenStrongClubPlayers = Arrays.asList(weakSpadePlayer, strongClubPlayer); + } + + @Test + public void CHOOSE_STRONGEST_CARD_IF_ROUND_CAN_BE_WON_oneInputOption_oneInputOption() { + List output = CHOOSE_STRONGEST_CARD_IF_ROUND_CAN_BE_WON.apply( + singleOptionInput, + null, + null + ); + assertEquals(singleOptionInput, output); + } + + @Test + public void CHOOSE_WEAKEST_CARD_THAT_CAN_BE_APPLIED_TO_ROUND_oneInputOption_oneInputOption() { + List output = CHOOSE_WEAKEST_CARD_THAT_CAN_BE_APPLIED_TO_ROUND.apply( + singleOptionInput, + null, + null + ); + assertEquals(singleOptionInput, output); + } + + @Test + public void CHOOSE_WEAKEST_CARD_oneInputOption_oneInputOption() { + List output = CHOOSE_WEAKEST_CARD.apply( + singleOptionInput, + null, + null + ); + assertEquals(singleOptionInput, output); + } + + @Test + public void CHOOSE_STRONGEST_CARD_IF_ROUND_CAN_BE_WON_weakClubAgainstStrongSpadeWithSpadeAsTrumpf_outputEqualInput() { + List teams = Arrays.asList(weakClubTeam, strongSpadeTeam); + GameSession spadeAsTrumpf = new GameSession(teams, strongSpadeThenWeakClubPlayers); + spadeAsTrumpf.startNewGame(trumpfSpades, false); + Move moveOfStrongSpadePlayer = strongSpadePlayer.makeMove(spadeAsTrumpf); + spadeAsTrumpf.makeMove(moveOfStrongSpadePlayer); + List output = CHOOSE_STRONGEST_CARD_IF_ROUND_CAN_BE_WON.apply( + weakClubOptions, + spadeAsTrumpf, + allMoves + ); + assertEquals(weakClubOptions, output); + } + + @Test + public void CHOOSE_STRONGEST_CARD_IF_ROUND_CAN_BE_WON_strongClubAgainstStrongSpadeWithClubAsTrumpf_jackOfClubs() { + List teams = Arrays.asList(strongClubTeam, strongSpadeTeam); + GameSession clubAsTrumpf = new GameSession(teams, strongSpadeThenStrongClubPlayers); + clubAsTrumpf.startNewGame(trumpfClubs, false); + Move moveOfStrongSpadePlayer = strongSpadePlayer.makeMove(clubAsTrumpf); + clubAsTrumpf.makeMove(moveOfStrongSpadePlayer); + List output = CHOOSE_STRONGEST_CARD_IF_ROUND_CAN_BE_WON.apply( + strongClubOptions, + clubAsTrumpf, + allMoves + ); + CardOption strongestCard = strongClubOptions.stream().max(Comparator.comparing(CardOption::getTrumpfRank)).get(); + assertEquals(strongestCard, output.get(0)); + assertEquals(Card.CLUB_JACK, strongestCard.getCard()); + } +} \ No newline at end of file diff --git a/src/test/java/com/zuehlke/jasschallenge/client/websocket/GameHandlerTest.java b/src/test/java/com/zuehlke/jasschallenge/client/websocket/GameHandlerTest.java index 01e871a..c43c376 100644 --- a/src/test/java/com/zuehlke/jasschallenge/client/websocket/GameHandlerTest.java +++ b/src/test/java/com/zuehlke/jasschallenge/client/websocket/GameHandlerTest.java @@ -80,7 +80,7 @@ public void onRequestSession_returnsSessionChoice() { final ChooseSession chooseSession = new GameHandler(new Player("local"), SessionType.TOURNAMENT).onRequestSessionChoice(); - assertThat(chooseSession, sameBeanAs(new ChooseSession(AUTOJOIN, "Java Client session", SessionType.TOURNAMENT))); + assertThat(chooseSession, sameBeanAs(new ChooseSession(AUTOJOIN, "tournament", SessionType.TOURNAMENT))); } @Test diff --git a/src/test/java/com/zuehlke/jasschallenge/client/websocket/RemoteGameSocketTest.java b/src/test/java/com/zuehlke/jasschallenge/client/websocket/RemoteGameSocketTest.java index 43aa482..d20b332 100644 --- a/src/test/java/com/zuehlke/jasschallenge/client/websocket/RemoteGameSocketTest.java +++ b/src/test/java/com/zuehlke/jasschallenge/client/websocket/RemoteGameSocketTest.java @@ -67,7 +67,7 @@ public void onMessage_repliesWitchSessionType_onChooseSession() throws IOExcepti remoteGameSocket.onWebSocketMessage("{\"type\":\"REQUEST_SESSION_CHOICE\"}"); verify(session.getRemote()).sendString("{\"type\":\"CHOOSE_SESSION\"," + - "\"data\":{\"sessionChoice\":\"AUTOJOIN\",\"sessionName\":\"Java Client Session\",\"sessionType\":\"TOURNAMENT\"}}"); + "\"data\":{\"sessionChoice\":\"AUTOJOIN\",\"sessionName\":\"tournament\",\"sessionType\":\"TOURNAMENT\"}}"); verify(handler).onRequestSessionChoice(); verifyNoMoreInteractions(handler); }