3

I'm actually working on a memory game and I'm stuck at the point where I should write the gameplay-part of the game. So: I have an array of N card objects. Each object has an attribute called cardNum - an identifier. I think I should write an actionListener on that array, so when I flip a card, it puts the flipped card's cardNum in an array of two elements and if the two elements of the array are equal, a pair is found. The problem is that I just don't know how to get the last flipped card's cardNum. Any help would be appreciated.

Here's the way I tried:

private void easyGame(Card[] cards) {
    int flippedCards = 0;
    int card1;

    while(flippedCards != 24) {
        for(int i=0; i<cards.length; i++) {
            if(cards[i].getIsFlipped())
                flippedCards ++;
        }
        if(flippedCards % 2 == 0 && flippedCards > 0)
            for(int i=0; i<cards.length; i++) {
                card1 = getCardIndByCardNum(cards[i].getCardNum(), cards, i);

                if(!cards[card1].getIsFlipped()) {
                    for(int j=0; j<cards.length; j++) {
                        if(cards[i].getIsFlipped())
                            cards[i].flip();
                    }
                    flippedCards = 0;
                    break;
                }
            }
    }
}

The problem is that if I call this method, the game won't be drawn. May I use use threads somehow?

EDIT

Here is how I get the indexes of the clicked cards, and I call it in the UI:

private void setCardHandlers() {
    for(final Card card : cards) {

        card.setOnMouseClicked(new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent t) {
                clickedCardInd = getChildren().indexOf(card)-1;
            }
        });
    }
}

Than here is how I am using it:

setOnMouseReleased(new EventHandler<MouseEvent> () {

        @Override
        public void handle(MouseEvent t) {
            int cardIndex = clickedCardInd; // get index of what user clicked
            clickedCardInd = -1;
            if (cardIndex != -1 && moveRequestedFlag) { // our controller waits for the move
                // get the position and report
                moveRequestedFlag = false; // we handled the move
                //System.out.println(cardIndex);
                nextMove.setMove(cardIndex); // this will unblock controller's thread
            }
        }

    });

It has a delay on fliping cards, also in the easyGame the requestMove method sets both indexes to the same.

Adorjan
  • 39
  • 9
  • 1
    Can you post any code that you have tried to use – takendarkk Dec 28 '13 at 01:50
  • I have edited my post and added some code. – Adorjan Dec 28 '13 at 11:09
  • Not just post your code, but can you also explain the logic of your code? – Paul Samsotha Dec 28 '13 at 11:11
  • Okay. So, I have a total of 24 cards. I have a loop to check if all the cards are flipped or not. If not, than ff the `flippedCards` is even, with an other loop I'm searching for pairs. If the flipped cards are not in pairs, than it should flip all cards back. – Adorjan Dec 28 '13 at 11:31

1 Answers1

1

I would recommend splitting you responsibilities a bit into Model/View/Controller modules, which, in simplest case would look like :

  • Model - your game current state and data, i.e. cards array Cards mCards = new Cards[24];
  • View - your UI, that can reflect current state of mCards(model) on screen in Main thread
  • Controller - your main game logic. This is most complex part, responsible for
    1. requesting/handling user move,
    2. updating mCards(model) based on user move,
    3. Requesting UI to re-draw.

Contoroller's code (easyGame method) should run on separate thread to not block the UI.

Below I sketched a skeleton code that should fit your requirements :

class Game {

    /*
     * controller - main logic
     */
    void startEasyGame() {
        // initialize cards array, shuffle if needed

        // we start with zero cards flipped
        int flippedCards = 0;

        // main loop
        while (flippedCards != mCards.length) {
            // 1. show updated UI
            mBoard.showUpdatedCards();

            // 2. request player move 
            // and block current thread to wait till move is done
            // the result of the move - index of the card
            int index1 = requestMove();
            // temporarily flip first card face-up
            mCards[index1].flip();
            // show it on screen
            mBoard.showUpdatedCards();

            // same for second card
            int index2 = requestMove();
            mCards[index2].flip();
            mBoard.showUpdatedCards();


            // 3. check the result
            if (mCards[index1].getCardNum() == mCards[index2].getCardNum()) {
                // hooray, correct guess, update count
                // possibly show some encouraging feedback to user
                flippedCards += 2;
            } else {
                // incorrect, flip cards back face down
                mCards[index1].flip();
                mCards[index2].flip();
            }

        } // end of while loop

        // game ended -> show score and time
        mBoard.showResult();    
    }

}

EDIT
Extra details on how to await for result from UI thread :

int requestMove() {
    // 1. show user prompt to make a move
    // ...

    // 2. construct latch to wait for move done on UI thread
    mBoard.moveRequestedFlag = true;
    NextMove nextMove = new NextMove();
    mBoard.nextMove = nextMove;

    // 3. await for move and get the result
    return nextMove.getMove();
}

then, somewhere in UI code :

// handling card onClick somewhere on UI thread
if (mBoard.moveRequestedFlag) { // our controller waits for the move
    // get the position and report
    int cardIndex = ... // get index of what user clicked
    mBoard.moveReqestedFlag = false; // we handled the move
    mBoard.nextMove.setMove(cardIndex); // this will unblock controller's thread
}

and NextMove utility class to sync threads :

public class NextMove {
    private volatile int mCardIndex;
    private final CountDownLatch mMoveReady = new CountDownLatch(1);

    public int getMove() throws InterruptedException {
        mMoveReady.await();
        return mCardIndex;
    }

    public synchronized void setMove(int selectedCardIndex) {
        if (mMoveReady.getCount() > 0) {
            mCardIndex = selectedCardIndex;
            mMoveReady.countDown();
        }
    }
}
kiruwka
  • 9,250
  • 4
  • 30
  • 41
  • Thanks for your response. Actually, the `easyGame` method is in a `GameState` class. I initialized and shuffled the cards in it's constructor. After all I tried to call it in `Main` class like: `GameState gameState = new GameState(difficulty); gameState.startGame();` . The question is that how should I implement the `requestMove` method and what would be in the `showUpdatedCards` ? – Adorjan Dec 28 '13 at 13:18
  • I added code for `requestMove`. As for `showUpdatedCards` - it should simply send notification to UI, so that UI thread iterate thru `mCards[]` array and displays its current state on screen. – kiruwka Dec 28 '13 at 15:19
  • Happy New Year ! I was tryinig to implement your solution, but I can't get it work. Wherever if I call the `requestMove` method, the game stops and freezes. I still don't know what should I write in the `showUpdatedCards` method, because the `flip` method does it already (it flips the card visually). Could you help me out furthermore? Thanks in advice! – Adorjan Jan 04 '14 at 18:34
  • sure. First, note that `startEasyGame` method is meant to be called from a non-UI thread, so you need to create one for that. I presume that is why you have freezing `requestMove` problem. As for `showUPdatedCards`, it is for informing UI thread to re-draw, which is sent from your dedicated controller thread, which as I said earlier you have to create. If your platform allows to update UI from other threads, then you don't need it, as your flip method will do the job. – kiruwka Jan 04 '14 at 19:10
  • Hooray ! Finally, I've succeed making that thread work. The only problem I am encountering with is gettind the clicked card's index. I've putted a field in the `Card` class named `clicked` and every time I click on the card, the eventhandler `setOnMouseClicked` sets it to true. In the game controller I have a method to get the card's index. I putted it in a `setOnMouseReleased` handler hoping that it will syncronize the events. Also gave the main Thread some time to sleep, but it seems to be pointless. Any advice? – Adorjan Jan 05 '14 at 21:04
  • I recommend you use `CountdownLatch` as shown in my answer to sync the threads. Also, make `clicked` variable (or whatever variable accessed by both threads, including card index itself) `volatile`. And Main thread should never sleep - that could lead to UI being unresponsive. – kiruwka Jan 06 '14 at 07:45
  • I'm still encountering a syncronization problem. I use your `NextMove` class. For the first click nothing's gonna happen. The, for the second click the first clicked card will show up, and it's index will be set. There's a delay with the flips, so my application works pretty funny. – Adorjan Jan 07 '14 at 22:49
  • 1
    I suggest you debug what is causing this : `For the first click nothing's gonna happen.` Also, I recommend you declare your `mBoard.nextMove` member `volatile`. (You assign it every time you create `new NextMove` in `requestMove`). I also don't really see the reason of splitting your UI handlers in two : `onClicked/onReleased`, try putting everything in `onClicked`. – kiruwka Jan 08 '14 at 07:45
  • I had to set the `nextMove` member to volatile. It's working. Hooray ! Can't explain how happy I am. Thanks a lot ! :) – Adorjan Jan 08 '14 at 08:44
  • @Adorjan Thanks for reporting back. I feel happy for you too : ) – kiruwka Jan 08 '14 at 08:45