implement dedicated classes for each combination type
parent
ae7890375e
commit
0d1f51d868
@ -1,190 +0,0 @@
|
|||||||
package eu.jonahbauer.tichu.common.model;
|
|
||||||
|
|
||||||
import eu.jonahbauer.tichu.common.model.exceptions.InvalidCombinationException;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A combination of cards.
|
|
||||||
* @param type the type of the combination
|
|
||||||
* @param cards the cards composing this combination
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public record Combination(
|
|
||||||
@NotNull CombinationType type,
|
|
||||||
@NotNull @Unmodifiable List<@NotNull Card> cards
|
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* Creates a new combination.
|
|
||||||
* @param type the type of the new combination
|
|
||||||
* @param cards the cards composing the new combination
|
|
||||||
* @throws InvalidCombinationException if the cards do not compose a valid combination of the given type
|
|
||||||
* @throws NullPointerException if {@code type}, {@code cards} or any element of {@code cards} is {@code null}
|
|
||||||
*/
|
|
||||||
public Combination(@NotNull CombinationType type, @NotNull List<@NotNull Card> cards) {
|
|
||||||
this.type = Objects.requireNonNull(type, "type must not be null");
|
|
||||||
this.cards = List.copyOf(Objects.requireNonNull(cards, "cards must not be null")); // List#copyOf performs null-checks
|
|
||||||
if (!type.check(this.cards)) {
|
|
||||||
throw new InvalidCombinationException(this.type, this.cards);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the card at the specified position. This is equivalent to {@code cards().get(index)}
|
|
||||||
* @param index index of the card to return
|
|
||||||
* @return the card at the specified position
|
|
||||||
* @see #cards()
|
|
||||||
*/
|
|
||||||
public @NotNull Card card(int index) {
|
|
||||||
return cards.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull String toString() {
|
|
||||||
return type + cards.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
//<editor-fold desc="Named Constructors" defaultstate="collapsed">
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#SINGLE}-Combination.
|
|
||||||
* @param card the card
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#SINGLE
|
|
||||||
*/
|
|
||||||
@Contract("_ -> new")
|
|
||||||
public static @NotNull Combination single(@NotNull Card card) {
|
|
||||||
return new Combination(CombinationType.SINGLE, List.of(card));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#PAIR}-Combination.
|
|
||||||
* @param card0 the first card
|
|
||||||
* @param card1 the second card
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#PAIR
|
|
||||||
*/
|
|
||||||
@Contract("_, _ -> new")
|
|
||||||
public static @NotNull Combination pair(@NotNull Card card0, @NotNull Card card1) {
|
|
||||||
return new Combination(CombinationType.PAIR, List.of(card0, card1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#TRIPLE}-Combination.
|
|
||||||
* @param card0 the first card
|
|
||||||
* @param card1 the second card
|
|
||||||
* @param card2 the third card
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#TRIPLE
|
|
||||||
*/
|
|
||||||
@Contract("_, _, _ -> new")
|
|
||||||
public static @NotNull Combination triple(@NotNull Card card0, @NotNull Card card1, @NotNull Card card2) {
|
|
||||||
return new Combination(CombinationType.TRIPLE, List.of(card0, card1, card2));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#FULL_HOUSE}-Combination.
|
|
||||||
* @param tripleCard0 the first card of the triple
|
|
||||||
* @param tripleCard1 the second card of the triple
|
|
||||||
* @param tripleCard2 the third card of the triple
|
|
||||||
* @param pairCard0 the first card of the pair
|
|
||||||
* @param pairCard1 the second card of the pair
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#FULL_HOUSE
|
|
||||||
*/
|
|
||||||
@Contract("_, _, _, _, _ -> new")
|
|
||||||
public static @NotNull Combination fullHouse(
|
|
||||||
@NotNull Card tripleCard0, @NotNull Card tripleCard1, @NotNull Card tripleCard2,
|
|
||||||
@NotNull Card pairCard0, @NotNull Card pairCard1
|
|
||||||
) {
|
|
||||||
return new Combination(CombinationType.FULL_HOUSE, List.of(
|
|
||||||
tripleCard0, tripleCard1, tripleCard2, pairCard0, pairCard1
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#SEQUENCE}-Combination.
|
|
||||||
* @param cards the sequence of cards (in correct order)
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#SEQUENCE
|
|
||||||
*/
|
|
||||||
@Contract("_ -> new")
|
|
||||||
public static @NotNull Combination sequence(@NotNull Card @NotNull... cards) {
|
|
||||||
return new Combination(CombinationType.SEQUENCE, List.of(cards));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#SEQUENCE}-Combination.
|
|
||||||
* @param cards the sequence of cards (in correct order)
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#SEQUENCE
|
|
||||||
*/
|
|
||||||
@Contract("_ -> new")
|
|
||||||
public static @NotNull Combination sequence(@NotNull List<@NotNull Card> cards) {
|
|
||||||
return new Combination(CombinationType.SEQUENCE, cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#PAIR_SEQUENCE}-Combination.
|
|
||||||
* @param cards the sequence of cards (in correct order)
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#PAIR_SEQUENCE
|
|
||||||
*/
|
|
||||||
@Contract("_ -> new")
|
|
||||||
public static @NotNull Combination pairSequence(@NotNull Card @NotNull... cards) {
|
|
||||||
return new Combination(CombinationType.PAIR_SEQUENCE, List.of(cards));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#PAIR_SEQUENCE}-Combination.
|
|
||||||
* @param cards the sequence of cards (in correct order)
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#PAIR_SEQUENCE
|
|
||||||
*/
|
|
||||||
@Contract("_ -> new")
|
|
||||||
public static @NotNull Combination pairSequence(@NotNull List<@NotNull Card> cards) {
|
|
||||||
return new Combination(CombinationType.PAIR_SEQUENCE, cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#BOMB}-Combination.
|
|
||||||
* @param card0 the first card
|
|
||||||
* @param card1 the second card
|
|
||||||
* @param card2 the third card
|
|
||||||
* @param card3 the fourth card
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#BOMB
|
|
||||||
*/
|
|
||||||
@Contract("_, _, _, _ -> new")
|
|
||||||
public static @NotNull Combination bomb(
|
|
||||||
@NotNull Card card0, @NotNull Card card1, @NotNull Card card2, @NotNull Card card3
|
|
||||||
) {
|
|
||||||
return new Combination(CombinationType.BOMB, List.of(card0, card1, card2, card3));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#BOMB_SEQUENCE}-Combination.
|
|
||||||
* @param cards the sequence of cards (in correct order)
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#BOMB_SEQUENCE
|
|
||||||
*/
|
|
||||||
@Contract("_ -> new")
|
|
||||||
public static @NotNull Combination bombSequence(@NotNull Card @NotNull... cards) {
|
|
||||||
return new Combination(CombinationType.BOMB_SEQUENCE, List.of(cards));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link CombinationType#BOMB_SEQUENCE}-Combination.
|
|
||||||
* @param cards the sequence of cards (in correct order)
|
|
||||||
* @return a new combination
|
|
||||||
* @see CombinationType#BOMB_SEQUENCE
|
|
||||||
*/
|
|
||||||
@Contract("_ -> new")
|
|
||||||
public static @NotNull Combination bombSequence(@NotNull List<@NotNull Card> cards) {
|
|
||||||
return new Combination(CombinationType.BOMB_SEQUENCE, cards);
|
|
||||||
}
|
|
||||||
//</editor-fold>
|
|
||||||
}
|
|
@ -0,0 +1,62 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import eu.jonahbauer.tichu.common.model.exceptions.InvalidCombinationException;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@EqualsAndHashCode
|
||||||
|
abstract sealed class AbstractCombination implements Combination permits Tuple, Sequence, FullHouse, Single {
|
||||||
|
private final @NotNull CombinationType type;
|
||||||
|
private final @Unmodifiable @NotNull List<@NotNull Card> cards;
|
||||||
|
|
||||||
|
AbstractCombination(@NotNull CombinationType type, @NotNull List<@NotNull Card> cards) {
|
||||||
|
this.type = Objects.requireNonNull(type, "type must not be null");
|
||||||
|
this.cards = List.copyOf(Objects.requireNonNull(cards, "cards must not be null"));
|
||||||
|
|
||||||
|
if (!this.type.check(this.cards)) {
|
||||||
|
throw new InvalidCombinationException(type, cards);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull CombinationType type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Unmodifiable @NotNull List<@NotNull Card> cards() {
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final @NotNull Card card(int index) {
|
||||||
|
return cards.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int size() {
|
||||||
|
return cards.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompatibleWith(@NotNull Stack stack) {
|
||||||
|
return stack.isEmpty() || stack.top().type() == type() && stack.top().size() == size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompatibleAndHigher(@NotNull Stack stack) {
|
||||||
|
return isCompatibleWith(stack) && isHigherThan(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return type + cards.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public sealed interface Bomb extends Combination permits BombTuple, BombSequence{
|
||||||
|
@Override
|
||||||
|
default boolean isCompatibleWith(@NotNull Stack stack) {
|
||||||
|
return stack.isEmpty()
|
||||||
|
|| stack.top().type() != CombinationType.SINGLE
|
||||||
|
|| stack.top().card(0) != Card.HOUND;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static eu.jonahbauer.tichu.common.model.CombinationType.BOMB_SEQUENCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combination of type {@link CombinationType#BOMB_SEQUENCE}
|
||||||
|
*/
|
||||||
|
public final class BombSequence extends Sequence implements Bomb {
|
||||||
|
public static BombSequence of(@NotNull List<@NotNull Card> cards) {
|
||||||
|
return new BombSequence(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
BombSequence(@NotNull List<@NotNull Card> cards) {
|
||||||
|
super(BOMB_SEQUENCE, cards, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompatibleWith(@NotNull Stack stack) {
|
||||||
|
return Bomb.super.isCompatibleWith(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHigherThan(@NotNull Stack stack) {
|
||||||
|
assert isCompatibleWith(stack);
|
||||||
|
return stack.isEmpty()
|
||||||
|
|| stack.top().type() != BOMB_SEQUENCE
|
||||||
|
|| size() > stack.top().size()
|
||||||
|
|| start() > ((BombSequence) stack.top()).start();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static eu.jonahbauer.tichu.common.model.CombinationType.BOMB;
|
||||||
|
import static eu.jonahbauer.tichu.common.model.CombinationType.BOMB_SEQUENCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combination of type {@link CombinationType#BOMB}
|
||||||
|
*/
|
||||||
|
public final class BombTuple extends Tuple implements Bomb {
|
||||||
|
public static BombTuple of(@NotNull List<@NotNull Card> cards) {
|
||||||
|
return new BombTuple(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BombTuple of(@NotNull Card card1, @NotNull Card card2, @NotNull Card card3, @NotNull Card card4) {
|
||||||
|
return new BombTuple(List.of(
|
||||||
|
Objects.requireNonNull(card1, "card1 must not be null"),
|
||||||
|
Objects.requireNonNull(card2, "card2 must not be null"),
|
||||||
|
Objects.requireNonNull(card3, "card3 must not be null"),
|
||||||
|
Objects.requireNonNull(card4, "card4 must not be null")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
BombTuple(@NotNull List<@NotNull Card> cards) {
|
||||||
|
super(BOMB, cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompatibleWith(@NotNull Stack stack) {
|
||||||
|
return Bomb.super.isCompatibleWith(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHigherThan(@NotNull Stack stack) {
|
||||||
|
assert isCompatibleWith(stack);
|
||||||
|
return stack.isEmpty()
|
||||||
|
|| stack.top().type() != BOMB && stack.top().type() != BOMB_SEQUENCE
|
||||||
|
|| stack.top().type() == BOMB && value() > ((BombTuple) stack.top()).value();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Range;
|
||||||
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public sealed interface Combination extends Serializable permits AbstractCombination, Bomb {
|
||||||
|
/**
|
||||||
|
* Returns the type of this combination.
|
||||||
|
* @return the type of this combination.
|
||||||
|
*/
|
||||||
|
@NotNull CombinationType type();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cards composing this combination.
|
||||||
|
* @return the cards composing this combination.
|
||||||
|
*/
|
||||||
|
@Unmodifiable @NotNull List<@NotNull Card> cards();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the card at the specified index. This is shorthand for {@link #cards()}{@code .}{@link List#get(int) get(index)}.
|
||||||
|
* @param index the index of the card to return
|
||||||
|
* @return the card at the specified index.
|
||||||
|
* @throws IndexOutOfBoundsException if the index is out of bounds ({@code index < 0 || index >= size()})
|
||||||
|
*/
|
||||||
|
@NotNull Card card(@Range(from = 0, to = 13) int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of this combination, i.e. the number of cards it consists of. This is shorthand for
|
||||||
|
* {@link #cards()}{@code .}{@link List#size() size()}.
|
||||||
|
* @return the size of this combination.
|
||||||
|
*/
|
||||||
|
@Range(from = 1, to = 14) int size();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this combination is <em>compatible</em> with the given stack. The definition of
|
||||||
|
* <em>compatible</em> depends on the type of this combination:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* a {@link Bomb} is compatible with any stack that does not have a {@linkplain Single single}
|
||||||
|
* {@link Card#HOUND} on top.
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* any other combination is compatible with an empty stack and all stacks that have a combination
|
||||||
|
* of the same {@link #type()} and {@link #size()} on top.
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* @param stack a stack of combinations
|
||||||
|
* @return {@code true}, iff this combination is compatible with the given stack.
|
||||||
|
*/
|
||||||
|
boolean isCompatibleWith(@NotNull Stack stack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this combination is <em>higher</em> than the given stack, i.e. it could be played on top of
|
||||||
|
* that stack. This method does not, however, ensure that this combination <em>can</em> be played on top of that
|
||||||
|
* stack in every circumstance, since mahjong-wishes and right to play are not accounted for.
|
||||||
|
*
|
||||||
|
* <p>This method assumes that this combination {@linkplain #isCompatibleWith(Stack) is compatible with} the
|
||||||
|
* given stack, otherwise the result is undefined and the method may throw an exception.
|
||||||
|
* @param stack a stack of combinations
|
||||||
|
* @return {@code true}, iff this combination is higher than the given stack.
|
||||||
|
*/
|
||||||
|
boolean isHigherThan(@NotNull Stack stack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this combination is {@linkplain #isCompatibleWith(Stack) compatible with} and
|
||||||
|
* {@linkplain #isHigherThan(Stack) higher than} the given stack.
|
||||||
|
* @param stack a stack of combinations
|
||||||
|
* @return {@code true}, iff this combination is compatible with and higher than the given stack.
|
||||||
|
*/
|
||||||
|
boolean isCompatibleAndHigher(@NotNull Stack stack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new combination of the given type consisting of the given cards.
|
||||||
|
* @param type a combination type
|
||||||
|
* @param cards a list of cards forming a {@linkplain CombinationType#check(List) valid} combination
|
||||||
|
* @return a new combination
|
||||||
|
*/
|
||||||
|
@Contract("_, _ -> new")
|
||||||
|
static Combination of(@NotNull CombinationType type, @NotNull List<@NotNull Card> cards) {
|
||||||
|
return switch (type) {
|
||||||
|
case SINGLE -> Single.of(cards);
|
||||||
|
case PAIR -> Pair.of(cards);
|
||||||
|
case TRIPLE -> Triple.of(cards);
|
||||||
|
case FULL_HOUSE -> FullHouse.of(cards);
|
||||||
|
case SEQUENCE -> SingleSequence.of(cards);
|
||||||
|
case PAIR_SEQUENCE -> PairSequence.of(cards);
|
||||||
|
case BOMB -> BombTuple.of(cards);
|
||||||
|
case BOMB_SEQUENCE -> BombSequence.of(cards);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combination of type {@link CombinationType#FULL_HOUSE}
|
||||||
|
*/
|
||||||
|
public final class FullHouse extends AbstractCombination {
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull FullHouse of(@NotNull List<@NotNull Card> cards) {
|
||||||
|
return new FullHouse(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, _, _, _, _ -> new")
|
||||||
|
public static @NotNull FullHouse of(
|
||||||
|
@NotNull Card triple1, @NotNull Card triple2, @NotNull Card triple3,
|
||||||
|
@NotNull Card pair1, @NotNull Card pair2
|
||||||
|
) {
|
||||||
|
return new FullHouse(List.of(
|
||||||
|
Objects.requireNonNull(triple1, "triple1 must not be null"),
|
||||||
|
Objects.requireNonNull(triple2, "triple2 must not be null"),
|
||||||
|
Objects.requireNonNull(triple3, "triple3 must not be null"),
|
||||||
|
Objects.requireNonNull(pair1, "pair1 must not be null"),
|
||||||
|
Objects.requireNonNull(pair2, "pair2 must not be null")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int triple;
|
||||||
|
private final int pair;
|
||||||
|
|
||||||
|
FullHouse(@NotNull List<@NotNull Card> cards) {
|
||||||
|
super(CombinationType.FULL_HOUSE, cards);
|
||||||
|
this.triple = Tuple.getSameValue(cards.subList(0, 3));
|
||||||
|
this.pair = Tuple.getSameValue(cards.subList(3, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@linkplain Card#value() value} of the triple.
|
||||||
|
*/
|
||||||
|
public int triple() {
|
||||||
|
return triple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@linkplain Card#value() value} of the pair.
|
||||||
|
*/
|
||||||
|
public int pair() {
|
||||||
|
return pair;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHigherThan(@NotNull Stack stack) {
|
||||||
|
assert isCompatibleWith(stack);
|
||||||
|
return stack.isEmpty() || triple() > ((FullHouse) stack.top()).triple();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combination of type {@link CombinationType#PAIR}
|
||||||
|
*/
|
||||||
|
public final class Pair extends Tuple {
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull Pair of(@NotNull List<@NotNull Card> cards) {
|
||||||
|
return new Pair(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, _ -> new")
|
||||||
|
public static @NotNull Pair of(@NotNull Card card1, @NotNull Card card2) {
|
||||||
|
return new Pair(List.of(
|
||||||
|
Objects.requireNonNull(card1, "card1 must not be null"),
|
||||||
|
Objects.requireNonNull(card2, "card2 must not be null")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair(@NotNull List<@NotNull Card> cards) {
|
||||||
|
super(CombinationType.PAIR, cards);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combination of type {@link CombinationType#PAIR_SEQUENCE}
|
||||||
|
*/
|
||||||
|
public final class PairSequence extends Sequence {
|
||||||
|
public static PairSequence of(@NotNull List<@NotNull Card> cards) {
|
||||||
|
return new PairSequence(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PairSequence of(@NotNull Card @NotNull... cards) {
|
||||||
|
return new PairSequence(List.of(cards));
|
||||||
|
}
|
||||||
|
|
||||||
|
PairSequence(@NotNull List<@NotNull Card> cards) {
|
||||||
|
super(CombinationType.PAIR_SEQUENCE, cards, 2);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
public abstract sealed class Sequence extends AbstractCombination permits SingleSequence, PairSequence, BombSequence {
|
||||||
|
private final int start;
|
||||||
|
private final int length;
|
||||||
|
|
||||||
|
Sequence(@NotNull CombinationType type, @NotNull List<@NotNull Card> cards, int n) {
|
||||||
|
super(type, cards);
|
||||||
|
this.length = cards.size() / n;
|
||||||
|
this.start = getStartValue(cards, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@linkplain Card#value() value} of the lowest card in this sequence.
|
||||||
|
*/
|
||||||
|
public int start() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of this sequence.
|
||||||
|
*/
|
||||||
|
public int length() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHigherThan(@NotNull Stack stack) {
|
||||||
|
assert isCompatibleWith(stack);
|
||||||
|
return stack.isEmpty() || start() > ((Sequence) stack.top()).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getStartValue(@NotNull List<@NotNull Card> cards, int n) {
|
||||||
|
for (int i = 0, size = cards.size(); i < size; i++) {
|
||||||
|
Integer value = cards.get(i).value();
|
||||||
|
if (value != null) return value - (i / n);
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combination of type {@link CombinationType#SINGLE}
|
||||||
|
*/
|
||||||
|
public final class Single extends AbstractCombination {
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull Single of(@NotNull List<@NotNull Card> cards) {
|
||||||
|
return new Single(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull Single of(@NotNull Card card) {
|
||||||
|
return new Single(List.of(Objects.requireNonNull(card, "card must not be null")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Single(@NotNull List<@NotNull Card> cards) {
|
||||||
|
super(CombinationType.SINGLE, cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the single card this combination consists of. This is shorthand for {@link #card(int) card(0)}.
|
||||||
|
* @return the single card this combination consists of.
|
||||||
|
*/
|
||||||
|
public @NotNull Card card() {
|
||||||
|
return card(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@linkplain Card#value() value} of the {@linkplain #card() card}.
|
||||||
|
* @return the value of the card.
|
||||||
|
*/
|
||||||
|
public Integer value() {
|
||||||
|
return card().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHigherThan(@NotNull Stack stack) {
|
||||||
|
assert isCompatibleWith(stack);
|
||||||
|
if (stack.isEmpty()) return true; // you can play any card onto an empty stack
|
||||||
|
|
||||||
|
var top = (Single) stack.top();
|
||||||
|
if (top.card() == Card.HOUND) return false; // you cannot play any card onto a hound
|
||||||
|
|
||||||
|
return switch (card()) {
|
||||||
|
case HOUND, MAHJONG -> false;
|
||||||
|
case DRAGON -> true;
|
||||||
|
case PHOENIX -> top.card() != Card.DRAGON;
|
||||||
|
default -> {
|
||||||
|
if (top.card() != Card.PHOENIX) {
|
||||||
|
yield value() > top.value();
|
||||||
|
} else if (stack.size() == 1) {
|
||||||
|
// every normal card has a value of at least 2, which is higher that the phoenix 1½
|
||||||
|
yield true;
|
||||||
|
} else {
|
||||||
|
yield value() > ((Single) stack.top(-1)).value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combination of type {@link CombinationType#SEQUENCE}
|
||||||
|
*/
|
||||||
|
public final class SingleSequence extends Sequence {
|
||||||
|
public static SingleSequence of(@NotNull List<@NotNull Card> cards) {
|
||||||
|
return new SingleSequence(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SingleSequence of(@NotNull Card @NotNull... cards) {
|
||||||
|
return new SingleSequence(List.of(cards));
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleSequence(@NotNull List<@NotNull Card> cards) {
|
||||||
|
super(CombinationType.SEQUENCE, cards, 1);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A combination of type {@link CombinationType#TRIPLE}
|
||||||
|
*/
|
||||||
|
public final class Triple extends Tuple {
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull Triple of(@NotNull List<@NotNull Card> cards) {
|
||||||
|
return new Triple(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, _, _ -> new")
|
||||||
|
public static @NotNull Triple of(@NotNull Card card1, @NotNull Card card2, @NotNull Card card3) {
|
||||||
|
return new Triple(List.of(
|
||||||
|
Objects.requireNonNull(card1, "card1 must not be null"),
|
||||||
|
Objects.requireNonNull(card2, "card2 must not be null"),
|
||||||
|
Objects.requireNonNull(card3, "card3 must not be null")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Triple(@NotNull List<@NotNull Card> cards) {
|
||||||
|
super(CombinationType.TRIPLE, cards);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
|
import eu.jonahbauer.tichu.common.model.Card;
|
||||||
|
import eu.jonahbauer.tichu.common.model.CombinationType;
|
||||||
|
import eu.jonahbauer.tichu.common.model.Stack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
public abstract sealed class Tuple extends AbstractCombination permits Pair, Triple, BombTuple {
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
Tuple(@NotNull CombinationType type, @NotNull List<@NotNull Card> cards) {
|
||||||
|
super(type, cards);
|
||||||
|
this.value = getSameValue(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@linkplain Card#value() value} of the cards in this tuple.
|
||||||
|
*/
|
||||||
|
public int value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHigherThan(@NotNull Stack stack) {
|
||||||
|
assert isCompatibleWith(stack);
|
||||||
|
return stack.isEmpty() || value() > ((Tuple) stack.top()).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assuming that all the {@linkplain Card#isNormal() normal cards} at the specified indices have the same value
|
||||||
|
* returns that value. The return value is unspecified if the assumption is violated.
|
||||||
|
* @param cards a list of cards
|
||||||
|
* @throws NoSuchElementException if there is no normal card among the specified indices
|
||||||
|
*/
|
||||||
|
static int getSameValue(@NotNull List<@NotNull Card> cards) {
|
||||||
|
for (var card : cards) {
|
||||||
|
if (card.isSpecial()) continue;
|
||||||
|
|
||||||
|
return card.value();
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
module eu.jonahbauer.tichu.common {
|
module eu.jonahbauer.tichu.common {
|
||||||
exports eu.jonahbauer.tichu.common.model;
|
exports eu.jonahbauer.tichu.common.model;
|
||||||
exports eu.jonahbauer.tichu.common.model.exceptions;
|
exports eu.jonahbauer.tichu.common.model.exceptions;
|
||||||
|
exports eu.jonahbauer.tichu.common.model.combinations;
|
||||||
|
|
||||||
requires static lombok;
|
requires static lombok;
|
||||||
requires static org.jetbrains.annotations;
|
requires static org.jetbrains.annotations;
|
||||||
|
Loading…
Reference in New Issue