The card deck as implemented in Java is fairly simple. It has one private property which is a collection of Card objects (will be discussed in future posts when we get to our card implementation in Forth). There are also several public methods for providing access to the private Card collection (Get a card, add a card, get the deck size).
There is not a shuffle method in the deck class - The getCard() method actually returns a random card from the deck. Though this works... it's not actually how cards are played. We decided to modify the logic a bit in our Forth implementation and actually have the deck of cards shuffled and have the "get a card" functionality just grab the "top" card on the deck.
To start with we represent a deck of cards in memory by using the create word, deck, as follows:
create deck 52 allot
This will allocate 52 slots in memory starting at the address of the deck word. In order to work with "cards" in the deck we created a word, deckcard, which represents an address location (one of the 52 spots in the allocated memory) of a "card". The definition of deckcard is as follows:
: deckcard deck + ;
So, to get an actual card representation (0-51) we can first push the number we are looking for, followed by the deckcard word. So, for example, to get the 9th card in the deck we could use the following code:
9 deckcard ( address of the 9th card )
c@ ( actual card, represented as 0-51 )
Building a DeckSo, once we built a way of representing a deck of cards in memory and a way of accessing slots within that "deck" to represent cards we needed to build a word for, well, building a deck by populating it with "cards". This word basically loops through 52 times and uses the deckcard word to store card representations (0-51) in the deck.
: builddeck 52 0 do ( loop 52 to 0 )
i i ( push current index of loop to stack twice )
deckcard ( i i addr -- i addr+i ) ( get card address in deck )
c! ( i addr+i -- ) ( stores index of loop in deck )
loop
52 #cardsindeck ! ( set #cardsindeck variable )
;
Notice the #cardsindeck word - this is a variable we used for tracking the number of cards in the deck as they are removed from the deck. This word maps to the Java method getSize(). Its value is actually decremented in another word (discussed later in this post) that handles getting the top card off the deck (the next card). This variable basically represents a sliding counter which can be added to the starting address of the deck. So, at any given time, adding the value of this variable to the starting address of the deck will give you access to the next top card on the deck.
Shuffling a DeckThe builddeck word simply loads the 52 memory slots with 0-51, representing 52 cards, in order. In a effort to make the game play a bit more interesting and realistic we created a word for shuffling the deck, shuffledeck. This also involved building a helper word, swapcards, which swaps the value of the top two memory addresses on the stack.
: swapcards ( c1 c2 -- ) ( swap the cards at 2 mem addrs in deck )
2dup ( addr1 addr2 -- addr1 addr2 addr1 addr2 )
c@ ( addr1 addr2 addr1 addr2 -- addr1 addr2 addr1 n )
swap ( addr1 addr2 addr1 n2 -- addr1 addr2 n2 addr1 )
c@ ( addr1 addr2 n2 addr1 -- addr1 addr2 n2 n1 )
rot ( addr1 addr2 n2 n1 -- addr1 n2 n1 addr2 )
c! ( addr1 n2 n1 addr2 -- addr1 n2 )
swap ( addr1 n2 -- n2 addr1 )
c! ( n2 addr1 -- )
;
: shuffledeck 52 0 do ( loop 52 to 0 )
i ( push current index of loop to stack )
deckcard ( i addr -- addr+i ) ( get card address in deck )
52 random ( push random number 0-52 to stack )
deckcard ( addr+i r -- addr+i addr+r ) ( get random card addr )
swapcards ( addr+i addr+r -- addr+r addr+i )
loop
;
The random word used in the shuffledeck word is provided by Win32Forth which pushes a random number between 0 and number atop the stack.
Our implementation of shuffling cards is a modified version of the one found in
Thinking Forth by Leo Brodie.
Getting the Next CardAs mentioned earlier we created a word that gives us the number, or index, within the deck's allocated memory slots that represents the "next card on the deck".
: nextcardfromdeck
#cardsindeck @ ( current number of cards )
1 - ( offset for zero-based deck )
deckcard ( get card address in deck )
c@ ( get card value )
#cardsindeck @ 1 - ( subtract 1 from number of cards )
#cardsindeck ! ( update number of cards )
;
This word uses the #cardsindeck variable as described above to get the next card on the deck. It simply pushes the card representation (0-51) to the stack and then subtracts 1 from the tracking variable. At the moment, the slot within the deck isn't actually cleared out or freed up, but the #cardsindeck does a good job of keeping track of what slots are left or still available in the deck.
Representing CardsCurrently, within our deck implementation, we are only referring to cards as numbers between 0-51. We will figure out a way to provide meaning to these numbers such as suit and value when we start building our card logic.