- discard cards after they are used, so that they can be reshuffled if more cards are required (hasn't been tested)
- much better handling when users leave the game: - put their hand in the discard pile - if they played this round, put that card in the discard pile and remove them from the played players list - if they were supposed to play this round, remove them from the players list - if they were judge, return all played cards to players and move to the next judge - if they weren't judge and were lower in judging order, fix the judge pointer - add a goal to the game (8 points for now, hard-coded) - keep track of which players are supposed to play this round so players joining and leaving don't affect the game progressing
This commit is contained in:
parent
ee327639eb
commit
2d5e9d18c0
|
@ -154,6 +154,7 @@ cah.$.GamePlayerStatus = function() {
|
|||
cah.$.GamePlayerStatus.prototype.dummyForAutocomplete = undefined;
|
||||
cah.$.GamePlayerStatus.HOST = "host";
|
||||
cah.$.GamePlayerStatus.IDLE = "idle";
|
||||
cah.$.GamePlayerStatus.WINNER = "winner";
|
||||
cah.$.GamePlayerStatus.PLAYING = "playing";
|
||||
cah.$.GamePlayerStatus.JUDGE = "judge";
|
||||
cah.$.GamePlayerStatus.JUDGING = "judging";
|
||||
|
@ -163,12 +164,14 @@ cah.$.GamePlayerStatus_msg['idle'] = "";
|
|||
cah.$.GamePlayerStatus_msg['judging'] = "Selecting";
|
||||
cah.$.GamePlayerStatus_msg['host'] = "Host";
|
||||
cah.$.GamePlayerStatus_msg['judge'] = "Card Czar";
|
||||
cah.$.GamePlayerStatus_msg['winner'] = "Winner!";
|
||||
cah.$.GamePlayerStatus_msg_2 = {};
|
||||
cah.$.GamePlayerStatus_msg_2['playing'] = "Select a card to play.";
|
||||
cah.$.GamePlayerStatus_msg_2['idle'] = "Waiting for players...";
|
||||
cah.$.GamePlayerStatus_msg_2['judging'] = "Select a winning card.";
|
||||
cah.$.GamePlayerStatus_msg_2['host'] = "Wait for players then click Start Game.";
|
||||
cah.$.GamePlayerStatus_msg_2['judge'] = "You are the Card Czar this round.";
|
||||
cah.$.GamePlayerStatus_msg_2['winner'] = "You have won!";
|
||||
|
||||
cah.$.GameState = function() {
|
||||
// Dummy constructor to make Eclipse auto-complete.
|
||||
|
@ -182,7 +185,7 @@ cah.$.GameState.DEALING = "dealing";
|
|||
cah.$.GameState_msg = {};
|
||||
cah.$.GameState_msg['playing'] = "In Progress";
|
||||
cah.$.GameState_msg['judging'] = "In Progress";
|
||||
cah.$.GameState_msg['lobby'] = "Joinable (Not Started)";
|
||||
cah.$.GameState_msg['lobby'] = "Not Started";
|
||||
cah.$.GameState_msg['dealing'] = "In Progress";
|
||||
cah.$.GameState_msg['round_over'] = "In Progress";
|
||||
|
||||
|
@ -190,17 +193,20 @@ cah.$.LongPollEvent = function() {
|
|||
// Dummy constructor to make Eclipse auto-complete.
|
||||
};
|
||||
cah.$.LongPollEvent.prototype.dummyForAutocomplete = undefined;
|
||||
cah.$.LongPollEvent.GAME_ROUND_COMPLETE = "game_round_complete";
|
||||
cah.$.LongPollEvent.NOOP = "noop";
|
||||
cah.$.LongPollEvent.GAME_PLAYER_INFO_CHANGE = "game_player_info_change";
|
||||
cah.$.LongPollEvent.GAME_STATE_CHANGE = "game_state_change";
|
||||
cah.$.LongPollEvent.GAME_PLAYER_LEAVE = "game_player_leave";
|
||||
cah.$.LongPollEvent.NEW_PLAYER = "new_player";
|
||||
cah.$.LongPollEvent.PLAYER_LEAVE = "player_leave";
|
||||
cah.$.LongPollEvent.GAME_PLAYER_JOIN = "game_player_join";
|
||||
cah.$.LongPollEvent.HAND_DEAL = "hand_deal";
|
||||
cah.$.LongPollEvent.CHAT = "chat";
|
||||
cah.$.LongPollEvent.GAME_LIST_REFRESH = "game_list_refresh";
|
||||
cah.$.LongPollEvent.GAME_ROUND_COMPLETE = "game_round_complete";
|
||||
cah.$.LongPollEvent.GAME_PLAYER_INFO_CHANGE = "game_player_info_change";
|
||||
cah.$.LongPollEvent.NOOP = "noop";
|
||||
cah.$.LongPollEvent.GAME_BLACK_RESHUFFLE = "game_black_reshuffle";
|
||||
cah.$.LongPollEvent.GAME_WHITE_RESHUFFLE = "game_white_reshuffle";
|
||||
cah.$.LongPollEvent.GAME_STATE_CHANGE = "game_state_change";
|
||||
cah.$.LongPollEvent.PLAYER_LEAVE = "player_leave";
|
||||
cah.$.LongPollEvent.CHAT = "chat";
|
||||
cah.$.LongPollEvent.HAND_DEAL = "hand_deal";
|
||||
cah.$.LongPollEvent.GAME_JUDGE_LEFT = "game_judge_left";
|
||||
|
||||
cah.$.LongPollResponse = function() {
|
||||
// Dummy constructor to make Eclipse auto-complete.
|
||||
|
|
|
@ -538,6 +538,9 @@ cah.Game.prototype.updateUserStatus = function(playerInfo) {
|
|||
this.handSelectedCard_ = null;
|
||||
$(".selected", $(".game_hand", this.element_)).removeClass("selected");
|
||||
}
|
||||
if (playerStatus == cah.$.GamePlayerStatus.HOST) {
|
||||
$("#start_game").show();
|
||||
}
|
||||
}
|
||||
|
||||
if (playerStatus == cah.$.GamePlayerStatus.JUDGE
|
||||
|
@ -572,6 +575,24 @@ cah.Game.prototype.roundComplete = function(data) {
|
|||
+ (data[cah.$.LongPollResponse.INTERMISSION] / 1000) + " seconds.");
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify the player that a deck has been reshuffled.
|
||||
*
|
||||
* @param {String}
|
||||
* deck Deck name which has been reshuffled.
|
||||
*/
|
||||
cah.Game.prototype.reshuffle = function(deck) {
|
||||
cah.log.status("The " + deck + " deck has been reshuffled.");
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify the player that the judge has left the game and cards are being returned to hands.
|
||||
*/
|
||||
cah.Game.prototype.judgeLeft = function() {
|
||||
cah.log
|
||||
.status("The judge has left the game. Cards played this round are being returned to hands.");
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for confirm selection button.
|
||||
*
|
||||
|
|
|
@ -81,6 +81,20 @@ cah.longpoll.EventHandlers[cah.$.LongPollEvent.GAME_ROUND_COMPLETE] = function(d
|
|||
"round complete");
|
||||
};
|
||||
|
||||
cah.longpoll.EventHandlers[cah.$.LongPollEvent.GAME_WHITE_RESHUFFLE] = function(data) {
|
||||
cah.longpoll.EventHandlers.__gameEvent(data, cah.Game.prototype.reshuffle, "white",
|
||||
"white reshuffle");
|
||||
};
|
||||
|
||||
cah.longpoll.EventHandlers[cah.$.LongPollEvent.GAME_BLACK_RESHUFFLE] = function(data) {
|
||||
cah.longpoll.EventHandlers.__gameEvent(data, cah.Game.prototype.reshuffle, "black",
|
||||
"black reshuffle");
|
||||
};
|
||||
|
||||
cah.longpoll.EventHandlers[cah.$.LongPollEvent.GAME_JUDGE_LEFT] = function(data) {
|
||||
cah.longpoll.EventHandlers.__gameEvent(data, cah.Game.prototype.judgeLeft, "", "judge left");
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper for event handlers for game events.
|
||||
*
|
||||
|
|
|
@ -200,12 +200,15 @@ public class Constants {
|
|||
|
||||
public enum LongPollEvent {
|
||||
CHAT("chat"),
|
||||
GAME_BLACK_RESHUFFLE("game_black_reshuffle"),
|
||||
GAME_JUDGE_LEFT("game_judge_left"),
|
||||
GAME_LIST_REFRESH("game_list_refresh"),
|
||||
GAME_PLAYER_INFO_CHANGE("game_player_info_change"),
|
||||
GAME_PLAYER_JOIN("game_player_join"),
|
||||
GAME_PLAYER_LEAVE("game_player_leave"),
|
||||
GAME_ROUND_COMPLETE("game_round_complete"),
|
||||
GAME_STATE_CHANGE("game_state_change"),
|
||||
GAME_WHITE_RESHUFFLE("game_white_reshuffle"),
|
||||
HAND_DEAL("hand_deal"),
|
||||
NEW_PLAYER("new_player"),
|
||||
NOOP("noop"),
|
||||
|
@ -295,7 +298,7 @@ public class Constants {
|
|||
public enum GameState implements Localizable {
|
||||
DEALING("dealing", "In Progress"),
|
||||
JUDGING("judging", "In Progress"),
|
||||
LOBBY("lobby", "Joinable (Not Started)"),
|
||||
LOBBY("lobby", "Not Started"),
|
||||
PLAYING("playing", "In Progress"),
|
||||
ROUND_OVER("round_over", "In Progress");
|
||||
|
||||
|
@ -358,7 +361,8 @@ public class Constants {
|
|||
IDLE("idle", "", "Waiting for players..."),
|
||||
JUDGE("judge", "Card Czar", "You are the Card Czar this round."),
|
||||
JUDGING("judging", "Selecting", "Select a winning card."),
|
||||
PLAYING("playing", "Playing", "Select a card to play.");
|
||||
PLAYING("playing", "Playing", "Select a card to play."),
|
||||
WINNER("winner", "Winner!", "You have won!");
|
||||
|
||||
private final String status;
|
||||
private final String message;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.socialgamer.cah.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.socialgamer.cah.HibernateUtil;
|
||||
|
@ -12,6 +13,7 @@ import org.hibernate.Session;
|
|||
public class BlackDeck {
|
||||
private final List<BlackCard> deck;
|
||||
private final List<BlackCard> dealt;
|
||||
private final List<BlackCard> discard;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public BlackDeck() {
|
||||
|
@ -19,14 +21,31 @@ public class BlackDeck {
|
|||
// TODO option to restrict to only stock cards or allow customs
|
||||
deck = session.createQuery("from BlackCard order by random()").list();
|
||||
dealt = new ArrayList<BlackCard>();
|
||||
discard = new ArrayList<BlackCard>();
|
||||
}
|
||||
|
||||
public BlackCard getNextCard() {
|
||||
// Without knowing what the implementation of List that Hibernate returns
|
||||
// is, I'm going to pull from the end instead of the beginning.
|
||||
public BlackCard getNextCard() throws OutOfCardsException {
|
||||
if (deck.size() == 0) {
|
||||
throw new OutOfCardsException();
|
||||
}
|
||||
// Hibernate is returning an ArrayList, so this is a bit faster.
|
||||
final BlackCard card = deck.get(deck.size() - 1);
|
||||
deck.remove(deck.size() - 1);
|
||||
dealt.add(card);
|
||||
return card;
|
||||
}
|
||||
|
||||
public void discard(final BlackCard card) {
|
||||
if (card != null) {
|
||||
discard.add(card);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles the discard pile and puts the cards under the cards remaining in the deck.
|
||||
*/
|
||||
public void reshuffle() {
|
||||
Collections.shuffle(discard);
|
||||
deck.addAll(0, discard);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.socialgamer.cah.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -51,6 +52,7 @@ import com.google.inject.Inject;
|
|||
public class Game {
|
||||
private final int id;
|
||||
private final List<Player> players = new ArrayList<Player>(10);
|
||||
private final List<Player> roundPlayers = new ArrayList<Player>(9);
|
||||
// TODO make this Map<Player, List<WhiteCard>> once we support the multiple play black cards
|
||||
private final BidiFromIdHashMap<Player, WhiteCard> playedCards =
|
||||
new BidiFromIdHashMap<Player, WhiteCard>();
|
||||
|
@ -62,14 +64,15 @@ public class Game {
|
|||
private final Object blackCardLock = new Object();
|
||||
private WhiteDeck whiteDeck;
|
||||
private GameState state;
|
||||
// TODO make this work with "draw x" cards. probably will not actually be done here.
|
||||
private final int currentHandSize = 10;
|
||||
// TODO make this host-configurable
|
||||
private final int maxPlayers = 10;
|
||||
// TODO also need to configure this
|
||||
private int judgeIndex = 0;
|
||||
private final static int ROUND_INTERMISSION = 8 * 1000;
|
||||
private Timer nextRoundTimer;
|
||||
private final Object nextRoundTimerLock = new Object();
|
||||
// TODO host config
|
||||
private final int scoreGoal = 8;
|
||||
|
||||
/**
|
||||
* TODO Injection here would be much nicer, but that would need a Provider for the id... Too much
|
||||
|
@ -110,12 +113,10 @@ public class Game {
|
|||
if (host == null) {
|
||||
host = player;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final HashMap<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_PLAYER_JOIN.toString());
|
||||
data.put(LongPollResponse.GAME_ID, id);
|
||||
data.put(LongPollResponse.NICKNAME, user.getNickname());
|
||||
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||
}
|
||||
|
@ -134,16 +135,14 @@ public class Game {
|
|||
* @return True if {@code user} was the last player in the game.
|
||||
*/
|
||||
public boolean removePlayer(final User user) {
|
||||
boolean wasJudge = false;
|
||||
synchronized (players) {
|
||||
final Iterator<Player> iterator = players.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final Player player = iterator.next();
|
||||
if (player.getUser() == user) {
|
||||
iterator.remove();
|
||||
user.leaveGame(this);
|
||||
final HashMap<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
|
||||
HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_PLAYER_LEAVE.toString());
|
||||
data.put(LongPollResponse.GAME_ID, id);
|
||||
data.put(LongPollResponse.NICKNAME, user.getNickname());
|
||||
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||
if (host == player) {
|
||||
|
@ -153,6 +152,63 @@ public class Game {
|
|||
host = null;
|
||||
}
|
||||
}
|
||||
// If they played this round, remove card from played card list.
|
||||
synchronized (playedCards) {
|
||||
if (playedCards.containsKey(player)) {
|
||||
synchronized (whiteDeck) {
|
||||
// FIXME for multi-play
|
||||
whiteDeck.discard(playedCards.get(player));
|
||||
}
|
||||
playedCards.remove(player);
|
||||
}
|
||||
}
|
||||
// If they are to play this round, remove them from that list.
|
||||
synchronized (roundPlayers) {
|
||||
if (roundPlayers.contains(player)) {
|
||||
roundPlayers.remove(player);
|
||||
if (roundPlayers.size() == playedCards.size()) {
|
||||
// FIXME for multi-play
|
||||
judgingState();
|
||||
}
|
||||
}
|
||||
}
|
||||
// If they have a hand, return it to discard pile.
|
||||
if (player.getHand().size() > 0) {
|
||||
synchronized (whiteDeck) {
|
||||
final List<WhiteCard> hand = player.getHand();
|
||||
for (final WhiteCard card : hand) {
|
||||
whiteDeck.discard(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If they are judge, return all played cards to hand, and move to next judge.
|
||||
if (getJudge() == player) {
|
||||
data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_JUDGE_LEFT.toString());
|
||||
broadcastToPlayers(MessageType.GAME_EVENT, data);
|
||||
synchronized (playedCards) {
|
||||
// FIXME for multi-play
|
||||
for (final Player p : playedCards.keySet()) {
|
||||
p.getHand().add(playedCards.get(p));
|
||||
sendCardsToPlayer(p, Arrays.asList(playedCards.get(p)));
|
||||
}
|
||||
// prevent startNextRound from discarding cards
|
||||
playedCards.clear();
|
||||
}
|
||||
// startNextRound will advance it again.
|
||||
judgeIndex--;
|
||||
// Can't start the next round right here.
|
||||
wasJudge = true;
|
||||
}
|
||||
// If they aren't judge but are earlier in judging order, fix the judge index.
|
||||
else if (players.indexOf(player) < judgeIndex) {
|
||||
judgeIndex--;
|
||||
}
|
||||
|
||||
// we can't actually remove them until down here because we need to deal with the judge
|
||||
// index stuff first.
|
||||
iterator.remove();
|
||||
user.leaveGame(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +218,8 @@ public class Game {
|
|||
}
|
||||
if (players.size() < 3 && state != GameState.LOBBY) {
|
||||
resetState(true);
|
||||
} else if (wasJudge) {
|
||||
startNextRound();
|
||||
}
|
||||
return players.size() == 0;
|
||||
}
|
||||
|
@ -261,7 +319,15 @@ public class Game {
|
|||
}
|
||||
break;
|
||||
case ROUND_OVER:
|
||||
if (getJudge() == player) {
|
||||
playerStatus = GamePlayerStatus.JUDGE;
|
||||
}
|
||||
// TODO win-by-x
|
||||
else if (player.getScore() == scoreGoal) {
|
||||
playerStatus = GamePlayerStatus.WINNER;
|
||||
} else {
|
||||
playerStatus = GamePlayerStatus.IDLE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown GameState " + state.toString());
|
||||
|
@ -283,7 +349,7 @@ public class Game {
|
|||
if (players.size() >= 3) {
|
||||
blackDeck = new BlackDeck();
|
||||
whiteDeck = new WhiteDeck();
|
||||
dealState();
|
||||
startNextRound();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -306,11 +372,22 @@ public class Game {
|
|||
for (final Player player : players) {
|
||||
final List<WhiteCard> hand = player.getHand();
|
||||
final List<WhiteCard> newCards = new LinkedList<WhiteCard>();
|
||||
while (hand.size() < currentHandSize) {
|
||||
final WhiteCard card = whiteDeck.getNextCard();
|
||||
synchronized (whiteDeck) {
|
||||
while (hand.size() < 10) {
|
||||
final WhiteCard card;
|
||||
try {
|
||||
card = whiteDeck.getNextCard();
|
||||
} catch (final OutOfCardsException e) {
|
||||
whiteDeck.reshuffle();
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_WHITE_RESHUFFLE);
|
||||
broadcastToPlayers(MessageType.GAME_EVENT, data);
|
||||
continue;
|
||||
}
|
||||
hand.add(card);
|
||||
newCards.add(card);
|
||||
}
|
||||
}
|
||||
sendCardsToPlayer(player, newCards);
|
||||
}
|
||||
}
|
||||
|
@ -320,11 +397,22 @@ public class Game {
|
|||
private void playingState() {
|
||||
state = GameState.PLAYING;
|
||||
|
||||
synchronized (playedCards) {
|
||||
playedCards.clear();
|
||||
}
|
||||
|
||||
synchronized (blackCardLock) {
|
||||
do {
|
||||
try {
|
||||
blackDeck.discard(blackCard);
|
||||
blackCard = blackDeck.getNextCard();
|
||||
} catch (final OutOfCardsException e) {
|
||||
blackDeck.reshuffle();
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_BLACK_RESHUFFLE);
|
||||
broadcastToPlayers(MessageType.GAME_EVENT, data);
|
||||
continue;
|
||||
}
|
||||
// TODO remove this loop once the game supports the pick and draw features
|
||||
} while (blackCard.getPick() != 1 || blackCard.getDraw() != 0);
|
||||
}
|
||||
|
@ -386,19 +474,35 @@ public class Game {
|
|||
synchronized (blackCardLock) {
|
||||
blackCard = null;
|
||||
}
|
||||
synchronized (playedCards) {
|
||||
playedCards.clear();
|
||||
}
|
||||
synchronized (roundPlayers) {
|
||||
roundPlayers.clear();
|
||||
}
|
||||
state = GameState.LOBBY;
|
||||
final Player judge = getJudge();
|
||||
judgeIndex = 0;
|
||||
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_STATE_CHANGE.toString());
|
||||
data.put(LongPollResponse.GAME_STATE, GameState.LOBBY.toString());
|
||||
broadcastToPlayers(MessageType.GAME_EVENT, data);
|
||||
|
||||
data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_PLAYER_INFO_CHANGE.toString());
|
||||
data.put(LongPollResponse.PLAYER_INFO, getPlayerInfo(host));
|
||||
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||
|
||||
data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_PLAYER_INFO_CHANGE.toString());
|
||||
data.put(LongPollResponse.PLAYER_INFO, getPlayerInfo(judge));
|
||||
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||
}
|
||||
|
||||
private void sendCardsToPlayer(final Player player, final List<WhiteCard> cards) {
|
||||
final Map<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
|
||||
final Map<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.HAND_DEAL.toString());
|
||||
data.put(LongPollResponse.GAME_ID, id);
|
||||
final List<Map<WhiteCardData, Object>> cardData = handSubsetToClient(cards);
|
||||
data.put(LongPollResponse.HAND, cardData);
|
||||
final QueuedMessage qm = new QueuedMessage(MessageType.GAME_EVENT, data);
|
||||
|
@ -506,7 +610,11 @@ public class Game {
|
|||
}
|
||||
|
||||
private Player getJudge() {
|
||||
if (judgeIndex >= 0 && judgeIndex < players.size()) {
|
||||
return players.get(judgeIndex);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ErrorCode playCard(final User user, final int cardId) {
|
||||
|
@ -534,15 +642,14 @@ public class Game {
|
|||
synchronized (playedCards) {
|
||||
playedCards.put(player, playCard);
|
||||
|
||||
final HashMap<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_PLAYER_INFO_CHANGE.toString());
|
||||
data.put(LongPollResponse.GAME_ID, id);
|
||||
data.put(LongPollResponse.PLAYER_INFO, getPlayerInfo(player));
|
||||
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||
|
||||
// TODO make this check that everybody has played proper number of cards when we support
|
||||
// multiple play blacks
|
||||
if (playedCards.size() == players.size() - 1) {
|
||||
if (playedCards.size() == roundPlayers.size()) {
|
||||
judgingState();
|
||||
}
|
||||
}
|
||||
|
@ -572,6 +679,7 @@ public class Game {
|
|||
}
|
||||
|
||||
cardPlayer.increaseScore();
|
||||
state = GameState.ROUND_OVER;
|
||||
|
||||
HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_ROUND_COMPLETE.toString());
|
||||
|
@ -583,7 +691,6 @@ public class Game {
|
|||
data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_PLAYER_INFO_CHANGE.toString());
|
||||
data.put(LongPollResponse.PLAYER_INFO, getPlayerInfo(getJudge()));
|
||||
state = GameState.ROUND_OVER;
|
||||
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||
|
||||
data = getEventMap();
|
||||
|
@ -593,23 +700,52 @@ public class Game {
|
|||
|
||||
synchronized (nextRoundTimerLock) {
|
||||
nextRoundTimer = new Timer();
|
||||
nextRoundTimer.schedule(new TimerTask() {
|
||||
final TimerTask task;
|
||||
// TODO win-by-x option
|
||||
if (cardPlayer.getScore() == scoreGoal) {
|
||||
task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
winState();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
startNextRound();
|
||||
}
|
||||
}, ROUND_INTERMISSION);
|
||||
};
|
||||
}
|
||||
nextRoundTimer.schedule(task, ROUND_INTERMISSION);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void startNextRound() {
|
||||
synchronized (whiteDeck) {
|
||||
synchronized (playedCards) {
|
||||
for (final WhiteCard card : playedCards.values()) {
|
||||
// TODO fix this for multiple played cards
|
||||
whiteDeck.discard(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (players) {
|
||||
judgeIndex++;
|
||||
if (judgeIndex >= players.size()) {
|
||||
judgeIndex = 0;
|
||||
}
|
||||
synchronized (roundPlayers) {
|
||||
roundPlayers.clear();
|
||||
for (final Player player : players) {
|
||||
if (player != getJudge()) {
|
||||
roundPlayers.add(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dealState();
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package net.socialgamer.cah.data;
|
||||
|
||||
public class OutOfCardsException extends Exception {
|
||||
private static final long serialVersionUID = -1797946826947407779L;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package net.socialgamer.cah.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.socialgamer.cah.HibernateUtil;
|
||||
|
@ -12,6 +13,7 @@ import org.hibernate.Session;
|
|||
public class WhiteDeck {
|
||||
private final List<WhiteCard> deck;
|
||||
private final List<WhiteCard> dealt;
|
||||
private final List<WhiteCard> discard;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public WhiteDeck() {
|
||||
|
@ -19,14 +21,31 @@ public class WhiteDeck {
|
|||
// TODO option to restrict to only stock cards or allow customs
|
||||
deck = session.createQuery("from WhiteCard order by random()").list();
|
||||
dealt = new ArrayList<WhiteCard>();
|
||||
discard = new ArrayList<WhiteCard>();
|
||||
}
|
||||
|
||||
public WhiteCard getNextCard() {
|
||||
// Without knowing what the implementation of List that Hibernate returns
|
||||
// is, I'm going to pull from the end instead of the beginning.
|
||||
public WhiteCard getNextCard() throws OutOfCardsException {
|
||||
if (deck.size() == 0) {
|
||||
throw new OutOfCardsException();
|
||||
}
|
||||
// Hibernate is returning an ArrayList, so this is a bit faster.
|
||||
final WhiteCard card = deck.get(deck.size() - 1);
|
||||
deck.remove(deck.size() - 1);
|
||||
dealt.add(card);
|
||||
return card;
|
||||
}
|
||||
|
||||
public void discard(final WhiteCard card) {
|
||||
if (card != null) {
|
||||
discard.add(card);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles the discard pile and puts the cards under the cards remaining in the deck.
|
||||
*/
|
||||
public void reshuffle() {
|
||||
Collections.shuffle(discard);
|
||||
deck.addAll(0, discard);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue