package jcrib;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Game {

    private static final Logger logger = Logger.getLogger("jcrib");

    public enum State { Cut, Crib, Play };

    private List<Player> players = new ArrayList<>();

    private State currentState;

    private int cuts;
    private Player turn;
    private Player dealer;
    private Hand crib;
    private Card starter;
    private Play play;
    private Deck deck;

    public Game(Player player1, Player player2) {
        players.add(player1);
        players.add(player2);

        deck = new Deck();
        cuts = 0;
        startCut();
        turn = player1;
    }

    private void startCut() {
        deck.shuffle();
        this.currentState = State.Cut;
    }

    private void startPlay() {
        deck = new Deck();
        deck.shuffle();

        int cards = 6;
        if (players.size() > 2) {
            cards = 5;
        }
        deck.deal(cards, players);

        crib = new Hand();
        currentState = State.Crib;
    }

    public Card drawStarter() {
        starter = deck.removeCard();
        return starter;
    }

    private void verifyAction(Player player, Game.State state)
    throws IllegalPlayException {
        if (state != currentState) {
            throw new IllegalPlayException("Incorrect game state");
        }

        if (player != turn) {
            throw new IllegalPlayException("Wrong player's turn");
        }
    }

    public void playCard(Player player, int cardNumber) {
        if (logger.isLoggable(Level.INFO)) {
            logger.info(player.getName() + " plays card " + cardNumber);
        }

        List<Score> scores =
            play.playCard(player.getHand().removeCard(cardNumber));
        player.addScores(scores);

        Player next = getNextPlayer();
        if (play.checkPlayable(next)) {
            nextPlayer();
        } else if (play.checkPlayable(player)) {
            /* Current player keeps playing */
            return;
        } else {
            /* Go */
            player.addScores(play.go());
            nextPlayer();
        }
    }

    public void moveToCrib(Player player, int cardNumber)
    throws IllegalPlayException {
        if (logger.isLoggable(Level.INFO)) {
            logger.info(player.getName() + " moves card " + cardNumber
                    + " to crib");
        }

        if (cardNumber >= player.getHand().numCards()) {
            throw new IllegalPlayException("Invalid card index: " + cardNumber);
        }

        crib.addCard(player.getHand().removeCard(cardNumber));

        if (logger.isLoggable(Level.INFO)) {
            logger.info("Crib: " + crib.toString());
        }

        /* Crib is ready to go, start playing. */
        if (crib.numCards() == 4) {
            currentState = State.Play;
            turn = dealer;
            nextPlayer();
            play = new Play();
        }
    }

    public void selectCutCard(Player player, int cardNumber)
    throws IllegalPlayException {
        if (cardNumber >= deck.numCards()) {
            cardNumber = deck.numCards() - 1;
        }

        player.setCutCard(deck.removeCard(cardNumber));
        cuts++;

        if (cuts == players.size()) {
            determineFirst();
        }
    }

    private boolean determineFirst() {
        int lowest = Integer.MAX_VALUE;
        boolean tie = false;
        Player first = null;

        for (Player player : players) {
            if (logger.isLoggable(Level.INFO)) {
                logger.info(player.getName() + " drew " + player.getCutCard());
            }

            int cardValue = player.getCutCard().getOrdinal();
            if (cardValue == lowest) {
                tie = true;
            } else if (cardValue < lowest) {
                lowest = cardValue;
                tie = false;
                first = player;
            }
        }

        if (!tie) {
            if (logger.isLoggable(Level.INFO)) {
                logger.info(first.getName() + " deals first");
            }

            dealer = first;
            startPlay();
            return true;
        } else {
            return false;
        }
    }

    public void nextPlayer() {
        turn = players.get((players.indexOf(turn) + 1) % players.size());
    }

    public Player getNextPlayer() {
        return players.get((players.indexOf(turn) + 1) % players.size());
    }

    public List<Player> getPlayers() {
        return players;
    }

    public Game.State getState() {
        return currentState;
    }

    public Play getPlayState() {
        return play;
    }

    public Player getCurrentPlayer() {
        return turn;
    }

    public Player getDealer() {
        return dealer;
    }
}

@@ -41,37 +41,8 @@
 st;%0A
-import java.util.LinkedList;%0A
 impo
@@ -56,24 +56,24 @@
 .util.List;%0A
+
 import java.
@@ -126,16 +126,92 @@
 ogger;%0A%0A
+import jcrib.cards.Card;%0Aimport jcrib.cards.Deck;%0Aimport jcrib.cards.Hand;%0A%0A
 public c
@@ -793,32 +793,447 @@
 player1;%0A    %7D%0A%0A
+    public static void deal(Deck deck, int numCards, Player... players) %7B%0A        deal(deck, numCards, players);%0A    %7D%0A%0A    public static void deal(Deck deck, int numCards, Iterable%3CPlayer%3E players) %7B%0A        for (int i = 0; i %3C numCards; ++i) %7B%0A            for (Player player : players) %7B%0A                Card card = deck.removeCard();%0A                player.getHand().addCard(card);%0A            %7D%0A        %7D%0A    %7D%0A
     private void
@@ -1503,16 +1503,17 @@
   de
-ck.deal(
+al(deck, 
 card
@@ -4693,32 +4693,54 @@
        %7D else %7B%0A
+            cuts = 0;%0A
             retu

Move dealing logic to Game