From a93ae9927a5e1d0a863d9566d15883ce0649a39a Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Mon, 24 Sep 2018 20:56:17 +0200 Subject: [PATCH 01/31] Add travis.yml --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..70eeb76 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: java +script: + - javac src/com/codecool/klondike/*.java \ No newline at end of file From 87bdb3e8a709a7659a395bae6ee4e11814b8e9dc Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Mon, 24 Sep 2018 20:57:44 +0200 Subject: [PATCH 02/31] Add badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3141299..77a6f6b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Klondike Solitaire +[![Build Status](https://travis-ci.org/AJuszczakiewicz/javase-klondike-solitaire-kosteczki.svg?branch=master)](https://travis-ci.org/AJuszczakiewicz/javase-klondike-solitaire-kosteczki) + A Klondike Solitaire game written in Java using JavaFX as a GUI. ### Rules of the game From 683ca5581ce57743cf1f16e55b65cd0545b0ea56 Mon Sep 17 00:00:00 2001 From: "Svetlana.Syo" Date: Wed, 26 Sep 2018 11:03:03 +0200 Subject: [PATCH 03/31] add isOppositeColor method in Card and add comment to Game TODO isOppositeColor --- src/com/codecool/klondike/Card.java | 5 +++-- src/com/codecool/klondike/Game.java | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/codecool/klondike/Card.java b/src/com/codecool/klondike/Card.java index 896f1fb..46cf604 100644 --- a/src/com/codecool/klondike/Card.java +++ b/src/com/codecool/klondike/Card.java @@ -78,8 +78,9 @@ public String toString() { } public static boolean isOppositeColor(Card card1, Card card2) { - //TODO - return true; + if (card1.suit <= 2 && card2.suit > 2) + return true; + return false; } public static boolean isSameSuit(Card card1, Card card2) { diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 32963d7..f5d02c0 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -80,6 +80,7 @@ public class Game extends Pane { Pile pile = getValidIntersectingPile(card, tableauPiles); //TODO if (pile != null) { + //TODO isOpositeColor handleValidMove(card, pile); } else { draggedCards.forEach(MouseUtil::slideBack); From c3a882b5fe3045602b9fbc911a2096ef0e9ab2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Wed, 26 Sep 2018 11:43:14 +0200 Subject: [PATCH 04/31] Implement refill of stock from discard --- src/com/codecool/klondike/Game.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 32963d7..fe31094 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -13,9 +13,7 @@ import javafx.scene.layout.BackgroundSize; import javafx.scene.layout.Pane; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; public class Game extends Pane { @@ -106,7 +104,11 @@ public void addMouseEventHandlers(Card card) { } public void refillStockFromDiscard() { - //TODO + Collections.reverse(discardPile.getCards()); + discardPile.getCards().forEach(card -> card.flip()); + + MouseUtil.slideToDest(discardPile.getCards(), stockPile); + System.out.println("Stock refilled from discard pile."); } From f5a24da7ba25e607eebe32a6fdf87ac910a8cbd5 Mon Sep 17 00:00:00 2001 From: "Svetlana.Syo" Date: Wed, 26 Sep 2018 11:47:18 +0200 Subject: [PATCH 05/31] isMoveValid method --- src/com/codecool/klondike/Game.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index f5d02c0..19e0e69 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -14,6 +14,7 @@ import javafx.scene.layout.Pane; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -112,8 +113,10 @@ public void refillStockFromDiscard() { } public boolean isMoveValid(Card card, Pile destPile) { - //TODO - return true; + if (Arrays.asList(foundationPiles).contains(destPile)) + return true; + else if (Arrays.asList(tableauPiles).contains(destPile)) + return true; } private Pile getValidIntersectingPile(Card card, List piles) { Pile result = null; From f629a5d1af5a1630587959c06475190ab56e8b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Wed, 26 Sep 2018 12:08:29 +0200 Subject: [PATCH 06/31] Implement method which return count of card on pile --- src/com/codecool/klondike/Pile.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/codecool/klondike/Pile.java b/src/com/codecool/klondike/Pile.java index 46ea20f..64552b1 100644 --- a/src/com/codecool/klondike/Pile.java +++ b/src/com/codecool/klondike/Pile.java @@ -40,8 +40,7 @@ public ObservableList getCards() { } public int numOfCards() { - //TODO - return 1; + return cards.size(); } public boolean isEmpty() { From 91fb5cdbb19f2a5d61a4db82d09e7d5285d88a75 Mon Sep 17 00:00:00 2001 From: "Svetlana.Syo" Date: Wed, 26 Sep 2018 13:23:05 +0200 Subject: [PATCH 07/31] add methods clear --- src/com/codecool/klondike/Pile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/codecool/klondike/Pile.java b/src/com/codecool/klondike/Pile.java index 46ea20f..65315d3 100644 --- a/src/com/codecool/klondike/Pile.java +++ b/src/com/codecool/klondike/Pile.java @@ -49,7 +49,7 @@ public boolean isEmpty() { } public void clear() { - //TODO + cards.removeAll(cards); } public void addCard(Card card) { From 5c38c6203aaba7bb09de050fdcaeb0e0c346e3ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Wed, 26 Sep 2018 14:32:57 +0200 Subject: [PATCH 08/31] Implement function to drag multiple cards at once --- src/com/codecool/klondike/Game.java | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 32963d7..bb55bb4 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -62,15 +62,20 @@ public class Game extends Pane { double offsetY = e.getSceneY() - dragStartY; draggedCards.clear(); - draggedCards.add(card); + int draggedCardIndex = activePile.getCards().indexOf(card); - card.getDropShadow().setRadius(20); - card.getDropShadow().setOffsetX(10); - card.getDropShadow().setOffsetY(10); + activePile.getCards().listIterator(draggedCardIndex) + .forEachRemaining(draggedCards::add); - card.toFront(); - card.setTranslateX(offsetX); - card.setTranslateY(offsetY); + draggedCards.forEach(draggedCard -> { + draggedCard.getDropShadow().setRadius(20); + draggedCard.getDropShadow().setOffsetX(10); + draggedCard.getDropShadow().setOffsetY(10); + + draggedCard.toFront(); + draggedCard.setTranslateX(offsetX); + draggedCard.setTranslateY(offsetY); + }); }; private EventHandler onMouseReleasedHandler = e -> { @@ -83,7 +88,7 @@ public class Game extends Pane { handleValidMove(card, pile); } else { draggedCards.forEach(MouseUtil::slideBack); - draggedCards = null; + //draggedCards = null; } }; From 4a769b42dd57c5b10256db9d4aa726ae2f5751de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Wed, 26 Sep 2018 14:39:23 +0200 Subject: [PATCH 09/31] Empty dragged container from left cards --- src/com/codecool/klondike/Game.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index bb55bb4..b738a48 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -88,7 +88,7 @@ public class Game extends Pane { handleValidMove(card, pile); } else { draggedCards.forEach(MouseUtil::slideBack); - //draggedCards = null; + draggedCards.removeAll(draggedCards); } }; From 9ef0155056024700268c3300edc5acbc2d5a0330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Wed, 26 Sep 2018 16:05:09 +0200 Subject: [PATCH 10/31] Fix missing return statement in isMoveValid --- src/com/codecool/klondike/Game.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index ce5a46b..da98856 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -118,6 +118,8 @@ public boolean isMoveValid(Card card, Pile destPile) { return true; else if (Arrays.asList(tableauPiles).contains(destPile)) return true; + + return false; } private Pile getValidIntersectingPile(Card card, List piles) { Pile result = null; From a3ac0300f68cb581b360b47fd9754918b8938785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Wed, 26 Sep 2018 16:29:59 +0200 Subject: [PATCH 11/31] Fix foudation piles blocking --- src/com/codecool/klondike/Game.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index da98856..361d059 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -76,13 +76,16 @@ public class Game extends Pane { return; Card card = (Card) e.getSource(); Pile pile = getValidIntersectingPile(card, tableauPiles); + if(pile != null) { + pile = getValidIntersectingPile(card, foundationPiles); + } //TODO if (pile != null) { //TODO isOpositeColor handleValidMove(card, pile); } else { draggedCards.forEach(MouseUtil::slideBack); - draggedCards = null; + //draggedCards = null; } }; @@ -114,9 +117,11 @@ public void refillStockFromDiscard() { } public boolean isMoveValid(Card card, Pile destPile) { - if (Arrays.asList(foundationPiles).contains(destPile)) + if (foundationPiles.contains(destPile)) { + System.out.println("asdasd"); return true; - else if (Arrays.asList(tableauPiles).contains(destPile)) + } + else if (tableauPiles.contains(destPile)) return true; return false; From 410e89e041b63b894e15ad5ae8f8b3a0f36e9c5e Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Mon, 24 Sep 2018 20:56:17 +0200 Subject: [PATCH 12/31] Add travis.yml --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..70eeb76 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: java +script: + - javac src/com/codecool/klondike/*.java \ No newline at end of file From 954878bd8a41204867548b0a49b9dea7a85db3a3 Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Mon, 24 Sep 2018 20:56:17 +0200 Subject: [PATCH 13/31] Add travis.yml --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..70eeb76 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: java +script: + - javac src/com/codecool/klondike/*.java \ No newline at end of file From 63c55a743c87a8a0106f9dcbb8d987c9a070f5ac Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Mon, 24 Sep 2018 20:56:17 +0200 Subject: [PATCH 14/31] Add travis.yml --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..70eeb76 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: java +script: + - javac src/com/codecool/klondike/*.java \ No newline at end of file From a8c37cfdad98884c4c7d28e37aec38a24df61bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Thu, 27 Sep 2018 11:33:23 +0200 Subject: [PATCH 15/31] Implement undo mechanism to game It's only undo last move on tableu and stock piles --- src/com/codecool/klondike/Undoer.java | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/com/codecool/klondike/Undoer.java diff --git a/src/com/codecool/klondike/Undoer.java b/src/com/codecool/klondike/Undoer.java new file mode 100644 index 0000000..b64a50f --- /dev/null +++ b/src/com/codecool/klondike/Undoer.java @@ -0,0 +1,45 @@ +package com.codecool.klondike; + +import java.util.*; +import java.util.Map.Entry; + +public class Undoer { + public enum ActionOwner { + USER, + GAME + } + + private static Undoer instance; + + private final LinkedList> undoSteps; + + private Undoer(){ + undoSteps = new LinkedList<>(); + } + + public void addAction(ActionOwner owner, Runnable undoStep) { + Entry step = new AbstractMap.SimpleEntry<>(owner, undoStep); + + undoSteps.push(step); + } + + public void undoAction() { + if(undoSteps.isEmpty()) return; + + Entry lastStep = undoSteps.pop(); + lastStep.getValue().run(); + + while(!undoSteps.isEmpty() && undoSteps.getLast().getKey() == ActionOwner.GAME) { + lastStep = undoSteps.pop(); + lastStep.getValue().run(); + } + } + + public static Undoer getInstance() { + if(instance == null) { + instance = new Undoer(); + } + + return instance; + } +} From 4e777c9642400067cddb368ee292832a40abc288 Mon Sep 17 00:00:00 2001 From: "Svetlana.Syo" Date: Thu, 27 Sep 2018 11:59:47 +0200 Subject: [PATCH 16/31] add enum class Suit and change it in methods --- src/com/codecool/klondike/Card.java | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/com/codecool/klondike/Card.java b/src/com/codecool/klondike/Card.java index 896f1fb..b826249 100644 --- a/src/com/codecool/klondike/Card.java +++ b/src/com/codecool/klondike/Card.java @@ -9,7 +9,7 @@ public class Card extends ImageView { - private int suit; + private Suit suit; private int rank; private boolean faceDown; @@ -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(Suit suit, int rank, boolean faceDown) { this.suit = suit; this.rank = rank; this.faceDown = faceDown; @@ -34,7 +34,7 @@ public Card(int suit, int rank, boolean faceDown) { setEffect(dropShadow); } - public int getSuit() { + public Suit getSuit() { return suit; } @@ -88,7 +88,9 @@ public static boolean isSameSuit(Card card1, Card card2) { public static List createNewDeck() { List result = new ArrayList<>(); - for (int suit = 1; suit < 5; suit++) { + + //Zamienic for na for generyczny + for (Suit suit : Suit.values()) { for (int rank = 1; rank < 14; rank++) { result.add(new Card(suit, rank, true)); } @@ -99,18 +101,18 @@ public static List createNewDeck() { public static void loadCardImages() { cardBackImage = new Image("card_images/card_back.png"); String suitName = ""; - for (int suit = 1; suit < 5; suit++) { + for (Suit suit : Suit.values()) { switch (suit) { - case 1: + case HEARTS: suitName = "hearts"; break; - case 2: + case DIAMONDS: suitName = "diamonds"; break; - case 3: + case SPADES: suitName = "spades"; break; - case 4: + case CLUBS: suitName = "clubs"; break; } @@ -123,4 +125,7 @@ public static void loadCardImages() { } } -} + + public enum Suit {DIAMONDS, HEARTS, CLUBS, SPADES} + +} \ No newline at end of file From 1a70b8305b39a1f47c8d4f61977af4e87fe69c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Thu, 27 Sep 2018 12:22:21 +0200 Subject: [PATCH 17/31] Implement mechanism which every move --- src/com/codecool/klondike/Game.java | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 32963d7..2a92b5c 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -147,7 +147,33 @@ private void handleValidMove(Card card, Pile destPile) { draggedCards.clear(); } + private void saveMove(Card card) { + List copyOfDraggedList = FXCollections.observableArrayList(draggedCards); + Pile sourcePile = card.getContainingPile(); + Runnable move; + if (card.getContainingPile().getPileType() == Pile.PileType.STOCK) { + move = () -> { + card.moveToPile(sourcePile); + card.flip(); + }; + } + else { + Boolean isLastCardFaceDown = sourcePile.getTopCard().isFaceDown(); + + move = () -> { + if(isLastCardFaceDown) { + sourcePile.getTopCard().flip(); + } + + MouseUtil.slideToDest(copyOfDraggedList, sourcePile); + }; + } + + Undoer.getInstance().addAction(Undoer.ActionOwner.USER, move); + } + + private void initPiles() { stockPile = new Pile(Pile.PileType.STOCK, "Stock", STOCK_GAP); stockPile.setBlurredBackground(); From 0251c072af0621adb79e71580dcab3557213f008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Thu, 27 Sep 2018 12:25:17 +0200 Subject: [PATCH 18/31] Add invocation for which save move --- src/com/codecool/klondike/Game.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 2a92b5c..747ecef 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -37,6 +37,7 @@ public class Game extends Pane { private EventHandler onMouseClickedHandler = e -> { Card card = (Card) e.getSource(); if (card.getContainingPile().getPileType() == Pile.PileType.STOCK) { + saveMove(card); card.moveToPile(discardPile); card.flip(); card.setMouseTransparent(false); @@ -80,6 +81,7 @@ public class Game extends Pane { Pile pile = getValidIntersectingPile(card, tableauPiles); //TODO if (pile != null) { + saveMove(card); handleValidMove(card, pile); } else { draggedCards.forEach(MouseUtil::slideBack); From fed3948e12a09ff7c0902fb5a0bc960be4ae42e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Thu, 27 Sep 2018 12:32:24 +0200 Subject: [PATCH 19/31] Add dummy button for test --- src/com/codecool/klondike/Game.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 747ecef..cd493fe 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -4,6 +4,7 @@ import javafx.collections.ListChangeListener; import javafx.event.EventHandler; import javafx.scene.control.Alert; +import javafx.scene.control.Button; import javafx.scene.image.Image; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Background; @@ -98,6 +99,19 @@ public Game() { deck = Card.createNewDeck(); initPiles(); dealCards(); + + // ======= Added dummy for test =============== + Button button = new Button(); + button.setText("Undo"); + button.setOnMouseClicked(new EventHandler() { + @Override + public void handle(MouseEvent event) { + Undoer.getInstance().undoAction(); + } + }); + // ============================================ + + getChildren().add(button); } public void addMouseEventHandlers(Card card) { From 3be6dae08b5f8f7bb225b37cc4ab3629cc359683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Thu, 27 Sep 2018 12:33:07 +0200 Subject: [PATCH 20/31] Improve style add few blank lines --- src/com/codecool/klondike/Game.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index cd493fe..d9bca43 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -39,6 +39,7 @@ public class Game extends Pane { Card card = (Card) e.getSource(); if (card.getContainingPile().getPileType() == Pile.PileType.STOCK) { saveMove(card); + card.moveToPile(discardPile); card.flip(); card.setMouseTransparent(false); @@ -83,6 +84,7 @@ public class Game extends Pane { //TODO if (pile != null) { saveMove(card); + handleValidMove(card, pile); } else { draggedCards.forEach(MouseUtil::slideBack); @@ -159,7 +161,9 @@ private void handleValidMove(Card card, Pile destPile) { msg = String.format("Placed %s to %s.", card, destPile.getTopCard()); } System.out.println(msg); + MouseUtil.slideToDest(draggedCards, destPile); + draggedCards.clear(); } @@ -189,7 +193,7 @@ private void saveMove(Card card) { Undoer.getInstance().addAction(Undoer.ActionOwner.USER, move); } - + private void initPiles() { stockPile = new Pile(Pile.PileType.STOCK, "Stock", STOCK_GAP); stockPile.setBlurredBackground(); From 2fd967b8d1c6634fc107f2c10d0ac15a4a3fa2d3 Mon Sep 17 00:00:00 2001 From: maretzky85 Date: Thu, 27 Sep 2018 13:18:07 +0200 Subject: [PATCH 21/31] Changed rank from int to enum type --- src/com/codecool/klondike/Card.java | 18 +++++++-------- src/com/codecool/klondike/Rank.java | 34 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 src/com/codecool/klondike/Rank.java diff --git a/src/com/codecool/klondike/Card.java b/src/com/codecool/klondike/Card.java index 896f1fb..07c322d 100644 --- a/src/com/codecool/klondike/Card.java +++ b/src/com/codecool/klondike/Card.java @@ -10,7 +10,7 @@ public class Card extends ImageView { private int suit; - private int rank; + private Rank 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(int suit, Rank rank, boolean faceDown) { this.suit = suit; this.rank = rank; this.faceDown = faceDown; @@ -38,7 +38,7 @@ public int getSuit() { return suit; } - public int getRank() { + public Rank getRank() { return rank; } @@ -47,7 +47,7 @@ public boolean isFaceDown() { } public String getShortName() { - return "S" + suit + "R" + rank; + return "S" + suit + "R" + rank.getValue(); } public DropShadow getDropShadow() { @@ -89,8 +89,8 @@ 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++) { - result.add(new Card(suit, rank, true)); + for (Rank cardRank: Rank.values()){ + result.add(new Card(suit, cardRank, true)); } } return result; @@ -114,9 +114,9 @@ public static void loadCardImages() { suitName = "clubs"; break; } - for (int rank = 1; rank < 14; rank++) { - String cardName = suitName + rank; - String cardId = "S" + suit + "R" + rank; + for (Rank cardRank: Rank.values()){ + String cardName = suitName + cardRank.getValue(); + String cardId = "S" + suit + "R" + cardRank.getValue(); String imageFileName = "card_images/" + cardName + ".png"; cardFaceImages.put(cardId, new Image(imageFileName)); } diff --git a/src/com/codecool/klondike/Rank.java b/src/com/codecool/klondike/Rank.java new file mode 100644 index 0000000..02e9f00 --- /dev/null +++ b/src/com/codecool/klondike/Rank.java @@ -0,0 +1,34 @@ +package com.codecool.klondike; + +public enum Rank { + Ace(1), + Eight(8), + Five(5), + Four(4), + Jack(11), + King(13), + Nine(9), + Queen(12), + Seven(7), + Six(6), + Ten(10), + Three(3), + Two(2); + + private int cardValue; + + Rank(int rankValue) { + cardValue = rankValue; + } + + public int getValue() { + return cardValue; + } + + public boolean previousEqual(Card card) { + return (cardValue - 1) == card.getRank().getValue(); + } + public boolean nextEqual(Card card) { + return ((cardValue + 1) == card.getRank().getValue()) + } +} From eeadbaee1096614f520d33479d22d2cbdf2f4d6d Mon Sep 17 00:00:00 2001 From: Maretzky85 <34583771+Maretzky85@users.noreply.github.com> Date: Thu, 27 Sep 2018 13:53:04 +0200 Subject: [PATCH 22/31] update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit missing średniczek --- src/com/codecool/klondike/Rank.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/codecool/klondike/Rank.java b/src/com/codecool/klondike/Rank.java index 02e9f00..8dd928b 100644 --- a/src/com/codecool/klondike/Rank.java +++ b/src/com/codecool/klondike/Rank.java @@ -29,6 +29,6 @@ public boolean previousEqual(Card card) { return (cardValue - 1) == card.getRank().getValue(); } public boolean nextEqual(Card card) { - return ((cardValue + 1) == card.getRank().getValue()) + return ((cardValue + 1) == card.getRank().getValue()); } } From c33c052664eac37236d5f37d45e4d097f200d202 Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Thu, 27 Sep 2018 14:27:15 +0200 Subject: [PATCH 23/31] Add restart button --- src/com/codecool/klondike/Game.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index a648b21..ccc9709 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -4,6 +4,7 @@ import javafx.collections.ListChangeListener; import javafx.event.EventHandler; import javafx.scene.control.Alert; +import javafx.scene.control.Button; import javafx.scene.image.Image; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Background; @@ -12,6 +13,7 @@ import javafx.scene.layout.BackgroundRepeat; import javafx.scene.layout.BackgroundSize; import javafx.scene.layout.Pane; +import javafx.scene.text.TextAlignment; import java.util.*; @@ -179,6 +181,13 @@ private void initPiles() { discardPile.setLayoutY(20); getChildren().add(discardPile); + Button restartBtn = new Button("Restart"); + restartBtn.setTextAlignment(TextAlignment.CENTER); + restartBtn.relocate(1300,840); + restartBtn.setStyle("-fx-font: 18 times-new-roman; -fx-base: #c26573;"); + getChildren().add(restartBtn); + + for (int i = 0; i < 4; i++) { Pile foundationPile = new Pile(Pile.PileType.FOUNDATION, "Foundation " + i, FOUNDATION_GAP); foundationPile.setBlurredBackground(); From 0d04cc4102dbad04b7d0bb8aba60d674c6594285 Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Thu, 27 Sep 2018 14:31:03 +0200 Subject: [PATCH 24/31] Add clear Pane method, deletes elements from Pane/Scene --- src/com/codecool/klondike/Game.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index ccc9709..9a3e9ab 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -206,6 +206,14 @@ private void initPiles() { } } + private void clearPane() { + stockPile.clear(); + discardPile.clear(); + foundationPiles.clear(); + tableauPiles.clear(); + this.getChildren().clear(); + } + public void dealCards() { Iterator deckIterator = deck.iterator(); //TODO From 7cb0c204da791a3055c4639c3adc869750312bea Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Thu, 27 Sep 2018 14:34:15 +0200 Subject: [PATCH 25/31] Add restart method that clears Pane and initiate the game. --- src/com/codecool/klondike/Game.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 9a3e9ab..42cf153 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -13,6 +13,7 @@ import javafx.scene.layout.BackgroundRepeat; import javafx.scene.layout.BackgroundSize; import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; import javafx.scene.text.TextAlignment; import java.util.*; @@ -214,6 +215,13 @@ private void clearPane() { this.getChildren().clear(); } + private void restart() { + clearPane(); + deck = Card.createNewDeck(); + initPiles(); + dealCards(); + } + public void dealCards() { Iterator deckIterator = deck.iterator(); //TODO From 2aaaf02378d615076fe161189d67f248eee2de9e Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Thu, 27 Sep 2018 14:38:57 +0200 Subject: [PATCH 26/31] Add mouse event handler for restart button --- src/com/codecool/klondike/Game.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 42cf153..165f441 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -188,6 +188,13 @@ private void initPiles() { restartBtn.setStyle("-fx-font: 18 times-new-roman; -fx-base: #c26573;"); getChildren().add(restartBtn); + restartBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { + @Override + public void handle(MouseEvent e) { + restart(); + } + }); + for (int i = 0; i < 4; i++) { Pile foundationPile = new Pile(Pile.PileType.FOUNDATION, "Foundation " + i, FOUNDATION_GAP); From 93be566c15a5ea0403cec822e335af83681e34d1 Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Thu, 27 Sep 2018 15:05:05 +0200 Subject: [PATCH 27/31] Shuffle the deck while creating new one (in createNewDeck method) --- src/com/codecool/klondike/Card.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/codecool/klondike/Card.java b/src/com/codecool/klondike/Card.java index 46cf604..2d27750 100644 --- a/src/com/codecool/klondike/Card.java +++ b/src/com/codecool/klondike/Card.java @@ -7,6 +7,8 @@ import java.util.*; +import static java.util.Collections.*; + public class Card extends ImageView { private int suit; @@ -94,9 +96,11 @@ public static List createNewDeck() { 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 = ""; From d62baeda95e3b39480c73d76bddea52a3735e60d Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Thu, 27 Sep 2018 16:10:09 +0200 Subject: [PATCH 28/31] Add beginning card distribution --- src/com/codecool/klondike/Game.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 165f441..5a863a9 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -231,7 +231,19 @@ private void restart() { public void dealCards() { Iterator deckIterator = deck.iterator(); - //TODO + for (Pile pile: tableauPiles + ) { + int index = tableauPiles.indexOf(pile); + System.out.println(index); + + for(int i = 0; i < index+1; i++){ + Card card = deckIterator.next(); + pile.addCard(card); + addMouseEventHandlers(card); + getChildren().add(card); + } + + } deckIterator.forEachRemaining(card -> { stockPile.addCard(card); addMouseEventHandlers(card); From 2a00da51c2bb55686da7065b5009a012ff78d680 Mon Sep 17 00:00:00 2001 From: AJuszczakiewicz Date: Thu, 27 Sep 2018 16:16:22 +0200 Subject: [PATCH 29/31] Flip last card in pile during game start Small refactoring of restart button --- src/com/codecool/klondike/Game.java | 40 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 5a863a9..50bdb2a 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -106,6 +106,7 @@ public Game() { deck = Card.createNewDeck(); initPiles(); dealCards(); + addRestartBtn(); } public void addMouseEventHandlers(Card card) { @@ -182,20 +183,6 @@ private void initPiles() { discardPile.setLayoutY(20); getChildren().add(discardPile); - Button restartBtn = new Button("Restart"); - restartBtn.setTextAlignment(TextAlignment.CENTER); - restartBtn.relocate(1300,840); - restartBtn.setStyle("-fx-font: 18 times-new-roman; -fx-base: #c26573;"); - getChildren().add(restartBtn); - - restartBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { - @Override - public void handle(MouseEvent e) { - restart(); - } - }); - - for (int i = 0; i < 4; i++) { Pile foundationPile = new Pile(Pile.PileType.FOUNDATION, "Foundation " + i, FOUNDATION_GAP); foundationPile.setBlurredBackground(); @@ -214,6 +201,20 @@ public void handle(MouseEvent e) { } } + private void addRestartBtn(){ + Button restartBtn = new Button("Restart"); + restartBtn.setTextAlignment(TextAlignment.CENTER); + restartBtn.relocate(1300,840); + restartBtn.setStyle("-fx-font: 18 times-new-roman; -fx-base: #c26573;"); + getChildren().add(restartBtn); + + restartBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { + @Override + public void handle(MouseEvent e) { + restart(); + } + }); + } private void clearPane() { stockPile.clear(); discardPile.clear(); @@ -227,23 +228,26 @@ private void restart() { deck = Card.createNewDeck(); initPiles(); dealCards(); + addRestartBtn(); } public void dealCards() { Iterator deckIterator = deck.iterator(); - for (Pile pile: tableauPiles - ) { + + for (Pile pile: tableauPiles) { int index = tableauPiles.indexOf(pile); - System.out.println(index); for(int i = 0; i < index+1; i++){ Card card = deckIterator.next(); pile.addCard(card); addMouseEventHandlers(card); getChildren().add(card); + if(i==index){ + card.flip(); + } } - } + deckIterator.forEachRemaining(card -> { stockPile.addCard(card); addMouseEventHandlers(card); From 9daa3c09b31476e01163d1ea9b438d300e3bd415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Thu, 27 Sep 2018 16:50:11 +0200 Subject: [PATCH 30/31] Fix last pile card flip --- src/com/codecool/klondike/Game.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index d9bca43..0a918be 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -180,10 +180,10 @@ private void saveMove(Card card) { } else { Boolean isLastCardFaceDown = sourcePile.getTopCard().isFaceDown(); - + Card saveLastCardFromPile = sourcePile.getTopCard(); move = () -> { if(isLastCardFaceDown) { - sourcePile.getTopCard().flip(); + saveLastCardFromPile.flip(); } MouseUtil.slideToDest(copyOfDraggedList, sourcePile); From d679f8107be0850b6de46dd052eec0406e8586a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Wa=C5=82ach?= Date: Thu, 27 Sep 2018 17:44:15 +0200 Subject: [PATCH 31/31] Integrate branch features/enum with develop --- src/com/codecool/klondike/Card.java | 32 +++------- src/com/codecool/klondike/Game.java | 91 ++++++++++++++++++++++----- src/com/codecool/klondike/Pile.java | 5 +- src/com/codecool/klondike/Suit.java | 24 +++++++ src/com/codecool/klondike/Undoer.java | 45 +++++++++++++ 5 files changed, 156 insertions(+), 41 deletions(-) create mode 100644 src/com/codecool/klondike/Suit.java create mode 100644 src/com/codecool/klondike/Undoer.java diff --git a/src/com/codecool/klondike/Card.java b/src/com/codecool/klondike/Card.java index aff2b00..9dca8d7 100644 --- a/src/com/codecool/klondike/Card.java +++ b/src/com/codecool/klondike/Card.java @@ -47,7 +47,7 @@ public boolean isFaceDown() { } public String getShortName() { - return "S" + suit + "R" + rank.getValue(); + return "S" + suit.getValue() + "R" + rank.getValue(); } public DropShadow getDropShadow() { @@ -78,8 +78,10 @@ public String toString() { } public static boolean isOppositeColor(Card card1, Card card2) { - //TODO - return true; + if (card1.suit.getValue() <= 2 && card2.suit.getValue() > 2) + return true; + + return false; } public static boolean isSameSuit(Card card1, Card card2) { @@ -100,32 +102,14 @@ public static List createNewDeck() { public static void loadCardImages() { cardBackImage = new Image("card_images/card_back.png"); - String suitName = ""; for (Suit suit : Suit.values()) { - switch (suit) { - case HEARTS: - suitName = "hearts"; - break; - case DIAMONDS: - suitName = "diamonds"; - break; - case SPADES: - suitName = "spades"; - break; - case CLUBS: - suitName = "clubs"; - break; - } for (Rank cardRank: Rank.values()){ - String cardName = suitName + cardRank.getValue(); - String cardId = "S" + suit + "R" + cardRank.getValue(); + String cardName = suit.getTextName() + cardRank.getValue(); + String cardId = "S" + suit.getValue() + "R" + cardRank.getValue(); String imageFileName = "card_images/" + cardName + ".png"; + System.out.println(imageFileName); cardFaceImages.put(cardId, new Image(imageFileName)); } } } - - - public enum Suit {DIAMONDS, HEARTS, CLUBS, SPADES} - } \ No newline at end of file diff --git a/src/com/codecool/klondike/Game.java b/src/com/codecool/klondike/Game.java index 32963d7..48b56d2 100644 --- a/src/com/codecool/klondike/Game.java +++ b/src/com/codecool/klondike/Game.java @@ -4,6 +4,7 @@ import javafx.collections.ListChangeListener; import javafx.event.EventHandler; import javafx.scene.control.Alert; +import javafx.scene.control.Button; import javafx.scene.image.Image; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Background; @@ -13,9 +14,7 @@ import javafx.scene.layout.BackgroundSize; import javafx.scene.layout.Pane; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; public class Game extends Pane { @@ -37,6 +36,8 @@ public class Game extends Pane { private EventHandler onMouseClickedHandler = e -> { Card card = (Card) e.getSource(); if (card.getContainingPile().getPileType() == Pile.PileType.STOCK) { + saveMove(card); + card.moveToPile(discardPile); card.flip(); card.setMouseTransparent(false); @@ -62,15 +63,20 @@ public class Game extends Pane { double offsetY = e.getSceneY() - dragStartY; draggedCards.clear(); - draggedCards.add(card); + int draggedCardIndex = activePile.getCards().indexOf(card); + + activePile.getCards().listIterator(draggedCardIndex) + .forEachRemaining(draggedCards::add); - card.getDropShadow().setRadius(20); - card.getDropShadow().setOffsetX(10); - card.getDropShadow().setOffsetY(10); + draggedCards.forEach(draggedCard -> { + draggedCard.getDropShadow().setRadius(20); + draggedCard.getDropShadow().setOffsetX(10); + draggedCard.getDropShadow().setOffsetY(10); - card.toFront(); - card.setTranslateX(offsetX); - card.setTranslateY(offsetY); + draggedCard.toFront(); + draggedCard.setTranslateX(offsetX); + draggedCard.setTranslateY(offsetY); + }); }; private EventHandler onMouseReleasedHandler = e -> { @@ -78,12 +84,18 @@ public class Game extends Pane { return; Card card = (Card) e.getSource(); Pile pile = getValidIntersectingPile(card, tableauPiles); + if(pile == null) { + pile = getValidIntersectingPile(card, foundationPiles); + } //TODO if (pile != null) { + saveMove(card); + + //TODO isOpositeColor handleValidMove(card, pile); } else { draggedCards.forEach(MouseUtil::slideBack); - draggedCards = null; + draggedCards.removeAll(draggedCards); } }; @@ -96,6 +108,19 @@ public Game() { deck = Card.createNewDeck(); initPiles(); dealCards(); + + // ======= Added dummy for test =============== + Button button = new Button(); + button.setText("Undo"); + button.setOnMouseClicked(new EventHandler() { + @Override + public void handle(MouseEvent event) { + Undoer.getInstance().undoAction(); + } + }); + // ============================================ + + getChildren().add(button); } public void addMouseEventHandlers(Card card) { @@ -106,13 +131,23 @@ public void addMouseEventHandlers(Card card) { } public void refillStockFromDiscard() { - //TODO + Collections.reverse(discardPile.getCards()); + discardPile.getCards().forEach(card -> card.flip()); + + MouseUtil.slideToDest(discardPile.getCards(), stockPile); + System.out.println("Stock refilled from discard pile."); } public boolean isMoveValid(Card card, Pile destPile) { - //TODO - return true; + if (foundationPiles.contains(destPile)) { + System.out.println("asdasd"); + return true; + } + else if (tableauPiles.contains(destPile)) + return true; + + return false; } private Pile getValidIntersectingPile(Card card, List piles) { Pile result = null; @@ -143,10 +178,38 @@ private void handleValidMove(Card card, Pile destPile) { msg = String.format("Placed %s to %s.", card, destPile.getTopCard()); } System.out.println(msg); + MouseUtil.slideToDest(draggedCards, destPile); + draggedCards.clear(); } + private void saveMove(Card card) { + List copyOfDraggedList = FXCollections.observableArrayList(draggedCards); + Pile sourcePile = card.getContainingPile(); + Runnable move; + + if (card.getContainingPile().getPileType() == Pile.PileType.STOCK) { + move = () -> { + card.moveToPile(sourcePile); + card.flip(); + }; + } + else { + Boolean isLastCardFaceDown = sourcePile.getTopCard().isFaceDown(); + Card saveLastCardFromPile = sourcePile.getTopCard(); + move = () -> { + if(isLastCardFaceDown) { + saveLastCardFromPile.flip(); + } + + MouseUtil.slideToDest(copyOfDraggedList, sourcePile); + }; + } + + Undoer.getInstance().addAction(Undoer.ActionOwner.USER, move); + } + private void initPiles() { stockPile = new Pile(Pile.PileType.STOCK, "Stock", STOCK_GAP); diff --git a/src/com/codecool/klondike/Pile.java b/src/com/codecool/klondike/Pile.java index 46ea20f..9f34243 100644 --- a/src/com/codecool/klondike/Pile.java +++ b/src/com/codecool/klondike/Pile.java @@ -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.removeAll(cards); } public void addCard(Card card) { diff --git a/src/com/codecool/klondike/Suit.java b/src/com/codecool/klondike/Suit.java new file mode 100644 index 0000000..c5146aa --- /dev/null +++ b/src/com/codecool/klondike/Suit.java @@ -0,0 +1,24 @@ +package com.codecool.klondike; + +public enum Suit { + DIAMONDS(1, "diamonds"), + HEARTS(2, "hearts"), + CLUBS(3, "clubs"), + SPADES(4, "spades"); + + private int number; + private String textName; + + Suit(int number, String textName) { + this.number = number; + this.textName = textName; + } + + public int getValue() { + return number; + } + + public String getTextName() { + return textName; + } +} diff --git a/src/com/codecool/klondike/Undoer.java b/src/com/codecool/klondike/Undoer.java new file mode 100644 index 0000000..b64a50f --- /dev/null +++ b/src/com/codecool/klondike/Undoer.java @@ -0,0 +1,45 @@ +package com.codecool.klondike; + +import java.util.*; +import java.util.Map.Entry; + +public class Undoer { + public enum ActionOwner { + USER, + GAME + } + + private static Undoer instance; + + private final LinkedList> undoSteps; + + private Undoer(){ + undoSteps = new LinkedList<>(); + } + + public void addAction(ActionOwner owner, Runnable undoStep) { + Entry step = new AbstractMap.SimpleEntry<>(owner, undoStep); + + undoSteps.push(step); + } + + public void undoAction() { + if(undoSteps.isEmpty()) return; + + Entry lastStep = undoSteps.pop(); + lastStep.getValue().run(); + + while(!undoSteps.isEmpty() && undoSteps.getLast().getKey() == ActionOwner.GAME) { + lastStep = undoSteps.pop(); + lastStep.getValue().run(); + } + } + + public static Undoer getInstance() { + if(instance == null) { + instance = new Undoer(); + } + + return instance; + } +}