Lingo Game: Memory

Play "memory_game"

In this classical game, an even number of cards are arranged face-down on a table in a rectangular fashion. One or more player(s) take turns flipping pairs of cards.

If they happen to match, it is a win for the player who turned them over. Then the cards stay open (or removed from the table).
If they do not match, they are flipped back over.

We will be discussing how to create a prototype of such a game with 8 cards and a single player (puzzle). The computer facilitates the playing.

To implement the memory game in Lingo, start by downloading memory_game0.dir .
We need to come up first with the data structures that will hold the information of the cards, and with pseudocode that will capture the game's mechanics.

Data Structures

We will use a list to keep track of the cards. We have placed cards in the first few entries of the cast, and for this prototype we will use cards in positions 1-4. Note that there is a blank card in position 51 which will serve as the generic card to flip.

It makes sense the initialization to happen in the preparatory frame or when we click the "New Game" button:

   -- assume 8 blank cards are placed in sprites (channels) 1-8
   totalCards = 8
   -- create a list  of totalCards/2 pairs of cards
   cardList = list()
   cardList = [1,1,2,2,3,3,4,4] -- they need to be shuffled

To start a game we will need to shuffle the cards.

Shuffling the cards

To create the effect of a shuffle, we need to be able to create a random list of pairs of numbers between 1 and half-the-number-of-cards on the table.

The easiest way would be to scramble them! We can start with the non-sorted cardList and then permute their order by repeatedly removing a random one and placing it into a new pile called aftershuffle.

on shuffleCards
   global cardList, totalCards
   aftershuffle = []  -- another way to create an empty list
   repeat while count(cardList) > 0
      pick = random(count(cardList))  -- pick a random card
      symbol = getAt(cardList, pick)  -- find out which its symbol is
      deleteAt cardList, pick         -- remove it from the cardList
      append aftershuffle, symbol     -- and place it in the new list
   end repeat
   cardList = aftershuffle     -- the new list becomes our shuffled cardList
end

After executing shuffleCards, we are ready to start a game!

The pseudocode

A card is selected for flipping by clicking on in. So, it makes sense to create a handler only on the cast member of the unflipped card (the blank card in position 51). (Why?)

Here is in pseudocode what should happen:

 on mouseUp
   if it is the first card to be flipped then
      flip it -- find out which its symbol is
      remember its symbol
   else -- if it is the second card flipped in this turn
      flip it -- find out which its symbol is
      check whether its symbol matches the previous card's symbol
   end if
end


We need to resolve the following problems:
1. How do we remember the card symbols?
2. How do we find out which symbol was picked?
3. How do we check for match?


1. How do we remember the card symbols?


To remember the card symbols between flips, we can simply use a couple of global variables, called card1 and card2 which are initialized at the beginning of the game to 0 and are reset at the end of every turn. The player can select for card1 and card2 any of the cards we have arranged on the table. It makes sense this to happen in a frame prior to the one playing the game since we may want to replay the game again and again.

-- Prepare for a new game
on exitFrame me
   global card1, card2
   card1 = 0     -- Note: 0 means "no card chosen yet"
   card2 = 0
   -- Also initialize your data structures as explained above
end
2. How do we find out which symbol was picked?

To find out which symbol was picked we need to see which blank card the user clicked on, and find the corresponding number from the cardList.

   card1 = the Clickon                -- Returns the channel number (1-8) of a selected card
   sprite(card1).member = cardList[card1]  -- Array-like code for cardList.getAt(card1)


3. How do we check for match?


To check for a match between cards we need to see if their symbols match. If they do, fine, give them some feedback via audio to that effect. If not, we need to flip them back and prepare for the next turn. Which means: make card1 and card2 be again 0.

on checkForMatch c1, c2
   global card1, card2
   if c1 = c2 then   -- Play a sound to give feedback
      sound(1).queue(member "Match Sound")
      sound(1).play() 
-- and leave the cards on else -- Play a different sound for feedback sound(1).queue(member "No Match Sound") sound(1).play() -- Not a match, turn the cards back to blank sprite(card1).member = 51 -- Remember that 51 is the blank card sprite(card2).member = 51 end if -- prepare for another round of the game card1 = 0 card2 = 0 end


So, the code on the blank card now is:

on mouseUp me
   global card1, card2, cardList
 
   if card1 = 0 then -- it was the first card of the pair to be clicked
      card1 = the Clickon
      sprite(card1).member = cardList[card1]
   else
      card2 = the ClickOn
      sprite(card2).member = cardList[card2]
      -- anything else?
   end if
end

This almost works. Because of the speed of the program flipping cards, the user will not be able to see what the second card is before it is flipped back. (For the first card there is no problem, because we are waiting for the user action.) We need to give the user time to see the second card. We can do that by initializing a timer

       myTimer = the ticks 

in the no-match case wait before flipping back for one second. (Note: You just introduced a new variable...)

Who will check whether the timer is done? The best place to do this done is to put the code for waiting in the game frame. (Having a repeat doing nothing will not work because the optimizing compiler will ignore it. I can hear the compiler saying: "You eventually want it to turn to blank, let me do it for you...")

-- Game frame
on exitFrame me
global card1, card2, cardList, myTimer
waitingTime = 60 -- That is: 1 sec = 60 ticks
if myTimer + waitingTime < the ticks then -- Time passed
if card2 <>0 then checkForMatch cardList[card1], cardList[card2]
end if

go to the frame
end
The rest

That's it. Well, almost.

  • We may need to detect when all cards have been flipped and declare the end of the game. How would you do it?
  • You may need to try to have a game with 16 cards, how would you do that?
  • You might want to make it more challenging by having an on-screen timer telling the player how long did it take them to solve the puzzle. But not too challenging!
  • You may want to change the game to have 2 levels, easy (as is) and tough (with 16 cards).
  • You may want to be able to save a game to finish later.
  • You may also need to make this a 2-player game, where players take turns flipping cards and the one with the most pairs wins. That's a little more challenging. How would you do that?

Achnowledgements

The game idea and images used were taken from Gary Rosenzweig's Advanced Lingo for Games

 

 

Maintained By: Takis Metaxas
Last Modified: November 12, 2014