Add files via upload
This commit is contained in:
parent
89dc6ee958
commit
c24b9f3d28
174
jass/CardSet.java
Normal file
174
jass/CardSet.java
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static ch.epfl.javass.Preconditions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of a {@link CardSet} by a list of {@link Card}.
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class CardSet {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An empty {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public static final CardSet EMPTY = new CardSet(PackedCardSet.EMPTY);
|
||||||
|
/**
|
||||||
|
* A {@link CardSet} containing all {@link Card} of a Jass.
|
||||||
|
*/
|
||||||
|
public static final CardSet ALL_CARDS = new CardSet(PackedCardSet.ALL_CARDS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get a {@link CardSet} contained in a list of cards.
|
||||||
|
* @param cards.
|
||||||
|
* @return a packed version of the {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public static CardSet of(List<Card> cards) {
|
||||||
|
long packed = PackedCardSet.EMPTY;
|
||||||
|
for(Card card : cards) {
|
||||||
|
packed = PackedCardSet.add(packed, card.packed());
|
||||||
|
}
|
||||||
|
return new CardSet(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get a {@link CardSet} knowing its packed version.
|
||||||
|
* @param packed, the packed version of the {@link CardSet}.
|
||||||
|
* @return a {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public static CardSet ofPacked(long packed) {
|
||||||
|
checkArgument(PackedCardSet.isValid(packed));
|
||||||
|
return new CardSet(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private final long packed;
|
||||||
|
|
||||||
|
private CardSet(long packed) {
|
||||||
|
this.packed = packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the packed version of a {@link CardSet}.
|
||||||
|
* @return a long representing the packed version of the {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public long packed() {
|
||||||
|
return this.packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to check if the {@link CardSet} contains no {@link Card}.
|
||||||
|
* @return a boolean true if the {@link CardSet} is empty.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return PackedCardSet.isEmpty(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the number of {@link Card} in the {@link CardSet}.
|
||||||
|
* @return an int representing the size of the {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return PackedCardSet.size(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to obtain the {@link Card} in the {@link CardSet} at the given index.
|
||||||
|
* @param index an int for the index of the wanted {@link Card}
|
||||||
|
* @return a {@link Card} in the {@link CardSet} at the given index.
|
||||||
|
*/
|
||||||
|
public Card get(int index) {
|
||||||
|
return Card.ofPacked(PackedCardSet.get(packed, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given {@link Card} to the {@link CardSet}.
|
||||||
|
* @param card the {@link Card} to add to the {@link CardSet}.
|
||||||
|
* @return the new {@link CardSet} containing the {@link Card}.
|
||||||
|
*/
|
||||||
|
public CardSet add(Card card) {
|
||||||
|
return new CardSet(PackedCardSet.add(packed, card.packed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given {@link Card} from the {@link CardSet}.
|
||||||
|
* @param card the {@link Card} to remove from the {@link CardSet}.
|
||||||
|
* @return the new {@link CardSet} without the given {@link Card}.
|
||||||
|
*/
|
||||||
|
public CardSet remove(Card card) {
|
||||||
|
return new CardSet(PackedCardSet.remove(packed, card.packed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the {@link CardSet} contains the given {@link Card}.
|
||||||
|
* @param card the {@link Card} to be checked.
|
||||||
|
* @return whether or not the {@link Card} is contained in {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public boolean contains(Card card) {
|
||||||
|
return PackedCardSet.contains(packed, card.packed());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes all the {@link Card}s not contained in {@link CardSet} in form of a new {@link CardSet}.
|
||||||
|
* @return a new {@link CardSet} containing all the {@link Card} not contained in the {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public CardSet complement() {
|
||||||
|
return new CardSet(PackedCardSet.complement(packed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the {@link CardSet} containing the {@link Card}s that are in the given {@link CardSet} and in the prior {@link CardSet}.
|
||||||
|
* @param that the given {@link CardSet} to merge with the prior {@link CardSet}.
|
||||||
|
* @return a new {@link CardSet} which is the union of the prior {@link CardSet} and the given {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public CardSet union(CardSet that) {
|
||||||
|
return new CardSet(PackedCardSet.union(packed, that.packed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the {@link CardSet} containing the {@link Card}s that are only in both the given {@link CardSet} and the prior {@link CardSet}.
|
||||||
|
* @param that the given {@link CardSet} to intersect with the prior {@link CardSet}.
|
||||||
|
* @return a new {@link CardSet} which is the intersection of the prior {@link CardSet} and the given {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public CardSet intersection(CardSet that) {
|
||||||
|
return new CardSet(PackedCardSet.intersection(packed, that.packed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the {@link CardSet} containing the {@link Card}s of the {@link CardSet} minus the {@link Card}s of the given {@link CardSet}.
|
||||||
|
* @param that the given {@link CardSet} to be substracted from the prior {@link CardSet}.
|
||||||
|
* @return a new {@link CardSet} which is the {@link CardSet} minus the given {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public CardSet difference(CardSet that) {
|
||||||
|
return new CardSet(PackedCardSet.difference(packed, that.packed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a {@link CardSet} containing only the {@link Card}s of a given {@link Color}.
|
||||||
|
* @param color the {@link Color} of the wanted subset of {@link CardSet}.
|
||||||
|
* @return a {@link CardSet} containing only the {@link Card}s of a given {@link Color}.
|
||||||
|
*/
|
||||||
|
public CardSet subsetOfColor(Card.Color color) {
|
||||||
|
return new CardSet(PackedCardSet.subsetOfColor(packed, color));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object that) {
|
||||||
|
return that != null
|
||||||
|
&& that.getClass() == this.getClass()
|
||||||
|
&& this.packed == ((CardSet)that).packed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Long.hashCode(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return PackedCardSet.toString(packed);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
jass/Jass.java
Normal file
30
jass/Jass.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface containing the main variables defining a Jass game
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface Jass {
|
||||||
|
/**
|
||||||
|
* Number of cards in one hand at the beginning of a round.
|
||||||
|
*/
|
||||||
|
int HAND_SIZE = 9;
|
||||||
|
/**
|
||||||
|
* Number of tricks in a round.
|
||||||
|
*/
|
||||||
|
int TRICKS_PER_TURN = 9;
|
||||||
|
/**
|
||||||
|
* Number of necessary points to win.
|
||||||
|
*/
|
||||||
|
int WINNING_POINTS = 1000;
|
||||||
|
/**
|
||||||
|
* Number of additional points won by a team which has won all the tricks of a round.
|
||||||
|
*/
|
||||||
|
int MATCH_ADDITIONAL_POINTS = 100;
|
||||||
|
/**
|
||||||
|
* Number of additional points won by a team winning the last trick.
|
||||||
|
*/
|
||||||
|
int LAST_TRICK_ADDITIONAL_POINTS = 5;
|
||||||
|
}
|
||||||
188
jass/JassGame.java
Normal file
188
jass/JassGame.java
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import ch.epfl.javass.jass.Card.Color;
|
||||||
|
import ch.epfl.javass.jass.Card.Rank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of a game of Jass.
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class JassGame {
|
||||||
|
|
||||||
|
private static final Card SEVEN_OF_DIAMONDS = Card.of(Color.DIAMOND, Rank.SEVEN);
|
||||||
|
|
||||||
|
private final Map<PlayerId, Player> players;
|
||||||
|
private final Map<PlayerId, String> playerNames;
|
||||||
|
private final Random shuffleRng;
|
||||||
|
private final Random trumpRng;
|
||||||
|
|
||||||
|
private final Map<Player, CardSet> hands = new HashMap<>();
|
||||||
|
private final List<Card> deck = new ArrayList<>();
|
||||||
|
private PlayerId firstPlayerId;
|
||||||
|
private Color trump;
|
||||||
|
|
||||||
|
private TurnState currentTurnState;
|
||||||
|
|
||||||
|
private boolean isGameOver = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of a game of Jass.
|
||||||
|
* @param rngSeed the seed for the random number generator (for the shuffle and the trump).
|
||||||
|
* @param players a map between the {@link PlayerId}s and the {@link Player}s of the game.
|
||||||
|
* @param playerNames a map between the {@link Players}s of the game and their names.
|
||||||
|
*/
|
||||||
|
public JassGame(long rngSeed, Map<PlayerId, Player> players,
|
||||||
|
Map<PlayerId, String> playerNames) {
|
||||||
|
this.players = Collections.unmodifiableMap(new EnumMap<>(players));
|
||||||
|
this.playerNames = Collections
|
||||||
|
.unmodifiableMap(new EnumMap<>(playerNames));
|
||||||
|
|
||||||
|
Random rng = new Random(rngSeed);
|
||||||
|
this.shuffleRng = new Random(rng.nextLong());
|
||||||
|
this.trumpRng = new Random(rng.nextLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to check if the game is over (if a team has more than 1000).
|
||||||
|
* @return a boolean which is true if a team has won
|
||||||
|
*/
|
||||||
|
public boolean isGameOver() {
|
||||||
|
return isGameOver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that plays the game until the end of the current {@link Trick}.
|
||||||
|
*/
|
||||||
|
public void advanceToEndOfNextTrick() {
|
||||||
|
if(!isGameOver) {
|
||||||
|
//Creates the first turn state if it does not exist yet.
|
||||||
|
if (currentTurnState == null)
|
||||||
|
newTurn(true);
|
||||||
|
|
||||||
|
//Collects the trick when it is full.
|
||||||
|
if (currentTurnState.trick().isFull())
|
||||||
|
currentTurnState = currentTurnState.withTrickCollected();
|
||||||
|
|
||||||
|
//Creates the next turn if all the tricks of the current turn have been played.
|
||||||
|
if (currentTurnState.isTerminal())
|
||||||
|
newTurn(false);
|
||||||
|
|
||||||
|
//Calls the methods to update the score for all players.
|
||||||
|
updateScoreForAll();
|
||||||
|
//Calls the methods to update the trick for all players.
|
||||||
|
updateTrickForAll();
|
||||||
|
|
||||||
|
//While the trick isn't full ask each player which card they play and update the trick.
|
||||||
|
while (!currentTurnState.trick().isFull())
|
||||||
|
updatePlayers();
|
||||||
|
|
||||||
|
checkIfTeamWon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void newTurn(boolean isFirst) {
|
||||||
|
trump = Color.ALL.get(trumpRng.nextInt(Color.COUNT));
|
||||||
|
initializeAndShuffleDeck();
|
||||||
|
initializePlayers(isFirst);
|
||||||
|
|
||||||
|
if (!isFirst) {
|
||||||
|
setNewFirstPlayer();
|
||||||
|
currentTurnState = TurnState.initial(trump,
|
||||||
|
currentTurnState.score().nextTurn(), firstPlayerId);
|
||||||
|
} else
|
||||||
|
currentTurnState = TurnState.initial(trump, Score.INITIAL,
|
||||||
|
firstPlayerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkIfTeamWon() {
|
||||||
|
for (int i = 0; i < TeamId.COUNT; i++) {
|
||||||
|
if (currentTurnState
|
||||||
|
.score()
|
||||||
|
.totalPoints(TeamId.ALL.get(i)) >= Jass.WINNING_POINTS)
|
||||||
|
updateWinningTeam(TeamId.ALL.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeAndShuffleDeck() {
|
||||||
|
deck.clear();
|
||||||
|
|
||||||
|
for (Color color : Color.ALL)
|
||||||
|
for (Rank rank : Rank.ALL)
|
||||||
|
deck.add(Card.of(color, rank));
|
||||||
|
|
||||||
|
Collections.shuffle(deck, shuffleRng);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializePlayers(boolean first) {
|
||||||
|
for (Map.Entry<PlayerId, Player> entry : players.entrySet()) {
|
||||||
|
Player p = entry.getValue();
|
||||||
|
PlayerId iD = entry.getKey();
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
p.setPlayers(entry.getKey(), playerNames);
|
||||||
|
|
||||||
|
//Distributes the cards among players.
|
||||||
|
hands.put(p, CardSet.of(deck.subList(iD.ordinal() * Jass.HAND_SIZE,
|
||||||
|
(iD.ordinal() + 1) * Jass.HAND_SIZE)));
|
||||||
|
p.updateHand(hands.get(p));
|
||||||
|
|
||||||
|
p.setTrump(trump);
|
||||||
|
|
||||||
|
//Compute which player is the first player i.e has the jack of diamonds.
|
||||||
|
if (first && hands.get(p)
|
||||||
|
.contains(SEVEN_OF_DIAMONDS))
|
||||||
|
firstPlayerId = iD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePlayers() {
|
||||||
|
PlayerId pid = currentTurnState.nextPlayer();
|
||||||
|
Player p = players.get(pid);
|
||||||
|
|
||||||
|
//Asks the player which card he wants to play.
|
||||||
|
Card c = p.cardToPlay(currentTurnState, hands.get(p));
|
||||||
|
|
||||||
|
//Updates the turn with the card played
|
||||||
|
currentTurnState = currentTurnState.withNewCardPlayed(c);
|
||||||
|
|
||||||
|
//Updates the hand of the player with the card played.
|
||||||
|
hands.put(p, hands.get(p).remove(c));
|
||||||
|
p.updateHand(hands.get(p));
|
||||||
|
|
||||||
|
//Calls the methods to update the trick for all players.
|
||||||
|
updateTrickForAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTrickForAll() {
|
||||||
|
for (Player p : players.values())
|
||||||
|
p.updateTrick(currentTurnState.trick());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateScoreForAll() {
|
||||||
|
for (Player p : players.values())
|
||||||
|
p.updateScore(currentTurnState.score());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setNewFirstPlayer() {
|
||||||
|
firstPlayerId = PlayerId.ALL
|
||||||
|
.get((firstPlayerId.ordinal() + 1) % PlayerId.COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateWinningTeam(TeamId team) {
|
||||||
|
isGameOver = true;
|
||||||
|
for (Player p : players.values()) {
|
||||||
|
p.updateScore(currentTurnState.score());
|
||||||
|
p.setWinningTeam(team);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
213
jass/MctsPlayer.java
Normal file
213
jass/MctsPlayer.java
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import static ch.epfl.javass.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.SplittableRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simulated player that plays cards selected by a Monte Carlo search tree Algorithm.
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class MctsPlayer implements Player {
|
||||||
|
|
||||||
|
private final static int EXPLORATION_CONST = 40;
|
||||||
|
|
||||||
|
private final SplittableRandom rng;
|
||||||
|
private final PlayerId id;
|
||||||
|
private final int iterations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simulated player that plays cards selected by a Monte Carlo search tree Algorithm.
|
||||||
|
* @param ownId the playerId of the simulated player.
|
||||||
|
* @param rngSeed a long the seed for the pseudo-random number generator used to simulate random turns.
|
||||||
|
* @param iterations the number of iteration of the algorithm, must be superior to the size of a hand of Jass.
|
||||||
|
* @throws IllegalArgumentException if the number of iterations is inferior to 9.
|
||||||
|
*/
|
||||||
|
public MctsPlayer(PlayerId ownId, long rngSeed, int iterations) {
|
||||||
|
checkArgument(iterations >= Jass.HAND_SIZE);
|
||||||
|
|
||||||
|
this.rng = new SplittableRandom(rngSeed);
|
||||||
|
this.id = ownId;
|
||||||
|
this.iterations = iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Card cardToPlay(TurnState state, CardSet hand) {
|
||||||
|
Node root = new Node(state, playableCards(state, hand.packed(), id), id);
|
||||||
|
ArrayList<Node> path;
|
||||||
|
|
||||||
|
for (int i = 0; i < iterations; ++i) {
|
||||||
|
//Add nodes to the path.
|
||||||
|
path = root.addNode(hand.packed());
|
||||||
|
//Simulate the last node of the path.
|
||||||
|
long score = simulate(path.get(path.size()-1), hand.packed());
|
||||||
|
//Propagate the score through all the nodes.
|
||||||
|
backPropagateScore(score, path);
|
||||||
|
}
|
||||||
|
return Card.ofPacked(PackedCardSet.get(playableCards(state, hand.packed(), id), root.highestV(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simulate the possible score of a random turn given a certain state and hand.
|
||||||
|
* Returns the final score of a finished random turn.
|
||||||
|
*/
|
||||||
|
private long simulate(Node node, long hand) {
|
||||||
|
TurnState s = node.state;
|
||||||
|
//Plays a random turn from the state of the given node.
|
||||||
|
if(s.isTerminal())
|
||||||
|
return s.packedScore();
|
||||||
|
else
|
||||||
|
while (!s.isTerminal()) {
|
||||||
|
long pC = playableCards(s, hand, id);
|
||||||
|
Card randomCard = Card.ofPacked(PackedCardSet.get(pC, rng.nextInt(PackedCardSet.size(pC))));
|
||||||
|
s = s.withNewCardPlayedAndTrickCollected(randomCard);
|
||||||
|
}
|
||||||
|
return s.packedScore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determines the playable cards for a given state taking account of the non-played cards and the hand of the payer.
|
||||||
|
* Returns the playable cards for state.
|
||||||
|
*/
|
||||||
|
private static long playableCards(TurnState state, long hand, PlayerId id) {
|
||||||
|
if (state.isTerminal())
|
||||||
|
return PackedCardSet.EMPTY;
|
||||||
|
|
||||||
|
return state.nextPlayer().equals(id) ?
|
||||||
|
PackedTrick.playableCards(state.packedTrick(), PackedCardSet.intersection(hand, state.packedUnplayedCards())) :
|
||||||
|
PackedTrick.playableCards(state.packedTrick(), PackedCardSet.difference(state.packedUnplayedCards(), hand));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A method that propagate the updated score along the path of nodes by updating the score of every nodes.
|
||||||
|
*/
|
||||||
|
private void backPropagateScore(long score, ArrayList<Node> path) {
|
||||||
|
path.get(0).updateScore(score, id.team());
|
||||||
|
|
||||||
|
for (int i = 1; i < path.size(); ++i)
|
||||||
|
path.get(i).updateScore(score,
|
||||||
|
path.get(i - 1)
|
||||||
|
.state.nextPlayer()
|
||||||
|
.team());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* A node of the Monte Carlo Tree Search algorithm.
|
||||||
|
* @author Charles Beauville
|
||||||
|
* @author Célia Houssiaux
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final class Node {
|
||||||
|
private final TurnState state;
|
||||||
|
private long potentialCards;
|
||||||
|
private final Node[] children;
|
||||||
|
private int n;
|
||||||
|
private int s;
|
||||||
|
private PlayerId id;
|
||||||
|
|
||||||
|
private Node(TurnState state, long playableCards, PlayerId id) {
|
||||||
|
this.state = state;
|
||||||
|
this.potentialCards = playableCards;
|
||||||
|
this.s = 0;
|
||||||
|
this.n = 0;
|
||||||
|
this.children = new Node[PackedCardSet.size(potentialCards)];
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A method that gives the index of the best child of a Node.
|
||||||
|
* That means the one who got the highest value of V.
|
||||||
|
* c is the exploration constant.
|
||||||
|
* Returns the index of the best child in children[].
|
||||||
|
*/
|
||||||
|
private int highestV(int c) {
|
||||||
|
int bestVIndex = 0;
|
||||||
|
double v;
|
||||||
|
double bestV = 0;
|
||||||
|
for (int i = 0; i < children.length; i++) {
|
||||||
|
if (children[i] == null)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
//Computes the function V.
|
||||||
|
if (children[i].n > 0)
|
||||||
|
v = (double) (children[i].s) / (double)(children[i].n)
|
||||||
|
+ c * Math.sqrt((Math.log(n+1) / (double)children[i].n));
|
||||||
|
else
|
||||||
|
return i;
|
||||||
|
|
||||||
|
//Computes if the node is the new best node.
|
||||||
|
if (v > bestV) {
|
||||||
|
bestV = v;
|
||||||
|
bestVIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestVIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A method that adds if possible a new node at the right place of the tree.
|
||||||
|
* Returns a List that contains the path from the root to the new node freshly created.
|
||||||
|
*/
|
||||||
|
private ArrayList<Node> addNode(long hand) {
|
||||||
|
ArrayList<Node> path = new ArrayList<>();
|
||||||
|
boolean newNodeAdded = false;
|
||||||
|
path.add(this);
|
||||||
|
|
||||||
|
Node lastNode = this;
|
||||||
|
//Goes through all the best children of the last node and adds them to the path until a node does not have all its children.
|
||||||
|
while (lastNode.hasAllChildren()) {
|
||||||
|
lastNode = lastNode.children[lastNode.highestV(EXPLORATION_CONST)];
|
||||||
|
path.add(lastNode);
|
||||||
|
if (lastNode.state.isTerminal())
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Creates a new node as a child of last node
|
||||||
|
if (!PackedCardSet.isEmpty(lastNode.potentialCards)) {
|
||||||
|
//Computes a new turn state with the first card of the potential cards played and removes the played card form the potential cards.
|
||||||
|
int card = PackedCardSet.get(lastNode.potentialCards, 0);
|
||||||
|
TurnState newState = lastNode.state
|
||||||
|
.withNewCardPlayedAndTrickCollected(Card.ofPacked(card));
|
||||||
|
lastNode.potentialCards = PackedCardSet.remove(lastNode.potentialCards, card);
|
||||||
|
|
||||||
|
long newPotentialCards = playableCards(newState, hand, id);
|
||||||
|
|
||||||
|
if (PackedCardSet.isEmpty(newPotentialCards))
|
||||||
|
return path;
|
||||||
|
|
||||||
|
Node node = new Node(newState, newPotentialCards, id);
|
||||||
|
|
||||||
|
//Adds the new computed node to the children of this node where there is space.
|
||||||
|
for (int i = 0; i < lastNode.children.length; i++) {
|
||||||
|
if (newNodeAdded)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (lastNode.children[i] == null) {
|
||||||
|
lastNode.children[i] = node;
|
||||||
|
newNodeAdded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This boolean indicates if a children[]is full.
|
||||||
|
* Returns true, if children[] is full.
|
||||||
|
*/
|
||||||
|
private boolean hasAllChildren() {
|
||||||
|
return children[children.length-1] != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This method updates the score of a given team.
|
||||||
|
*/
|
||||||
|
private void updateScore(long score, TeamId t) {
|
||||||
|
s += PackedScore.totalPoints(score, t);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
80
jass/PacedPlayer.java
Normal file
80
jass/PacedPlayer.java
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ch.epfl.javass.jass.Card.Color;
|
||||||
|
import static ch.epfl.javass.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player that slows down the game
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class PacedPlayer implements Player {
|
||||||
|
private final Player underlyingPlayer;
|
||||||
|
private final long minTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player that slows down the game
|
||||||
|
* @param underlyingPlayer the {@link Player} associated with this {@link PacedPlayer}.
|
||||||
|
* @param minTime a double the minimum time in seconds to wait for the {@link Player}.
|
||||||
|
*/
|
||||||
|
public PacedPlayer(Player underlyingPlayer, double minTime) {
|
||||||
|
checkArgument(minTime >= 0);
|
||||||
|
|
||||||
|
this.underlyingPlayer = underlyingPlayer;
|
||||||
|
this.minTime = (long) minTime * 1000;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Card cardToPlay(TurnState state, CardSet hand) {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
Card c = underlyingPlayer.cardToPlay(state, hand);
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
|
||||||
|
long timeElapsed = (end - start);
|
||||||
|
|
||||||
|
if (timeElapsed >= minTime)
|
||||||
|
return c;
|
||||||
|
else
|
||||||
|
try {
|
||||||
|
Thread.sleep((minTime - timeElapsed));
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
/* ignore */ }
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPlayers(PlayerId ownId, Map<PlayerId, String> playerNames) {
|
||||||
|
underlyingPlayer.setPlayers(ownId, playerNames);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateHand(CardSet newHand) {
|
||||||
|
underlyingPlayer.updateHand(newHand);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTrump(Color trump) {
|
||||||
|
underlyingPlayer.setTrump(trump);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateTrick(Trick newTrick) {
|
||||||
|
underlyingPlayer.updateTrick(newTrick);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateScore(Score score) {
|
||||||
|
underlyingPlayer.updateScore(score);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWinningTeam(TeamId winningTeam) {
|
||||||
|
underlyingPlayer.setWinningTeam(winningTeam);
|
||||||
|
}
|
||||||
|
}
|
||||||
165
jass/PackedCard.java
Normal file
165
jass/PackedCard.java
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import ch.epfl.javass.bits.Bits32;
|
||||||
|
import ch.epfl.javass.jass.Card.Color;
|
||||||
|
import ch.epfl.javass.jass.Card.Rank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows us to manipulate cards from a Jass game packed in an integer.
|
||||||
|
*
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class PackedCard {
|
||||||
|
|
||||||
|
private PackedCard() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the binary value 111111, which represents an invalid packed card
|
||||||
|
*/
|
||||||
|
public static final int INVALID = 0b111111;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the packed card having the proper {@link Color} and {@link Rank}.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* the {@link Color} of the wanted packed {@link Card}.
|
||||||
|
* @param r
|
||||||
|
* the {@link Rank} of the wanted packed {@link Card}.
|
||||||
|
* @return the packed {@link Card} version of the given {@link Rank} and
|
||||||
|
* {@link Color}.
|
||||||
|
*/
|
||||||
|
public static int pack(Color c, Rank r) {
|
||||||
|
return Bits32.pack(r.ordinal(), RANK_SIZE, c.ordinal(), COLOR_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int RANK_INDEX = 0;
|
||||||
|
private static final int COLOR_INDEX = 4;
|
||||||
|
|
||||||
|
private static final int MAX_RANK_VALUE = 8;
|
||||||
|
|
||||||
|
private static final int RANK_SIZE = 4;
|
||||||
|
private static final int COLOR_SIZE = 2;
|
||||||
|
private static final int UNUSED_SIZE = 25;
|
||||||
|
|
||||||
|
private static final int INDEX_TRUMP = 0;
|
||||||
|
private static final int INDEX_NORMAL = 1;
|
||||||
|
|
||||||
|
private static final int[][] PTS = { { 0, 0 }, { 0, 0 }, { 0, 0 },
|
||||||
|
{ 14, 0 }, { 10, 10 }, { 20, 2 }, { 3, 3 }, { 4, 4 }, { 11, 11 } };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true only if the value is a valid packed {@link Card}, which
|
||||||
|
* means if the bits containing the rank contain a value between 0 and
|
||||||
|
* 8(included) and if the useless bits are all equal to 0.
|
||||||
|
*
|
||||||
|
* @param pkCard
|
||||||
|
* an int representing the packed version of a Jass {@link Card}.
|
||||||
|
* @return a boolean true if the packed version of the {@link Card} is valid
|
||||||
|
*/
|
||||||
|
public static boolean isValid(int pkCard) {
|
||||||
|
return (Bits32.extract(pkCard, RANK_INDEX, RANK_SIZE) <= MAX_RANK_VALUE
|
||||||
|
&& Bits32.extract(pkCard, RANK_INDEX, RANK_SIZE) >= 0
|
||||||
|
&& Bits32.extract(pkCard, RANK_SIZE + COLOR_SIZE,
|
||||||
|
UNUSED_SIZE) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Color} of the packed {@link Card}.
|
||||||
|
*
|
||||||
|
* @param pkCard
|
||||||
|
* an int representing the packed version of a Jass {@link Card}.
|
||||||
|
* @return the {@link Color} of the given packed {@link Card}.
|
||||||
|
*/
|
||||||
|
public static Color color(int pkCard) {
|
||||||
|
assert isValid(pkCard) : "Invalid card in color function of pkCard";
|
||||||
|
|
||||||
|
int pkColor = Bits32.extract(pkCard, COLOR_INDEX, COLOR_SIZE);
|
||||||
|
|
||||||
|
return Color.ALL.get(pkColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Rank} of the packed {@link Card}.
|
||||||
|
*
|
||||||
|
* @param pkCard
|
||||||
|
* an int representing the packed version of a Jass {@link Card}.
|
||||||
|
* @return the {@link Rank} of the given packed {@link Card}.
|
||||||
|
*/
|
||||||
|
public static Rank rank(int pkCard) {
|
||||||
|
assert isValid(pkCard) : "Invalid card in rank function of pkCard";
|
||||||
|
|
||||||
|
int pkRank = Bits32.extract(pkCard, RANK_INDEX, RANK_SIZE);
|
||||||
|
|
||||||
|
return Rank.ALL.get(pkRank);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the first {@link Card} entered with the second one, knowing that
|
||||||
|
* the {@link Color} is trump.
|
||||||
|
*
|
||||||
|
* @param trump
|
||||||
|
* the trump {@link Color}
|
||||||
|
* @param pkCardL
|
||||||
|
* the {@link Card} to be compared with
|
||||||
|
* @param pkCardR
|
||||||
|
* the {@link Card} to be compared to
|
||||||
|
* @return a boolean which is true only if the first {@link Card} entered is
|
||||||
|
* superior to the second one, knowing that trump is the asset.
|
||||||
|
*/
|
||||||
|
public static boolean isBetter(Color trump, int pkCardL, int pkCardR) {
|
||||||
|
assert isValid(
|
||||||
|
pkCardL) : "Invalid pkCardL in isBetter function of pkCard";
|
||||||
|
assert isValid(
|
||||||
|
pkCardR) : "Invalid pkCardR in isBetter function of pkCard";
|
||||||
|
|
||||||
|
boolean bothTrump = color(pkCardL).equals(trump) && color(pkCardR).equals(trump);
|
||||||
|
boolean betterTrumpRank = rank(pkCardL).trumpOrdinal() > rank(pkCardR).trumpOrdinal();
|
||||||
|
boolean betterRank = rank(pkCardL).ordinal() > rank(pkCardR).ordinal();
|
||||||
|
boolean bothNotTrump = !color(pkCardL).equals(trump) && !color(pkCardR).equals(trump);
|
||||||
|
boolean sameColor = color(pkCardL).equals(color(pkCardR));
|
||||||
|
|
||||||
|
if(sameColor)
|
||||||
|
return bothTrump ? betterTrumpRank : betterRank;
|
||||||
|
else
|
||||||
|
return !bothNotTrump ? color(pkCardL).equals(trump) : false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the packed {@link Card}.
|
||||||
|
*
|
||||||
|
* @param trump
|
||||||
|
* the trump {@link Color}.
|
||||||
|
* @param pkCard
|
||||||
|
* an int representing the packed version of a Jass {@link Card}.
|
||||||
|
* @return the value of the given packed {@link Card}.
|
||||||
|
*/
|
||||||
|
public static int points(Color trump, int pkCard) {
|
||||||
|
assert isValid(pkCard) : "Invalid card in points function of pkCard";
|
||||||
|
|
||||||
|
return color(pkCard).equals(trump)
|
||||||
|
? PTS[rank(pkCard).ordinal()][INDEX_TRUMP]
|
||||||
|
: PTS[rank(pkCard).ordinal()][INDEX_NORMAL];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a representation of the packed {@link Card} under a string of
|
||||||
|
* character containing the symbol of the {@link Color} and the shorted name
|
||||||
|
* of the {@link Rank}.
|
||||||
|
*
|
||||||
|
* @param pkCard
|
||||||
|
* an int representing the packed version of a Jass {@link Card}.
|
||||||
|
* @return a string the description of the given packed {@link Card} (its
|
||||||
|
* {@link Rank} and its {@link Color}).
|
||||||
|
*/
|
||||||
|
public static String toString(int pkCard) {
|
||||||
|
assert isValid(pkCard) : "Invalid card in toString function of pkCard";
|
||||||
|
|
||||||
|
return color(pkCard).toString() +
|
||||||
|
rank(pkCard).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
236
jass/PackedCardSet.java
Normal file
236
jass/PackedCardSet.java
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import ch.epfl.javass.jass.Card.Rank;
|
||||||
|
|
||||||
|
import static ch.epfl.javass.bits.Bits64.mask;
|
||||||
|
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packed representation of a card set
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class PackedCardSet {
|
||||||
|
private PackedCardSet() {}
|
||||||
|
|
||||||
|
private static final int COLOR_SIZE = 16;
|
||||||
|
|
||||||
|
private static final int SPADES_INDEX = 0;
|
||||||
|
private static final int HEARTS_INDEX = 16;
|
||||||
|
private static final int DIAMONDS_INDEX = 32;
|
||||||
|
private static final int CLUBS_INDEX = 48;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packed version of an empty {@link CardSet}.
|
||||||
|
*/
|
||||||
|
public static final long EMPTY = 0L;
|
||||||
|
/**
|
||||||
|
* The packed version off a {@link CardSet} containing all {@link Card}s of jass.
|
||||||
|
*/
|
||||||
|
public static final long ALL_CARDS = mask(CLUBS_INDEX, Rank.COUNT) | mask(DIAMONDS_INDEX, Rank.COUNT) | mask(HEARTS_INDEX, Rank.COUNT) | mask(SPADES_INDEX, Rank.COUNT);
|
||||||
|
|
||||||
|
private static final long[] trumpTab =
|
||||||
|
{0b111111110L,
|
||||||
|
0b111111100L,
|
||||||
|
0b111111000L,
|
||||||
|
0b000100000L,
|
||||||
|
0b111101000L,
|
||||||
|
0b000000000L,
|
||||||
|
0b110101000L,
|
||||||
|
0b100101000L,
|
||||||
|
0b000101000L};
|
||||||
|
|
||||||
|
private static final long[] colorTab = {mask(SPADES_INDEX, Rank.COUNT), mask(HEARTS_INDEX, Rank.COUNT), mask(DIAMONDS_INDEX, Rank.COUNT), mask(CLUBS_INDEX, Rank.COUNT)};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if and only if the given {@link PackedCard}set is valid, none of the unused bits are equal to 1
|
||||||
|
* @param {@link pkCardSet} the packed version of a {@link CardSet} to be checked
|
||||||
|
* @return a boolean whether or not the given {@link pkCardSet} is valid
|
||||||
|
*/
|
||||||
|
public static boolean isValid(long pkCardSet) {
|
||||||
|
return (pkCardSet | ALL_CARDS) == ALL_CARDS
|
||||||
|
&& pkCardSet >= 0
|
||||||
|
&& pkCardSet <= ALL_CARDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the packed version of a {@link CardSet} containing all {@link Card}s of {@link Color} trump above the given {@link packedCard}
|
||||||
|
* @param pkCard the {@link packedCard} version of a {@link Card} you want to find which trumps are above
|
||||||
|
* @return a long which represents the packed version of the {@link CardSet} of trump cards above the given card
|
||||||
|
*/
|
||||||
|
public static long trumpAbove(int pkCard) {
|
||||||
|
assert PackedCard.isValid(pkCard): "Invalid card set in trumpAbove function of pkCardSet";
|
||||||
|
|
||||||
|
return trumpTab[PackedCard.rank(pkCard).ordinal()] << PackedCard.color(pkCard).ordinal() * COLOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the packed version of the {@link CardSet} containing only the given {@link packedCard}
|
||||||
|
* @param pkCard the packed version of the {@link Card} from which the {@link CardSet} is created
|
||||||
|
* @return a long the packed version of a {@link CardSet} containing the given {@link Card}
|
||||||
|
*/
|
||||||
|
public static long singleton(int pkCard) {
|
||||||
|
assert PackedCard.isValid(pkCard): "Invalid card set in singleton function of pkCardSet";
|
||||||
|
|
||||||
|
return 1L << pkCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given {@link pkCardSet} is empty or not (ie contains no {@link Card})
|
||||||
|
* @param pkCardSet a long representing the {@link CardSet} to check
|
||||||
|
* @return a boolean true only if the {@link CardSet} is empty(ie all his bits are null)
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(long pkCardSet) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in isEmpty function of pkCardSet";
|
||||||
|
|
||||||
|
return pkCardSet == EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the size of the given {@link pkCardSet} (ie the number of {@link Card}s it contains)
|
||||||
|
* @param pkCardSet a long representing the packed version of a {@link CardSet}
|
||||||
|
* @return an int the size of the {@link CardSet} (ie the number of {@link Card}s it contains)
|
||||||
|
*/
|
||||||
|
public static int size(long pkCardSet) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in size function of pkCardSet";
|
||||||
|
|
||||||
|
return Long.bitCount(pkCardSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the packed version of the {@link Card} of the given index in the given {@link CardSet}
|
||||||
|
* @param pkCardSet a long representing the {@link pkCardSet} to get the {@link Card} from
|
||||||
|
* @param index an int, the index of the {@link Card} to extract from the {@link CardSet}
|
||||||
|
* @return an int representing the packed version of a {@link Card}
|
||||||
|
*/
|
||||||
|
public static int get(long pkCardSet, int index) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in get function of pkCardSet";
|
||||||
|
assert (index < size(pkCardSet) && index >= 0): "Invalid index in get function of pkCardSet";
|
||||||
|
|
||||||
|
for (int i = 1; i <= index; i++)
|
||||||
|
pkCardSet = pkCardSet ^ Long.lowestOneBit(pkCardSet);
|
||||||
|
|
||||||
|
return Long.numberOfTrailingZeros(pkCardSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a {@link packedCard} to the packed version of a {@link CardSet}
|
||||||
|
* @param pkCardSet a long representing the packed version of a {@link CardSet}
|
||||||
|
* @param pkCard an int representing the packed version of a {@link Card}
|
||||||
|
* @return a long the {@link Card} set after the given {@link Card} has been added
|
||||||
|
*/
|
||||||
|
public static long add(long pkCardSet, int pkCard) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in add function of pkCardSet";
|
||||||
|
assert PackedCard.isValid(pkCard): "Invalid card in add function of pkCardSet";
|
||||||
|
|
||||||
|
return pkCardSet | singleton(pkCard) ;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a given {@link packedCard} from a {@link pkCardSet}
|
||||||
|
* @param pkCardSet a long representing the packed version of a {@link CardSet}
|
||||||
|
* @param pkCard an int representing a {@link packedCard}
|
||||||
|
* @return a long the {@link CardSet} after the given {@link Card} has been removed
|
||||||
|
*/
|
||||||
|
public static long remove(long pkCardSet, int pkCard) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in remove function of pkCardSet";
|
||||||
|
assert PackedCard.isValid(pkCard): "Invalid card in remove function of pkCardSet";
|
||||||
|
|
||||||
|
return pkCardSet & ~singleton(pkCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given {@link packedCard} is in the given {@link pkCardSet}
|
||||||
|
* @param pkCardSet a long representing a {@link pkCardSet}
|
||||||
|
* @param pkCard an int representing a {@link packedCard}
|
||||||
|
* @return a boolean true if the given card is in the given {@link CardSet}
|
||||||
|
*/
|
||||||
|
public static boolean contains(long pkCardSet, int pkCard) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in contains function of pkCardSet";
|
||||||
|
assert PackedCard.isValid(pkCard): "Invalid card in contains function of pkCardSet";
|
||||||
|
|
||||||
|
return (pkCardSet & singleton(pkCard)) != EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the inverse of the given p{@link pkCardSet} meaning all the {@link Card}s that are not in the given {@link CardSet}
|
||||||
|
* @param pkCardSet a long representing a {@link pkCardSet}
|
||||||
|
* @return a long representing the packed version of the {@link CardSet} containing all the {@link Card}s that were not in the given {@link CardSet}
|
||||||
|
*/
|
||||||
|
public static long complement(long pkCardSet) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in complement function of pkCardSet";
|
||||||
|
|
||||||
|
return pkCardSet ^ ALL_CARDS ;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the union of the given {@link CardSet}s meaning the {@link CardSet} which contains all the {@link Card}s from both {@link CardSet}s
|
||||||
|
* @param pkCardSet1 a long representing the packed version of a {@link CardSet}
|
||||||
|
* @param pkCardSet2 a long representing the packed version of a {@link CardSet}
|
||||||
|
* @return a long which represents a set containing all the {@link Card}s for both {@link CardSet}s
|
||||||
|
*/
|
||||||
|
public static long union(long pkCardSet1, long pkCardSet2) {
|
||||||
|
assert isValid(pkCardSet1): "Invalid card set 1 in union function of pkCardSet";
|
||||||
|
assert isValid(pkCardSet2): "Invalid card set 2 in union function of pkCardSet";
|
||||||
|
|
||||||
|
return pkCardSet1 | pkCardSet2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the intersection of the given {@link CardSet}s meaning the {@link CardSet} which contains the {@link Card}s that are in both {@link CardSet}s
|
||||||
|
* @param pkCardSet1 a long representing the packed version of a {@link CardSet}
|
||||||
|
* @param pkCardSet2 a long representing the packed version of a {@link CardSet}
|
||||||
|
* @return a long which represents a {@link CardSet} containing the {@link Card}s that are in both {@link CardSet}s
|
||||||
|
*/
|
||||||
|
public static long intersection(long pkCardSet1, long pkCardSet2) {
|
||||||
|
assert isValid(pkCardSet1): "Invalid card set 1 in intersection function of pkCardSet";
|
||||||
|
assert isValid(pkCardSet2): "Invalid card set 2 in intersection function of pkCardSet";
|
||||||
|
|
||||||
|
return pkCardSet1 & pkCardSet2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the difference between the two given {@link CardSet}s meaning the {@link CardSet} which contains the {@link Card}s that are in the first but not in the second one
|
||||||
|
* @param pkCardSet1 a long representing the packed version of a {@link CardSet}
|
||||||
|
* @param pkCardSet2 a long representing the packed version of a {@link CardSet}
|
||||||
|
* @return a long which represents a {@link CardSet} containing the {@link Card}s that are in the first but not in the second one
|
||||||
|
*/
|
||||||
|
public static long difference(long pkCardSet1, long pkCardSet2) {
|
||||||
|
assert isValid(pkCardSet1): "Invalid card set 1 in difference function of pkCardSet";
|
||||||
|
assert isValid(pkCardSet2): "Invalid card set 2 in difference function of pkCardSet";
|
||||||
|
|
||||||
|
return pkCardSet1 & complement(pkCardSet2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the packed version of a {@link CardSet} containing only the {@link Card}s of the given {@link Color}.
|
||||||
|
* @param pkCardSet a long representing the packed version of a {@link CardSet}
|
||||||
|
* @param color the {@link Color} of the wanted subset of the {@link CardSet}.
|
||||||
|
* @return a long the packed version of a {@link CardSet} containing only the {@link Card}s of the given {@link Color}.
|
||||||
|
*/
|
||||||
|
public static long subsetOfColor(long pkCardSet, Card.Color color) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in subsetOfColor function of pkCardSet";
|
||||||
|
|
||||||
|
return pkCardSet & colorTab[color.ordinal()] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a textual representation of a set of cards
|
||||||
|
* @param pkCardSet a long representing the packed version of a set of cards
|
||||||
|
* @return a string describing the given packed set of cards
|
||||||
|
*/
|
||||||
|
public static String toString(long pkCardSet) {
|
||||||
|
assert isValid(pkCardSet): "Invalid card set in toString function of pkCardSet";
|
||||||
|
|
||||||
|
StringJoiner sJ = new StringJoiner(",", "{", "}");
|
||||||
|
|
||||||
|
for(int i = 0; i < size(pkCardSet); ++i)
|
||||||
|
sJ.add(PackedCard.toString(get(pkCardSet, i)));
|
||||||
|
|
||||||
|
return sJ.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
206
jass/PackedScore.java
Normal file
206
jass/PackedScore.java
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import static ch.epfl.javass.Preconditions.checkArgument;
|
||||||
|
import ch.epfl.javass.bits.Bits32;
|
||||||
|
import ch.epfl.javass.bits.Bits64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of a score of the game of Jass in a packed form (as a long).
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class PackedScore {
|
||||||
|
private PackedScore() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A long representing the initial score.
|
||||||
|
*/
|
||||||
|
public static final long INITIAL = 0b000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packs the six components of a {@link Score} in one long.
|
||||||
|
* @param turnTricks1 number of tricks gained by team1 during the current turn.
|
||||||
|
* @param turnPoints1 number of points gained by team1 during the current turn.
|
||||||
|
* @param gamePoints1 number of points gained by team1 during the previous turns (excluding the current turn).
|
||||||
|
* @param turnTricks2 number of tricks gained by team2 during the current turn.
|
||||||
|
* @param turnPoints2 number of points gained by team2 during the current turn.
|
||||||
|
* @param gamePoints2 number of points gained by team2 during the previous turns (excluding the current turn).
|
||||||
|
* @throws IllegalArgumentException if turnTricks1,turnPoints1,gamePoints1 && turnTricks2,turnPoints2,gamePoints2 are non valid.
|
||||||
|
* @return a long the packed version of the scores of both team.
|
||||||
|
*/
|
||||||
|
public static long pack(int turnTricks1, int turnPoints1, int gamePoints1, int turnTricks2, int turnPoints2, int gamePoints2) {
|
||||||
|
checkArgument( areArgumentsValid(turnTricks1,turnPoints1,gamePoints1) && areArgumentsValid(turnTricks2,turnPoints2,gamePoints2));
|
||||||
|
|
||||||
|
int t1Score = pack32(turnTricks1,turnPoints1,gamePoints1);
|
||||||
|
int t2Score = pack32(turnTricks2, turnPoints2, gamePoints2);
|
||||||
|
|
||||||
|
return Bits64.pack(t1Score, Integer.SIZE, t2Score, Integer.SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int TRICK_COUNT_INDEX = 0;
|
||||||
|
private static final int TURN_PTS_INDEX = 4;
|
||||||
|
private static final int GAME_PTS_INDEX = 13;
|
||||||
|
|
||||||
|
private static final int TRICK_COUNT_SIZE = 4;
|
||||||
|
private static final int TURN_PTS_SIZE = 9;
|
||||||
|
private static final int GAME_PTS_SIZE = 11;
|
||||||
|
private static final int UNUSED_SIZE = 8;
|
||||||
|
|
||||||
|
private static final int INITIAL_TRICK_COUNT = 0;
|
||||||
|
private static final int INITIAL_TURN_POINTS = 0;
|
||||||
|
private static final int MAX_TURN_PTS = 257;
|
||||||
|
private static final int MAX_GAME_PTS = 2000;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the value of the packed score is valid.
|
||||||
|
* @param pkScore a long the packed version of a score.
|
||||||
|
* @return a boolean true if the given packed score is valid.
|
||||||
|
*/
|
||||||
|
public static boolean isValid(long pkScore) {
|
||||||
|
long nbTricks1 = Bits64.extract(pkScore, TRICK_COUNT_INDEX, TRICK_COUNT_SIZE);
|
||||||
|
long nbTricks2 = Bits64.extract(pkScore, Integer.SIZE + TRICK_COUNT_INDEX, TRICK_COUNT_SIZE);
|
||||||
|
|
||||||
|
long turnPts1 = Bits64.extract(pkScore, TURN_PTS_INDEX, TURN_PTS_SIZE);
|
||||||
|
long turnPts2 = Bits64.extract(pkScore, Integer.SIZE + TURN_PTS_INDEX, TURN_PTS_SIZE);
|
||||||
|
|
||||||
|
long gamePts1 = Bits64.extract(pkScore, GAME_PTS_INDEX, GAME_PTS_SIZE);
|
||||||
|
long gamePts2 = Bits64.extract(pkScore, Integer.SIZE + GAME_PTS_INDEX, GAME_PTS_SIZE);
|
||||||
|
|
||||||
|
long unusedBits1 = Bits64.extract(pkScore, GAME_PTS_INDEX + GAME_PTS_SIZE, UNUSED_SIZE);
|
||||||
|
long unusedBits2 = Bits64.extract(pkScore, Integer.SIZE + GAME_PTS_INDEX + GAME_PTS_SIZE, UNUSED_SIZE);
|
||||||
|
|
||||||
|
return ((nbTricks1 <= Jass.TRICKS_PER_TURN)
|
||||||
|
&& (turnPts1 <= MAX_TURN_PTS)
|
||||||
|
&& (gamePts1 <= MAX_GAME_PTS)
|
||||||
|
&& (unusedBits1 == 0)
|
||||||
|
&& (nbTricks2 <= Jass.TRICKS_PER_TURN)
|
||||||
|
&& (turnPts2 <= MAX_TURN_PTS)
|
||||||
|
&& (gamePts2 <= MAX_GAME_PTS)
|
||||||
|
&& (unusedBits2 == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the number of {@link Trick}s made by a given team in the current turn.
|
||||||
|
* @param pkScore a long the packed version of a {@link Score}.
|
||||||
|
* @param t a {@link TeamId} the team of which we want the number of {@link Trick}s.
|
||||||
|
* @return an int the number of {@link Trick}s gained by team t during the current turn.
|
||||||
|
*/
|
||||||
|
public static int turnTricks(long pkScore, TeamId t) {
|
||||||
|
assert isValid(pkScore): "Invalid pkScore in turnTricks function of PackedScore";
|
||||||
|
|
||||||
|
return t == TeamId.TEAM_1 ?
|
||||||
|
(int) Bits64.extract(pkScore, TRICK_COUNT_INDEX, TRICK_COUNT_SIZE) :
|
||||||
|
(int) Bits64.extract(pkScore, Integer.SIZE + TRICK_COUNT_INDEX, TRICK_COUNT_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the number of points made by a given team in the current turn.
|
||||||
|
* @param pkScore a long the packed version of a {@link Score}.
|
||||||
|
* @param t a {@link TeamId} the team of which we want the number of points made durring the turn.
|
||||||
|
* @return an int the number of points gained by team t during the current turn.
|
||||||
|
*/
|
||||||
|
public static int turnPoints(long pkScore, TeamId t) {
|
||||||
|
assert isValid(pkScore): "Invalid pkScore in turnPoints function of PackedScore";
|
||||||
|
|
||||||
|
return t == TeamId.TEAM_1 ?
|
||||||
|
(int) Bits64.extract(pkScore, TURN_PTS_INDEX, TURN_PTS_SIZE) :
|
||||||
|
(int) Bits64.extract(pkScore, Integer.SIZE + TURN_PTS_INDEX, TURN_PTS_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the number of points made by a given team before the current turn.
|
||||||
|
* @param pkScore a long the packed version of a {@link Score} .
|
||||||
|
* @param t a {@link TeamId} the team of which we want the number of points.
|
||||||
|
* @return an int the number of points gained by team t before the current turn.
|
||||||
|
*/
|
||||||
|
public static int gamePoints(long pkScore, TeamId t) {
|
||||||
|
assert isValid(pkScore): "Invalid pkScore in gamePoints function of PackedScore";
|
||||||
|
|
||||||
|
return t == TeamId.TEAM_1 ?
|
||||||
|
(int) Bits64.extract(pkScore, GAME_PTS_INDEX, GAME_PTS_SIZE) :
|
||||||
|
(int) Bits64.extract(pkScore, Integer.SIZE + GAME_PTS_INDEX, GAME_PTS_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the number of points made by a given team in the current turn and before.
|
||||||
|
* @param pkScore a long the packed version of a {@link Score}.
|
||||||
|
* @param t a {@link TeamId} the team of which we want the number of points.
|
||||||
|
* @return an int the number of points gained by team t during the current turn and before.
|
||||||
|
*/
|
||||||
|
public static int totalPoints(long pkScore, TeamId t) {
|
||||||
|
assert isValid(pkScore): "Invalid pkScore in totalPoints function of PackedScore";
|
||||||
|
|
||||||
|
return t == TeamId.TEAM_1 ?
|
||||||
|
gamePoints(pkScore,TeamId.TEAM_1) + turnPoints(pkScore,TeamId.TEAM_1) :
|
||||||
|
gamePoints(pkScore,TeamId.TEAM_2) + turnPoints(pkScore,TeamId.TEAM_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the {@link Score} of the team after a {@link Trick} has been played.
|
||||||
|
* @param pkScore the packed version of a {@link Score}.
|
||||||
|
* @param winningTeam a {@link TeamId} the team that won the {@link Trick}.
|
||||||
|
* @param trickPoints the amount of points the {@link Trick} is worth.
|
||||||
|
* @throws IllegalArgumentException if trickPoints is negative.
|
||||||
|
* @return a long the updated {@link packedScore}.
|
||||||
|
*/
|
||||||
|
public static long withAdditionalTrick(long pkScore, TeamId winningTeam, int trickPoints) {
|
||||||
|
assert isValid(pkScore): "Invalid pkScore in withAdditionalTrick function of PackedScore";
|
||||||
|
checkArgument(trickPoints >=0);
|
||||||
|
|
||||||
|
int winningTurnTricks = turnTricks(pkScore,winningTeam) + 1;
|
||||||
|
int winningTurnPoints = turnPoints(pkScore,winningTeam) + trickPoints;
|
||||||
|
int winningGamePoints = gamePoints(pkScore,winningTeam);
|
||||||
|
|
||||||
|
int otherTurnTricks = turnTricks(pkScore,winningTeam.other());
|
||||||
|
int otherTurnPoints = turnPoints(pkScore,winningTeam.other());
|
||||||
|
int otherGamePoints = gamePoints(pkScore,winningTeam.other());
|
||||||
|
|
||||||
|
if(winningTurnTricks == Jass.TRICKS_PER_TURN)
|
||||||
|
winningTurnPoints += Jass.MATCH_ADDITIONAL_POINTS;
|
||||||
|
|
||||||
|
return winningTeam == TeamId.TEAM_1 ?
|
||||||
|
pack(winningTurnTricks, winningTurnPoints, winningGamePoints, otherTurnTricks, otherTurnPoints, otherGamePoints) :
|
||||||
|
pack(otherTurnTricks, otherTurnPoints, otherGamePoints, winningTurnTricks, winningTurnPoints, winningGamePoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the packed version of the {@link Score} at the beginning of the next turn.
|
||||||
|
* @param pkScore the packed version of the {@link Score}.
|
||||||
|
* @return a long the updated {@link packedScore} for the next turn.
|
||||||
|
*/
|
||||||
|
public static long nextTurn(long pkScore) {
|
||||||
|
assert isValid(pkScore): "Invalid pkScore in nextTurn function of PackedScore";
|
||||||
|
|
||||||
|
return pack(INITIAL_TRICK_COUNT, INITIAL_TURN_POINTS, totalPoints(pkScore,TeamId.TEAM_1),
|
||||||
|
INITIAL_TRICK_COUNT, INITIAL_TURN_POINTS, totalPoints(pkScore,TeamId.TEAM_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a textual representation of the {@link Score}.
|
||||||
|
* @param pkScore a long the packed version of the {@link Score}.
|
||||||
|
* @return a string the representation of a {@link Score}.
|
||||||
|
*/
|
||||||
|
public static String toString(long pkScore) {
|
||||||
|
assert isValid(pkScore): "Invalid pkScore in toString function of PackedScore";
|
||||||
|
|
||||||
|
return ("(" + Integer.toUnsignedString(turnTricks(pkScore,TeamId.TEAM_1)) +
|
||||||
|
"," + Integer.toUnsignedString(turnPoints(pkScore,TeamId.TEAM_1)) +
|
||||||
|
"," + Integer.toUnsignedString(gamePoints(pkScore,TeamId.TEAM_1)) +
|
||||||
|
")/(" + Integer.toUnsignedString(turnTricks(pkScore,TeamId.TEAM_2)) +
|
||||||
|
"," + Integer.toUnsignedString(turnPoints(pkScore,TeamId.TEAM_2)) +
|
||||||
|
"," + Integer.toUnsignedString(gamePoints(pkScore,TeamId.TEAM_2)) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int pack32(int turnTricks, int turnPoints, int gamePoints) {
|
||||||
|
return Bits32.pack(turnTricks, TRICK_COUNT_SIZE, turnPoints, TURN_PTS_SIZE, gamePoints, GAME_PTS_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean areArgumentsValid(int turnTricks, int turnPoints, int gamePoints) {
|
||||||
|
return ((turnTricks >= 0) && (turnTricks <= Jass.TRICKS_PER_TURN) &&
|
||||||
|
(turnPoints >= 0) && (turnPoints <= MAX_TURN_PTS) &&
|
||||||
|
(gamePoints >= 0) && (gamePoints <= MAX_GAME_PTS));
|
||||||
|
}
|
||||||
|
}
|
||||||
357
jass/PackedTrick.java
Normal file
357
jass/PackedTrick.java
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import ch.epfl.javass.jass.Card.Color;
|
||||||
|
import ch.epfl.javass.jass.Card.Rank;
|
||||||
|
import ch.epfl.javass.bits.Bits32;
|
||||||
|
import static ch.epfl.javass.Preconditions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of a trick of a jass game in a packed form that is as an int.
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class PackedTrick {
|
||||||
|
private PackedTrick() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An integer representing an invalid packed {@link Trick} that is an empty {@link Trick}.
|
||||||
|
*/
|
||||||
|
public static final int INVALID = ~0;
|
||||||
|
|
||||||
|
private static final int LAST_CARD_INDEX = 18;
|
||||||
|
|
||||||
|
private static final int CARD_COLOR_INDEX = 4;
|
||||||
|
private static final int CARD_COLOR_SIZE = 2;
|
||||||
|
|
||||||
|
private static final int TRUMP_INDEX = 30;
|
||||||
|
private static final int FIRST_PLAYER_INDEX = 28;
|
||||||
|
|
||||||
|
private static final int TRICK_INDEX_POS = 24;
|
||||||
|
private static final int TRICK_INDEX_SIZE = 4;
|
||||||
|
|
||||||
|
private static final int FIRST_TRICK_INDEX = 0;
|
||||||
|
private static final int LAST_TRICK_INDEX = 8;
|
||||||
|
|
||||||
|
private static final int FIRST_PLAYER_SIZE = 2;
|
||||||
|
private static final int TRUMP_SIZE = 2;
|
||||||
|
private static final int CARD_SIZE = 6;
|
||||||
|
private static final int CARD_NBR = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given {@link PackedTrick} is valid, that is that no card is not invalid while the previous cards are and that it represents a real {@link Trick}.
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @return a boolean whether or not the {@link Trick} is valid.
|
||||||
|
*/
|
||||||
|
public static boolean isValid(int pkTrick) {
|
||||||
|
int trickIndex = Bits32.extract(pkTrick, TRICK_INDEX_POS, TRICK_INDEX_SIZE);
|
||||||
|
|
||||||
|
boolean isValid = true;
|
||||||
|
|
||||||
|
for (int i = 0; i <= LAST_CARD_INDEX; i += CARD_SIZE)
|
||||||
|
if(Bits32.extract(pkTrick, i, CARD_SIZE) == PackedCard.INVALID)
|
||||||
|
for (int j = i; j <= LAST_CARD_INDEX; j += CARD_SIZE)
|
||||||
|
isValid &= Bits32.extract(pkTrick, j, CARD_SIZE) == PackedCard.INVALID;
|
||||||
|
|
||||||
|
return trickIndex < Jass.TRICKS_PER_TURN && isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the first empty {@link Trick}, meaning with no cards, a given trump and a given first player.
|
||||||
|
* @param trump the {@link Color} of the trump for the {@link Trick}.
|
||||||
|
* @param firstPlayer the {@link PlayerId} of the first player of the {@link Trick}.
|
||||||
|
* @return an integer representing the first empty {@link Trick}.
|
||||||
|
*/
|
||||||
|
public static int firstEmpty(Color trump, PlayerId firstPlayer) {
|
||||||
|
return Bits32.pack(PackedCard.INVALID, CARD_SIZE,
|
||||||
|
PackedCard.INVALID, CARD_SIZE,
|
||||||
|
PackedCard.INVALID, CARD_SIZE,
|
||||||
|
PackedCard.INVALID, CARD_SIZE,
|
||||||
|
FIRST_TRICK_INDEX, TRICK_INDEX_SIZE,
|
||||||
|
firstPlayer.ordinal(), FIRST_PLAYER_SIZE,
|
||||||
|
trump.ordinal(), TRUMP_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the next empty {@link Trick}, meaning with the winner of the last {@link Trick} as first player, no {@link Card}, the next index and the same trump.
|
||||||
|
* @param pkTrick an integer representing the packed version of a {@link Trick}.
|
||||||
|
* @return an integer representing a the packed version of the next empty {@link Trick}.
|
||||||
|
*/
|
||||||
|
public static int nextEmpty(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in nextEmpty function of pkTrick";
|
||||||
|
|
||||||
|
return isLast(pkTrick) ?
|
||||||
|
INVALID :
|
||||||
|
Bits32.pack(PackedCard.INVALID, CARD_SIZE,
|
||||||
|
PackedCard.INVALID, CARD_SIZE,
|
||||||
|
PackedCard.INVALID, CARD_SIZE,
|
||||||
|
PackedCard.INVALID, CARD_SIZE,
|
||||||
|
index(pkTrick) + 1, TRICK_INDEX_SIZE,
|
||||||
|
winningPlayer(pkTrick).ordinal(), FIRST_PLAYER_SIZE,
|
||||||
|
trump(pkTrick).ordinal(), TRUMP_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the trick is the last {@link Trick} of the turn meaning that it's the 9th {@link Trick}.
|
||||||
|
* @param pkTrick an integer representing the packed version of a {@link Trick}.
|
||||||
|
* @return a boolean true if the index of the {@link Trick} is equal to the LAST_TRICK_INDEX.
|
||||||
|
*/
|
||||||
|
public static boolean isLast(int pkTrick) {
|
||||||
|
assert isValid(pkTrick) : "Invalid trick in isLast function of pkTrick";
|
||||||
|
|
||||||
|
return index(pkTrick) == LAST_TRICK_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the {@link Trick} contains no card meaning it hasn't started.
|
||||||
|
* @param pkTrick an integer representing the packed version of a {@link Trick}.
|
||||||
|
* @return a boolean true if no {@link Card} is in the {@link Trick}.
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in isEmpty function of pkTrick";
|
||||||
|
|
||||||
|
return Bits32.extract(pkTrick, 0, CARD_SIZE * CARD_NBR) == Bits32.mask(0, CARD_SIZE * CARD_NBR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the {@link Trick} is full meaning that all {@link Card}s have been played.
|
||||||
|
* @param pkTrick an integer representing the packed version of a {@link Trick}.
|
||||||
|
* @return a boolean true if all the cards have been played.
|
||||||
|
*/
|
||||||
|
public static boolean isFull(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in isFull function of pkTrick";
|
||||||
|
|
||||||
|
return size(pkTrick) == TRICK_INDEX_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the size of the {@link Trick}, meaning the number of {@link Card}s that have been played.
|
||||||
|
* @param pkTrick an integer representing the packed version of a {@link Trick}.
|
||||||
|
* @return an integer the number of {@link Card} that have been played.
|
||||||
|
*/
|
||||||
|
public static int size(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in size function of pkTrick";
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for(int i = 0 ; i < PlayerId.COUNT ; ++i)
|
||||||
|
if(card(pkTrick, i) != PackedCard.INVALID)
|
||||||
|
++ count;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the {@link PlayerId} that played at a given index in the given packed {@link Trick}.
|
||||||
|
* @param pkTrick an integer representing the packed version of a {@link Trick}.
|
||||||
|
* @param index an integer the index of the {@link Card} played by the wanted player.
|
||||||
|
* @throws IndexOutOfBoundsException if the index is bigger than the PlayerId.COUNT or negative
|
||||||
|
* @return the {@link PlayerId} that played the {@link Card} at the given index.
|
||||||
|
*/
|
||||||
|
public static PlayerId player(int pkTrick, int index) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in player function of pkTrick";
|
||||||
|
|
||||||
|
int i = checkIndex(index, PlayerId.COUNT);
|
||||||
|
return PlayerId.ALL.get((firstPlayerIndex(pkTrick) + i) % PlayerId.COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the trump {@link Color} of the {@link Trick}.
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @return the trump {@link Color} of the {@link Trick}.
|
||||||
|
*/
|
||||||
|
public static Color trump(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in trump function of pkTrick";
|
||||||
|
|
||||||
|
return Color.ALL.get(Bits32.extract(pkTrick, TRUMP_INDEX, TRUMP_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the index of the {@link Trick}.
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @return an int the index of the given packed trick.
|
||||||
|
*/
|
||||||
|
public static int index(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in index function of pkTrick";
|
||||||
|
|
||||||
|
return Bits32.extract(pkTrick, TRICK_INDEX_POS, TRICK_INDEX_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the packed version of the index's {@link Card} in the packedTrick.
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @param index an int the index of the wanted card.
|
||||||
|
* @return the packed version of the index's {@link Card}.
|
||||||
|
*/
|
||||||
|
public static int card(int pkTrick, int index) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in card function of pkTrick";
|
||||||
|
assert (index >= 0 && index <= CARD_NBR): "Invalid index in card function of pkTrick";
|
||||||
|
|
||||||
|
return Bits32.extract(pkTrick, index * CARD_SIZE, CARD_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that gives the same {@link Trick} that the one given (supposed non empty) with the pkCard added.
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @param pkCard an int the packed representation of a card.
|
||||||
|
* @return pkTrick with pkCard that has been added.
|
||||||
|
*/
|
||||||
|
public static int withAddedCard(int pkTrick, int pkCard) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in withAddedCard function of pkTrick";
|
||||||
|
|
||||||
|
int start = size(pkTrick) * CARD_SIZE;
|
||||||
|
|
||||||
|
return pkTrick & ~Bits32.mask(start, CARD_SIZE) | pkCard << start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the initial {@link Color} of the {@link Trick} (the {@link Color} of the first {@link Card}.
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @return the {@link Trick}'s base's {@link Color}.
|
||||||
|
*/
|
||||||
|
public static Color baseColor(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in baseColor function of pkTrick";
|
||||||
|
|
||||||
|
return Color.ALL.get(Bits32.extract(pkTrick, CARD_COLOR_INDEX, CARD_COLOR_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the subset in a packed version of the {@link Card} of pkHand which can be played as the next card of the {@link Trick} pkTrick (supposed non empty).
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @param pkHand a long representing the packed version of a card set.
|
||||||
|
* @return a pkCardSet, subset of pkHand.
|
||||||
|
*/
|
||||||
|
public static long playableCards(int pkTrick, long pkHand) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in playableCards function of pkTrick";
|
||||||
|
assert PackedCardSet.isValid(pkHand): "Invalid hand in playableCards function of pkTrick";
|
||||||
|
|
||||||
|
long trumpsAbove = PackedCardSet.EMPTY;
|
||||||
|
long trumps = PackedCardSet.subsetOfColor(pkHand, trump(pkTrick));
|
||||||
|
long baseColorCards = PackedCardSet.subsetOfColor(pkHand, baseColor(pkTrick));
|
||||||
|
long allButTrumps = PackedCardSet.difference(pkHand, trumps);
|
||||||
|
long jackSingleton = PackedCardSet
|
||||||
|
.singleton(PackedCard
|
||||||
|
.pack(trump(pkTrick), Rank.JACK));
|
||||||
|
|
||||||
|
boolean hasTrumpAbove = false;
|
||||||
|
boolean isTrump = false;
|
||||||
|
boolean hasNoBaseColor = PackedCardSet.isEmpty(PackedCardSet.intersection(baseColorCards, pkHand));
|
||||||
|
boolean hasNoTrump = PackedCardSet.isEmpty(PackedCardSet.intersection(trumps, pkHand));
|
||||||
|
|
||||||
|
int winningCard = card(pkTrick, winningCardIndex(pkTrick));
|
||||||
|
|
||||||
|
if(isEmpty(pkTrick))
|
||||||
|
return pkHand;
|
||||||
|
|
||||||
|
//Computes the trump cards of the hand that are better than the current winning card and puts them in the packed card set trumpsAbove.
|
||||||
|
for (int i = 0; i < PackedCardSet.size(trumps); i++)
|
||||||
|
if(PackedCard.isBetter(trump(pkTrick), PackedCardSet.get(trumps,i), winningCard)) {
|
||||||
|
trumpsAbove = PackedCardSet.add(trumpsAbove, PackedCardSet.get(trumps,i));
|
||||||
|
hasTrumpAbove = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Checks if any player has played a trump color card and puts the result in the boolean isTrump.
|
||||||
|
for (int j = 0; j < size(pkTrick); j++)
|
||||||
|
if(PackedCard.color(card(pkTrick, j)).equals(trump(pkTrick)))
|
||||||
|
isTrump = true;
|
||||||
|
|
||||||
|
//Returns the entire hand if it contains no base color cards and no trump or if the base color is trump and the hand contains the jack of trump.
|
||||||
|
if(((!isTrump || hasNoTrump) && hasNoBaseColor) ||
|
||||||
|
(baseColor(pkTrick).equals(trump(pkTrick))
|
||||||
|
&& PackedCardSet.isEmpty(PackedCardSet.difference(trumps, jackSingleton))))
|
||||||
|
return pkHand;
|
||||||
|
|
||||||
|
if(baseColor(pkTrick) != trump(pkTrick))
|
||||||
|
if(hasNoBaseColor && !PackedCardSet.isEmpty(allButTrumps))
|
||||||
|
return PackedCardSet.union(allButTrumps, trumpsAbove);
|
||||||
|
else if(!hasNoBaseColor)
|
||||||
|
return PackedCardSet.union(baseColorCards, trumpsAbove);
|
||||||
|
|
||||||
|
if((baseColor(pkTrick).equals(trump(pkTrick))) ||
|
||||||
|
(hasNoBaseColor && !hasTrumpAbove && PackedCardSet.isEmpty(allButTrumps)))
|
||||||
|
return trumps;
|
||||||
|
|
||||||
|
if(hasNoBaseColor && !hasTrumpAbove && !PackedCardSet.isEmpty(allButTrumps))
|
||||||
|
return allButTrumps;
|
||||||
|
|
||||||
|
if(hasNoBaseColor && hasTrumpAbove)
|
||||||
|
return trumpsAbove;
|
||||||
|
|
||||||
|
return pkHand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the value of the {@link Trick}.
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @return the integer value of the {@link Trick}.
|
||||||
|
*/
|
||||||
|
public static int points(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in points function of pkTrick";
|
||||||
|
|
||||||
|
int pts = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < CARD_NBR; i++)
|
||||||
|
if(card(pkTrick, i) != PackedCard.INVALID)
|
||||||
|
pts += PackedCard.points(trump(pkTrick), card(pkTrick, i));
|
||||||
|
|
||||||
|
if(isLast(pkTrick))
|
||||||
|
pts += Jass.LAST_TRICK_ADDITIONAL_POINTS;
|
||||||
|
|
||||||
|
return pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that gives the identity of the current winning {@link PlayerId} of the {@link Trick} (supposed non empty).
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @return the {@link PlayerId} that is winning the current {@link Trick}.
|
||||||
|
*/
|
||||||
|
public static PlayerId winningPlayer(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in winningPlayer function of pkTrick";
|
||||||
|
|
||||||
|
return player(pkTrick, winningCardIndex(pkTrick));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a textual representation of a given trick.
|
||||||
|
* @param pkTrick an integer representing a packed {@link Trick}.
|
||||||
|
* @return a String representing the given trick.
|
||||||
|
*/
|
||||||
|
public static String toString(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in toString function of pkTrick";
|
||||||
|
|
||||||
|
StringBuilder s = new StringBuilder("Pli ")
|
||||||
|
.append(index(pkTrick))
|
||||||
|
.append(", commence par ")
|
||||||
|
.append(player(pkTrick, 0).toString())
|
||||||
|
.append(" : ");
|
||||||
|
|
||||||
|
for (int i = 0; i < size(pkTrick); i++)
|
||||||
|
if(i == 0)
|
||||||
|
s.append(PackedCard.toString(card(pkTrick, i)));
|
||||||
|
else
|
||||||
|
s.append(", ")
|
||||||
|
.append(PackedCard.toString(card(pkTrick, i)));
|
||||||
|
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int winningCardIndex(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in winningCardIndex function of pkTrick";
|
||||||
|
|
||||||
|
int winningCardIndex = 0;
|
||||||
|
|
||||||
|
//Compares all the cards of the trick to find the index of the one better that all the others.
|
||||||
|
for(int i=0 ; i < size(pkTrick)-1 ; ++i)
|
||||||
|
if(PackedCard.isBetter(trump(pkTrick), card(pkTrick, i+1), card(pkTrick, winningCardIndex)))
|
||||||
|
winningCardIndex = (i+1);
|
||||||
|
|
||||||
|
return winningCardIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int firstPlayerIndex(int pkTrick) {
|
||||||
|
assert isValid(pkTrick): "Invalid trick in firstPlayerIndex function of pkTrick";
|
||||||
|
|
||||||
|
return Bits32.extract(pkTrick, FIRST_PLAYER_INDEX, FIRST_PLAYER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
67
jass/Player.java
Normal file
67
jass/Player.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ch.epfl.javass.jass.Card.Color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface that represent a player of a game of Jass.
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface Player {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to choose which card to play
|
||||||
|
* @param state the current TurnState of the game
|
||||||
|
* @param hand the current hand of the Player
|
||||||
|
* @return the card to be played
|
||||||
|
*/
|
||||||
|
abstract Card cardToPlay(TurnState state, CardSet hand);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates the Players with their name
|
||||||
|
* @param ownId the iD of the Player
|
||||||
|
* @param playerNames a map that associates the PlayerId with the names of the players
|
||||||
|
*/
|
||||||
|
default void setPlayers(PlayerId ownId, Map<PlayerId, String>playerNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the hand of the player with the new current hand
|
||||||
|
* @param newHand
|
||||||
|
*/
|
||||||
|
default void updateHand(CardSet newHand) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the trump color of the player with the new current trump
|
||||||
|
* @param trump
|
||||||
|
*/
|
||||||
|
default void setTrump(Color trump) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the trick of the player with the new current trick
|
||||||
|
* @param newTrick
|
||||||
|
*/
|
||||||
|
default void updateTrick(Trick newTrick) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the score of the player with the new current score
|
||||||
|
* @param score
|
||||||
|
*/
|
||||||
|
default void updateScore(Score score) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the winning of the game
|
||||||
|
* @param winningTeam the team that won the game
|
||||||
|
*/
|
||||||
|
default void setWinningTeam(TeamId winningTeam) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
37
jass/PlayerId.java
Normal file
37
jass/PlayerId.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum PlayerId {
|
||||||
|
PLAYER_1,
|
||||||
|
PLAYER_2,
|
||||||
|
PLAYER_3,
|
||||||
|
PLAYER_4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of players.
|
||||||
|
*/
|
||||||
|
public static final int COUNT = 4;
|
||||||
|
/**
|
||||||
|
* The list of all players
|
||||||
|
*/
|
||||||
|
public static final List<PlayerId> ALL = Collections.unmodifiableList(Arrays.asList(values()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the team of the player
|
||||||
|
* @return a TeamId the team of the player
|
||||||
|
*/
|
||||||
|
public TeamId team() {
|
||||||
|
return this.equals(PLAYER_1) || this.equals(PLAYER_3) ?
|
||||||
|
TeamId.TEAM_1 :
|
||||||
|
TeamId.TEAM_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
114
jass/Score.java
Normal file
114
jass/Score.java
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import static ch.epfl.javass.Preconditions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class Score {
|
||||||
|
|
||||||
|
public static final Score INITIAL = new Score(PackedScore.INITIAL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the packed score is valid.
|
||||||
|
* @param a long packed the packed score of the team
|
||||||
|
* @throws IllegalArgumentException if packed is not valid.
|
||||||
|
* @return the score associated with packed.
|
||||||
|
*/
|
||||||
|
public static Score ofPacked(long packed) {
|
||||||
|
checkArgument(PackedScore.isValid(packed));
|
||||||
|
Score s = new Score(packed);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final long packed;
|
||||||
|
|
||||||
|
private Score(long packed) {
|
||||||
|
this.packed = packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the packed version of the Score.
|
||||||
|
* @return a long representing the packed version of the score.
|
||||||
|
*/
|
||||||
|
public long packed() {
|
||||||
|
return packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the number of tricks made by a given team in the current turn.
|
||||||
|
* @param t the teamId for the wanted number of tricks.
|
||||||
|
* @return an int the number of tricks gained by the team during the receiver's current turn.
|
||||||
|
*/
|
||||||
|
public int turnTricks(TeamId t) {
|
||||||
|
return PackedScore.turnTricks(packed, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the number of points made by a given team in the current turn.
|
||||||
|
* @param t the team.
|
||||||
|
* @return an int the number of points gained by the team during the receiver's current turn.
|
||||||
|
*/
|
||||||
|
public int turnPoints(TeamId t) {
|
||||||
|
return PackedScore.turnPoints(packed, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the number of points made by a given team before the current turn.
|
||||||
|
* @param t the team.
|
||||||
|
* @return an int the number of points gained by the team during the receiver's previous turns (excluding the current one).
|
||||||
|
*/
|
||||||
|
public int gamePoints(TeamId t) {
|
||||||
|
return PackedScore.gamePoints(packed, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the number of points made by a given team in the current turn and before.
|
||||||
|
* @param t the team.
|
||||||
|
* @return an int the total number of points gained by the team t during the receiver's current game.
|
||||||
|
*/
|
||||||
|
public int totalPoints(TeamId t) {
|
||||||
|
return PackedScore.totalPoints(packed, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the {@link Score} of the team after a {@link Trick} has been played.
|
||||||
|
* @param winningTeam the teamId of the team that won the trick.
|
||||||
|
* @param trickPoints number of tricks gained by the team during the current turn.
|
||||||
|
* @throws IllegalArgumentException if trickPoints is negative.
|
||||||
|
* @return the updated scores.
|
||||||
|
*/
|
||||||
|
public Score withAdditionalTrick(TeamId winningTeam, int trickPoints) {
|
||||||
|
checkArgument(trickPoints>=0);
|
||||||
|
|
||||||
|
return new Score(PackedScore.withAdditionalTrick(packed, winningTeam, trickPoints));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the {@link Score} at the beginning of the next turn.
|
||||||
|
* @return the updated scores for the next turn
|
||||||
|
*/
|
||||||
|
public Score nextTurn() {
|
||||||
|
return new Score(PackedScore.nextTurn(packed));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object that0) {
|
||||||
|
return that0 != null
|
||||||
|
&& that0.getClass() == this.getClass()
|
||||||
|
&& this.packed() == ((Score)that0).packed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Long.hashCode(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return PackedScore.toString(packed);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
jass/TeamId.java
Normal file
32
jass/TeamId.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public enum TeamId {
|
||||||
|
TEAM_1,
|
||||||
|
TEAM_2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of teams.
|
||||||
|
*/
|
||||||
|
public static final int COUNT = 2;
|
||||||
|
/**
|
||||||
|
* The list of all teams.
|
||||||
|
*/
|
||||||
|
public static final List<TeamId> ALL = Collections.unmodifiableList(Arrays.asList(values()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the other team.
|
||||||
|
* @return a TeamId the opposing team.
|
||||||
|
*/
|
||||||
|
public TeamId other() {
|
||||||
|
return this.equals(TEAM_1) ? TEAM_2 : TEAM_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
207
jass/Trick.java
Normal file
207
jass/Trick.java
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import static ch.epfl.javass.Preconditions.*;
|
||||||
|
|
||||||
|
import ch.epfl.javass.jass.Card.Color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of a trick of a jass game.
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class Trick {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The trick representing an invalid trick that is the representation of an empty trick.
|
||||||
|
*/
|
||||||
|
public final static Trick INVALID = new Trick(PackedTrick.INVALID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gives a brand new empty trick associated with the first player.
|
||||||
|
* @param trump
|
||||||
|
* @param firstPlayer
|
||||||
|
* @return a new empty trick.
|
||||||
|
*/
|
||||||
|
public static Trick firstEmpty(Color trump, PlayerId firstPlayer) {
|
||||||
|
return new Trick(PackedTrick.firstEmpty(trump, firstPlayer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gives the trick based on it packed version.
|
||||||
|
* @param packed
|
||||||
|
* @throws IllegalArgumentException if packed is not valid
|
||||||
|
* @return a the trick of the packed version.
|
||||||
|
*/
|
||||||
|
public static Trick ofPacked(int packed) {
|
||||||
|
checkArgument(PackedTrick.isValid(packed));
|
||||||
|
|
||||||
|
return new Trick(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int packed;
|
||||||
|
|
||||||
|
private Trick(int packed) {
|
||||||
|
this.packed = packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gives the packed version of a trick.
|
||||||
|
* @return packed.
|
||||||
|
*/
|
||||||
|
public int packed() {
|
||||||
|
return packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The empty trick that follows (this). It means the trick empty has the same trump color, the following index of (this)
|
||||||
|
and the first player winner of (this).
|
||||||
|
* @throws IllegalStateException if packed is the last trick.
|
||||||
|
* @return an empty trick that follows (this).
|
||||||
|
*/
|
||||||
|
public Trick nextEmpty() {
|
||||||
|
if (!isFull())
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
return new Trick(PackedTrick.nextEmpty(packed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the trick is empty meaning no card have been played.
|
||||||
|
* @return true if (this) is empty.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return PackedTrick.isEmpty(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the trick is full meaning all cards have been played.
|
||||||
|
* @return true if (this) is full.
|
||||||
|
*/
|
||||||
|
public boolean isFull() {
|
||||||
|
return PackedTrick.isFull(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current trick is the last one of the turn
|
||||||
|
* @return true if (this) is the last trick.
|
||||||
|
*/
|
||||||
|
public boolean isLast() {
|
||||||
|
return PackedTrick.isLast(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the size of the trick i.e the number of cards that have been played.
|
||||||
|
* @return an int the size of the trick.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return PackedTrick.size(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the trump color of the trick.
|
||||||
|
* @return a Color the trump color of the trick.
|
||||||
|
*/
|
||||||
|
public Color trump() {
|
||||||
|
return PackedTrick.trump(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the index of the trick in the current turn.
|
||||||
|
* @return an int the index of the trick.
|
||||||
|
*/
|
||||||
|
public int index() {
|
||||||
|
return PackedTrick.index(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gives the player at the given index.
|
||||||
|
* @param index an int.
|
||||||
|
* @throws IndexOutOfBoundsException if the index is invalid.
|
||||||
|
* @return the player of the trick at the given index.
|
||||||
|
*/
|
||||||
|
public PlayerId player(int index) {
|
||||||
|
return PackedTrick.player(packed, checkIndex(index, PlayerId.COUNT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the card of the trick at the given index.
|
||||||
|
* @param index an int.
|
||||||
|
* @return the card of the trick at the given index.
|
||||||
|
*/
|
||||||
|
public Card card(int index) {
|
||||||
|
return Card.ofPacked(PackedTrick.card(packed, checkIndex(index, size())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gives a trick identical as (this) plus the card c.
|
||||||
|
* @param c a card to be added to the trick.
|
||||||
|
* @throws IllegalStateException if packed is full.
|
||||||
|
* @return (this) plus the card c.
|
||||||
|
*/
|
||||||
|
public Trick withAddedCard(Card c) {
|
||||||
|
if (isFull())
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
return new Trick(PackedTrick.withAddedCard(packed, c.packed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gives the original color of the trick (the color of the first card played).
|
||||||
|
* @throws IllegalStateException if packed is empty.
|
||||||
|
* @return the color of the first card played.
|
||||||
|
*/
|
||||||
|
public Color baseColor() {
|
||||||
|
if (isEmpty())
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
return PackedTrick.baseColor(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gives a subset of hand representing all the cards that can be played during the next trick.
|
||||||
|
* @param hand the cardset to check the playable cards in it.
|
||||||
|
* @throws IllegalStateException if packed is full.
|
||||||
|
* @return a subset of hand representing all the cards that can be played by the player during the next trick.
|
||||||
|
*/
|
||||||
|
public CardSet playableCards(CardSet hand) {
|
||||||
|
if (isFull())
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
return CardSet
|
||||||
|
.ofPacked(PackedTrick.playableCards(packed, hand.packed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the number of points the trick is worth.
|
||||||
|
* @return an int the number of points the trick is worth.
|
||||||
|
*/
|
||||||
|
public int points() {
|
||||||
|
return PackedTrick.points(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the winning player of the trick
|
||||||
|
* @return the PlayerId of the player winning the trick.
|
||||||
|
*/
|
||||||
|
public PlayerId winningPlayer() {
|
||||||
|
return PackedTrick.winningPlayer(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return PackedTrick.toString(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object that0) {
|
||||||
|
return that0 != null && that0.getClass() == this.getClass()
|
||||||
|
&& this.packed() == ((Trick) that0).packed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Integer.hashCode(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
162
jass/TurnState.java
Normal file
162
jass/TurnState.java
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package ch.epfl.javass.jass;
|
||||||
|
|
||||||
|
import ch.epfl.javass.jass.Card.Color;
|
||||||
|
|
||||||
|
import static ch.epfl.javass.Preconditions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The representation of the state of a turn during a game of Jass.
|
||||||
|
* @author Charles BEAUVILLE
|
||||||
|
* @author Celia HOUSSIAUX
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class TurnState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the initial state of a turn
|
||||||
|
* @param trump the trump color of the turn
|
||||||
|
* @param score the score at the beginning of the turn
|
||||||
|
* @param firstPlayer the first player of the turn
|
||||||
|
* @return the initial state of the turn
|
||||||
|
*/
|
||||||
|
public static TurnState initial(Color trump, Score score, PlayerId firstPlayer) {
|
||||||
|
long aS = score.packed();
|
||||||
|
long uC = PackedCardSet.ALL_CARDS;
|
||||||
|
int aT = PackedTrick.firstEmpty(trump, firstPlayer);
|
||||||
|
return new TurnState(aS, uC, aT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new turnstate given a packed score, a packed set of unplayed cards and a packed trick.
|
||||||
|
* @param pkScore the packed version of the score the turn will have.
|
||||||
|
* @param pkUnplayedCards the packed version of the set of cards that have not yet been played during the turn.
|
||||||
|
* @param pkTrick the packed version of the trick the turn will have
|
||||||
|
* @throws IllegalArgumentException if pkScore, pkUnplayedCards and pkTrick are not valid.
|
||||||
|
* @return the new turn state with the new score, set of unplayed cards and trick.
|
||||||
|
*/
|
||||||
|
public static TurnState ofPackedComponents(long pkScore, long pkUnplayedCards, int pkTrick) {
|
||||||
|
checkArgument(PackedScore.isValid(pkScore) && PackedCardSet.isValid(pkUnplayedCards) && PackedTrick.isValid(pkTrick));
|
||||||
|
|
||||||
|
return new TurnState(pkScore, pkUnplayedCards, pkTrick);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final long currentScore;
|
||||||
|
private final long unplayedCards;
|
||||||
|
private final int currentTrick;
|
||||||
|
|
||||||
|
private TurnState(long aS, long uC, int aT){
|
||||||
|
currentScore = aS;
|
||||||
|
unplayedCards = uC;
|
||||||
|
currentTrick = aT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the packed version of the current score of the turn.
|
||||||
|
* @return a long the packed version of the score of the turn.
|
||||||
|
*/
|
||||||
|
public long packedScore() {
|
||||||
|
return currentScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the packed version of the current unplayed cards of the turn.
|
||||||
|
* @return a long the packed version of the set of unplayed cards of the turn.
|
||||||
|
*/
|
||||||
|
public long packedUnplayedCards() {
|
||||||
|
return unplayedCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the packed version of the current trick of the turn.
|
||||||
|
* @return a long the packed version of the trick of the turn.
|
||||||
|
*/
|
||||||
|
public int packedTrick() {
|
||||||
|
return currentTrick;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current score of the turn.
|
||||||
|
* @return the score of the turn.
|
||||||
|
*/
|
||||||
|
public Score score() {
|
||||||
|
return Score.ofPacked(currentScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current CardSet of unplayed cards of the turn.
|
||||||
|
* @return the CardSet of unplayed cards of the turn.
|
||||||
|
*/
|
||||||
|
public CardSet unplayedCards() {
|
||||||
|
return CardSet.ofPacked(unplayedCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current trick of the turn.
|
||||||
|
* @return the trick of the turn.
|
||||||
|
*/
|
||||||
|
public Trick trick() {
|
||||||
|
return Trick.ofPacked(currentTrick);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the trick is the last one before the end of the turn.
|
||||||
|
* @return a boolean if the trick is the last one.
|
||||||
|
*/
|
||||||
|
public boolean isTerminal() {
|
||||||
|
return currentTrick == PackedTrick.INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next player that will play in the turn.
|
||||||
|
* @throws IllegalStateException if the current trick is full.
|
||||||
|
* @return the PlayerId of the next player of the trick.
|
||||||
|
*/
|
||||||
|
public PlayerId nextPlayer() {
|
||||||
|
if(trick().isFull())
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
return PackedTrick.player(currentTrick, PackedTrick.size(currentTrick));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the new turnState when a given card has been played
|
||||||
|
* @param card the card to be played
|
||||||
|
* @throws IllegalStateException if the trick is full.
|
||||||
|
* @return the new turn state with the card played
|
||||||
|
*/
|
||||||
|
public TurnState withNewCardPlayed(Card card) {
|
||||||
|
if(trick().isFull() && unplayedCards().contains(card))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
return ofPackedComponents(currentScore, PackedCardSet.remove(unplayedCards, card.packed()), PackedTrick.withAddedCard(currentTrick, card.packed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a new turn state after the trick has been collected, with updated score and a new trick.
|
||||||
|
* @throws IllegalStateException the trick is not full.
|
||||||
|
* @return the new turn state with the next trick and the updated score
|
||||||
|
*/
|
||||||
|
public TurnState withTrickCollected() {
|
||||||
|
if(!trick().isFull())
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
return new TurnState(
|
||||||
|
PackedScore.withAdditionalTrick(currentScore, PackedTrick.winningPlayer(currentTrick).team(), PackedTrick.points(currentTrick)),
|
||||||
|
unplayedCards,
|
||||||
|
PackedTrick.nextEmpty(currentTrick));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a new turn state with a new card and collects the trick if it is full
|
||||||
|
* @param card the card to be played
|
||||||
|
* @return the new turn state when the given card is played or the trick is full
|
||||||
|
*/
|
||||||
|
public TurnState withNewCardPlayedAndTrickCollected(Card card) {
|
||||||
|
TurnState t = withNewCardPlayed(card);
|
||||||
|
|
||||||
|
if(t.trick().isFull())
|
||||||
|
t = t.withTrickCollected();
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user