Scrambling an Array

BlitzMax Forums/BlitzMax Beginners Area/Scrambling an Array

I am trying to make a game similar to Memory, but I am struggling to find a way to mix up the cards while making sure that there are only 2 alike cards on the board. Is there a way to do it, or am I going about this all wrong?

I use this shuffle function:

Method Shuffle()
Local c:Object[]=ListToArray(Cards)
Local j:Int
Local card1:Object
For Local i:Int=c.length-1 To 0 Step -1
j=Rand(0, c.length-1)
card1=c[i]
c[i]=c[j]
c[j]=card1
Next
Cards=ListFromArray(c)
End Method

I'm sorry but I don't understand. I am working with an array called "cards". It may have 104 cards, only 8, or something in between. Mostly, I'm confused where to put the method in my code (I thought you could only use methods with types), and I am still quite a bit unclear on how to use lists.

I use an index # for my crads. 0 to 51. 0 is ace of hearts, 1 is two of hearts, etc. Then I know

Index \ 13

0=Heart, 1=Spade, 2=Club, 3=Diamond.

Set you array of cards with indexes=0 to 51, I used an array of types for my cards (I actually use a TList, but an array would work fine), that can keep track of facedown, location, etc...

Type TCard
Field X:Int, Y:Int
Field FaceDown:Int
Field Index:Int
End Type

Local Cards:TCard[51]

For Local i:Int=0 to 51
TCard[i].Index=i
Next

Then I call that shuffle routine and it mixes them all up.

Try making another array and picking one at random from the source array and copying it to the destination array and then removing that item from the source array. Actually this works better with lists which you can transform into arrays and vice versa.

Here's a generic function that should shuffle an array of strings or types. Just pass the array to it as the first parameter.

SuperStrict

'**** Shuffle function (Array of strings or user defined types required)

Function Shuffle(arr:Object[] Var, topindex:Int = -1, fragmentation:Float = 1.0)

	SeedRnd MilliSecs()

	If (topindex < 0) Or (topindex > arr.length - 1) Then topindex = arr.length - 1
		
	For Local n:Int = 1 To Int(topindex * fragmentation)
		
		Local i1:Int = Rand(0, topindex)
		Local i2:Int = Rand(0, topindex)
		
		Local temp:Object = arr[i1]
		arr[i1] = arr[i2]
		arr[i2] = temp
	
	Next
	
EndFunction



'**** Example

Graphics 800, 800

Local Deck:TCard[]							'<- **********
Local NumCards:Int
Local LastDealtCard:Int

RestoreData Cards
ReadData NumCards
Deck = New TCard[NumCards]
For Local i:Int = 0 To NumCards - 1
	Deck[i] = New TCard
	ReadData Deck[i].Label
Next

Shuffle Deck								'<- **********

LastDealtCard = NumCards

Local UndealtToggle:Int = True

While Not KeyHit(KEY_ESCAPE)

	'deal a card
	If KeyHit(KEY_SPACE)

		LastDealtCard :- 1
		If LastDealtCard < 0 

			Notify "All cards dealt."		
			End
	
		EndIf
	
	EndIf
	
	'Undo last dealt card
	If KeyHit(KEY_BACKSPACE) And LastDealtCard < NumCards
		LastDealtCard :+ 1
	EndIf
	
	'shuffle remaining undealt cards
	If KeyHit(KEY_S)
		Shuffle Deck, LastDealtCard - 1
	EndIf
	
	'Toggle undealt card view
	If KeyHit(KEY_TAB) Then UndealtToggle = Not(UndealtToggle)
		
	Cls
	

	SetColor 0, 255, 255
	DrawText "[S] - Shuffle undealt cards, [Space] - Deal a card, [BackSpace] - UNdeal card. [TAB] - Toggle undealt.", 2, 10
	

	SetColor 255, 255, 255
	DrawText "DEALT:", 10, 30
	For Local i:Int = NumCards - 1 To LastDealtCard Step - 1
		Local py:Int = ((NumCards - i) * 12) + 40
		DrawText (NumCards - i) + ": " + Deck[i].Label, 10, py
	Next
	
	If UndealtToggle
		SetColor 255, 0, 0
		DrawText "UNDEALT:", 200, 30
		For Local i:Int = 0 To LastDealtCard - 1
			Local py:Int = ((i + 1) * 12) + 40
			DrawText (i + 1) + ": " + Deck[i].Label, 200, py
		Next

	EndIf	
	
	Flip
		
Wend

End


Type TCard

	Field Label:String

End Type


#Cards
DefData 54
DefData "Ace, CLUBS", "2, CLUBS", "3, CLUBS", "4, CLUBS", "5, CLUBS"
DefData "6, CLUBS", "7, CLUBS", "8, CLUBS", "9, CLUBS", "10, CLUBS"
DefData "Jack, CLUBS", "Queen, CLUBS", "King, CLUBS"
DefData "Ace, DIAMONDS", "2, DIAMONDS", "3, DIAMONDS", "4, DIAMONDS", "5, DIAMONDS"
DefData "6, DIAMONDS", "7, DIAMONDS", "8, DIAMONDS", "9, DIAMONDS", "10, DIAMONDS"
DefData "Jack, DIAMONDS", "Queen, DIAMONDS", "King, DIAMONDS"
DefData "Ace, SPADES", "2, SPADES", "3, SPADES", "4, SPADES", "5, SPADES"
DefData "6, SPADES", "7, SPADES", "8, SPADES", "9, SPADES", "10, SPADES"
DefData "Jack, SPADES", "Queen, SPADES", "King, SPADES"
DefData "Ace, HEARTS", "2, HEARTS", "3, HEARTS", "4, HEARTS", "5, HEARTS"
DefData "6, HEARTS", "7, HEARTS", "8, HEARTS", "9, HEARTS", "10, HEARTS"
DefData "Jack, HEARTS", "Queen, HEARTS", "King, HEARTS"
DefData "Joker, RED", "Joker, BLACK"


If you want a detailed explanation of what the function code (and/or the example) is doing, let me know.

EDIT: Added a 'view undealt cards' toggle to the example.

I thought for a while and came up with an idea that seems to work on a similar concept as impixi's.

Function ScrambleCards(Array:CardType[])
	Local Tmp:CardType
	For x=0 To Len(Array)-1
		SeedRnd MilliSecs()*Rnd(100)
		Tmp=Array[0]
		Shuf=Rnd(Len(Array)-1)
		Array[0]=Array[Shuf]
		Array[Shuf]=Tmp
	Next
	'MIX THE LAST ITEM WITH ANOTHER
	Shuf=Rnd(Len(Array)-1)
	tmp=Array[Shuf]
	Array[Shuf]=Array[Len(Array)-1]
	Array[Len(Array)-1]=Tmp
EndFunction


You really don't want to call SeedRnd in your loop, as your loop will execute many times within 1 millisecond and you'll be reseeding it with the same value (and getting the same return value) until the millisecond timer ticks.

You don't need to call SeedRnd at all unless you want to be able to recreate a specific scramble at some point. Of course, in that case you should pass the Seed as a parameter (so that you can save the seed somewhere else and then pass it back tot he function to get the same results) and only call SeedRnd ONCE at the top of that code (not in a loop!)