Fehler in Spiellogik von Jongleur und Wolke behoben (#12)

main
Jonah Bauer 3 years ago
parent daa64e0393
commit 25ca90ddf5

@ -1,8 +0,0 @@
package eu.jonahbauer.wizard.common.messages.observer;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
public final class JugglingMessage extends ObserverMessage {
}

@ -6,7 +6,7 @@ import eu.jonahbauer.wizard.common.util.SealedClassTypeAdapterFactory;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public abstract sealed class ObserverMessage permits CardMessage, HandMessage, JugglingMessage, PredictionMessage, ScoreMessage, StateMessage, TrickMessage, TrumpMessage, UserInputMessage {
public abstract sealed class ObserverMessage permits CardMessage, HandMessage, PredictionMessage, ScoreMessage, StateMessage, TrickMessage, TrumpMessage, UserInputMessage {
public static final Gson GSON = new GsonBuilder()
.registerTypeAdapterFactory(SealedClassTypeAdapterFactory.of(ObserverMessage.class, "Message"))
.create();

@ -1,8 +1,10 @@
package eu.jonahbauer.wizard.common.messages.observer;
import eu.jonahbauer.wizard.common.messages.player.JuggleMessage;
import eu.jonahbauer.wizard.common.messages.player.PickTrumpMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayCardMessage;
import eu.jonahbauer.wizard.common.messages.player.PredictMessage;
import eu.jonahbauer.wizard.common.model.card.Card;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -17,7 +19,8 @@ import java.util.UUID;
@EqualsAndHashCode(callSuper = true)
public final class UserInputMessage extends ObserverMessage {
/**
* The UUID of the player whose input is required.
* The UUID of the player whose input is required. May be {@code null} to indicate that an input is required from
* every player.
*/
private final UUID player;
/**
@ -45,6 +48,12 @@ public final class UserInputMessage extends ObserverMessage {
* {@link UserInputMessage#getAction()} should be responded to with a {@link PlayCardMessage}.
*/
PLAY_CARD,
/**
* An action that indicates that a player should choose a card that will be given to his left neighbor as a
* result of a played {@link Card#JUGGLER}. A {@link UserInputMessage} with this
* {@link UserInputMessage#getAction()} should be responded to with a {@link JuggleMessage}.
*/
JUGGLE_CARD,
/**
* An action that indicates that a player should pick a trump suit. A {@link UserInputMessage} with this
* {@link UserInputMessage#getAction()} should be responded to with a {@link PickTrumpMessage}.

@ -0,0 +1,13 @@
package eu.jonahbauer.wizard.common.messages.player;
import eu.jonahbauer.wizard.common.model.card.Card;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = true)
public final class JuggleMessage extends PlayerMessage {
private final Card card;
}

@ -6,7 +6,7 @@ import eu.jonahbauer.wizard.common.util.SealedClassTypeAdapterFactory;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public abstract sealed class PlayerMessage permits PickTrumpMessage, PlayCardMessage, PredictMessage {
public abstract sealed class PlayerMessage permits JuggleMessage, PickTrumpMessage, PlayCardMessage, PredictMessage {
public static final Gson GSON = new GsonBuilder()
.registerTypeAdapterFactory(SealedClassTypeAdapterFactory.of(PlayerMessage.class, "Message"))
.create();

@ -12,26 +12,28 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.*;
import java.util.function.Supplier;
@Unmodifiable
@EqualsAndHashCode(of = {"values", "present"})
public final class GameData {
private static final int SIZE = 11;
private static final int SIZE = 12;
public static final GameData EMPTY = new GameData();
public static final Key<List<UUID>> PLAYERS = new Key<>("players", 0);
public static final Key<Map<UUID, Integer>> SCORE = new Key<>("score", 1);
public static final Key<Integer> ROUND = new Key<>("round", 2);
public static final Key<Map<UUID, List<Card>>> HANDS = new Key<>("hands", 3);
public static final Key<Card> TRUMP_CARD = new Key<>("trumpCard", 4);
public static final Key<Card.Suit> TRUMP_SUIT = new Key<>("trumpSuit", 5);
public static final Key<Map<UUID, Integer>> PREDICTIONS = new Key<>("predictions", 6);
public static final Key<Map<UUID, Integer>> TRICKS = new Key<>("tricks", 7);
public static final Key<Integer> TRICK = new Key<>("trick", 8);
public static final Key<List<Pair<UUID, Card>>> STACK = new Key<>("stack", 9);
public static final Key<UUID> CURRENT_PLAYER = new Key<>("currentPlayer", 10);
public static final Key<List<UUID>> PLAYERS = new Key<>("players", 0, null);
public static final Key<Map<UUID, Integer>> SCORE = new Key<>("score", 1, Map::of);
public static final Key<Integer> ROUND = new Key<>("round", 2, () -> 0);
public static final Key<Map<UUID, List<Card>>> HANDS = new Key<>("hands", 3, null);
public static final Key<Card> TRUMP_CARD = new Key<>("trumpCard", 4, null);
public static final Key<Card.Suit> TRUMP_SUIT = new Key<>("trumpSuit", 5, null);
public static final Key<Map<UUID, Integer>> PREDICTIONS = new Key<>("predictions", 6, Map::of);
public static final Key<Map<UUID, Integer>> TRICKS = new Key<>("tricks", 7, Map::of);
public static final Key<Integer> TRICK = new Key<>("trick", 8, () -> 0);
public static final Key<List<Pair<UUID, Card>>> STACK = new Key<>("stack", 9, List::of);
public static final Key<UUID> CURRENT_PLAYER = new Key<>("currentPlayer", 10, null);
public static final Key<UUID> CLOUDED_PLAYER = new Key<>("cloudedPlayer", 11, null);
private final Object[] values;
private final boolean[] present;
private transient final boolean[] required = new boolean[SIZE];
@ -47,18 +49,35 @@ public final class GameData {
}
/**
* Returns the value to which the specified key is mapped or {@code null} if this map contains no mapping for the
* key.
* Returns the value to which the specified key is mapped, the default value for the specified key, or throws
* a {@link NoSuchElementException} if this map neither contains a mapping for the key nor does the key have a
* default value.
* @param key the key whose associated value is to be returned
* @param <T> the value type
* @return the value to which the specified key is mapped, or null if this map contains no mapping for the key
*/
public <T> T get(@NotNull Key<T> key) {
int index = key.index();
if (!present[index]) throw new NoSuchElementException();
if (present[index]) {
//noinspection unchecked
return (T) values[index];
} else if (key.defaultValue() != null) {
present[index] = true;
values[index] = key.defaultValue().get();
//noinspection unchecked
return (T) values[index];
} else {
throw new NoSuchElementException();
}
}
//noinspection unchecked
return (T) values[index];
/**
* Returns {@code true} if this map contains a mapping for the specified key.
* @param key key whose presence in this map is to be tested
* @return {@code true} if this map contains a mapping for the specified key
*/
public boolean has(@NotNull Key<?> key) {
return present[key.index()];
}
/**
@ -172,13 +191,21 @@ public final class GameData {
*/
@Contract("_ -> this")
public GameData require(@NotNull Key<?> key) {
if (!present[key.index()]) {
throw new NoSuchElementException("Could not find required value '" + key + "'.");
if (!present[key.index()] && key.defaultValue() == null) {
throw new InvalidDataException("Could not find required value '" + key + "'.");
}
required[key.index()] = true;
return this;
}
/**
* @see #clean()
*/
public GameData keep(@NotNull Key<?> key) {
required[key.index()] = true;
return this;
}
/**
* Returns {@code this} if this map contains a mapping for each of the specified keys or throws a
* {@link NoSuchElementException} otherwise.
@ -213,13 +240,13 @@ public final class GameData {
var mapValue = get(map);
var listValue = get(list);
for (K k : listValue) {
if (!mapValue.containsKey(k)) throw new NoSuchElementException("Could not find required value: " + map.name() + "[" + k + "].");
if (!mapValue.containsKey(k)) throw new InvalidDataException("Could not find required value: " + map.name() + "[" + k + "].");
}
return this;
}
/**
* Retains only the mappings that have been required since object creation.
* Retains only the mappings that have been required or kept since object creation.
* @return {@code this}
*/
public GameData clean() {
@ -246,6 +273,7 @@ public final class GameData {
public static class Key<T> {
String name;
int index;
Supplier<T> defaultValue;
@Override
public String toString() {

@ -1,14 +1,10 @@
package eu.jonahbauer.wizard.core.machine.states.game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.GameState;
import eu.jonahbauer.wizard.core.machine.states.round.StartingRound;
import java.util.Map;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class Starting extends GameState {
public Starting(GameData data) {
@ -17,9 +13,6 @@ public final class Starting extends GameState {
@Override
public void onEnter(Game game) {
transition(game, new StartingRound(getData().with(
ROUND, 0,
SCORE, Map.of()
)));
transition(game, new StartingRound(getData()));
}
}

@ -1,37 +1,38 @@
package eu.jonahbauer.wizard.core.machine.states.trick;
package eu.jonahbauer.wizard.core.machine.states.round;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.round.FinishingRound;
import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.messages.player.PredictMessage;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.CHANGE_PREDICTION;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.*;
public final class ChangingPrediction extends TrickState {
public final class ChangingPrediction extends RoundState {
private transient final int oldPrediction;
public ChangingPrediction(GameData data) {
super(data);
oldPrediction = get(PREDICTIONS).get(get(CURRENT_PLAYER));
super(data.requireEach(PLAYERS, PREDICTIONS).require(TRICKS, SCORE, CLOUDED_PLAYER));
checkData(data);
oldPrediction = get(PREDICTIONS).get(get(CLOUDED_PLAYER));
}
@Override
public void onEnter(Game game) {
game.notify(new UserInputMessage(get(CURRENT_PLAYER), CHANGE_PREDICTION, getTimeout(game, true)));
game.notify(new UserInputMessage(get(CLOUDED_PLAYER), CHANGE_PREDICTION, getTimeout(game, true)));
timeout(game);
}
@Override
public void onMessage(Game game, UUID player, PlayerMessage message) {
if (get(CURRENT_PLAYER).equals(player) && message instanceof PredictMessage predictMessage) {
if (get(CLOUDED_PLAYER).equals(player) && message instanceof PredictMessage predictMessage) {
checkPrediction(predictMessage.getPrediction());
transition(game, predictMessage.getPrediction());
} else {
@ -53,21 +54,22 @@ public final class ChangingPrediction extends TrickState {
}
private void transition(Game game, int prediction) {
game.notify(new PredictionMessage(get(CURRENT_PLAYER), prediction));
game.notify(new PredictionMessage(get(CLOUDED_PLAYER), prediction));
// add prediction
var predictions = new HashMap<>(get(PREDICTIONS));
predictions.put(get(CURRENT_PLAYER), prediction);
predictions.put(get(CLOUDED_PLAYER), prediction);
GameData data = getData().with(
PREDICTIONS, Map.copyOf(predictions)
);
boolean hasNextTrick = get(TRICK) < get(ROUND);
if (hasNextTrick) {
transition(game, new StartingTrick(data.with(TRICK, get(TRICK) + 1)));
} else {
transition(game, new FinishingRound(data));
transition(game, new FinishingRound(data));
}
private void checkData(GameData data) {
if (data.get(CLOUDED_PLAYER) == null) {
throw new InvalidDataException("Clouded player is null.");
}
}
}

@ -80,7 +80,6 @@ public final class DeterminingTrump extends RoundState {
private void transition(Game game, @NotNull Card.Suit trumpSuit) {
GameData data = getData().with(
TRUMP_SUIT, trumpSuit,
PREDICTIONS, Map.of(),
CURRENT_PLAYER, getNextPlayer(getDealer())
);

@ -80,10 +80,6 @@ public final class Predicting extends RoundState {
);
if (isLastPlayer()) {
data = data.with(
TRICK, 0,
TRICKS, Map.of()
);
transition(game, new StartingTrick(data));
} else {
transition(game, new Predicting(data));

@ -4,9 +4,8 @@ import eu.jonahbauer.wizard.common.model.card.Card;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
import eu.jonahbauer.wizard.core.machine.states.round.ChangingPrediction;
import eu.jonahbauer.wizard.core.machine.states.round.FinishingRound;
import eu.jonahbauer.wizard.common.messages.observer.HandMessage;
import eu.jonahbauer.wizard.common.messages.observer.JugglingMessage;
import eu.jonahbauer.wizard.common.messages.observer.TrickMessage;
import eu.jonahbauer.wizard.core.model.card.*;
import eu.jonahbauer.wizard.core.util.Pair;
@ -43,32 +42,30 @@ public final class FinishingTrick extends TrickState {
|| cards.contains(Card.CLOUD_YELLOW);
boolean hasNextTrick = get(TRICK) < get(ROUND);
// juggle hands
if (juggler && hasNextTrick) {
game.notify(new JugglingMessage());
var hands = get(HANDS);
Map<UUID, List<Card>> juggledHands = new HashMap<>();
hands.forEach((player, hand) -> juggledHands.put(getNextPlayer(player), hand));
data = data.with(HANDS, Map.copyOf(juggledHands));
juggledHands.forEach((player, hand) -> game.notify(player, new HandMessage(player, hand)));
}
// trick is not counted when a bomb is present
if (!bomb) {
// trick is not counted when a bomb is present
var tricks = new HashMap<>(get(TRICKS));
tricks.merge(winner, 1, Integer::sum);
data = data.with(TRICKS, Map.copyOf(tricks));
// mark "clouded player"
if (cloud) {
data = data.with(CLOUDED_PLAYER, winner);
}
}
data = data.with(CURRENT_PLAYER, winner);
if (cloud && !bomb) {
// adjust prediction
transition(game, new ChangingPrediction(data));
} else if (hasNextTrick) {
transition(game, new StartingTrick(data.with(TRICK, get(TRICK) + 1)));
if (!hasNextTrick) {
if (data.has(CLOUDED_PLAYER)) {
transition(game, new ChangingPrediction(data));
} else {
transition(game, new FinishingRound(data));
}
} else if (juggler) {
transition(game, new Juggling(data));
} else {
transition(game, new FinishingRound(data));
transition(game, new StartingTrick(data.with(TRICK, get(TRICK) + 1)));
}
}

@ -0,0 +1,82 @@
package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.common.messages.observer.HandMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.JuggleMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.model.card.Card;
import eu.jonahbauer.wizard.core.machine.Game;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.*;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
public final class Juggling extends TrickState {
private final transient Map<UUID, Card> juggledCards = new ConcurrentHashMap<>();
public Juggling(GameData data) {
super(data);
}
@Override
public void onEnter(Game game) {
game.notify(new UserInputMessage(null, JUGGLE_CARD, getTimeout(game, true)));
timeout(game);
}
@Override
public void onTimeout(Game game) {
for (UUID player : get(PLAYERS)) {
juggledCards.computeIfAbsent(player, p -> {
var hand = get(HANDS).get(p);
return hand.get(game.getRandom().nextInt(hand.size()));
});
}
juggle(game);
}
@Override
public void onMessage(Game game, UUID player, PlayerMessage message) {
if (get(PLAYERS).contains(player) && message instanceof JuggleMessage juggleMessage) {
Card card = juggleMessage.getCard();
if (!get(HANDS).get(player).contains(card)) {
throw new IllegalArgumentException("You do not have this card on your hand.");
}
juggledCards.put(player, card);
if (juggledCards.size() == get(PLAYERS).size()) {
juggle(game);
}
} else {
super.onMessage(game, player, message);
}
}
private void juggle(Game game) {
Map<UUID, Card> newCards = new HashMap<>();
juggledCards.forEach((player, card) -> newCards.put(getNextPlayer(player), card));
var mutableHands = new HashMap<>(get(HANDS));
for (UUID player : get(PLAYERS)) {
var mutableHand = new ArrayList<>(mutableHands.get(player));
var oldCard = juggledCards.get(player);
var newCard = newCards.get(player);
mutableHand.set(mutableHand.indexOf(oldCard), newCard);
var immutableHand = List.copyOf(mutableHand);
mutableHands.put(player, immutableHand);
game.notify(player, new HandMessage(player, immutableHand));
}
var data = getData().with(
HANDS, Map.copyOf(mutableHands),
TRICK, get(TRICK) + 1
);
transition(game, new StartingTrick(data));
}
}

@ -1,11 +1,7 @@
package eu.jonahbauer.wizard.core.machine.states.trick;
import eu.jonahbauer.wizard.core.machine.states.GameData;
import eu.jonahbauer.wizard.core.machine.Game;
import java.util.List;
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
import eu.jonahbauer.wizard.core.machine.states.GameData;
public final class StartingTrick extends TrickState {
public StartingTrick(GameData data) {
@ -14,6 +10,6 @@ public final class StartingTrick extends TrickState {
@Override
public void onEnter(Game game) {
transition(game, new PlayingCard(getData().with(STACK, List.of())));
transition(game, new PlayingCard(getData()));
}
}

@ -11,6 +11,7 @@ public abstract class TrickState extends RoundState {
data.requireEach(PLAYERS, HANDS)
.requireEach(PLAYERS, PREDICTIONS)
.require(TRUMP_SUIT, TRICK, TRICKS, CURRENT_PLAYER)
.keep(CLOUDED_PLAYER)
);
}
}

@ -2,10 +2,7 @@ package eu.jonahbauer.wizard.core.machine;
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
import eu.jonahbauer.wizard.common.messages.player.PickTrumpMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayCardMessage;
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
import eu.jonahbauer.wizard.common.messages.player.PredictMessage;
import eu.jonahbauer.wizard.common.messages.player.*;
import eu.jonahbauer.wizard.common.model.card.Card;
import eu.jonahbauer.wizard.core.messages.Observer;
import lombok.Getter;
@ -31,8 +28,14 @@ public class MessageQueue implements Observer {
@Setter
private Game game;
private BulkQueuedMessage bulk;
public MessageQueue add(UUID player, UserInputMessage.Action action, PlayerMessage message) {
messages.add(new QueuedMessage(player, action, message));
if (bulk != null) {
bulk.getMessages().add(new SingleQueuedMessage(player, action, message));
} else {
messages.add(new SingleQueuedMessage(player, action, message));
}
return this;
}
@ -40,6 +43,10 @@ public class MessageQueue implements Observer {
return add(player, UserInputMessage.Action.PLAY_CARD, new PlayCardMessage(card));
}
public MessageQueue addJuggle(UUID player, Card card) {
return add(player, UserInputMessage.Action.JUGGLE_CARD, new JuggleMessage(card));
}
public MessageQueue addPrediction(UUID player, int prediction) {
return add(player, UserInputMessage.Action.MAKE_PREDICTION, new PredictMessage(prediction));
}
@ -76,7 +83,27 @@ public class MessageQueue implements Observer {
}
public MessageQueue assertThrows(Class<? extends Exception> exception) {
messages.getLast().setException(exception);
var message = messages.getLast();
if (message instanceof SingleQueuedMessage singleMessage) {
singleMessage.setException(exception);
} else if (message instanceof BulkQueuedMessage bulkMessage) {
bulkMessage.getMessages().get(bulkMessage.getMessages().size() - 1).setException(exception);
}
return this;
}
public MessageQueue begin() {
if (bulk != null) {
end();
}
bulk = new BulkQueuedMessage();
messages.add(bulk);
return this;
}
public MessageQueue end() {
if (bulk == null) throw new IllegalStateException();
bulk = null;
return this;
}
@ -85,18 +112,27 @@ public class MessageQueue implements Observer {
System.out.println(om);
if (om instanceof UserInputMessage message) {
UUID player = message.getPlayer();
while (true) {
assertFalse(messages.isEmpty(), "User input is required but none is provided.");
assertFalse(messages.isEmpty(), "User input is required but none is provided.");
var queued = messages.poll();
var queued = messages.poll();
List<SingleQueuedMessage> list;
if (queued instanceof SingleQueuedMessage s) {
list = List.of(s);
} else if (queued instanceof BulkQueuedMessage b) {
list = b.getMessages();
} else {
throw new AssertionError();
}
var queuedPlayer = queued.getPlayer();
var queuedAction = queued.getAction();
var queuedMessage = queued.getMessage();
var exception = queued.getException();
for (SingleQueuedMessage queuedSingle : list) {
var queuedPlayer = queuedSingle.getPlayer();
var queuedAction = queuedSingle.getAction();
var queuedMessage = queuedSingle.getMessage();
var exception = queuedSingle.getException();
if (exception == null) {
assertEquals(queuedPlayer, player);
if (player != null) assertEquals(queuedPlayer, player);
assertEquals(queuedAction, message.getAction());
}
@ -114,7 +150,6 @@ public class MessageQueue implements Observer {
);
} else {
Assertions.assertDoesNotThrow(() -> game.onMessage(queuedPlayer, queuedMessage).get());
return;
}
}
}
@ -133,13 +168,20 @@ public class MessageQueue implements Observer {
}
private interface QueuedMessage {}
@Getter
@Setter
@RequiredArgsConstructor
private static class QueuedMessage {
private static class SingleQueuedMessage implements QueuedMessage {
private final UUID player;
private final UserInputMessage.Action action;
private final PlayerMessage message;
private Class<? extends Exception> exception;
}
@Getter
private static class BulkQueuedMessage implements QueuedMessage {
private final List<SingleQueuedMessage> messages = new ArrayList<>();
}
}

@ -103,16 +103,21 @@ public class PredictingTest {
public void predicting_WithWrongInput() {
// play cards in given order
MessageQueue queue = new MessageQueue()
.addPrediction(players[0], -1).assertThrows(IllegalArgumentException.class)
.addPrediction(players[0], 6).assertThrows(IllegalArgumentException.class)
.addPrediction(players[0], 4)
.addPrediction(players[2], 3).assertThrows(IllegalStateException.class)
.addPrediction(players[1], 3)
.addPrediction(players[2], 5).assertThrows(IllegalArgumentException.class)
.addPrediction(players[2], 3)
.addCard(players[3], Card.GREEN_WIZARD).assertThrows(IllegalStateException.class)
.addPickTrump(players[3], Card.Suit.GREEN).assertThrows(IllegalStateException.class)
.addPrediction(players[3], 0);
.begin()
.addPrediction(players[0], -1).assertThrows(IllegalArgumentException.class)
.addPrediction(players[0], 6).assertThrows(IllegalArgumentException.class)
.addPrediction(players[0], 4)
.begin()
.addPrediction(players[2], 3).assertThrows(IllegalStateException.class)
.addPrediction(players[1], 3)
.begin()
.addPrediction(players[2], 5).assertThrows(IllegalArgumentException.class)
.addPrediction(players[2], 3)
.begin()
.addCard(players[3], Card.GREEN_WIZARD).assertThrows(IllegalStateException.class)
.addPickTrump(players[3], Card.Suit.GREEN).assertThrows(IllegalStateException.class)
.addPrediction(players[3], 0)
.end();
Game game = performTest(Configurations.ANNIVERSARY_2021, 3, queue);
@ -143,8 +148,10 @@ public class PredictingTest {
.addPrediction(players[0], 1)
.addPrediction(players[1], 1)
.addPrediction(players[2], 1)
.addPrediction(players[3], 1).assertThrows(IllegalArgumentException.class)
.addPrediction(players[3], 0);
.begin()
.addPrediction(players[3], 1).assertThrows(IllegalArgumentException.class)
.addPrediction(players[3], 0)
.end();
Game game = performTest(Configurations.ANNIVERSARY_2021_PM1, 3, queue);

@ -142,45 +142,56 @@ public class RoundTest {
.addPrediction(players[3], 2)
.addPrediction(players[0], 2)
.addPrediction(players[1], 2)
.addPrediction(players[2], 1).assertThrows(IllegalArgumentException.class)
.addPrediction(players[2], 3)
.begin()
.addPrediction(players[2], 1).assertThrows(IllegalArgumentException.class)
.addPrediction(players[2], 3)
.end()
// trick 0
.addCard(players[3], Card.RED_11).assertThrows(IllegalArgumentException.class)
.addCard(players[3], Card.BLUE_2)
.addCard(players[3], Card.RED_11).assertThrows(IllegalStateException.class)
.addCard(players[0], Card.YELLOW_8)
.begin()
.addCard(players[3], Card.RED_11).assertThrows(IllegalArgumentException.class)
.addCard(players[3], Card.BLUE_2)
.begin()
.addCard(players[3], Card.RED_11).assertThrows(IllegalStateException.class)
.addCard(players[0], Card.YELLOW_8)
.end()
.addCard(players[1], Card.BLUE_9)
.addCard(players[2], Card.GREEN_WIZARD)
// trick 1
.addCard(players[3], Card.RED_11).assertThrows(IllegalStateException.class)
.addCard(players[2], Card.YELLOW_4)
.begin()
.addCard(players[3], Card.RED_11).assertThrows(IllegalStateException.class)
.addCard(players[2], Card.YELLOW_4)
.end()
.addCard(players[3], Card.YELLOW_3)
.addCard(players[0], Card.YELLOW_WIZARD)
.addCard(players[1], Card.BOMB)
// trick 2
.addCard(players[0], Card.RED_3)
.addCard(players[3], Card.RED_11).assertThrows(IllegalStateException.class)
.addCard(players[1], Card.RED_12)
.begin()
.addCard(players[3], Card.RED_11).assertThrows(IllegalStateException.class)
.addCard(players[1], Card.RED_12)
.end()
.addCard(players[2], Card.RED_2)
.addCard(players[3], Card.DRAGON)
// trick 3
.addCard(players[3], Card.BLUE_13)
.addCard(players[0], Card.CLOUD).assertThrows(IllegalArgumentException.class)
.addCard(players[0], Card.CLOUD_YELLOW)
.addCard(players[3], Card.RED_11).assertThrows(IllegalStateException.class)
.addCard(players[1], Card.YELLOW_13)
.begin()
.addCard(players[0], Card.CLOUD).assertThrows(IllegalArgumentException.class)
.addCard(players[0], Card.CLOUD_YELLOW)
.begin()
.addCard(players[3], Card.RED_11).assertThrows(IllegalStateException.class)
.addCard(players[1], Card.YELLOW_13)
.end()
.addCard(players[2], Card.BLUE_1)
.addChangePrediction(players[1], 0).assertThrows(IllegalArgumentException.class)
.addChangePrediction(players[1], 2).assertThrows(IllegalArgumentException.class)
.addChangePrediction(players[1], 4).assertThrows(IllegalArgumentException.class)
.addChangePrediction(players[1], 1)
// trick 4
.addCard(players[1], Card.RED_7)
.addCard(players[2], Card.YELLOW_11)
.addCard(players[3], Card.RED_11).assertThrows(IllegalArgumentException.class)
.addCard(players[3], Card.RED_5)
.addCard(players[0], Card.CLOUD).assertThrows(IllegalArgumentException.class)
.addCard(players[0], Card.CHANGELING_WIZARD)
.begin()
.addCard(players[3], Card.RED_11).assertThrows(IllegalArgumentException.class)
.addCard(players[3], Card.RED_5)
.begin()
.addCard(players[0], Card.CLOUD).assertThrows(IllegalArgumentException.class)
.addCard(players[0], Card.CHANGELING_WIZARD)
.end()
// trick 5
.addCard(players[0], Card.GREEN_7)
.addCard(players[1], Card.FAIRY)
@ -190,7 +201,14 @@ public class RoundTest {
.addCard(players[2], Card.BLUE_4)
.addCard(players[3], Card.BLUE_11)
.addCard(players[0], Card.GREEN_1)
.addCard(players[1], Card.GREEN_11);
.addCard(players[1], Card.GREEN_11)
// cloud
.begin()
.addChangePrediction(players[1], 0).assertThrows(IllegalArgumentException.class)
.addChangePrediction(players[1], 2).assertThrows(IllegalArgumentException.class)
.addChangePrediction(players[1], 4).assertThrows(IllegalArgumentException.class)
.addChangePrediction(players[1], 1)
.end();
int round = 6;
Game game = performTest(227L, Configurations.ANNIVERSARY_2021_PM1, round, queue);
@ -219,12 +237,10 @@ public class RoundTest {
}
order.verify(game).notify(any(StateMessage.class)); // finishing_trick
order.verify(game).notify(any(TrickMessage.class)); // trick
if (i == 3) {
order.verify(game).notify(any(StateMessage.class)); // change prediction
order.verify(game).notify(any(UserInputMessage.class));
order.verify(game).notify(any(PredictionMessage.class));
}
}
order.verify(game).notify(any(StateMessage.class)); // change prediction
order.verify(game).notify(any(UserInputMessage.class));
order.verify(game).notify(any(PredictionMessage.class));
order.verify(game).notify(any(StateMessage.class)); // finishing_round
order.verify(game).notify(argThat(message ->
message instanceof ScoreMessage score

@ -102,12 +102,16 @@ public class TrickTest {
// play cards in given order
MessageQueue queue = new MessageQueue()
.addCard(players[0], Card.RED_1)
.addCard(players[2], Card.GREEN_1).assertThrows(IllegalStateException.class)
.addCard(players[1], Card.GREEN_1).assertThrows(IllegalArgumentException.class)
.addCard(players[1], Card.YELLOW_1)
.begin()
.addCard(players[2], Card.GREEN_1).assertThrows(IllegalStateException.class)
.addCard(players[1], Card.GREEN_1).assertThrows(IllegalArgumentException.class)
.addCard(players[1], Card.YELLOW_1)
.end()
.addCard(players[2], Card.GREEN_1)
.addPrediction(players[3], 1).assertThrows(IllegalStateException.class)
.addCard(players[3], Card.BLUE_1);
.begin()
.addPrediction(players[3], 1).assertThrows(IllegalStateException.class)
.addCard(players[3], Card.BLUE_1)
.end();
Game game = performTest(Configurations.DEFAULT, 0, 0, hands, Card.Suit.BLUE, queue);
@ -178,7 +182,7 @@ public class TrickTest {
players[0], List.of(Card.RED_1, Card.GREEN_12),
players[1], List.of(Card.JUGGLER, Card.YELLOW_3),
players[2], List.of(Card.GREEN_1, Card.BLUE_4),
players[3], List.of(Card.BLUE_1, Card.RED_5)
players[3], List.of(Card.RED_5, Card.BLUE_1)
);
// play cards in given order
@ -186,7 +190,13 @@ public class TrickTest {
.addCard(players[0], Card.RED_1)
.addCard(players[1], Card.JUGGLER_RED)
.addCard(players[2], Card.GREEN_1)
.addCard(players[3], Card.RED_5);
.addCard(players[3], Card.RED_5)
.begin()
.addJuggle(players[0], Card.GREEN_12)
.addJuggle(players[1], Card.YELLOW_3)
.addJuggle(players[2], Card.BLUE_4)
.addJuggle(players[3], Card.BLUE_1)
.end();
Game game = performTest(Configurations.ANNIVERSARY_2021, 1, 0, hands, Card.Suit.YELLOW, queue);
@ -207,7 +217,8 @@ public class TrickTest {
order.verify(game).notify(any(CardMessage.class)); // user response
order.verify(game).notify(any(StateMessage.class)); // finishing trick
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[1])); // trick with correct winner
order.verify(game).notify(any(JugglingMessage.class));
order.verify(game).notify(any(StateMessage.class)); // juggling
order.verify(game).notify(any(UserInputMessage.class)); // user input request
order.verify(game, times(4)).notify(any(), any(HandMessage.class));
order.verify(game).transition(any(), any(StartingTrick.class)); // there is another trick
order.verify(game).notify(any(StateMessage.class)); // finish

Loading…
Cancel
Save