Monday, April 25, 2011

Play a Card

The previous post discussed the logic used to determine if a card can legally be played according to rules of the game. Once a card is determined to be playable using that word, we need to actually discard the card on the the discard pile.

The discard pile is simply another allocation in memory similar to the deck and the hands.

The technical process for removing a card from a hand and placing it in the discard pile is as follows:

1) Put the card value in the next available memory slot in the discard pile allocation.
2) Remove the card value from the hand's memory allocation slot

Step 2 in this process took a bit of thinking - since we read a hand by traversing the memory slots until we reach a value of 0 we couldn't just replace the discarded card value with a 0. Suppose you were discarding the second card in a hand of 6 cards... the next time we iterate through that hand only the first card would be counted. The way we overcame this was copying the card value from the last slot in the hand's allocation to the slot of the discarded card value and then replacing the last slot's card value with 0.

For example:

Hand: 2 | 3 | 4 | 5 | 6

Discarding the 3 would result in the following hand:
Hand: 2 | 6 | 4 | 5


Here is the code. The second half handles moving the last card value into the discarded card's slot. There is also a check to see if the discarded card is the last card in the hand in which case we just replace it with 0.

: playcard
discardpile ( add card to discard pile )
gethandcardcount
swap
+
c!

dup
rot
gethandcardcount 1 -
rot
= if
+
0
swap
c!
else
dup
gethandcardcount 1 -
swap
+
c@ ( last card in hand )
rot rot
dup
gethandcardcount 1 -
swap
+
0
swap
c!
+
c!
then
;

Game Play Logic - ?playcard

In order to determine if a card is a "legal" move according to the rules of the Crazy 8s game, we developed this word, ?playcard. This word will place either true (-1) or false (0) on the stack.
: ?playcard
dup
13 mod 8 = if
drop ( drop dup )
true
else
discardpile
lastcard
dup
8 = if
getcardsuit
swap
getcardsuit
= if
true
else
false
then
else
2dup
13 mod
swap
13 mod
= if ( same rank/face )
drop drop ( drop 2dup )
true
else
getcardsuit
swap
getcardsuit
= if ( same suit )
true
else
false
then
then
then
then
;

Update: Displaying a Card

We have modified our code from the previous post for displaying cards.

Mainly, we broke out the suit determination logic into its own word for code reuse.

Here is the new version of our Card logic:

: getcardsuit
dup 14 < if 1 swap drop else ( 1=Diamonds )
dup 27 < if 2 swap drop else ( 2=Hearts )
dup 40 < if 3 swap drop else ( 3=Clubs )
dup 53 < if 4 swap drop else ( 4=Spades )
." Error - can't determine suit "
drop
then then then then
;

: getcardface
dup 1 = if ." Ace " else
dup 11 = if ." Jack " else
dup 12 = if ." Queen " else
dup 0 = if ." King " else
dup .
then then then then drop
;

: displaycard
dup ( c -- c1 c2 ) ( duplicate the card number )
13
mod ( c 13 -- c1 ) ( use mod division to get the card face # )

getcardface
getcardsuit

dup 1 = if ." of Diamonds " else
dup 2 = if ." of Hearts " else
dup 3 = if ." of Clubs " else
dup 4 = if ." of Spades " else
." Error Displaying Card "
then then then then drop
;

Friday, April 22, 2011

Displaying a Card

In previous posts we've made mention of cards as numbers between 0-51 (soon to be 1-52). In order to translate this card number to the user in the form of a playing card we wrote a word, displaycard, which take a card number from the stack and prints that card's Face/Suit to the prompt.

This is slightly different than our Pseudo code in that it uses the mod word to determine the card's rank within the suit.

: displaycard
dup ( c -- c1 c2 ) ( duplicate the card number )
13
mod ( c 13 -- c1 ) ( use mod division to get the card face # )

dup 1 = if ." Ace " else
dup 11 = if ." Jack " else
dup 12 = if ." Queen " else
dup 0 = if ." King " else
dup .
then then then then drop

dup 13 <= if ." of Diamonds " else
dup 26 <= if ." of Hearts " else
dup 39 <= if ." of Clubs " else
dup 52 <= if ." of Spades " else
." Error Displaying Card "
then then then then drop
;


Example of usage:
Assuming the number 34....

displaycard

8 of Clubs ok

In Action...

Determining Card Face and Rank - Pseudo code

This is our Pseudo code for how we will determine a card's rank and suit based on the cards index representation. For example, card #34 is 8 of Clubs.

Var cardNum

If cardNum < 13
If cardNum = 1
cardName = "ace"
Elseif cardNum = 11
cardName = "jack"
Elseif cardNum = 12
cardName = "queen"
Elseif cardNum = 13
cardName = "king"
Else
cardName = card.tostring()

cardName of Diamonds
Else
Suit = cardNum / 13
Suite1stcard = Suit * 13
Card = cardNum - suite1stcard

If card = 1
cardName = "ace"
Elseif card = 11
cardName = "jack"
Elseif card = 12
cardName = "queen"
Elseif card = 13
cardName = "king"
Else
cardName = card.tostring()

If suit = 1
cardName of hearts
Else if suit = 2
cardName of clubs
Else if suit = 3
cardName of spades
End

Thursday, April 14, 2011

Showing a Hand

We have created a word to display the cards in a specific hand:
: ?emptyhand
gethandcardcount 0=
;

: showhand
?emptyhand if
." Empty Hand "
else
gethandcardcount 0 do
dup
i
swap
+
c@ . ." "
loop
then
;

The use for this word is similiar to that of the drawcard word... simply push the hand in question to the stack and the the showhand word, as follows:
playerhand showhand

Below is a screen capture of drawcard and showhand in action.

playerhand drawcard ok

In our previous post we explained the Forth words we created for building and shuffling a deck as well as for getting the next card off the deck.

Now we have started building up a library of words for working with "hands" (a player's set of cards).

First of all, as in the original Java version, Crazy Eights is a two player game between the player and the dealer (computer). In the Java version these two hands are represented by instantiating instances of the Hand class.

In our Forth implementation we are representing the two hands by allocating space in memory as follows:
create playerhand 12 allot
create dealerhand 12 allot

This code creates two sets of 12 empty slots in memory.

Next we created a word for drawing a card:
: drawcard
nextcardfromdeck
swap
gethandcardcount
swap
+
c!
;

This word calls on two other words: nextcardfromdeck which gets the next card from the top of the deck (as explained in our previous post) and gethandcardcount. The gethandcardcount word is a "helper" word we created which uses a variable (#cardcount) to count how many cards are in a hand. It does this by starting at the address of the "hand" and looping through until it finds the first memory slot with the value of 0 which represents a blank card. Below is the code for the gethandcardcount word.
: gethandcardcount
0 #cardcount !
begin
dup
#cardcount @
swap
+
c@
0>
while
#cardcount @ 1 +
#cardcount !
repeat
#cardcount @
;


So, now that we have a word that draws a card from the deck we can use it as follows:
playerhand drawcard

Monday, April 11, 2011

Deck in Forth

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 Deck
So, 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 Deck
The 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 Card
As 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 Cards
Currently, 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.

.java to .f ... Making the move

The logic within the Java code for Crazy Eights is basically broken up into 4 main files:
  1. CrazyEights.java - game logic, handles interaction with the deck, manages player turns, etc.
  2. Deck.java - represents a deck of 52 card objects
  3. Card.java - represents a card
  4. Hand.java - represents a players hand of card objects

There are also two files for controlling the actual game play (one for command based and one for GUI based.)

As we build out the Forth implementation of Crazy Eights we will map the components of these Java files to Forth files made up of words and variables which provide similar functionality.

To start with we built a Forth file called DECK.F which contains all card deck logic such as building the deck, shuffling the deck, and pulling the top card from the deck. Our next post will be specifically focused on our deck implementation of the in Forth.

Project Introduction and Concept

For our project, we will be porting a card game that was originally built in Java to Forth. The card game is called Crazy Eights and was build by Ed for an assignment in his undergraduate computer science class at Radford University.

The game was originally built as two versions, 1) GUI, and 2) command line. Both versions actually use the same back-end game logic code but each provide a different presentation layer on top.

Initially, we will be porting the actual game logic code to Forth.

We plan to use arrays, similar to our SBL assignment a few weeks ago, to represent collections of cards (deck, hands, discard, etc.).

As we press forward we will continue to post our progress and stumbling points to this weblog.