diff --git a/src/com/codecool/klondike/Card.java b/src/com/codecool/klondike/Card.java index 896f1fb..d1ea5e7 100644 --- a/src/com/codecool/klondike/Card.java +++ b/src/com/codecool/klondike/Card.java @@ -9,8 +9,8 @@ public class Card extends ImageView { - private int suit; - private int rank; + private Suits suit; + private Ranks rank; private boolean faceDown; private Image backFace; @@ -23,7 +23,7 @@ public class Card extends ImageView { public static final int WIDTH = 150; public static final int HEIGHT = 215; - public Card(int suit, int rank, boolean faceDown) { + public Card(Suits suit, Ranks rank, boolean faceDown) { this.suit = suit; this.rank = rank; this.faceDown = faceDown; @@ -34,14 +34,18 @@ public Card(int suit, int rank, boolean faceDown) { setEffect(dropShadow); } - public int getSuit() { + public Suits getSuit() { return suit; } - public int getRank() { + public Ranks getRank() { return rank; } + public int getRankNumber() { + return Integer.parseInt(rank.getNumber()); + } + public boolean isFaceDown() { return faceDown; } @@ -88,39 +92,29 @@ public static boolean isSameSuit(Card card1, Card card2) { public static List createNewDeck() { List result = new ArrayList<>(); - for (int suit = 1; suit < 5; suit++) { - for (int rank = 1; rank < 14; rank++) { + for (Suits suit : Suits.values()) { + for (Ranks rank : Ranks.values()) { result.add(new Card(suit, rank, true)); } } + + Collections.shuffle(result); return result; } + public static void loadCardImages() { cardBackImage = new Image("card_images/card_back.png"); - String suitName = ""; - for (int suit = 1; suit < 5; suit++) { - switch (suit) { - case 1: - suitName = "hearts"; - break; - case 2: - suitName = "diamonds"; - break; - case 3: - suitName = "spades"; - break; - case 4: - suitName = "clubs"; - break; - } - for (int rank = 1; rank < 14; rank++) { - String cardName = suitName + rank; + for (Suits suit : Suits.values()) { + String suitName = suit.getName(); + for (Ranks rank : Ranks.values()) { + String rankNumber = rank.getNumber(); + String cardName = suitName + rankNumber; String cardId = "S" + suit + "R" + rank; String imageFileName = "card_images/" + cardName + ".png"; cardFaceImages.put(cardId, new Image(imageFileName)); } } } - + } diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 32963d7..efeab52 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -14,17 +14,23 @@ import javafx.scene.layout.Pane; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; public class Game extends Pane { - private List deck = new ArrayList<>(); + private List deck; + + private final int PILES_NUM = 7; private Pile stockPile; private Pile discardPile; + private Pile activePile; + private Card firstClickedTarget = null; private List foundationPiles = FXCollections.observableArrayList(); private List tableauPiles = FXCollections.observableArrayList(); + private Card cardToUncover; private double dragStartX, dragStartY; private List draggedCards = FXCollections.observableArrayList(); @@ -36,11 +42,31 @@ public class Game extends Pane { private EventHandler onMouseClickedHandler = e -> { Card card = (Card) e.getSource(); - if (card.getContainingPile().getPileType() == Pile.PileType.STOCK) { - card.moveToPile(discardPile); - card.flip(); - card.setMouseTransparent(false); - System.out.println("Placed " + card + " to the waste."); + activePile = card.getContainingPile(); + if (isCardDraggable(card)){ + if (firstClickedTarget == card) { + System.out.println("double click"); + for (Pile pile : foundationPiles) { + if (isMoveValid(card, pile)) { + + card.moveToPile(pile); + } + } + if (activePile.getTopCard().isFaceDown()){ + activePile.getTopCard().flip(); + } + firstClickedTarget = null; + } + // else { + firstClickedTarget = card; + System.out.println("one click"); + if (card.getContainingPile().getPileType() == Pile.PileType.STOCK) { + card.moveToPile(discardPile); + card.flip(); + card.setMouseTransparent(false); + System.out.println("Placed " + card + " to the waste."); + // } + } } }; @@ -55,15 +81,34 @@ public class Game extends Pane { private EventHandler onMouseDraggedHandler = e -> { Card card = (Card) e.getSource(); - Pile activePile = card.getContainingPile(); - if (activePile.getPileType() == Pile.PileType.STOCK) - return; - double offsetX = e.getSceneX() - dragStartX; - double offsetY = e.getSceneY() - dragStartY; - draggedCards.clear(); - draggedCards.add(card); + if (isCardDraggable(card)) { + activePile = card.getContainingPile(); + if (isOnStock()) { + return; + } + double offsetX = e.getSceneX() - dragStartX; + double offsetY = e.getSceneY() - dragStartY; + + draggedCards.clear(); + boolean isCardOnTop = (card == activePile.getTopCard()); + if (isCardOnTop) { + addToDraggedCards(offsetX, offsetY, card); + } else { + for (Card currentCard : activePile.getCards()) { + boolean rankIsLower = card.getRankNumber() >= currentCard.getRankNumber(); + if (rankIsLower && isOnTableau() && !currentCard.isFaceDown()) { + addToDraggedCards(offsetX, offsetY, currentCard); + + } + } + } + + } + }; + private void addToDraggedCards (double offsetX, double offsetY, Card card){ + draggedCards.add(card); card.getDropShadow().setRadius(20); card.getDropShadow().setOffsetX(10); card.getDropShadow().setOffsetY(10); @@ -71,22 +116,38 @@ public class Game extends Pane { card.toFront(); card.setTranslateX(offsetX); card.setTranslateY(offsetY); - }; + } private EventHandler onMouseReleasedHandler = e -> { - if (draggedCards.isEmpty()) - return; - Card card = (Card) e.getSource(); - Pile pile = getValidIntersectingPile(card, tableauPiles); - //TODO - if (pile != null) { - handleValidMove(card, pile); - } else { - draggedCards.forEach(MouseUtil::slideBack); - draggedCards = null; - } + if (!draggedCards.isEmpty()) { + Card card = (Card) e.getSource(); + Pile pile = getValidIntersectingPile(card); + //TODO + if (pile != null) { + System.out.println(activePile.cards.size() > draggedCards.size()); +// + if (isOnTableau() && (activePile.cards.size() > draggedCards.size())) { + Card cardToUncover = activePile.cards.get(activePile.cards.size() - draggedCards.size() - 1); + if (cardToUncover.isFaceDown()) { + cardToUncover.flip(); + } + } + handleValidMove(card, pile); + } else { + draggedCards.forEach(c -> MouseUtil.slideBack(c)); + draggedCards.clear(); + } + } }; + private boolean isOnTableau(){ + return activePile.getPileType().equals(Pile.PileType.TABLEAU); + } + + private boolean isOnStock(){ + return activePile.getPileType().equals(Pile.PileType.STOCK); + } + public boolean isGameWon() { //TODO return false; @@ -95,9 +156,17 @@ public boolean isGameWon() { public Game() { deck = Card.createNewDeck(); initPiles(); + setupCards(); dealCards(); } + private void setupCards() { + deck.forEach(card -> { + addMouseEventHandlers(card); + getChildren().add(card); + }); + } + public void addMouseEventHandlers(Card card) { card.setOnMousePressed(onMousePressedHandler); card.setOnMouseDragged(onMouseDraggedHandler); @@ -106,17 +175,73 @@ public void addMouseEventHandlers(Card card) { } public void refillStockFromDiscard() { + + List cardsToInverse = discardPile.getCards(); + Collections.reverse(cardsToInverse); + stockPile.clear(); + + for (Card card : cardsToInverse) { + + card.flip(); + stockPile.addCard(card); + + } + discardPile.clear(); + //TODO + System.out.println("Stock refilled from discard pile."); } + private boolean isCardDraggable(Card card) { + + boolean topStockCard = card == stockPile.getTopCard(); + boolean topDiscardCard = card == discardPile.getTopCard(); + boolean topCard = card == card.getContainingPile().getTopCard(); + boolean foundationCard = card.getContainingPile().getPileType().equals(Pile.PileType.FOUNDATION); + boolean tableauCard = card.getContainingPile().getPileType().equals(Pile.PileType.TABLEAU); + boolean uncoveredCard = !card.isFaceDown(); + System.out.println(topStockCard); + System.out.println(); + System.out.println(tableauCard); + System.out.println(uncoveredCard); + return tableauCard && uncoveredCard || foundationCard && topCard || topStockCard || topDiscardCard; + } + public boolean isMoveValid(Card card, Pile destPile) { - //TODO - return true; + + if (destPile.getPileType().equals(Pile.PileType.TABLEAU)) { + if (!destPile.isEmpty()){ + boolean rankOneHigher = (card.getRankNumber() == destPile.getTopCard().getRankNumber() - 1); + boolean oppositeColour = (!card.getSuit().getColour().equals(destPile.getTopCard().getSuit().getColour())); + return rankOneHigher && oppositeColour; + } else { + return canPutOnEmptyPlace(card, Ranks.KING, destPile); + } + } + + if (destPile.getPileType().equals(Pile.PileType.FOUNDATION)) { + return canPutOnEmptyPlace(card, Ranks.ACE, destPile) || hasSameSuitAndIsHigher(card, destPile); + } + + return false; + } + + private boolean hasSameSuitAndIsHigher(Card card, Pile destPile) { + return !destPile.isEmpty() && destPile.getTopCard().getSuit().equals(card.getSuit()) + && destPile.getTopCard().getRankNumber() + 1 == card.getRankNumber(); + } + + private boolean canPutOnEmptyPlace(Card card, Ranks cardRank, Pile destPile) { + return destPile.isEmpty() && card.getRank().equals(cardRank); } - private Pile getValidIntersectingPile(Card card, List piles) { + + private Pile getValidIntersectingPile(Card card) { Pile result = null; - for (Pile pile : piles) { + List allPiles = new ArrayList<>(tableauPiles); + allPiles.addAll(foundationPiles); + + for (Pile pile : allPiles) { if (!pile.equals(card.getContainingPile()) && isOverPile(card, pile) && isMoveValid(card, pile)) @@ -126,10 +251,12 @@ private Pile getValidIntersectingPile(Card card, List piles) { } private boolean isOverPile(Card card, Pile pile) { - if (pile.isEmpty()) + if (pile.isEmpty()) { return card.getBoundsInParent().intersects(pile.getBoundsInParent()); - else + } else { return card.getBoundsInParent().intersects(pile.getTopCard().getBoundsInParent()); + + } } private void handleValidMove(Card card, Pile destPile) { @@ -147,7 +274,6 @@ private void handleValidMove(Card card, Pile destPile) { draggedCards.clear(); } - private void initPiles() { stockPile = new Pile(Pile.PileType.STOCK, "Stock", STOCK_GAP); stockPile.setBlurredBackground(); @@ -170,7 +296,7 @@ private void initPiles() { foundationPiles.add(foundationPile); getChildren().add(foundationPile); } - for (int i = 0; i < 7; i++) { + for (int i = 0; i < PILES_NUM; i++) { Pile tableauPile = new Pile(Pile.PileType.TABLEAU, "Tableau " + i, TABLEAU_GAP); tableauPile.setBlurredBackground(); tableauPile.setLayoutX(95 + i * 180); @@ -182,12 +308,13 @@ private void initPiles() { public void dealCards() { Iterator deckIterator = deck.iterator(); - //TODO - deckIterator.forEachRemaining(card -> { - stockPile.addCard(card); - addMouseEventHandlers(card); - getChildren().add(card); - }); + for (int i = 0; i < PILES_NUM; i++) { + for (int j = 0; j < i + 1; j++) { + tableauPiles.get(i).addCard(deckIterator.next()); + } + tableauPiles.get(i).getTopCard().flip(); + } + deckIterator.forEachRemaining(card -> stockPile.addCard(card)); } @@ -197,4 +324,4 @@ public void setTableBackground(Image tableBackground) { BackgroundPosition.CENTER, BackgroundSize.DEFAULT))); } -} +} \ No newline at end of file diff --git a/src/com/codecool/klondike/Pile.java b/src/com/codecool/klondike/Pile.java index 46ea20f..7d9ed11 100644 --- a/src/com/codecool/klondike/Pile.java +++ b/src/com/codecool/klondike/Pile.java @@ -16,7 +16,7 @@ public class Pile extends Pane { private PileType pileType; private String name; private double cardGap; - private ObservableList cards = FXCollections.observableArrayList(); + protected ObservableList cards = FXCollections.observableArrayList(); public Pile(PileType pileType, String name, double cardGap) { this.pileType = pileType; @@ -40,8 +40,7 @@ public ObservableList getCards() { } public int numOfCards() { - //TODO - return 1; + return cards.size(); } public boolean isEmpty() { @@ -49,7 +48,7 @@ public boolean isEmpty() { } public void clear() { - //TODO + cards.clear(); } public void addCard(Card card) { @@ -74,6 +73,13 @@ public Card getTopCard() { return cards.get(cards.size() - 1); } + public Card getSecondCard() { + if (cards.isEmpty()) + return null; + else + return cards.get(cards.size() - 2); + } + public void setBlurredBackground() { setPrefSize(Card.WIDTH, Card.HEIGHT); BackgroundFill backgroundFill = new BackgroundFill(Color.gray(0.0, 0.2), null, null); diff --git a/src/com/codecool/klondike/Ranks.java b/src/com/codecool/klondike/Ranks.java new file mode 100644 index 0000000..6a593b9 --- /dev/null +++ b/src/com/codecool/klondike/Ranks.java @@ -0,0 +1,27 @@ +package com.codecool.klondike; + +public enum Ranks { + ACE ("1"), + TWO ("2"), + THREE ("3"), + FOUR ("4"), + FIVE ("5"), + SIX ("6"), + SEVEN ("7"), + EIGHT ("8"), + NINE ("9"), + TEN ("10"), + JACK ("11"), + QUEEN ("12"), + KING ("13"); + + private final String number; + + Ranks(String text) { + this.number = text; + } + + public String getNumber() { + return this.number; + } +} diff --git a/src/com/codecool/klondike/Suits.java b/src/com/codecool/klondike/Suits.java new file mode 100644 index 0000000..d12d203 --- /dev/null +++ b/src/com/codecool/klondike/Suits.java @@ -0,0 +1,26 @@ +package com.codecool.klondike; + +public enum Suits { + HEARTS ("hearts"), + DIAMONDS ("diamonds"), + SPADES ("spades"), + CLUBS ("clubs"); + + private final String name; + + Suits(String text) { + this.name = text; + } + + public String getName() { + return this.name; + } + + public String getColour() { + if (this.name == "hearts" || this.name == "diamonds"){ + return "red"; + } else { + return "black"; + } + } +}