diff --git a/src/main/java/com/charego/freecellfx/FreeCellApplication.java b/src/main/java/com/charego/freecellfx/FreeCellApplication.java index a3d9299..088ab37 100644 --- a/src/main/java/com/charego/freecellfx/FreeCellApplication.java +++ b/src/main/java/com/charego/freecellfx/FreeCellApplication.java @@ -12,32 +12,30 @@ import javafx.stage.Stage; public class FreeCellApplication extends Application { - public static void main(String[] args) { - launch(FreeCellApplication.class, args); - } + public static void main(String[] args) { + launch(FreeCellApplication.class, args); + } - @Override - public void start(Stage stage) throws Exception { - VBox root = new VBox(); - root.setBackground(new Background(new BackgroundImage(new Image( - "/deck/FELT.jpg"), BackgroundRepeat.NO_REPEAT, - BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER, - BackgroundSize.DEFAULT))); + @Override + public void start(Stage stage) throws Exception { + VBox root = new VBox(); + root.setBackground(new Background(new BackgroundImage(new Image("/deck/FELT.jpg"), BackgroundRepeat.NO_REPEAT, + BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER, BackgroundSize.DEFAULT))); - Game game = new Game(); - GameMenuBar menuBar = new GameMenuBar(); - GameCanvas canvas = new GameCanvas(game, 731, 600); - menuBar.setNewGameAction(canvas.getNewGameAction()); - menuBar.setUndoAction(canvas.getUndoAction()); - menuBar.setRedoAction(canvas.getRedoAction()); - menuBar.setExitAction(e -> Platform.exit()); - root.getChildren().addAll(menuBar, canvas); - Scene scene = new Scene(root); + Game game = new Game(); + GameMenuBar menuBar = new GameMenuBar(); + GameCanvas canvas = new GameCanvas(game, 731, 600); + menuBar.setNewGameAction(canvas.getNewGameAction()); + menuBar.setUndoAction(canvas.getUndoAction()); + menuBar.setRedoAction(canvas.getRedoAction()); + menuBar.setExitAction(e -> Platform.exit()); + root.getChildren().addAll(menuBar, canvas); + Scene scene = new Scene(root); - stage.setTitle("FreeCell"); - stage.setScene(scene); - stage.setResizable(false); - stage.show(); - } + stage.setTitle("FreeCell"); + stage.setScene(scene); + stage.setResizable(false); + stage.show(); + } } diff --git a/src/main/java/com/charego/freecellfx/model/Card.java b/src/main/java/com/charego/freecellfx/model/Card.java index 984021e..2d94625 100644 --- a/src/main/java/com/charego/freecellfx/model/Card.java +++ b/src/main/java/com/charego/freecellfx/model/Card.java @@ -10,95 +10,94 @@ import javafx.scene.paint.Color; */ public class Card { - private static DoubleKeyedMap faceImages; - public static final Image backImage = new Image("/deck/CARDBACK.png"); - public static final double width = backImage.getWidth(); - public static final double height = backImage.getHeight(); + private static DoubleKeyedMap faceImages; + public static final Image backImage = new Image("/deck/CARDBACK.png"); + public static final double width = backImage.getWidth(); + public static final double height = backImage.getHeight(); - public final Rank rank; - public final Suit suit; - public final Color color; - private boolean faceUp; + public final Rank rank; + public final Suit suit; + public final Color color; + private boolean faceUp; - public Card(Rank rank, Suit suit) { - this.rank = rank; - this.suit = suit; - this.color = suit.color(); - this.faceUp = false; - } + public Card(Rank rank, Suit suit) { + this.rank = rank; + this.suit = suit; + this.color = suit.color(); + this.faceUp = false; + } - /** - * Returns the backside image of a card. - */ - public static Image backImage() { - return backImage; - } + /** + * Returns the backside image of a card. + */ + public static Image backImage() { + return backImage; + } - /** - * Returns the face image of a card. - */ - public static Image faceImage(Rank rank, Suit suit) { - if (faceImages == null) { - faceImages = new DoubleKeyedMap<>(); - } - if (!faceImages.contains(rank, suit)) { - faceImages.put(rank, suit, new Image("/deck/" + rank.value - + suit.firstLetter + ".png")); - } - return faceImages.get(rank, suit); - } + /** + * Returns the face image of a card. + */ + public static Image faceImage(Rank rank, Suit suit) { + if (faceImages == null) { + faceImages = new DoubleKeyedMap<>(); + } + if (!faceImages.contains(rank, suit)) { + faceImages.put(rank, suit, new Image("/deck/" + rank.value + suit.firstLetter + ".png")); + } + return faceImages.get(rank, suit); + } - /** - * Returns the card's face image if its face is up or its backside image - * otherwise. - */ - public Image image() { - return faceUp ? faceImage(rank, suit) : backImage(); - } + /** + * Returns the card's face image if its face is up or its backside image + * otherwise. + */ + public Image image() { + return faceUp ? faceImage(rank, suit) : backImage(); + } - /** - * Turns the card over, negating its face up status. - */ - public void turn() { - faceUp = !faceUp; - } + /** + * Turns the card over, negating its face up status. + */ + public void turn() { + faceUp = !faceUp; + } - @Override - public boolean equals(Object other) { - if (other == null || !(other instanceof Card)) { - return false; - } - Card card = (Card) other; - return rank == card.rank && suit == card.suit; - } + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof Card)) { + return false; + } + Card card = (Card) other; + return rank == card.rank && suit == card.suit; + } - @Override - public String toString() { - return rank + " of " + suit; - } + @Override + public String toString() { + return rank + " of " + suit; + } - public enum Rank { - ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING; + public enum Rank { + ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING; - /** - * 1 for Ace up to 13 for King. - */ - public final int value = ordinal() + 1; - } + /** + * 1 for Ace up to 13 for King. + */ + public final int value = ordinal() + 1; + } - public enum Suit { - SPADES, HEARTS, DIAMONDS, CLUBS; + public enum Suit { + SPADES, HEARTS, DIAMONDS, CLUBS; - /** - * S for SPADES, H for HEARTS, D for DIAMONDS, or C for CLUBS. - */ - public final Character firstLetter = name().charAt(0); + /** + * S for SPADES, H for HEARTS, D for DIAMONDS, or C for CLUBS. + */ + public final Character firstLetter = name().charAt(0); - /** - * Black or red. - */ - public Color color() { - return (this == SPADES || this == CLUBS) ? Color.BLACK : Color.RED; - } - } + /** + * Black or red. + */ + public Color color() { + return (this == SPADES || this == CLUBS) ? Color.BLACK : Color.RED; + } + } } diff --git a/src/main/java/com/charego/freecellfx/model/Deck.java b/src/main/java/com/charego/freecellfx/model/Deck.java index deef5b4..9b2946d 100644 --- a/src/main/java/com/charego/freecellfx/model/Deck.java +++ b/src/main/java/com/charego/freecellfx/model/Deck.java @@ -13,41 +13,41 @@ import java.util.List; */ public class Deck { - private Deque cards = new LinkedList<>(); + private Deque cards = new LinkedList<>(); - private Deck() { - } + private Deck() { + } - /** - * Makes a 52-card, unshuffled deck. - */ - public static Deck newDeck() { - Deck deck = new Deck(); - for (Rank rank : Rank.values()) { - for (Suit suit : Suit.values()) { - deck.cards.push(new Card(rank, suit)); - } - } - return deck; - } + /** + * Makes a 52-card, unshuffled deck. + */ + public static Deck newDeck() { + Deck deck = new Deck(); + for (Rank rank : Rank.values()) { + for (Suit suit : Suit.values()) { + deck.cards.push(new Card(rank, suit)); + } + } + return deck; + } - /** - * Deals a card from the top of the deck. - */ - public Card deal() { - return cards.pop(); - } + /** + * Deals a card from the top of the deck. + */ + public Card deal() { + return cards.pop(); + } - /** - * Shuffles the deck. - */ - @SuppressWarnings("unchecked") - public void shuffle() { - Collections.shuffle((List) cards); - } + /** + * Shuffles the deck. + */ + @SuppressWarnings("unchecked") + public void shuffle() { + Collections.shuffle((List) cards); + } - @Override - public String toString() { - return cards.toString(); - } + @Override + public String toString() { + return cards.toString(); + } } \ No newline at end of file diff --git a/src/main/java/com/charego/freecellfx/model/Game.java b/src/main/java/com/charego/freecellfx/model/Game.java index 6751e2d..f5bea05 100644 --- a/src/main/java/com/charego/freecellfx/model/Game.java +++ b/src/main/java/com/charego/freecellfx/model/Game.java @@ -12,184 +12,184 @@ import java.util.LinkedList; import java.util.List; public class Game { - private List cells = new ArrayList<>(4); - private List foundations = new ArrayList<>(4); - private List tableaux = new ArrayList<>(8); - private MoveTracker moveTracker = new MoveTracker(); + private List cells = new ArrayList<>(4); + private List foundations = new ArrayList<>(4); + private List tableaux = new ArrayList<>(8); + private MoveTracker moveTracker = new MoveTracker(); - public Game() { - for (int i = 0; i < 4; i++) { - cells.add(new Cell()); - foundations.add(new Foundation()); - } - for (int i = 0; i < 8; i++) - tableaux.add(new Tableau()); - newGame(); - } + public Game() { + for (int i = 0; i < 4; i++) { + cells.add(new Cell()); + foundations.add(new Foundation()); + } + for (int i = 0; i < 8; i++) + tableaux.add(new Tableau()); + newGame(); + } - public List getCells() { - return cells; - } + public List getCells() { + return cells; + } - public List getFoundations() { - return foundations; - } + public List getFoundations() { + return foundations; + } - public List getTableaux() { - return tableaux; - } + public List getTableaux() { + return tableaux; + } - /** - * Moves cards from one pile to another, if the move is valid. - */ - public boolean tryMove(Pile fromPile, Pile toPile) { - int cardsMoved = toPile.moveFrom(fromPile); - if (cardsMoved > 0) { - moveTracker.addMove(new MoveAction(fromPile, toPile, cardsMoved)); - return true; - } - return false; - } + /** + * Moves cards from one pile to another, if the move is valid. + */ + public boolean tryMove(Pile fromPile, Pile toPile) { + int cardsMoved = toPile.moveFrom(fromPile); + if (cardsMoved > 0) { + moveTracker.addMove(new MoveAction(fromPile, toPile, cardsMoved)); + return true; + } + return false; + } - /** - * Redo a move that was previously undone. - * - * @return true if another redo can be performed - */ - public boolean redo() { - if (moveTracker.hasNextMove()) { - moveTracker.getNextMove().redo(); - } - return moveTracker.hasNextMove(); - } + /** + * Redo a move that was previously undone. + * + * @return true if another redo can be performed + */ + public boolean redo() { + if (moveTracker.hasNextMove()) { + moveTracker.getNextMove().redo(); + } + return moveTracker.hasNextMove(); + } - /** - * Undo a previous move. - * - * @return true if another undo can be performed - */ - public boolean undo() { - if (moveTracker.hasLastMove()) { - moveTracker.getLastMove().undo(); - } - return moveTracker.hasLastMove(); - } + /** + * Undo a previous move. + * + * @return true if another undo can be performed + */ + public boolean undo() { + if (moveTracker.hasLastMove()) { + moveTracker.getLastMove().undo(); + } + return moveTracker.hasLastMove(); + } - /** - * Returns true if the game cannot be lost. - */ - public boolean isWon() { - for (Pile pile : tableaux) { - if (!pile.isInOrder()) { - return false; - } - } - return true; - } + /** + * Returns true if the game cannot be lost. + */ + public boolean isWon() { + for (Pile pile : tableaux) { + if (!pile.isInOrder()) { + return false; + } + } + return true; + } - /** - * Returns true if the game cannot be won. - */ - public boolean isLost() { - // Are free cells full? - for (Pile pile : cells) { - if (pile.isEmpty()) { - return false; - } - } - // Can you not move to any tableau? - for (Pile pile : tableaux) { - for (Pile tableau : tableaux) { - if (pile.canMoveFrom(tableau)) { - return false; - } - } - for (Pile cell : cells) { - if (pile.canMoveFrom(cell)) { - return false; - } - } - } - // Can you not move to any home cell? - for (Pile pile : foundations) { - for (Pile tableau : tableaux) { - if (pile.canMoveFrom(tableau)) { - return false; - } - } - for (Pile cell : cells) { - if (pile.canMoveFrom(cell)) { - return false; - } - } - } - return true; - } + /** + * Returns true if the game cannot be won. + */ + public boolean isLost() { + // Are free cells full? + for (Pile pile : cells) { + if (pile.isEmpty()) { + return false; + } + } + // Can you not move to any tableau? + for (Pile pile : tableaux) { + for (Pile tableau : tableaux) { + if (pile.canMoveFrom(tableau)) { + return false; + } + } + for (Pile cell : cells) { + if (pile.canMoveFrom(cell)) { + return false; + } + } + } + // Can you not move to any home cell? + for (Pile pile : foundations) { + for (Pile tableau : tableaux) { + if (pile.canMoveFrom(tableau)) { + return false; + } + } + for (Pile cell : cells) { + if (pile.canMoveFrom(cell)) { + return false; + } + } + } + return true; + } - public void newGame() { - Deck deck = Deck.newDeck(); - deck.shuffle(); - moveTracker.clearMoves(); - tableaux.forEach(Pile::clear); - cells.forEach(Pile::clear); - foundations.forEach(Pile::clear); - // Deal 6 cards to each tableau. - for (int i = 0; i < 6; i++) { - for (int j = 0; j < 8; j++) { - Card card = deck.deal(); - card.turn(); - tableaux.get(j).addCard(card); - } - } - // Deal an additional card to first 4. - for (int i = 0; i < 4; i++) { - Card card = deck.deal(); - card.turn(); - tableaux.get(i).addCard(card); - } - } + public void newGame() { + Deck deck = Deck.newDeck(); + deck.shuffle(); + moveTracker.clearMoves(); + tableaux.forEach(Pile::clear); + cells.forEach(Pile::clear); + foundations.forEach(Pile::clear); + // Deal 6 cards to each tableau. + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 8; j++) { + Card card = deck.deal(); + card.turn(); + tableaux.get(j).addCard(card); + } + } + // Deal an additional card to first 4. + for (int i = 0; i < 4; i++) { + Card card = deck.deal(); + card.turn(); + tableaux.get(i).addCard(card); + } + } - private static class MoveTracker { - private final Deque nextMoves = new LinkedList<>(); - private final Deque previousMoves = new LinkedList<>(); + private static class MoveTracker { + private final Deque nextMoves = new LinkedList<>(); + private final Deque previousMoves = new LinkedList<>(); - public void clearMoves() { - nextMoves.clear(); - previousMoves.clear(); - } + public void clearMoves() { + nextMoves.clear(); + previousMoves.clear(); + } - public boolean hasLastMove() { - return !previousMoves.isEmpty(); - } + public boolean hasLastMove() { + return !previousMoves.isEmpty(); + } - public MoveAction getLastMove() { - MoveAction lastMove = previousMoves.pop(); - nextMoves.push(lastMove); - return lastMove; - } + public MoveAction getLastMove() { + MoveAction lastMove = previousMoves.pop(); + nextMoves.push(lastMove); + return lastMove; + } - public boolean hasNextMove() { - return !nextMoves.isEmpty(); - } + public boolean hasNextMove() { + return !nextMoves.isEmpty(); + } - public MoveAction getNextMove() { - MoveAction nextMove = nextMoves.pop(); - previousMoves.push(nextMove); - return nextMove; - } + public MoveAction getNextMove() { + MoveAction nextMove = nextMoves.pop(); + previousMoves.push(nextMove); + return nextMove; + } - public void addMove(MoveAction move) { - MoveAction nextMove = nextMoves.peek(); - /* - * if new move differs from saved next move, clear the remaining + public void addMove(MoveAction move) { + MoveAction nextMove = nextMoves.peek(); + /* + * if new move differs from saved next move, clear the remaining * next moves */ - if (move.equals(nextMove)) { - nextMoves.pop(); - } else { - nextMoves.clear(); - } - previousMoves.push(move); - } - } + if (move.equals(nextMove)) { + nextMoves.pop(); + } else { + nextMoves.clear(); + } + previousMoves.push(move); + } + } } diff --git a/src/main/java/com/charego/freecellfx/model/action/Action.java b/src/main/java/com/charego/freecellfx/model/action/Action.java index 68c00bb..d1e4460 100644 --- a/src/main/java/com/charego/freecellfx/model/action/Action.java +++ b/src/main/java/com/charego/freecellfx/model/action/Action.java @@ -1,7 +1,7 @@ package com.charego.freecellfx.model.action; public interface Action { - void redo(); + void redo(); - void undo(); + void undo(); } diff --git a/src/main/java/com/charego/freecellfx/model/action/MoveAction.java b/src/main/java/com/charego/freecellfx/model/action/MoveAction.java index 10d3f03..32bdb5d 100644 --- a/src/main/java/com/charego/freecellfx/model/action/MoveAction.java +++ b/src/main/java/com/charego/freecellfx/model/action/MoveAction.java @@ -3,34 +3,32 @@ package com.charego.freecellfx.model.action; import com.charego.freecellfx.model.pile.Pile; public class MoveAction implements Action { - private final Pile fromPile; - private final Pile toPile; - private final int numCards; + private final Pile fromPile; + private final Pile toPile; + private final int numCards; - public MoveAction(Pile fromPile, Pile toPile, int numCards) { - this.fromPile = fromPile; - this.toPile = toPile; - this.numCards = numCards; - } + public MoveAction(Pile fromPile, Pile toPile, int numCards) { + this.fromPile = fromPile; + this.toPile = toPile; + this.numCards = numCards; + } - @Override - public void redo() { - toPile.moveFromBlindly(fromPile, numCards); - } + @Override + public void redo() { + toPile.moveFromBlindly(fromPile, numCards); + } - @Override - public void undo() { - fromPile.moveFromBlindly(toPile, numCards); - } + @Override + public void undo() { + fromPile.moveFromBlindly(toPile, numCards); + } - @Override - public boolean equals(Object other) { - if (other == null || !(other instanceof MoveAction)) { - return false; - } - MoveAction action = (MoveAction) other; - return fromPile == action.fromPile - && toPile == action.toPile - && numCards == action.numCards; - } + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof MoveAction)) { + return false; + } + MoveAction action = (MoveAction) other; + return fromPile == action.fromPile && toPile == action.toPile && numCards == action.numCards; + } } diff --git a/src/main/java/com/charego/freecellfx/model/pile/AbstractPile.java b/src/main/java/com/charego/freecellfx/model/pile/AbstractPile.java index b6e5d23..da94d46 100644 --- a/src/main/java/com/charego/freecellfx/model/pile/AbstractPile.java +++ b/src/main/java/com/charego/freecellfx/model/pile/AbstractPile.java @@ -8,41 +8,41 @@ import java.util.Iterator; abstract class AbstractPile implements Pile { - private Deque stack = new ArrayDeque<>(); + private Deque stack = new ArrayDeque<>(); - @Override - public void addCard(Card card) { - stack.push(card); - } + @Override + public void addCard(Card card) { + stack.push(card); + } - @Override - public Card removeCard() { - return stack.pop(); - } + @Override + public Card removeCard() { + return stack.pop(); + } - @Override - public Card topCard() { - return stack.peek(); - } + @Override + public Card topCard() { + return stack.peek(); + } - @Override - public void clear() { - stack.clear(); - } + @Override + public void clear() { + stack.clear(); + } - @Override - public boolean isEmpty() { - return stack.isEmpty(); - } + @Override + public boolean isEmpty() { + return stack.isEmpty(); + } - @Override - public int size() { - return stack.size(); - } + @Override + public int size() { + return stack.size(); + } - @Override - public Iterator iterator() { - return stack.descendingIterator(); - } + @Override + public Iterator iterator() { + return stack.descendingIterator(); + } } diff --git a/src/main/java/com/charego/freecellfx/model/pile/Cell.java b/src/main/java/com/charego/freecellfx/model/pile/Cell.java index b188c3c..5b37e99 100644 --- a/src/main/java/com/charego/freecellfx/model/pile/Cell.java +++ b/src/main/java/com/charego/freecellfx/model/pile/Cell.java @@ -2,31 +2,31 @@ package com.charego.freecellfx.model.pile; public class Cell extends AbstractPile { - @Override - public boolean canMoveFrom(Pile other) { - return isEmpty() && !other.isEmpty(); - } + @Override + public boolean canMoveFrom(Pile other) { + return isEmpty() && !other.isEmpty(); + } - @Override - public int moveFrom(Pile other) { - if (canMoveFrom(other)) { - addCard(other.removeCard()); - return 1; - } - return 0; - } + @Override + public int moveFrom(Pile other) { + if (canMoveFrom(other)) { + addCard(other.removeCard()); + return 1; + } + return 0; + } - @Override - public void moveFromBlindly(Pile other, int numCards) { - if (numCards != 1) { - throw new IllegalArgumentException("numCards must be 1"); - } - addCard(other.removeCard()); - } + @Override + public void moveFromBlindly(Pile other, int numCards) { + if (numCards != 1) { + throw new IllegalArgumentException("numCards must be 1"); + } + addCard(other.removeCard()); + } - @Override - public boolean isInOrder() { - return true; - } + @Override + public boolean isInOrder() { + return true; + } } diff --git a/src/main/java/com/charego/freecellfx/model/pile/Foundation.java b/src/main/java/com/charego/freecellfx/model/pile/Foundation.java index e1cd3b7..ae9c71e 100644 --- a/src/main/java/com/charego/freecellfx/model/pile/Foundation.java +++ b/src/main/java/com/charego/freecellfx/model/pile/Foundation.java @@ -5,42 +5,41 @@ import static com.charego.freecellfx.model.Card.Suit; public class Foundation extends AbstractPile { - @Override - public boolean canMoveFrom(Pile other) { - if (other.isEmpty()) { - return false; - } - Rank otherRank = other.topCard().rank; - Suit otherSuit = other.topCard().suit; - if (isEmpty()) { - return otherRank == Rank.ACE; - } - Rank thisRank = topCard().rank; - Suit thisSuit = topCard().suit; - return otherSuit == thisSuit - && otherRank.value == thisRank.value + 1; - } + @Override + public boolean canMoveFrom(Pile other) { + if (other.isEmpty()) { + return false; + } + Rank otherRank = other.topCard().rank; + Suit otherSuit = other.topCard().suit; + if (isEmpty()) { + return otherRank == Rank.ACE; + } + Rank thisRank = topCard().rank; + Suit thisSuit = topCard().suit; + return otherSuit == thisSuit && otherRank.value == thisRank.value + 1; + } - @Override - public int moveFrom(Pile other) { - if (canMoveFrom(other)) { - addCard(other.removeCard()); - return 1; - } - return 0; - } + @Override + public int moveFrom(Pile other) { + if (canMoveFrom(other)) { + addCard(other.removeCard()); + return 1; + } + return 0; + } - @Override - public void moveFromBlindly(Pile other, int numCards) { - if (numCards != 1) { - throw new IllegalArgumentException("numCards must be 1"); - } - addCard(other.removeCard()); - } + @Override + public void moveFromBlindly(Pile other, int numCards) { + if (numCards != 1) { + throw new IllegalArgumentException("numCards must be 1"); + } + addCard(other.removeCard()); + } - @Override - public boolean isInOrder() { - return true; - } + @Override + public boolean isInOrder() { + return true; + } } diff --git a/src/main/java/com/charego/freecellfx/model/pile/Pile.java b/src/main/java/com/charego/freecellfx/model/pile/Pile.java index 9d13303..1791197 100644 --- a/src/main/java/com/charego/freecellfx/model/pile/Pile.java +++ b/src/main/java/com/charego/freecellfx/model/pile/Pile.java @@ -4,56 +4,57 @@ import com.charego.freecellfx.model.Card; public interface Pile extends Iterable { - /** - * Adds a card to the top of the pile. - */ - void addCard(Card card); + /** + * Adds a card to the top of the pile. + */ + void addCard(Card card); - /** - * Removes the top card from the pile. - */ - Card removeCard(); + /** + * Removes the top card from the pile. + */ + Card removeCard(); - /** - * Returns true if the contents of another pile can be moved to this pile. - */ - boolean canMoveFrom(Pile other); + /** + * Returns true if the contents of another pile can be moved to this pile. + */ + boolean canMoveFrom(Pile other); - /** - * Moves cards from another pile to this pile, if the tryMove is valid. - * - * @return the number of cards moved (i.e., possibly 0) - */ - int moveFrom(Pile other); + /** + * Moves cards from another pile to this pile, if the tryMove is valid. + * + * @return the number of cards moved (i.e., possibly 0) + */ + int moveFrom(Pile other); - /** - * Moves cards from another pile to this pile, whether or not the tryMove is valid. - */ - void moveFromBlindly(Pile other, int numCards); + /** + * Moves cards from another pile to this pile, whether or not the tryMove is + * valid. + */ + void moveFromBlindly(Pile other, int numCards); - /** - * Returns the top card in the pile, if a card is present. - */ - Card topCard(); + /** + * Returns the top card in the pile, if a card is present. + */ + Card topCard(); - /** - * Returns true if the contents of the pile are in order. - */ - boolean isInOrder(); + /** + * Returns true if the contents of the pile are in order. + */ + boolean isInOrder(); - /** - * Clears the contents of the pile. - */ - void clear(); + /** + * Clears the contents of the pile. + */ + void clear(); - /** - * Returns true if the pile is empty. - */ - boolean isEmpty(); + /** + * Returns true if the pile is empty. + */ + boolean isEmpty(); - /** - * Returns the size of the pile. - */ - int size(); + /** + * Returns the size of the pile. + */ + int size(); } diff --git a/src/main/java/com/charego/freecellfx/model/pile/Tableau.java b/src/main/java/com/charego/freecellfx/model/pile/Tableau.java index 8162569..c4cbb87 100644 --- a/src/main/java/com/charego/freecellfx/model/pile/Tableau.java +++ b/src/main/java/com/charego/freecellfx/model/pile/Tableau.java @@ -7,134 +7,133 @@ import java.util.LinkedList; public class Tableau extends AbstractPile { - /** - * Maintains the ordering of the cards within the pile. - */ - private Deque orderStack = new LinkedList<>(); + /** + * Maintains the ordering of the cards within the pile. + */ + private Deque orderStack = new LinkedList<>(); - @Override - public void addCard(Card card) { - countAddedCard(card); - super.addCard(card); - } + @Override + public void addCard(Card card) { + countAddedCard(card); + super.addCard(card); + } - private void countAddedCard(Card card) { - if (isEmpty() || orderStack.isEmpty()) { - orderStack.push(1); - } else if (card.color != topCard().color && - card.rank.value == topCard().rank.value - 1) { - orderStack.push(orderStack.pop() + 1); - } else { - orderStack.push(1); - } - } + private void countAddedCard(Card card) { + if (isEmpty() || orderStack.isEmpty()) { + orderStack.push(1); + } else if (card.color != topCard().color && card.rank.value == topCard().rank.value - 1) { + orderStack.push(orderStack.pop() + 1); + } else { + orderStack.push(1); + } + } - @Override - public Card removeCard() { - countRemovedCard(); - return super.removeCard(); - } + @Override + public Card removeCard() { + countRemovedCard(); + return super.removeCard(); + } - private void countRemovedCard() { - int topInOrder = orderStack.pop(); - if (topInOrder > 1) { - orderStack.push(topInOrder - 1); - } - } + private void countRemovedCard() { + int topInOrder = orderStack.pop(); + if (topInOrder > 1) { + orderStack.push(topInOrder - 1); + } + } - @Override - public boolean canMoveFrom(Pile other) { - if (other.isEmpty()) { - return false; - } - if (isEmpty()) { - return true; - } - int rankDiff = topCard().rank.value - other.topCard().rank.value; - if (rankDiff < 1) { - return false; - } - if (other instanceof Foundation || other instanceof Cell) { - return rankDiff == 1 && topCard().color != other.topCard().color; - } - if (other instanceof Tableau) { - int otherTopInOrder = ((Tableau) other).topInOrder(); - if (otherTopInOrder >= rankDiff) { - switch (rankDiff % 2) { - case 0: - return topCard().color == other.topCard().color; - case 1: - return topCard().color != other.topCard().color; - } - } - } - return false; - } + @Override + public boolean canMoveFrom(Pile other) { + if (other.isEmpty()) { + return false; + } + if (isEmpty()) { + return true; + } + int rankDiff = topCard().rank.value - other.topCard().rank.value; + if (rankDiff < 1) { + return false; + } + if (other instanceof Foundation || other instanceof Cell) { + return rankDiff == 1 && topCard().color != other.topCard().color; + } + if (other instanceof Tableau) { + int otherTopInOrder = ((Tableau) other).topInOrder(); + if (otherTopInOrder >= rankDiff) { + switch (rankDiff % 2) { + case 0: + return topCard().color == other.topCard().color; + case 1: + return topCard().color != other.topCard().color; + } + } + } + return false; + } - @Override - public int moveFrom(Pile other) { - if (!canMoveFrom(other)) { - return 0; - } - if (other instanceof Foundation || other instanceof Cell) { - addCard(other.removeCard()); - return 1; - } - if (other instanceof Tableau) { - if (isEmpty()) { - // Move all ordered cards to this pile - int otherTopInOrder = ((Tableau) other).topInOrder(); - moveFrom(other, otherTopInOrder); - return otherTopInOrder; - } else { - // Move ordered cards until they reach the current top card - int rankDiff = topCard().rank.value - other.topCard().rank.value; - moveFrom(other, rankDiff); - return rankDiff; - } - } - throw new IllegalStateException(); - } + @Override + public int moveFrom(Pile other) { + if (!canMoveFrom(other)) { + return 0; + } + if (other instanceof Foundation || other instanceof Cell) { + addCard(other.removeCard()); + return 1; + } + if (other instanceof Tableau) { + if (isEmpty()) { + // Move all ordered cards to this pile + int otherTopInOrder = ((Tableau) other).topInOrder(); + moveFrom(other, otherTopInOrder); + return otherTopInOrder; + } else { + // Move ordered cards until they reach the current top card + int rankDiff = topCard().rank.value - other.topCard().rank.value; + moveFrom(other, rankDiff); + return rankDiff; + } + } + throw new IllegalStateException(); + } - /** - * Moves {@code n} cards from the other pile to this pile. - */ - private void moveFrom(Pile other, int n) { - Deque topCards = new LinkedList<>(); - for (int i = 0; i < n; i++) { - topCards.addLast(other.removeCard()); - } - while (!topCards.isEmpty()) { - addCard(topCards.removeLast()); - } - } + /** + * Moves {@code n} cards from the other pile to this pile. + */ + private void moveFrom(Pile other, int n) { + Deque topCards = new LinkedList<>(); + for (int i = 0; i < n; i++) { + topCards.addLast(other.removeCard()); + } + while (!topCards.isEmpty()) { + addCard(topCards.removeLast()); + } + } - @Override - public void moveFromBlindly(Pile other, int numCards) { - moveFrom(other, numCards); - } + @Override + public void moveFromBlindly(Pile other, int numCards) { + moveFrom(other, numCards); + } - @Override - public void clear() { - super.clear(); - orderStack.clear(); - } + @Override + public void clear() { + super.clear(); + orderStack.clear(); + } - @Override - public boolean isInOrder() { - return (orderStack.size() < 2); - } + @Override + public boolean isInOrder() { + return (orderStack.size() < 2); + } - /** - * Returns the number of cards in order at the top of the pile. - */ - public int topInOrder() { - return orderStack.size() == 0 ? 0 : orderStack.peek(); - } + /** + * Returns the number of cards in order at the top of the pile. + */ + public int topInOrder() { + return orderStack.size() == 0 ? 0 : orderStack.peek(); + } - @Override - public String toString() { - return orderStack.toString(); - } + @Override + public String toString() { + return orderStack.toString(); + } } \ No newline at end of file diff --git a/src/main/java/com/charego/freecellfx/util/DoubleKeyedMap.java b/src/main/java/com/charego/freecellfx/util/DoubleKeyedMap.java index 72acc12..3a1b60d 100644 --- a/src/main/java/com/charego/freecellfx/util/DoubleKeyedMap.java +++ b/src/main/java/com/charego/freecellfx/util/DoubleKeyedMap.java @@ -6,29 +6,32 @@ import java.util.Map; /** * Maps a pair of coordinates to an object. * - * @param the first coordinate - * @param the second coordinate - * @param the treasure + * @param + * the first coordinate + * @param + * the second coordinate + * @param + * the treasure */ public class DoubleKeyedMap { - private Map> map = new HashMap<>(); + private Map> map = new HashMap<>(); - public boolean contains(K1 firstKey, K2 secondKey) { - return map.containsKey(firstKey) && map.get(firstKey).containsKey(secondKey); - } + public boolean contains(K1 firstKey, K2 secondKey) { + return map.containsKey(firstKey) && map.get(firstKey).containsKey(secondKey); + } - public V get(K1 firstKey, K2 secondKey) { - return map.get(firstKey).get(secondKey); - } + public V get(K1 firstKey, K2 secondKey) { + return map.get(firstKey).get(secondKey); + } - /** - * Returns the old value if it exists, or null. - */ - public V put(K1 firstKey, K2 secondKey, V newValue) { - if (!map.containsKey(firstKey)) { - map.put(firstKey, new HashMap<>()); - } - return map.get(firstKey).put(secondKey, newValue); - } + /** + * Returns the old value if it exists, or null. + */ + public V put(K1 firstKey, K2 secondKey, V newValue) { + if (!map.containsKey(firstKey)) { + map.put(firstKey, new HashMap<>()); + } + return map.get(firstKey).put(secondKey, newValue); + } } diff --git a/src/main/java/com/charego/freecellfx/view/GameCanvas.java b/src/main/java/com/charego/freecellfx/view/GameCanvas.java index c19a6a9..4aa519e 100644 --- a/src/main/java/com/charego/freecellfx/view/GameCanvas.java +++ b/src/main/java/com/charego/freecellfx/view/GameCanvas.java @@ -18,152 +18,152 @@ import java.util.List; public class GameCanvas extends Canvas { - private final Game game; - private final GraphicsContext gc; - private final PileView[] pileViews; + private final Game game; + private final GraphicsContext gc; + private final PileView[] pileViews; - private PileView fromPile; - private boolean winMessageShown; + private PileView fromPile; + private boolean winMessageShown; - public GameCanvas(Game game, double width, double height) { - super(width, height); - this.game = game; - this.gc = getGraphicsContext2D(); - this.pileViews = constructPileViews(game); - setOnMouseClicked(new MouseClickHandler()); - updateView(); - } + public GameCanvas(Game game, double width, double height) { + super(width, height); + this.game = game; + this.gc = getGraphicsContext2D(); + this.pileViews = constructPileViews(game); + setOnMouseClicked(new MouseClickHandler()); + updateView(); + } - private static PileView[] constructPileViews(Game game) { - List list = new ArrayList<>(); - for (Pile pile : game.getCells()) { - list.add(new StackedPileView(pile)); - } - for (Pile pile : game.getFoundations()) { - list.add(new StackedPileView(pile)); - } - for (Pile pile : game.getTableaux()) { - list.add(new CascadingPileView(pile)); - } - return list.toArray(new PileView[16]); - } + private static PileView[] constructPileViews(Game game) { + List list = new ArrayList<>(); + for (Pile pile : game.getCells()) { + list.add(new StackedPileView(pile)); + } + for (Pile pile : game.getFoundations()) { + list.add(new StackedPileView(pile)); + } + for (Pile pile : game.getTableaux()) { + list.add(new CascadingPileView(pile)); + } + return list.toArray(new PileView[16]); + } - private void checkEndingConditions() { - if (winMessageShown) { - return; - } - if (game.isWon()) { - winMessageShown = true; - new Alert(Alert.AlertType.INFORMATION, "You won!").showAndWait(); - } else if (game.isLost()) { - new Alert(Alert.AlertType.INFORMATION, "You lost!").showAndWait(); - } - } + private void checkEndingConditions() { + if (winMessageShown) { + return; + } + if (game.isWon()) { + winMessageShown = true; + new Alert(Alert.AlertType.INFORMATION, "You won!").showAndWait(); + } else if (game.isLost()) { + new Alert(Alert.AlertType.INFORMATION, "You lost!").showAndWait(); + } + } - private PileView findPile(double x, double y) { - int columnIndex = 0; - x -= 15; - while (x > 90) { - x -= 90; - columnIndex++; - } - boolean columnPressed = x < Card.width; - if (!columnPressed) { - return null; - } - if (y > 15 && y < 15 + Card.height) { - return pileViews[columnIndex]; - } - if (y > 120) { - PileView pileView = pileViews[columnIndex + 8]; - Pile pile = pileView.getPile(); - double pileHeight = 120 + (pile.size() - 1) * CascadingPileView.CARD_MARGIN + Card.height; - if (y < pileHeight) { - return pileView; - } - } - return null; - } + private PileView findPile(double x, double y) { + int columnIndex = 0; + x -= 15; + while (x > 90) { + x -= 90; + columnIndex++; + } + boolean columnPressed = x < Card.width; + if (!columnPressed) { + return null; + } + if (y > 15 && y < 15 + Card.height) { + return pileViews[columnIndex]; + } + if (y > 120) { + PileView pileView = pileViews[columnIndex + 8]; + Pile pile = pileView.getPile(); + double pileHeight = 120 + (pile.size() - 1) * CascadingPileView.CARD_MARGIN + Card.height; + if (y < pileHeight) { + return pileView; + } + } + return null; + } - public void updateView() { - gc.clearRect(0, 0, super.getWidth(), super.getHeight()); - double x = 15; - for (int i = 0; i < 8; i++) { - pileViews[i].paint(gc, x, 15); - x += 90; - } - x = 15; - for (int i = 8; i < 16; i++) { - pileViews[i].paint(gc, x, 120); - x += 90; - } - } + public void updateView() { + gc.clearRect(0, 0, super.getWidth(), super.getHeight()); + double x = 15; + for (int i = 0; i < 8; i++) { + pileViews[i].paint(gc, x, 15); + x += 90; + } + x = 15; + for (int i = 8; i < 16; i++) { + pileViews[i].paint(gc, x, 120); + x += 90; + } + } - public EventHandler getNewGameAction() { - return e -> { - game.newGame(); - winMessageShown = false; - if (fromPile != null && fromPile.isSelected()) { - fromPile.toggleSelected(); - } - fromPile = null; - updateView(); - }; - } + public EventHandler getNewGameAction() { + return e -> { + game.newGame(); + winMessageShown = false; + if (fromPile != null && fromPile.isSelected()) { + fromPile.toggleSelected(); + } + fromPile = null; + updateView(); + }; + } - public EventHandler getUndoAction() { - return e -> { - game.undo(); - updateView(); - }; - } + public EventHandler getUndoAction() { + return e -> { + game.undo(); + updateView(); + }; + } - public EventHandler getRedoAction() { - return e -> { - game.redo(); - updateView(); - }; - } + public EventHandler getRedoAction() { + return e -> { + game.redo(); + updateView(); + }; + } - private class MouseClickHandler implements EventHandler { - @Override - public void handle(MouseEvent e) { - PileView clickedPile = findPile(e.getX(), e.getY()); - if (clickedPile == null) { - if (fromPile != null) { - fromPile.toggleSelected(); - } - fromPile = null; - updateView(); - return; - } - if (fromPile == null) { - fromPile = clickedPile; - fromPile.toggleSelected(); - updateView(); - return; - } - if (fromPile == clickedPile) { - // Double click: try moving to a foundation. - fromPile.toggleSelected(); - for (int i = 4; i < 8; i++) { - if (game.tryMove(fromPile.getPile(), pileViews[i].getPile())) { - checkEndingConditions(); - break; - } - } - fromPile = null; - updateView(); - } else { - // Try moving to other cell. - fromPile.toggleSelected(); - if (game.tryMove(fromPile.getPile(), clickedPile.getPile())) { - checkEndingConditions(); - } - fromPile = null; - updateView(); - } - } - } + private class MouseClickHandler implements EventHandler { + @Override + public void handle(MouseEvent e) { + PileView clickedPile = findPile(e.getX(), e.getY()); + if (clickedPile == null) { + if (fromPile != null) { + fromPile.toggleSelected(); + } + fromPile = null; + updateView(); + return; + } + if (fromPile == null) { + fromPile = clickedPile; + fromPile.toggleSelected(); + updateView(); + return; + } + if (fromPile == clickedPile) { + // Double click: try moving to a foundation. + fromPile.toggleSelected(); + for (int i = 4; i < 8; i++) { + if (game.tryMove(fromPile.getPile(), pileViews[i].getPile())) { + checkEndingConditions(); + break; + } + } + fromPile = null; + updateView(); + } else { + // Try moving to other cell. + fromPile.toggleSelected(); + if (game.tryMove(fromPile.getPile(), clickedPile.getPile())) { + checkEndingConditions(); + } + fromPile = null; + updateView(); + } + } + } } diff --git a/src/main/java/com/charego/freecellfx/view/GameMenuBar.java b/src/main/java/com/charego/freecellfx/view/GameMenuBar.java index b539a31..858eb6a 100644 --- a/src/main/java/com/charego/freecellfx/view/GameMenuBar.java +++ b/src/main/java/com/charego/freecellfx/view/GameMenuBar.java @@ -11,44 +11,44 @@ import javafx.scene.input.KeyCombination; public class GameMenuBar extends MenuBar { - private final MenuItem newGame; - private final MenuItem undoMove; - private final MenuItem redoMove; - private final MenuItem exitGame; + private final MenuItem newGame; + private final MenuItem undoMove; + private final MenuItem redoMove; + private final MenuItem exitGame; - public GameMenuBar() { - super(); - newGame = new MenuItem("New"); - undoMove = new MenuItem("Undo"); - redoMove = new MenuItem("Redo"); - exitGame = new MenuItem("Exit"); - setAccelerators(); - Menu gameMenu = new Menu("Game"); - gameMenu.getItems().addAll(newGame, undoMove, redoMove, exitGame); - super.getMenus().add(gameMenu); - } + public GameMenuBar() { + super(); + newGame = new MenuItem("New"); + undoMove = new MenuItem("Undo"); + redoMove = new MenuItem("Redo"); + exitGame = new MenuItem("Exit"); + setAccelerators(); + Menu gameMenu = new Menu("Game"); + gameMenu.getItems().addAll(newGame, undoMove, redoMove, exitGame); + super.getMenus().add(gameMenu); + } - private void setAccelerators() { - newGame.setAccelerator(new KeyCodeCombination(KeyCode.F2)); - undoMove.setAccelerator(new KeyCodeCombination(KeyCode.Z, KeyCombination.CONTROL_DOWN)); - redoMove.setAccelerator(new KeyCodeCombination(KeyCode.Y, KeyCombination.CONTROL_DOWN)); - exitGame.setAccelerator(new KeyCodeCombination(KeyCode.ESCAPE)); - } + private void setAccelerators() { + newGame.setAccelerator(new KeyCodeCombination(KeyCode.F2)); + undoMove.setAccelerator(new KeyCodeCombination(KeyCode.Z, KeyCombination.CONTROL_DOWN)); + redoMove.setAccelerator(new KeyCodeCombination(KeyCode.Y, KeyCombination.CONTROL_DOWN)); + exitGame.setAccelerator(new KeyCodeCombination(KeyCode.ESCAPE)); + } - public void setNewGameAction(EventHandler handler) { - newGame.setOnAction(handler); - } + public void setNewGameAction(EventHandler handler) { + newGame.setOnAction(handler); + } - public void setUndoAction(EventHandler handler) { - undoMove.setOnAction(handler); - } + public void setUndoAction(EventHandler handler) { + undoMove.setOnAction(handler); + } - public void setRedoAction(EventHandler handler) { - redoMove.setOnAction(handler); - } + public void setRedoAction(EventHandler handler) { + redoMove.setOnAction(handler); + } - public void setExitAction(EventHandler handler) { - exitGame.setOnAction(handler); - } + public void setExitAction(EventHandler handler) { + exitGame.setOnAction(handler); + } } diff --git a/src/main/java/com/charego/freecellfx/view/pile/CascadingPileView.java b/src/main/java/com/charego/freecellfx/view/pile/CascadingPileView.java index 6ff19e2..0a00fed 100644 --- a/src/main/java/com/charego/freecellfx/view/pile/CascadingPileView.java +++ b/src/main/java/com/charego/freecellfx/view/pile/CascadingPileView.java @@ -8,43 +8,42 @@ import javafx.scene.paint.Color; public class CascadingPileView extends PileView { - public static final int CARD_MARGIN = 22; - public static final int TOP_MARGIN = 0; + public static final int CARD_MARGIN = 22; + public static final int TOP_MARGIN = 0; - public CascadingPileView(Pile pile) { - super(pile); - } + public CascadingPileView(Pile pile) { + super(pile); + } - @Override - public void paint(GraphicsContext gc, double x, double y) { - if (pile.isEmpty()) { - // draw an outline - gc.setStroke(Color.YELLOW); - gc.strokeRect(x, y, Card.width, Card.height); - if (isSelected()) { - // highlight empty cell - gc.setFill(highlight); - gc.fillRect(x, y, Card.width, Card.height); - } - } else { - double yCurrent = y; - // draw the cards in a cascade from top to bottom - for (Card card : pile) { - gc.drawImage(card.image(), x, yCurrent); - yCurrent += CARD_MARGIN; - } - if (isSelected()) { - // highlight ordered cards - if (yCurrent > TOP_MARGIN) { - yCurrent -= CARD_MARGIN; - } - int multiplier = ((Tableau) pile).topInOrder(); - int heightOfRect = (multiplier - 1) * CARD_MARGIN; - gc.setFill(highlight); - gc.fillRect(x, yCurrent - heightOfRect, Card.width, heightOfRect - + Card.height); - } - } - } + @Override + public void paint(GraphicsContext gc, double x, double y) { + if (pile.isEmpty()) { + // draw an outline + gc.setStroke(Color.YELLOW); + gc.strokeRect(x, y, Card.width, Card.height); + if (isSelected()) { + // highlight empty cell + gc.setFill(highlight); + gc.fillRect(x, y, Card.width, Card.height); + } + } else { + double yCurrent = y; + // draw the cards in a cascade from top to bottom + for (Card card : pile) { + gc.drawImage(card.image(), x, yCurrent); + yCurrent += CARD_MARGIN; + } + if (isSelected()) { + // highlight ordered cards + if (yCurrent > TOP_MARGIN) { + yCurrent -= CARD_MARGIN; + } + int multiplier = ((Tableau) pile).topInOrder(); + int heightOfRect = (multiplier - 1) * CARD_MARGIN; + gc.setFill(highlight); + gc.fillRect(x, yCurrent - heightOfRect, Card.width, heightOfRect + Card.height); + } + } + } } diff --git a/src/main/java/com/charego/freecellfx/view/pile/PileView.java b/src/main/java/com/charego/freecellfx/view/pile/PileView.java index 6da31a1..27c3060 100644 --- a/src/main/java/com/charego/freecellfx/view/pile/PileView.java +++ b/src/main/java/com/charego/freecellfx/view/pile/PileView.java @@ -6,26 +6,26 @@ import javafx.scene.paint.Color; public abstract class PileView { - protected static final Color highlight = new Color(1, 1, 0, 0.6); - protected final Pile pile; - private boolean isSelected = false; + protected static final Color highlight = new Color(1, 1, 0, 0.6); + protected final Pile pile; + private boolean isSelected = false; - public PileView(Pile pile) { - this.pile = pile; - } + public PileView(Pile pile) { + this.pile = pile; + } - public Pile getPile() { - return pile; - } + public Pile getPile() { + return pile; + } - public abstract void paint(GraphicsContext gc, double x, double y); + public abstract void paint(GraphicsContext gc, double x, double y); - public boolean isSelected() { - return isSelected; - } + public boolean isSelected() { + return isSelected; + } - public void toggleSelected() { - isSelected = !isSelected; - } + public void toggleSelected() { + isSelected = !isSelected; + } } diff --git a/src/main/java/com/charego/freecellfx/view/pile/StackedPileView.java b/src/main/java/com/charego/freecellfx/view/pile/StackedPileView.java index 24a4d4f..6a86ccc 100644 --- a/src/main/java/com/charego/freecellfx/view/pile/StackedPileView.java +++ b/src/main/java/com/charego/freecellfx/view/pile/StackedPileView.java @@ -7,24 +7,24 @@ import javafx.scene.paint.Color; public class StackedPileView extends PileView { - public StackedPileView(Pile pile) { - super(pile); - } + public StackedPileView(Pile pile) { + super(pile); + } - @Override - public void paint(GraphicsContext gc, double x, double y) { - if (pile.isEmpty()) { - // draw an outline - gc.setStroke(Color.YELLOW); - gc.strokeRect(x, y, Card.width, Card.height); - } else { - // draw the top card - gc.drawImage(pile.topCard().image(), x, y); - } - if (isSelected()) { - gc.setFill(highlight); - gc.fillRect(x, y, Card.width, Card.height); - } - } + @Override + public void paint(GraphicsContext gc, double x, double y) { + if (pile.isEmpty()) { + // draw an outline + gc.setStroke(Color.YELLOW); + gc.strokeRect(x, y, Card.width, Card.height); + } else { + // draw the top card + gc.drawImage(pile.topCard().image(), x, y); + } + if (isSelected()) { + gc.setFill(highlight); + gc.fillRect(x, y, Card.width, Card.height); + } + } }