Reworked state machine
This commit is contained in:
parent
a9e2b1ddd9
commit
d037ada8c5
@ -0,0 +1,123 @@
|
|||||||
|
package eu.jonahbauer.wizard.common.machine;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public abstract class Context<S extends State<S,C>, C extends Context<S,C>> {
|
||||||
|
@Getter
|
||||||
|
private @NotNull S state;
|
||||||
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
public Context(@NotNull S initialState) {
|
||||||
|
this.state = initialState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically executes the given supplier and transitions to the returned state if present.
|
||||||
|
* @see #transition(State)
|
||||||
|
*/
|
||||||
|
public void execute(Supplier<Optional<@NotNull S>> transition) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
Optional<S> next = transition.get();
|
||||||
|
next.ifPresent(this::transition);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically applies the given function to the current state and transitions to the returned state if present.
|
||||||
|
* @see #transition(State)
|
||||||
|
*/
|
||||||
|
public void execute(Function<@NotNull S, Optional<@NotNull S>> transition) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
Optional<S> next = transition.apply(state);
|
||||||
|
next.ifPresent(this::transition);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically transitions to the specified state calling the state lifecycle methods.
|
||||||
|
* When an error occurs during execution of the lifecycle methods {@link #handleError(Throwable)} is called.
|
||||||
|
* @param state the next state
|
||||||
|
* @see State#onExit(Context)
|
||||||
|
* @see State#onEnter(Context)
|
||||||
|
* @see #handleError(Throwable)
|
||||||
|
*/
|
||||||
|
public void transition(@NotNull S state) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
//noinspection unchecked
|
||||||
|
this.state.onExit((C) this);
|
||||||
|
onTransition(this.state, state);
|
||||||
|
this.state = state;
|
||||||
|
//noinspection unchecked
|
||||||
|
state.onEnter((C) this).ifPresent(this::transition);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
handleError(t);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically checks that the current state is {@linkplain Object#equals(Object) equal to} the specified expected
|
||||||
|
* state and transitions to the specified next state.
|
||||||
|
* @param expected the expected current state
|
||||||
|
* @param next the next state
|
||||||
|
* @see #transition(State)
|
||||||
|
* @throws IllegalStateException if the current state is not equal to the expected state
|
||||||
|
*/
|
||||||
|
public void transition(@NotNull S expected, @NotNull S next) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
if (Objects.equals(expected, this.state)) {
|
||||||
|
transition(next);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Current state is not " + expected + ".");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomically transitions to the specified state without calling the state lifecycle methods. This method can
|
||||||
|
* be used to recover from errors occurring during {@link State#onExit(Context)} which would otherwise prevent
|
||||||
|
* the context from transitioning to another state.
|
||||||
|
* @param state the next state
|
||||||
|
*/
|
||||||
|
protected void forceTransition(@NotNull S state) {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
onTransition(this.state, state);
|
||||||
|
this.state = state;
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method that will synchronously be called on transitioning between two states.
|
||||||
|
* @param from the previous state
|
||||||
|
* @param to the next state
|
||||||
|
*/
|
||||||
|
protected void onTransition(S from, S to) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method that will synchronously be called when an error occurs during execution of state lifecycle
|
||||||
|
* methods.
|
||||||
|
* @param t the cause
|
||||||
|
*/
|
||||||
|
protected abstract void handleError(Throwable t);
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package eu.jonahbauer.wizard.common.machine;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface State<S extends State<S,C>, C extends Context<S,C>> {
|
||||||
|
default Optional<S> onEnter(C context) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onExit(C context) {}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package eu.jonahbauer.wizard.common.machine;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public abstract class TimeoutContext<S extends TimeoutState<S,C>, C extends TimeoutContext<S,C>> extends Context<S,C> {
|
||||||
|
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
|
public TimeoutContext(@NotNull S initialState) {
|
||||||
|
super(initialState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void timeout(@NotNull S currentState, long delay) {
|
||||||
|
scheduler.schedule(() -> {
|
||||||
|
if (Objects.equals(getState(), currentState)) {
|
||||||
|
execute(() -> {
|
||||||
|
if (Objects.equals(getState(), currentState)) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return currentState.onTimeout((C) this);
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, delay, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdownNow() {
|
||||||
|
scheduler.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package eu.jonahbauer.wizard.common.machine;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface TimeoutState<S extends TimeoutState<S,C>, C extends TimeoutContext<S,C>> extends State<S,C> {
|
||||||
|
default Optional<S> onTimeout(C context) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public class CLI {
|
public class CLI {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
GameConfiguration config = Configurations.DEFAULT.withTimeout(0);
|
GameConfiguration config = Configurations.DEFAULT;
|
||||||
Observer observer = (player, msg) -> System.out.println(msg);
|
Observer observer = (player, msg) -> System.out.println(msg);
|
||||||
Game game = new Game(config, observer);
|
Game game = new Game(config, observer);
|
||||||
var players = List.of(
|
var players = List.of(
|
||||||
@ -52,24 +52,15 @@ public class CLI {
|
|||||||
switch (command) {
|
switch (command) {
|
||||||
case "predict" -> {
|
case "predict" -> {
|
||||||
int prediction = Integer.parseInt(param);
|
int prediction = Integer.parseInt(param);
|
||||||
game.onMessage(players.get(id), new PredictMessage(prediction))
|
game.onMessage(players.get(id), new PredictMessage(prediction));
|
||||||
.whenComplete((v, err) -> {
|
|
||||||
if (err != null) err.printStackTrace();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
case "play" -> {
|
case "play" -> {
|
||||||
Card card = Card.valueOf(param);
|
Card card = Card.valueOf(param);
|
||||||
game.onMessage(players.get(id), new PlayCardMessage(card))
|
game.onMessage(players.get(id), new PlayCardMessage(card));
|
||||||
.whenComplete((v, err) -> {
|
|
||||||
if (err != null) err.printStackTrace();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
case "trump" -> {
|
case "trump" -> {
|
||||||
Card.Suit suit = Card.Suit.valueOf(param);
|
Card.Suit suit = Card.Suit.valueOf(param);
|
||||||
game.onMessage(players.get(id), new PickTrumpMessage(suit))
|
game.onMessage(players.get(id), new PickTrumpMessage(suit));
|
||||||
.whenComplete((v, err) -> {
|
|
||||||
if (err != null) err.printStackTrace();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
default -> System.err.println("Unknown command: " + command);
|
default -> System.err.println("Unknown command: " + command);
|
||||||
}
|
}
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
package eu.jonahbauer.wizard.core.machine;
|
|
||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.states.State;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.jetbrains.annotations.Blocking;
|
|
||||||
import org.jetbrains.annotations.NonBlocking;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public abstract class Context<S extends State<S,C>, C extends Context<S,C>> {
|
|
||||||
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
|
||||||
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
|
|
||||||
1, 1,
|
|
||||||
0, TimeUnit.SECONDS,
|
|
||||||
new PriorityBlockingQueue<>(
|
|
||||||
11,
|
|
||||||
Comparator.comparingInt(r -> r instanceof PriorityRunnable prio ? prio.getPriority() : Integer.MAX_VALUE)
|
|
||||||
.thenComparingLong(r -> r instanceof PriorityRunnable prio ? prio.getTimestamp() : Long.MAX_VALUE)
|
|
||||||
),
|
|
||||||
r -> {
|
|
||||||
var t = new Thread(r);
|
|
||||||
t.setUncaughtExceptionHandler((t1, e) -> finish(e));
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
protected S state;
|
|
||||||
|
|
||||||
private final CompletableFuture<Void> future = new CompletableFuture<>();
|
|
||||||
private final CompletableFuture<Void> finished = future.whenComplete((v, t) -> {
|
|
||||||
executor.shutdownNow();
|
|
||||||
scheduler.shutdownNow();
|
|
||||||
});
|
|
||||||
|
|
||||||
@NonBlocking
|
|
||||||
protected CompletableFuture<Void> submit(Runnable runnable) {
|
|
||||||
var future = new CompletableFuture<Void>();
|
|
||||||
executor.execute(new PriorityRunnable(100, () -> {
|
|
||||||
try {
|
|
||||||
runnable.run();
|
|
||||||
future.complete(null);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.completeExceptionally(t);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
return future;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Blocking
|
|
||||||
protected void start(@NotNull S state) {
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
AtomicReference<RuntimeException> exception = new AtomicReference<>();
|
|
||||||
|
|
||||||
executor.execute(new PriorityRunnable(0, () -> {
|
|
||||||
if (future.isDone()) {
|
|
||||||
exception.set(new IllegalStateException("Context has already finished."));
|
|
||||||
latch.countDown();
|
|
||||||
} else {
|
|
||||||
latch.countDown();
|
|
||||||
doTransition(null, state);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
latch.await();
|
|
||||||
if (exception.get() != null) {
|
|
||||||
throw exception.get();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} catch (InterruptedException ignored) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonBlocking
|
|
||||||
public void transition(S currentState, S newState) {
|
|
||||||
executor.execute(new PriorityRunnable(0, () -> doTransition(currentState, newState)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonBlocking
|
|
||||||
public void finish() {
|
|
||||||
finish(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonBlocking
|
|
||||||
public void finish(Throwable exception) {
|
|
||||||
executor.execute(new PriorityRunnable(0, () -> doFinish(exception)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonBlocking
|
|
||||||
public void cancel() {
|
|
||||||
finish(new CancellationException());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* internal methods that are called on the executor
|
|
||||||
*/
|
|
||||||
|
|
||||||
private void doTransition(S currentState, S newState) {
|
|
||||||
if (state == currentState) {
|
|
||||||
state = newState;
|
|
||||||
if (currentState != null) //noinspection unchecked
|
|
||||||
currentState.onExit((C) this);
|
|
||||||
onTransition(currentState, newState);
|
|
||||||
if (newState != null) //noinspection unchecked
|
|
||||||
newState.onEnter((C) this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doFinish(Throwable t) {
|
|
||||||
if (future.isDone()) return;
|
|
||||||
|
|
||||||
doTransition(state, null);
|
|
||||||
if (t != null) {
|
|
||||||
future.completeExceptionally(t);
|
|
||||||
} else {
|
|
||||||
future.complete(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onTransition(S from, S to) {}
|
|
||||||
|
|
||||||
@Blocking
|
|
||||||
public void await() throws InterruptedException, ExecutionException, CancellationException {
|
|
||||||
finished.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void timeout(@NotNull S currentState, long delay) {
|
|
||||||
scheduler.schedule(() -> {
|
|
||||||
submit(() -> {
|
|
||||||
if (state == currentState) {
|
|
||||||
//noinspection unchecked
|
|
||||||
state.onTimeout((C) this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, delay, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
private static class PriorityRunnable implements Runnable {
|
|
||||||
private final int priority;
|
|
||||||
private final long timestamp = System.nanoTime();
|
|
||||||
private final Runnable runnable;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
runnable.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +1,58 @@
|
|||||||
package eu.jonahbauer.wizard.core.machine;
|
package eu.jonahbauer.wizard.core.machine;
|
||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.common.machine.TimeoutContext;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameState;
|
|
||||||
import eu.jonahbauer.wizard.core.machine.states.game.Starting;
|
|
||||||
import eu.jonahbauer.wizard.core.messages.Observer;
|
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.StateMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.StateMessage;
|
||||||
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.game.Created;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.game.Error;
|
||||||
|
import eu.jonahbauer.wizard.core.messages.Observer;
|
||||||
import eu.jonahbauer.wizard.core.model.GameConfiguration;
|
import eu.jonahbauer.wizard.core.model.GameConfiguration;
|
||||||
import eu.jonahbauer.wizard.core.util.Util;
|
import eu.jonahbauer.wizard.core.util.Util;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import org.jetbrains.annotations.Blocking;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
|
public final class Game extends TimeoutContext<GameState, Game> {
|
||||||
|
|
||||||
public final class Game extends Context<GameState, Game> {
|
|
||||||
@Getter
|
@Getter
|
||||||
private final Random random;
|
private final Random random;
|
||||||
@Getter
|
@Getter
|
||||||
private final GameConfiguration config;
|
private final GameConfiguration config;
|
||||||
private final Observer observer;
|
private final Observer observer;
|
||||||
|
|
||||||
|
private final CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
|
||||||
public Game(GameConfiguration config, Observer observer) {
|
public Game(GameConfiguration config, Observer observer) {
|
||||||
|
super(new Created());
|
||||||
this.random = new Random();
|
this.random = new Random();
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.observer = observer;
|
this.observer = observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Game(long seed, GameConfiguration config, Observer observer) {
|
public Game(long seed, GameConfiguration config, Observer observer) {
|
||||||
|
super(new Created());
|
||||||
this.random = new Random(seed);
|
this.random = new Random(seed);
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.observer = observer;
|
this.observer = observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(List<UUID> players) {
|
|
||||||
start(new Starting(GameData.EMPTY.with(PLAYERS, List.copyOf(players))));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void resume(GameState state) {
|
public void resume(GameState state) {
|
||||||
start(state);
|
transition(new Created(), state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Void> onMessage(UUID player, PlayerMessage message) {
|
public void start(List<UUID> players) {
|
||||||
return submit(() -> {
|
execute(s -> s.start(this, players));
|
||||||
if (state != null) {
|
}
|
||||||
state.onMessage(this, player, message);
|
|
||||||
}
|
public void onMessage(UUID player, PlayerMessage message) {
|
||||||
});
|
execute(s -> s.onMessage(this, player, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -58,6 +60,13 @@ public final class Game extends Context<GameState, Game> {
|
|||||||
notify(new StateMessage(to != null ? Util.toSnakeCase(to.getClass().getSimpleName()) : "null"));
|
notify(new StateMessage(to != null ? Util.toSnakeCase(to.getClass().getSimpleName()) : "null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleError(Throwable t) {
|
||||||
|
// don't use usual transition procedure to prevent errors during onExit from causing stack overflow
|
||||||
|
forceTransition(new Error(t));
|
||||||
|
this.complete(t);
|
||||||
|
}
|
||||||
|
|
||||||
public void notify(ObserverMessage message) {
|
public void notify(ObserverMessage message) {
|
||||||
try {
|
try {
|
||||||
observer.notify(message);
|
observer.notify(message);
|
||||||
@ -73,4 +82,18 @@ public final class Game extends Context<GameState, Game> {
|
|||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void complete(@Nullable Throwable t) {
|
||||||
|
shutdownNow();
|
||||||
|
if (t != null) {
|
||||||
|
future.completeExceptionally(t);
|
||||||
|
} else {
|
||||||
|
future.complete(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Blocking
|
||||||
|
public void await() throws InterruptedException, ExecutionException, CancellationException {
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,57 @@
|
|||||||
package eu.jonahbauer.wizard.core.machine.states;
|
package eu.jonahbauer.wizard.core.machine;
|
||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.common.machine.TimeoutState;
|
||||||
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.CURRENT_PLAYER;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.CURRENT_PLAYER;
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
|
||||||
|
|
||||||
@Unmodifiable
|
@Unmodifiable
|
||||||
public abstract class GameState implements State<GameState, Game> {
|
public abstract class GameState implements TimeoutState<GameState, Game> {
|
||||||
@Getter
|
@Getter
|
||||||
private final GameData data;
|
private final GameData data;
|
||||||
|
|
||||||
public GameState(GameData data) {
|
public GameState(GameData data) {
|
||||||
this.data = data.require(PLAYERS).clean();
|
this.data = data.clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void transition(Game game, GameState state) {
|
//<editor-fold desc="Utility Methods" defaultstate="collapsed">
|
||||||
game.transition(this, state);
|
protected final Optional<GameState> timeout(Game game) {
|
||||||
}
|
|
||||||
|
|
||||||
protected final void timeout(Game game) {
|
|
||||||
game.timeout(this, getTimeout(game, false));
|
game.timeout(this, getTimeout(game, false));
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Optional<GameState> transition(GameState state) {
|
||||||
|
return Optional.of(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final long getTimeout(Game game, boolean absolute) {
|
protected final long getTimeout(Game game, boolean absolute) {
|
||||||
return (absolute ? System.currentTimeMillis() : 0) + game.getConfig().timeout();
|
return (absolute ? System.currentTimeMillis() : 0) + game.getConfig().timeout();
|
||||||
}
|
}
|
||||||
|
//</editor-fold>
|
||||||
|
|
||||||
public void onMessage(Game game, UUID player, PlayerMessage message) {
|
public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {
|
||||||
throw new IllegalStateException("You cannot do that right now.");
|
throw new IllegalStateException("You cannot do that right now.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<GameState> start(Game game, List<UUID> players) {
|
||||||
|
throw new IllegalStateException("Game has already started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getSimpleName();
|
return getClass().getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//<editor-fold desc="GameData accessors" defaultstate="collapsed">
|
||||||
public <T> T get(GameData.Key<T> key) {
|
public <T> T get(GameData.Key<T> key) {
|
||||||
return getData().get(key);
|
return getData().get(key);
|
||||||
}
|
}
|
||||||
@ -68,4 +80,5 @@ public abstract class GameState implements State<GameState, Game> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//</editor-fold>
|
||||||
}
|
}
|
@ -1,9 +0,0 @@
|
|||||||
package eu.jonahbauer.wizard.core.machine.states;
|
|
||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.Context;
|
|
||||||
|
|
||||||
public interface State<S extends State<S,C>, C extends Context<S,C>> {
|
|
||||||
default void onEnter(C context) {}
|
|
||||||
default void onTimeout(C context) {}
|
|
||||||
default void onExit(C context) {}
|
|
||||||
}
|
|
@ -0,0 +1,25 @@
|
|||||||
|
package eu.jonahbauer.wizard.core.machine.states.game;
|
||||||
|
|
||||||
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class Created extends GameState {
|
||||||
|
public Created() {
|
||||||
|
super(GameData.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<GameState> start(Game game, List<UUID> players) {
|
||||||
|
return transition(new Starting(GameData.EMPTY.with(GameData.PLAYERS, players)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof Created;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package eu.jonahbauer.wizard.core.machine.states.game;
|
||||||
|
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public final class Error extends GameState {
|
||||||
|
private final Throwable cause;
|
||||||
|
|
||||||
|
public Error(Throwable cause) {
|
||||||
|
super(GameData.EMPTY);
|
||||||
|
this.cause = cause;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package eu.jonahbauer.wizard.core.machine.states.game;
|
||||||
|
|
||||||
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class Finished extends GameState {
|
||||||
|
public Finished() {
|
||||||
|
super(GameData.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<GameState> onEnter(Game context) {
|
||||||
|
context.complete(null);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,11 @@ package eu.jonahbauer.wizard.core.machine.states.game;
|
|||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameState;
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
||||||
|
|
||||||
public final class Finishing extends GameState {
|
public final class Finishing extends GameState {
|
||||||
@ -14,8 +16,8 @@ public final class Finishing extends GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
game.notify(new ScoreMessage(get(SCORE)));
|
game.notify(new ScoreMessage(get(SCORE)));
|
||||||
game.finish();
|
return transition(new Finished());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,21 @@ package eu.jonahbauer.wizard.core.machine.states.game;
|
|||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameState;
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.round.StartingRound;
|
import eu.jonahbauer.wizard.core.machine.states.round.StartingRound;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.PLAYERS;
|
||||||
|
|
||||||
public final class Starting extends GameState {
|
public final class Starting extends GameState {
|
||||||
|
|
||||||
public Starting(GameData data) {
|
public Starting(GameData data) {
|
||||||
super(data);
|
super(data.require(PLAYERS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
transition(game, new StartingRound(getData()));
|
return transition(new StartingRound(getData()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,12 @@ import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
|||||||
import eu.jonahbauer.wizard.common.messages.player.PredictMessage;
|
import eu.jonahbauer.wizard.common.messages.player.PredictMessage;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
|
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.CHANGE_PREDICTION;
|
import static eu.jonahbauer.wizard.common.messages.observer.UserInputMessage.Action.CHANGE_PREDICTION;
|
||||||
@ -25,24 +27,24 @@ public final class ChangingPrediction extends RoundState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
game.notify(new UserInputMessage(get(CLOUDED_PLAYER), CHANGE_PREDICTION, getTimeout(game, true)));
|
game.notify(new UserInputMessage(get(CLOUDED_PLAYER), CHANGE_PREDICTION, getTimeout(game, true)));
|
||||||
timeout(game);
|
return timeout(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(Game game, UUID player, PlayerMessage message) {
|
public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {
|
||||||
if (get(CLOUDED_PLAYER).equals(player) && message instanceof PredictMessage predictMessage) {
|
if (get(CLOUDED_PLAYER).equals(player) && message instanceof PredictMessage predictMessage) {
|
||||||
checkPrediction(predictMessage.getPrediction());
|
checkPrediction(predictMessage.getPrediction());
|
||||||
transition(game, predictMessage.getPrediction());
|
return transition(game, predictMessage.getPrediction());
|
||||||
} else {
|
} else {
|
||||||
super.onMessage(game, player, message);
|
return super.onMessage(game, player, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeout(Game game) {
|
public Optional<GameState> onTimeout(Game game) {
|
||||||
transition(game, oldPrediction + 1);
|
return transition(game, oldPrediction + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkPrediction(int prediction) {
|
private void checkPrediction(int prediction) {
|
||||||
@ -53,7 +55,7 @@ public final class ChangingPrediction extends RoundState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transition(Game game, int prediction) {
|
private Optional<GameState> transition(Game game, int prediction) {
|
||||||
game.notify(new PredictionMessage(get(CLOUDED_PLAYER), prediction));
|
game.notify(new PredictionMessage(get(CLOUDED_PLAYER), prediction));
|
||||||
|
|
||||||
// add prediction
|
// add prediction
|
||||||
@ -64,7 +66,7 @@ public final class ChangingPrediction extends RoundState {
|
|||||||
PREDICTIONS, Map.copyOf(predictions)
|
PREDICTIONS, Map.copyOf(predictions)
|
||||||
);
|
);
|
||||||
|
|
||||||
transition(game, new FinishingRound(data));
|
return transition(new FinishingRound(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkData(GameData data) {
|
private void checkData(GameData data) {
|
||||||
|
@ -4,12 +4,10 @@ import eu.jonahbauer.wizard.common.messages.observer.HandMessage;
|
|||||||
import eu.jonahbauer.wizard.common.model.Card;
|
import eu.jonahbauer.wizard.common.model.Card;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.core.model.deck.Deck;
|
import eu.jonahbauer.wizard.core.model.deck.Deck;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
||||||
|
|
||||||
@ -20,7 +18,7 @@ public final class Dealing extends RoundState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
Deck deck = new Deck(game.getConfig().cards());
|
Deck deck = new Deck(game.getConfig().cards());
|
||||||
deck.shuffle(game.getRandom());
|
deck.shuffle(game.getRandom());
|
||||||
|
|
||||||
@ -39,7 +37,7 @@ public final class Dealing extends RoundState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Card trumpCard = deck.draw();
|
Card trumpCard = deck.draw();
|
||||||
transition(game, new DeterminingTrump(
|
return transition(new DeterminingTrump(
|
||||||
getData().with(
|
getData().with(
|
||||||
HANDS, Map.copyOf(hands),
|
HANDS, Map.copyOf(hands),
|
||||||
TRUMP_CARD, trumpCard
|
TRUMP_CARD, trumpCard
|
||||||
|
@ -8,6 +8,7 @@ import eu.jonahbauer.wizard.common.messages.observer.TrumpMessage;
|
|||||||
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
||||||
import eu.jonahbauer.wizard.common.messages.player.PickTrumpMessage;
|
import eu.jonahbauer.wizard.common.messages.player.PickTrumpMessage;
|
||||||
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.core.model.card.GameCards;
|
import eu.jonahbauer.wizard.core.model.card.GameCards;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ public final class DeterminingTrump extends RoundState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
Card trumpCard = get(TRUMP_CARD);
|
Card trumpCard = get(TRUMP_CARD);
|
||||||
|
|
||||||
// handle werewolf
|
// handle werewolf
|
||||||
@ -38,8 +39,7 @@ public final class DeterminingTrump extends RoundState {
|
|||||||
game.notify(new TrumpMessage(trumpCard, null));
|
game.notify(new TrumpMessage(trumpCard, null));
|
||||||
game.notify(new TrumpMessage(Card.WEREWOLF, null));
|
game.notify(new TrumpMessage(Card.WEREWOLF, null));
|
||||||
game.notify(new UserInputMessage(this.player, PICK_TRUMP, getTimeout(game, true)));
|
game.notify(new UserInputMessage(this.player, PICK_TRUMP, getTimeout(game, true)));
|
||||||
timeout(game);
|
return timeout(game);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,30 +49,30 @@ public final class DeterminingTrump extends RoundState {
|
|||||||
this.player = getDealer();
|
this.player = getDealer();
|
||||||
game.notify(new TrumpMessage(trumpCard, null));
|
game.notify(new TrumpMessage(trumpCard, null));
|
||||||
game.notify(new UserInputMessage(this.player, PICK_TRUMP, getTimeout(game, true)));
|
game.notify(new UserInputMessage(this.player, PICK_TRUMP, getTimeout(game, true)));
|
||||||
timeout(game);
|
return timeout(game);
|
||||||
} else {
|
} else {
|
||||||
transition(game, trumpSuit);
|
return transition(game, trumpSuit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeout(Game game) {
|
public Optional<GameState> onTimeout(Game game) {
|
||||||
Card.Suit[] suits;
|
Card.Suit[] suits;
|
||||||
if (werewolf) {
|
if (werewolf) {
|
||||||
suits = new Card.Suit[]{Card.Suit.BLUE, Card.Suit.GREEN, Card.Suit.RED, Card.Suit.YELLOW, Card.Suit.NONE};
|
suits = new Card.Suit[]{Card.Suit.BLUE, Card.Suit.GREEN, Card.Suit.RED, Card.Suit.YELLOW, Card.Suit.NONE};
|
||||||
} else {
|
} else {
|
||||||
suits = new Card.Suit[]{Card.Suit.BLUE, Card.Suit.GREEN, Card.Suit.RED, Card.Suit.YELLOW};
|
suits = new Card.Suit[]{Card.Suit.BLUE, Card.Suit.GREEN, Card.Suit.RED, Card.Suit.YELLOW};
|
||||||
}
|
}
|
||||||
transition(game, suits[game.getRandom().nextInt(suits.length)]);
|
return transition(game, suits[game.getRandom().nextInt(suits.length)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(Game game, UUID player, PlayerMessage message) {
|
public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {
|
||||||
if (this.player.equals(player) && message instanceof PickTrumpMessage trumpMessage) {
|
if (this.player.equals(player) && message instanceof PickTrumpMessage trumpMessage) {
|
||||||
checkTrumpSuit(trumpMessage.getTrumpSuit());
|
checkTrumpSuit(trumpMessage.getTrumpSuit());
|
||||||
transition(game, trumpMessage.getTrumpSuit());
|
return transition(game, trumpMessage.getTrumpSuit());
|
||||||
} else {
|
} else {
|
||||||
super.onMessage(game, player, message);
|
return super.onMessage(game, player, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ public final class DeterminingTrump extends RoundState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transition(Game game, @NotNull Card.Suit trumpSuit) {
|
private Optional<GameState> transition(Game game, @NotNull Card.Suit trumpSuit) {
|
||||||
GameData data = getData().with(
|
GameData data = getData().with(
|
||||||
TRUMP_SUIT, trumpSuit,
|
TRUMP_SUIT, trumpSuit,
|
||||||
CURRENT_PLAYER, getNextPlayer(getDealer())
|
CURRENT_PLAYER, getNextPlayer(getDealer())
|
||||||
@ -101,6 +101,6 @@ public final class DeterminingTrump extends RoundState {
|
|||||||
} else {
|
} else {
|
||||||
game.notify(new TrumpMessage(get(TRUMP_CARD), trumpSuit));
|
game.notify(new TrumpMessage(get(TRUMP_CARD), trumpSuit));
|
||||||
}
|
}
|
||||||
transition(game, new Predicting(data));
|
return transition(new Predicting(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,13 @@ package eu.jonahbauer.wizard.core.machine.states.round;
|
|||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.game.Finishing;
|
import eu.jonahbauer.wizard.core.machine.states.game.Finishing;
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.ScoreMessage;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
||||||
@ -18,7 +20,7 @@ public final class FinishingRound extends RoundState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
var points = getPoints();
|
var points = getPoints();
|
||||||
game.notify(new ScoreMessage(points));
|
game.notify(new ScoreMessage(points));
|
||||||
|
|
||||||
@ -28,9 +30,9 @@ public final class FinishingRound extends RoundState {
|
|||||||
GameData data = getData().with(SCORE, Map.copyOf(score));
|
GameData data = getData().with(SCORE, Map.copyOf(score));
|
||||||
|
|
||||||
if (60 / getPlayerCount() == get(ROUND) + 1) {
|
if (60 / getPlayerCount() == get(ROUND) + 1) {
|
||||||
transition(game, new Finishing(data));
|
return transition(new Finishing(data));
|
||||||
} else {
|
} else {
|
||||||
transition(game, new StartingRound(data.with(ROUND, get(ROUND) + 1)));
|
return transition(new StartingRound(data.with(ROUND, get(ROUND) + 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package eu.jonahbauer.wizard.core.machine.states.round;
|
|||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.trick.StartingTrick;
|
import eu.jonahbauer.wizard.core.machine.states.trick.StartingTrick;
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.PredictionMessage;
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
||||||
@ -11,6 +12,7 @@ import lombok.Getter;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
||||||
@ -23,20 +25,20 @@ public final class Predicting extends RoundState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
game.notify(new UserInputMessage(get(CURRENT_PLAYER), MAKE_PREDICTION, getTimeout(game, true)));
|
game.notify(new UserInputMessage(get(CURRENT_PLAYER), MAKE_PREDICTION, getTimeout(game, true)));
|
||||||
timeout(game);
|
return timeout(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeout(Game game) {
|
public Optional<GameState> onTimeout(Game game) {
|
||||||
try {
|
try {
|
||||||
checkPrediction(game, 0);
|
checkPrediction(game, 0);
|
||||||
transition(game, 0);
|
return transition(game, 0);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
try {
|
try {
|
||||||
checkPrediction(game, 1);
|
checkPrediction(game, 1);
|
||||||
transition(game, 1);
|
return transition(game, 1);
|
||||||
} catch (IllegalArgumentException e2) {
|
} catch (IllegalArgumentException e2) {
|
||||||
throw new AssertionError(e2);
|
throw new AssertionError(e2);
|
||||||
}
|
}
|
||||||
@ -44,12 +46,12 @@ public final class Predicting extends RoundState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(Game game, UUID player, PlayerMessage message) {
|
public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {
|
||||||
if (get(CURRENT_PLAYER).equals(player) && message instanceof PredictMessage predictMessage) {
|
if (get(CURRENT_PLAYER).equals(player) && message instanceof PredictMessage predictMessage) {
|
||||||
checkPrediction(game, predictMessage.getPrediction());
|
checkPrediction(game, predictMessage.getPrediction());
|
||||||
transition(game, predictMessage.getPrediction());
|
return transition(game, predictMessage.getPrediction());
|
||||||
} else {
|
} else {
|
||||||
super.onMessage(game, player, message);
|
return super.onMessage(game, player, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +69,7 @@ public final class Predicting extends RoundState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transition(Game game, int prediction) {
|
private Optional<GameState> transition(Game game, int prediction) {
|
||||||
game.notify(new PredictionMessage(get(CURRENT_PLAYER), prediction));
|
game.notify(new PredictionMessage(get(CURRENT_PLAYER), prediction));
|
||||||
|
|
||||||
// add prediction
|
// add prediction
|
||||||
@ -80,9 +82,9 @@ public final class Predicting extends RoundState {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (isLastPlayer()) {
|
if (isLastPlayer()) {
|
||||||
transition(game, new StartingTrick(data));
|
return transition(new StartingTrick(data));
|
||||||
} else {
|
} else {
|
||||||
transition(game, new Predicting(data));
|
return transition(new Predicting(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package eu.jonahbauer.wizard.core.machine.states.round;
|
package eu.jonahbauer.wizard.core.machine.states.round;
|
||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameState;
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
|||||||
|
|
||||||
public abstract class RoundState extends GameState {
|
public abstract class RoundState extends GameState {
|
||||||
public RoundState(GameData data) {
|
public RoundState(GameData data) {
|
||||||
super(data.require(ROUND, SCORE));
|
super(data.require(PLAYERS, ROUND, SCORE));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UUID getDealer() {
|
protected UUID getDealer() {
|
||||||
|
@ -2,6 +2,9 @@ package eu.jonahbauer.wizard.core.machine.states.round;
|
|||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class StartingRound extends RoundState {
|
public final class StartingRound extends RoundState {
|
||||||
public StartingRound(GameData data) {
|
public StartingRound(GameData data) {
|
||||||
@ -9,7 +12,7 @@ public final class StartingRound extends RoundState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
transition(game, new Dealing(getData()));
|
return transition(new Dealing(getData()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package eu.jonahbauer.wizard.core.machine.states.trick;
|
|||||||
import eu.jonahbauer.wizard.common.model.Card;
|
import eu.jonahbauer.wizard.common.model.Card;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
|
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.ChangingPrediction;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.round.FinishingRound;
|
import eu.jonahbauer.wizard.core.machine.states.round.FinishingRound;
|
||||||
@ -22,7 +23,7 @@ public final class FinishingTrick extends TrickState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
var stack = get(STACK);
|
var stack = get(STACK);
|
||||||
var cards = stack.stream().map(Pair::second).toList();
|
var cards = stack.stream().map(Pair::second).toList();
|
||||||
|
|
||||||
@ -58,14 +59,14 @@ public final class FinishingTrick extends TrickState {
|
|||||||
|
|
||||||
if (!hasNextTrick) {
|
if (!hasNextTrick) {
|
||||||
if (data.has(CLOUDED_PLAYER)) {
|
if (data.has(CLOUDED_PLAYER)) {
|
||||||
transition(game, new ChangingPrediction(data));
|
return transition(new ChangingPrediction(data));
|
||||||
} else {
|
} else {
|
||||||
transition(game, new FinishingRound(data));
|
return transition(new FinishingRound(data));
|
||||||
}
|
}
|
||||||
} else if (juggler) {
|
} else if (juggler) {
|
||||||
transition(game, new Juggling(data));
|
return transition(new Juggling(data));
|
||||||
} else {
|
} else {
|
||||||
transition(game, new StartingTrick(data.with(TRICK, get(TRICK) + 1)));
|
return transition(new StartingTrick(data.with(TRICK, get(TRICK) + 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import eu.jonahbauer.wizard.common.messages.player.PlayerMessage;
|
|||||||
import eu.jonahbauer.wizard.common.model.Card;
|
import eu.jonahbauer.wizard.common.model.Card;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -22,24 +23,24 @@ public final class Juggling extends TrickState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
game.notify(new UserInputMessage(null, JUGGLE_CARD, getTimeout(game, true)));
|
game.notify(new UserInputMessage(null, JUGGLE_CARD, getTimeout(game, true)));
|
||||||
timeout(game);
|
return timeout(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeout(Game game) {
|
public Optional<GameState> onTimeout(Game game) {
|
||||||
for (UUID player : get(PLAYERS)) {
|
for (UUID player : get(PLAYERS)) {
|
||||||
juggledCards.computeIfAbsent(player, p -> {
|
juggledCards.computeIfAbsent(player, p -> {
|
||||||
var hand = get(HANDS).get(p);
|
var hand = get(HANDS).get(p);
|
||||||
return hand.get(game.getRandom().nextInt(hand.size()));
|
return hand.get(game.getRandom().nextInt(hand.size()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
juggle(game);
|
return juggle(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(Game game, UUID player, PlayerMessage message) {
|
public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {
|
||||||
if (get(PLAYERS).contains(player) && message instanceof JuggleMessage juggleMessage) {
|
if (get(PLAYERS).contains(player) && message instanceof JuggleMessage juggleMessage) {
|
||||||
Card card = juggleMessage.getCard();
|
Card card = juggleMessage.getCard();
|
||||||
|
|
||||||
@ -49,14 +50,16 @@ public final class Juggling extends TrickState {
|
|||||||
|
|
||||||
juggledCards.put(player, card);
|
juggledCards.put(player, card);
|
||||||
if (juggledCards.size() == get(PLAYERS).size()) {
|
if (juggledCards.size() == get(PLAYERS).size()) {
|
||||||
juggle(game);
|
return juggle(game);
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
super.onMessage(game, player, message);
|
return super.onMessage(game, player, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void juggle(Game game) {
|
private Optional<GameState> juggle(Game game) {
|
||||||
Map<UUID, Card> newCards = new HashMap<>();
|
Map<UUID, Card> newCards = new HashMap<>();
|
||||||
juggledCards.forEach((player, card) -> newCards.put(getNextPlayer(player), card));
|
juggledCards.forEach((player, card) -> newCards.put(getNextPlayer(player), card));
|
||||||
|
|
||||||
@ -77,6 +80,6 @@ public final class Juggling extends TrickState {
|
|||||||
TRICK, get(TRICK) + 1
|
TRICK, get(TRICK) + 1
|
||||||
);
|
);
|
||||||
|
|
||||||
transition(game, new StartingTrick(data));
|
return transition(new StartingTrick(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package eu.jonahbauer.wizard.core.machine.states.trick;
|
|||||||
import eu.jonahbauer.wizard.common.model.Card;
|
import eu.jonahbauer.wizard.common.model.Card;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
|
import eu.jonahbauer.wizard.core.machine.states.InvalidDataException;
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.CardMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.CardMessage;
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
||||||
@ -25,25 +26,25 @@ public final class PlayingCard extends TrickState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
game.notify(new UserInputMessage(get(CURRENT_PLAYER), PLAY_CARD, getTimeout(game, true)));
|
game.notify(new UserInputMessage(get(CURRENT_PLAYER), PLAY_CARD, getTimeout(game, true)));
|
||||||
timeout(game);
|
return timeout(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(Game game, UUID player, PlayerMessage message) {
|
public Optional<GameState> onMessage(Game game, UUID player, PlayerMessage message) {
|
||||||
if (get(CURRENT_PLAYER).equals(player) && message instanceof PlayCardMessage cardMessage) {
|
if (get(CURRENT_PLAYER).equals(player) && message instanceof PlayCardMessage cardMessage) {
|
||||||
if (cardMessage.getCard() == null) {
|
if (cardMessage.getCard() == null) {
|
||||||
throw new IllegalArgumentException("Card must not be null.");
|
throw new IllegalArgumentException("Card must not be null.");
|
||||||
}
|
}
|
||||||
transition(game, cardMessage.getCard());
|
return transition(game, cardMessage.getCard());
|
||||||
} else {
|
} else {
|
||||||
super.onMessage(game, player, message);
|
return super.onMessage(game, player, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTimeout(Game game) {
|
public Optional<GameState> onTimeout(Game game) {
|
||||||
var hand = get(HANDS).get(get(CURRENT_PLAYER));
|
var hand = get(HANDS).get(get(CURRENT_PLAYER));
|
||||||
var stack = get(STACK);
|
var stack = get(STACK);
|
||||||
|
|
||||||
@ -52,10 +53,10 @@ public final class PlayingCard extends TrickState {
|
|||||||
.findAny()
|
.findAny()
|
||||||
.orElseThrow(() -> new AssertionError("Cannot play any card."));
|
.orElseThrow(() -> new AssertionError("Cannot play any card."));
|
||||||
|
|
||||||
transition(game, card.getCard());
|
return transition(game, card.getCard());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transition(Game game, @NotNull Card card) {
|
private Optional<GameState> transition(Game game, @NotNull Card card) {
|
||||||
var currentPlayer = get(CURRENT_PLAYER);
|
var currentPlayer = get(CURRENT_PLAYER);
|
||||||
|
|
||||||
// create mutable stack
|
// create mutable stack
|
||||||
@ -87,11 +88,10 @@ public final class PlayingCard extends TrickState {
|
|||||||
.summaryStatistics();
|
.summaryStatistics();
|
||||||
|
|
||||||
if (summary.getMax() == summary.getMin()) { // everybody has the same amount of cards
|
if (summary.getMax() == summary.getMin()) { // everybody has the same amount of cards
|
||||||
transition(game, new FinishingTrick(data));
|
return transition(new FinishingTrick(data));
|
||||||
} else {
|
} else {
|
||||||
transition(game, new PlayingCard(data.with(CURRENT_PLAYER, getNextPlayer())));
|
return transition(new PlayingCard(data.with(CURRENT_PLAYER, getNextPlayer())));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkData(GameData data) {
|
private static void checkData(GameData data) {
|
||||||
|
@ -2,6 +2,9 @@ package eu.jonahbauer.wizard.core.machine.states.trick;
|
|||||||
|
|
||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class StartingTrick extends TrickState {
|
public final class StartingTrick extends TrickState {
|
||||||
public StartingTrick(GameData data) {
|
public StartingTrick(GameData data) {
|
||||||
@ -9,7 +12,7 @@ public final class StartingTrick extends TrickState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnter(Game game) {
|
public Optional<GameState> onEnter(Game game) {
|
||||||
transition(game, new PlayingCard(getData()));
|
return transition(new PlayingCard(getData()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ public class ColoredCard extends GameCard {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void play(UUID player, List<Card> hand, List<Pair<UUID, Card>> stack) {
|
public void play(UUID player, List<Card> hand, List<Pair<UUID, Card>> stack) {
|
||||||
|
// check hand first
|
||||||
|
checkHand(hand);
|
||||||
|
|
||||||
Card.Suit suit = CardUtils.getTrickSuit(stack);
|
Card.Suit suit = CardUtils.getTrickSuit(stack);
|
||||||
if (this.suit != suit && canFollowSuit(hand, suit)) {
|
if (this.suit != suit && canFollowSuit(hand, suit)) {
|
||||||
throw new IllegalArgumentException("Must follow suit.");
|
throw new IllegalArgumentException("Must follow suit.");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package eu.jonahbauer.wizard.core.machine;
|
package eu.jonahbauer.wizard.core.machine;
|
||||||
|
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.game.Finished;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
@ -7,6 +8,7 @@ import org.mockito.stubbing.Stubber;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ public class GameTestUtils {
|
|||||||
public static <T> Answer<T> finish() {
|
public static <T> Answer<T> finish() {
|
||||||
return invocation -> {
|
return invocation -> {
|
||||||
Game game = (Game) invocation.getMock();
|
Game game = (Game) invocation.getMock();
|
||||||
game.finish();
|
game.execute(() -> Optional.of(new Finished()));
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
|
|||||||
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.UserInputMessage;
|
||||||
import eu.jonahbauer.wizard.common.messages.player.*;
|
import eu.jonahbauer.wizard.common.messages.player.*;
|
||||||
import eu.jonahbauer.wizard.common.model.Card;
|
import eu.jonahbauer.wizard.common.model.Card;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.messages.Observer;
|
import eu.jonahbauer.wizard.core.messages.Observer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -11,7 +12,6 @@ import lombok.Setter;
|
|||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
@ -138,23 +138,23 @@ public class MessageQueue implements Observer {
|
|||||||
|
|
||||||
System.out.println(queuedPlayer + ": " + queuedMessage);
|
System.out.println(queuedPlayer + ": " + queuedMessage);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
var executionException = Assertions.assertThrows(
|
Assertions.assertThrows(
|
||||||
ExecutionException.class,
|
|
||||||
() -> game.onMessage(queuedPlayer, queuedMessage).get(),
|
|
||||||
"Excepted exception for message " + queuedMessage + " from player " + queuedPlayer + "."
|
|
||||||
);
|
|
||||||
assertInstanceOf(
|
|
||||||
exception,
|
exception,
|
||||||
executionException.getCause(),
|
() -> game.onMessage(queuedPlayer, queuedMessage),
|
||||||
"Excepted exception for message " + queuedMessage + " from player " + queuedPlayer + "."
|
"Excepted exception for message " + queuedMessage + " from player " + queuedPlayer + "."
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Assertions.assertDoesNotThrow(() -> game.onMessage(queuedPlayer, queuedMessage).get());
|
Assertions.assertDoesNotThrow(() -> game.onMessage(queuedPlayer, queuedMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
game.finish(t);
|
game.transition(new GameState(GameData.EMPTY) {
|
||||||
|
@Override
|
||||||
|
public Optional<GameState> onEnter(Game context) {
|
||||||
|
throw t;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package eu.jonahbauer.wizard.core.machine.states;
|
package eu.jonahbauer.wizard.core.machine.states;
|
||||||
|
|
||||||
|
import eu.jonahbauer.wizard.core.machine.GameState;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -8,6 +8,7 @@ import eu.jonahbauer.wizard.common.model.Card;
|
|||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
import eu.jonahbauer.wizard.core.machine.MessageQueue;
|
import eu.jonahbauer.wizard.core.machine.MessageQueue;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.game.Finished;
|
||||||
import eu.jonahbauer.wizard.core.model.GameConfiguration;
|
import eu.jonahbauer.wizard.core.model.GameConfiguration;
|
||||||
import eu.jonahbauer.wizard.core.model.Configurations;
|
import eu.jonahbauer.wizard.core.model.Configurations;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@ -37,7 +38,7 @@ public class DeterminingTrumpTest {
|
|||||||
@SuppressWarnings("SameParameterValue")
|
@SuppressWarnings("SameParameterValue")
|
||||||
private Game performTest(GameConfiguration configuration, int round, Map<UUID, List<Card>> hands, Card trumpCard, MessageQueue queue) {
|
private Game performTest(GameConfiguration configuration, int round, Map<UUID, List<Card>> hands, Card trumpCard, MessageQueue queue) {
|
||||||
Game game = spy(new Game(configuration, queue));
|
Game game = spy(new Game(configuration, queue));
|
||||||
doFinish().when(game).transition(any(), any(Predicting.class));
|
doFinish().when(game).transition(any(Predicting.class));
|
||||||
queue.setGame(game);
|
queue.setGame(game);
|
||||||
|
|
||||||
var playerList = List.of(players);
|
var playerList = List.of(players);
|
||||||
@ -53,7 +54,7 @@ public class DeterminingTrumpTest {
|
|||||||
game.resume(new DeterminingTrump(data));
|
game.resume(new DeterminingTrump(data));
|
||||||
game.await();
|
game.await();
|
||||||
|
|
||||||
verify(game, never()).transition(any(), isNull());
|
verify(game).transition(any(Finished.class));
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ public class DeterminingTrumpTest {
|
|||||||
InOrder order = inOrder(game);
|
InOrder order = inOrder(game);
|
||||||
order.verify(game).notify(any(StateMessage.class)); // determining trump
|
order.verify(game).notify(any(StateMessage.class)); // determining trump
|
||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.YELLOW_1 && trump.getSuit() == Card.Suit.YELLOW));
|
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.YELLOW_1 && trump.getSuit() == Card.Suit.YELLOW));
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // round is finished
|
order.verify(game).transition(any(Predicting.class)); // round is finished
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -99,7 +100,7 @@ public class DeterminingTrumpTest {
|
|||||||
InOrder order = inOrder(game);
|
InOrder order = inOrder(game);
|
||||||
order.verify(game).notify(any(StateMessage.class)); // determining trump
|
order.verify(game).notify(any(StateMessage.class)); // determining trump
|
||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.GREEN_JESTER && trump.getSuit() == Card.Suit.NONE));
|
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.GREEN_JESTER && trump.getSuit() == Card.Suit.NONE));
|
||||||
order.verify(game).transition(any(), any(Predicting.class));
|
order.verify(game).transition(any(Predicting.class));
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -126,7 +127,7 @@ public class DeterminingTrumpTest {
|
|||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.BLUE_WIZARD && trump.getSuit() == null));
|
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.BLUE_WIZARD && trump.getSuit() == null));
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.BLUE_WIZARD && trump.getSuit() == Card.Suit.GREEN));
|
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.BLUE_WIZARD && trump.getSuit() == Card.Suit.GREEN));
|
||||||
order.verify(game).transition(any(), any(Predicting.class));
|
order.verify(game).transition(any(Predicting.class));
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -155,7 +156,7 @@ public class DeterminingTrumpTest {
|
|||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.WEREWOLF && trump.getSuit() == Card.Suit.YELLOW));
|
order.verify(game).notify(argThat(msg -> msg instanceof TrumpMessage trump && trump.getCard() == Card.WEREWOLF && trump.getSuit() == Card.Suit.YELLOW));
|
||||||
order.verify(game).notify(eq(players[3]), any(HandMessage.class)); // swap trump card and werewolf
|
order.verify(game).notify(eq(players[3]), any(HandMessage.class)); // swap trump card and werewolf
|
||||||
order.verify(game).transition(any(), any(Predicting.class));
|
order.verify(game).transition(any(Predicting.class));
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
|
@ -8,6 +8,7 @@ import eu.jonahbauer.wizard.core.machine.Game;
|
|||||||
import eu.jonahbauer.wizard.core.machine.GameTestUtils;
|
import eu.jonahbauer.wizard.core.machine.GameTestUtils;
|
||||||
import eu.jonahbauer.wizard.core.machine.MessageQueue;
|
import eu.jonahbauer.wizard.core.machine.MessageQueue;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
|
import eu.jonahbauer.wizard.core.machine.states.game.Finished;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.trick.StartingTrick;
|
import eu.jonahbauer.wizard.core.machine.states.trick.StartingTrick;
|
||||||
import eu.jonahbauer.wizard.core.model.GameConfiguration;
|
import eu.jonahbauer.wizard.core.model.GameConfiguration;
|
||||||
import eu.jonahbauer.wizard.core.model.Configurations;
|
import eu.jonahbauer.wizard.core.model.Configurations;
|
||||||
@ -24,7 +25,6 @@ import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
|
|||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.isNull;
|
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
public class PredictingTest {
|
public class PredictingTest {
|
||||||
@ -46,7 +46,7 @@ public class PredictingTest {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Game game = spy(new Game(configuration, queue));
|
Game game = spy(new Game(configuration, queue));
|
||||||
doFinish().when(game).transition(any(), any(StartingTrick.class));
|
doFinish().when(game).transition(any(StartingTrick.class));
|
||||||
queue.setGame(game);
|
queue.setGame(game);
|
||||||
|
|
||||||
var playerList = List.of(players);
|
var playerList = List.of(players);
|
||||||
@ -64,7 +64,7 @@ public class PredictingTest {
|
|||||||
game.resume(new Predicting(data));
|
game.resume(new Predicting(data));
|
||||||
game.await();
|
game.await();
|
||||||
|
|
||||||
verify(game, never()).transition(any(), isNull());
|
verify(game).transition(any(Finished.class));
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,16 +84,16 @@ public class PredictingTest {
|
|||||||
order.verify(game).notify(any(StateMessage.class)); // predicting
|
order.verify(game).notify(any(StateMessage.class)); // predicting
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(StartingTrick.class)); // starting trick
|
order.verify(game).transition(any(StartingTrick.class)); // starting trick
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -126,16 +126,16 @@ public class PredictingTest {
|
|||||||
order.verify(game).notify(any(StateMessage.class)); // predicting
|
order.verify(game).notify(any(StateMessage.class)); // predicting
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(StartingTrick.class)); // starting trick
|
order.verify(game).transition(any(StartingTrick.class)); // starting trick
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -160,16 +160,16 @@ public class PredictingTest {
|
|||||||
order.verify(game).notify(any(StateMessage.class)); // predicting
|
order.verify(game).notify(any(StateMessage.class)); // predicting
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(Predicting.class)); // next player
|
order.verify(game).transition(any(Predicting.class)); // next player
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(StartingTrick.class)); // starting trick
|
order.verify(game).transition(any(StartingTrick.class)); // starting trick
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
|
@ -5,8 +5,9 @@ import eu.jonahbauer.wizard.common.model.Card;
|
|||||||
import eu.jonahbauer.wizard.core.machine.Game;
|
import eu.jonahbauer.wizard.core.machine.Game;
|
||||||
import eu.jonahbauer.wizard.core.machine.MessageQueue;
|
import eu.jonahbauer.wizard.core.machine.MessageQueue;
|
||||||
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
import eu.jonahbauer.wizard.core.machine.states.GameData;
|
||||||
import eu.jonahbauer.wizard.core.model.GameConfiguration;
|
import eu.jonahbauer.wizard.core.machine.states.game.Finished;
|
||||||
import eu.jonahbauer.wizard.core.model.Configurations;
|
import eu.jonahbauer.wizard.core.model.Configurations;
|
||||||
|
import eu.jonahbauer.wizard.core.model.GameConfiguration;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
@ -16,11 +17,10 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
|
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.finish;
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.isNull;
|
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
public class RoundTest {
|
public class RoundTest {
|
||||||
@ -34,7 +34,7 @@ public class RoundTest {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private Game performTest(long seed, GameConfiguration configuration, int round, MessageQueue queue) {
|
private Game performTest(long seed, GameConfiguration configuration, int round, MessageQueue queue) {
|
||||||
Game game = spy(new Game(seed, configuration, queue));
|
Game game = spy(new Game(seed, configuration, queue));
|
||||||
doFinish().when(game).transition(any(), any(StartingRound.class));
|
doCallRealMethod().doAnswer(finish()).when(game).transition(any(StartingRound.class));
|
||||||
queue.setGame(game);
|
queue.setGame(game);
|
||||||
|
|
||||||
var playerList = List.of(players);
|
var playerList = List.of(players);
|
||||||
@ -48,7 +48,7 @@ public class RoundTest {
|
|||||||
game.resume(new StartingRound(data));
|
game.resume(new StartingRound(data));
|
||||||
game.await();
|
game.await();
|
||||||
|
|
||||||
verify(game, never()).transition(any(), isNull());
|
verify(game).transition(any(Finished.class));
|
||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ public class RoundTest {
|
|||||||
&& score.getPoints().get(players[2]) == 30
|
&& score.getPoints().get(players[2]) == 30
|
||||||
&& score.getPoints().get(players[3]) == -10
|
&& score.getPoints().get(players[3]) == -10
|
||||||
)); // score
|
)); // score
|
||||||
order.verify(game).transition(any(), any(StartingRound.class)); // next round
|
order.verify(game).transition(any(StartingRound.class)); // next round
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -249,7 +249,7 @@ public class RoundTest {
|
|||||||
&& score.getPoints().get(players[2]) == -10
|
&& score.getPoints().get(players[2]) == -10
|
||||||
&& score.getPoints().get(players[3]) == 40
|
&& score.getPoints().get(players[3]) == 40
|
||||||
)); // score
|
)); // score
|
||||||
order.verify(game).transition(any(), any(StartingRound.class)); // next round
|
order.verify(game).transition(any(StartingRound.class)); // next round
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
|
@ -18,6 +18,7 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
|
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.doFinish;
|
||||||
|
import static eu.jonahbauer.wizard.core.machine.GameTestUtils.finish;
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.*;
|
||||||
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
|
import static eu.jonahbauer.wizard.core.machine.states.GameData.TypedValue.entry;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@ -36,8 +37,8 @@ public class TrickTest {
|
|||||||
@SuppressWarnings("SameParameterValue")
|
@SuppressWarnings("SameParameterValue")
|
||||||
private Game performTest(GameConfiguration configuration, int round, int trick, Map<UUID, List<Card>> hands, Card.Suit trump, MessageQueue queue) {
|
private Game performTest(GameConfiguration configuration, int round, int trick, Map<UUID, List<Card>> hands, Card.Suit trump, MessageQueue queue) {
|
||||||
Game game = spy(new Game(configuration, queue));
|
Game game = spy(new Game(configuration, queue));
|
||||||
doFinish().when(game).transition(any(), any(StartingTrick.class));
|
doCallRealMethod().doAnswer(finish()).when(game).transition(any(StartingTrick.class));
|
||||||
doFinish().when(game).transition(any(), any(FinishingRound.class));
|
doFinish().when(game).transition(any(FinishingRound.class));
|
||||||
queue.setGame(game);
|
queue.setGame(game);
|
||||||
|
|
||||||
var playerList = List.of(players);
|
var playerList = List.of(players);
|
||||||
@ -85,7 +86,7 @@ public class TrickTest {
|
|||||||
}
|
}
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finishing trick
|
order.verify(game).notify(any(StateMessage.class)); // finishing trick
|
||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[3])); // trick with correct winner
|
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[3])); // trick with correct winner
|
||||||
order.verify(game).transition(any(), any(FinishingRound.class)); // round is finished
|
order.verify(game).transition(any(FinishingRound.class)); // round is finished
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -132,7 +133,7 @@ public class TrickTest {
|
|||||||
order.verify(game).notify(any(CardMessage.class)); // user response
|
order.verify(game).notify(any(CardMessage.class)); // user response
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finishing trick
|
order.verify(game).notify(any(StateMessage.class)); // finishing trick
|
||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[3])); // trick with correct winner
|
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[3])); // trick with correct winner
|
||||||
order.verify(game).transition(any(), any(FinishingRound.class)); // round is finished
|
order.verify(game).transition(any(FinishingRound.class)); // round is finished
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -170,7 +171,7 @@ public class TrickTest {
|
|||||||
order.verify(game).notify(any(StateMessage.class)); // changing prediction
|
order.verify(game).notify(any(StateMessage.class)); // changing prediction
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
order.verify(game).notify(any(PredictionMessage.class)); // user response
|
||||||
order.verify(game).transition(any(), any(FinishingRound.class)); // round is finished
|
order.verify(game).transition(any(FinishingRound.class)); // round is finished
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -220,7 +221,7 @@ public class TrickTest {
|
|||||||
order.verify(game).notify(any(StateMessage.class)); // juggling
|
order.verify(game).notify(any(StateMessage.class)); // juggling
|
||||||
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
order.verify(game).notify(any(UserInputMessage.class)); // user input request
|
||||||
order.verify(game, times(4)).notify(any(), any(HandMessage.class));
|
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).transition(any(StartingTrick.class)); // there is another trick
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -261,7 +262,7 @@ public class TrickTest {
|
|||||||
order.verify(game).notify(any(CardMessage.class)); // user response
|
order.verify(game).notify(any(CardMessage.class)); // user response
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finishing trick
|
order.verify(game).notify(any(StateMessage.class)); // finishing trick
|
||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[3])); // trick with correct winner
|
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[3])); // trick with correct winner
|
||||||
order.verify(game).transition(any(), any(FinishingRound.class)); // round is finished
|
order.verify(game).transition(any(FinishingRound.class)); // round is finished
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
@ -302,7 +303,7 @@ public class TrickTest {
|
|||||||
order.verify(game).notify(any(CardMessage.class)); // user response
|
order.verify(game).notify(any(CardMessage.class)); // user response
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finishing trick
|
order.verify(game).notify(any(StateMessage.class)); // finishing trick
|
||||||
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[0])); // trick with correct winner
|
order.verify(game).notify(argThat(msg -> msg instanceof TrickMessage trick && trick.getPlayer() == players[0])); // trick with correct winner
|
||||||
order.verify(game).transition(any(), any(FinishingRound.class)); // round is finished
|
order.verify(game).transition(any(FinishingRound.class)); // round is finished
|
||||||
order.verify(game).notify(any(StateMessage.class)); // finish
|
order.verify(game).notify(any(StateMessage.class)); // finish
|
||||||
order.verify(game, never()).notify(any());
|
order.verify(game, never()).notify(any());
|
||||||
order.verify(game, never()).notify(any(), any());
|
order.verify(game, never()).notify(any(), any());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user