Articles Archive
Articles Search
Director Wiki

Random Shuffle Memory Match

March 14, 1999
by Pat McClellan

Dear Multimedia Handyman,

I've been "noodling" with your memory game code and I need to make some modifications. I want to be able to have the cards shuffle themselves so they are a random set of 6 matched pairs, and then using some kind of a swap to place them at a certain locH and locV. Can you help?

Confused in Canada

Dear Confused in Canada,

This is a great addition to the behavior. Let's start by reviewing the original behavior. You drop it on the sprites representing the "backs" of the cards to be "flipped". When you drop it on there, you can specify the front (which is stored in a property called pFront.) The rest of the behavior handles swapping the "front" and "back" when they're clicked -- as well as tracking whether it's a match.

-- MatchGame behavior
-- Used to set up pairs of matching sprites
property pMyID  -- matching pairs will have the same ID
property pMySpriteNum
property pFront -- the "front" of the card
property pBack -- the "back" of the card
property pSelected -- state of the card, selected/not
property pLocH
global gSpritesMatched
on getPropertyDescriptionList
  set pmySpriteNum = the currentSpriteNum
  if pMySpriteNum = 0 then 
    set memdefault = 0 
    set memref = the member of sprite pMySpriteNum
    set castlibnum = the castlibnum of memref
    set memDefault = member (the membernum of ¬
      member memref + 1) of castlib castlibnum
  end if
  set p_list = [#pMyID: [ #comment:"ID:", #format: ¬
    #symbol, #default: #item1 ], #pFront:[ ¬
    #comment:"Member:", #format: #graphic, ¬
    #default: memDefault] ]
  return p_list  
on beginSprite me
  set pMySpriteNum = the spriteNum of me
  --  put "Sprite" && pMySpriteNum && "is" ¬
    && pMyID && pFront
  set pSelected = FALSE
  set pBack = the member of sprite pMySpriteNum
  set pLocH = the locH of sprite pMySpriteNum
on mouseUp me
  set pSelected = TRUE
  set the member of sprite pMySpriteNum to pFront
  sendAllSprites(#checkMatch, pMyID, pMySpriteNum)
on checkMatch me, testID, testSpriteNum
  if pSelected = TRUE AND testSpriteNum <> ¬
    pMySpriteNum then
    if testID = pMyID then
      repeat while the timer < 60 -- delay
      end repeat
      makeMatch me
      sendSprite(testSpriteNum, #makeMatch)
      repeat while the timer < 60 -- delay
      end repeat
      flipBack me
      sendSprite(testSpriteNum, #flipBack)
    end if
  end if
on makeMatch me
  -- do whatever you want
  set the locH of sprite pMySpriteNum to 800
  set pSelected = FALSE
  set gSpritesMatched = gSpritesMatched + 1
on flipBack me
  -- play annoying sfx if desired
  set the member of sprite pMySpriteNum to pBack
  set pSelected = FALSE
on reset me
  set the member of sprite pMySpriteNum to pBack
  set the locH of sprite pMySpriteNum to pLocH

Now the part you want to add is, essentially, randomizing the values for pFront. If we just add a handler to the bottom of this behavior, you'll still be asked to specify a front when you drop it on the sprite, but that can easily be over-ridden when you want to reset the game.

What we'll need to do is create two property lists of the possible castmembers that could be assigned to the cards, along with an "item ID number" so that we'll know which ones match. This item ID is important because you might want to assign 2 different cast members as a pair -- different cast members with the same item ID. For this demo, I set these lists up in the resetGame movie script:

on resetGame
  global gSpritesMatched
  set gSpritesMatched = 0
  sendAllSprites (#reset)
  set cardList1 = [#item1:(member 3 of castLib 1), ¬
    #item2:(member 4 of castLib 1), #item3:(member 5 ¬
    of castLib 1), #item4:(member 6 of castLib 1), ¬
    #item5:(member 7 of castLib 1), #item6:(member 8 ¬
    of castLib 1)]
  set cardList2 = [#item1:(member 3 of castLib 1), ¬
  #item2:(member 4 of castLib 1), #item3:(member 5 of ¬
  of castLib 1)]
  sendAllSprites(#randomizeCards, cardList1, cardList2)

This handler starts by resetting all the sprites to their original positions. Next, I create my two cardLists; in this case, they are identical because I'm using the same castmembers as pairs. NOTE: you can't simply say "set cardList2 = cardList1". It will screw it up. If you're looking for a shortcut, you have to say...

set cardList = duplicate(cardList1)

The reason is that if you just say set cardList2 = cardList1, it doesn't create a copy, but rather just a pointer to cardList1. That means if you delete something from cardList2, it really would get deleted from cardList1. Not good for our purposes.

The last step of the resetGame handler is to send out the message to all sprites to "randomizeCards". That's the new handler that will be added to the existing behavior:

-- add to bottom of Matchgame behavior
on randomizeCards me, cardList1, cardlist2
  set count1 = count(cardList1)
  set count2 = count(cardList2)
  if count1 = 0 and count2 = 0 then
  else if count1 = 0 then
    set myList = cardList2
  else if count2 = 0 then
    set myList = cardList1
    put "random assignment"   
    case random(2) of 
      1: set myList = cardList1
      2: set myList = cardList2
    end case
  end if
  set cardsLeft = count(myList)
  set whichCard = random(cardsLeft)
  set pFront = getAt(myList, whichCard)    
  set pMyID = getPropAt(myList, whichCard)
  deleteProp myList, pMyID

Most of this handler is dedicated to choosing an item from one of the lists at random. You can analyze the logic if you really have the time. Once that's decided, the itemID and pFront castmember are assigned, and then that selection is deleted from the list to avoid duplication. (This is the delete part that would screw us up if we hadn't made a duplicate of the list.)

That's the way it works. Download the code if you want to check under the hood (Mac or PC).

