Red hair helps?

So far the weekend has been quite interesting with plenty of relax time (which is a good thing). Friday was an interesting day at work as I learned a lot about the (political) situation about enterprise architecture, a topic that strongly influences my own work. The trip back was not as smooth as I hoped given the holiday traffic, though. I left at 4 but it still took over 2 hours to get home! In the evening Koen and I watched some TV which was nice… He preferred sandwiches over pizza so that’s what we had for dinner. I did some reading in the early evening (Booch’ new book! yay :) ).

Saturday I got to sleep in. I had some vague plans to go to “van Mourick” to look at some paint samples but really wasn’t in the mood for it. Instead we went to the new Plus-supermarket here. Nice store. Big, which a lot of fresh products including fish. After that we had to quickly rush to the mall to buy some presents for ErikP and Ton (aka Loekie), rush home and hop in the car to Erik’s party in Arnhem. All in all a great day.

Finally, yesterday I put in some of Koen’s red gel. It looks more impressive on him, though, as his hair is much lighter than mine. But still, it’s fun to change your hair color for a day. We both got to sleep in (sort of) and were planning on going out for a walk. However, since the weather was so bad we had to postpone that untill approx 15.00 :( Well, it did give me plenty of time to do some coding!

I’ve taken several attempts to create an app in Ruby that generates bridge deals. There are several apps around to do that but it is more fun to do it yourself. Besides, it gave me something nice to code while / to learn Ruby better (desperately need to brush up on my ruby if I want to do some coding for AbTLinux). Anyway, I had a rough version with some unit tests but it had … issues. After some refactoring and modeling I came up with a solution. Here’s a high-level overview of the design:

I ended up splitting the code in a generic part for card games, and a bridge specific part. The latter is responsible some bridge specific stuff such as the number of players, the order of the cards and so on

  • Originally I had a Suit class, which consisted of the name of a suit as well as cards of that suit. That only made the code more complex. Got rid of that and introduced a CardSet class. From a set of cards you can get a card, add a card, shuffle the cards (a deck is-a CardSet) and order the cards (delegation!)
  • Of course I created unit tests and rdoc as well. The entire package can be downloaded here

Last but not least, the code:

[ruby]
# a library for generating bridge deals. still needs some refactoring: move
# the bridge specific material to its own file
#
# author: Bas van Gils

# a player has a name and may hold a set of cards
class Player
attr_reader :name, :cards
attr_writer :cards

# if no name is given, then the player name is set to something generic.
def initialize(n=”John Doe”)
@name = n
@cards = CardSet.new
end

# in the context of cardgames the name of the player may change (i.e., if
# someone replaces another player) over the course of time
def setName!(n)
@name = n
end

def cardsOfSuit(sn)
return @cards.cardsOfSuit(sn)
end
end

# a suit has a name and a shortname (for prettyprinting)
class Suit
attr_reader :name, :short

# pass in the name of the suit, the shortname is generated
def initialize(n)
@name = n
@short = n.slice(0,1)
end
end

# cards have a suit and a value.
class Card
attr_reader :suit, :value

# pass in suit and value
def initialize(s,v)
@suit = s
@value = v
end

# print the shortname of the suit with the value of this card
def to_s
return @value
end
end

# auxillary class CardSet keeps track of cards as well as sets of cards
# belonging to the same suit. Used by Player and Deck
class CardSet
attr_reader :cards

# initialize with empty set of cards
def initialize
@cards = Array.new
end

# add a card
def <<(c)
@cards << c
end

# get the first card from the set
def getCard
if @cards.size == 0
raise "No more cards in the deck"
end
return @cards.slice!(0)
end

# number of cards in the set
def size
return @cards.size
end

# get all cards of a specified suit
def cardsOfSuit(sn)
return @cards.select{ |card| card.suit.name==sn }
end

# shuffle the deck to randomize the cards
def shuffle!
# check out this site for neat tricks with array sorting:
# http://www.rabbitcreative.com/ruby/articles/randomize-an-array-in-ruby/
dupOrig = @cards.dup
newArr = Array.new
newArr << dupOrig.slice!( rand(dupOrig.size) ) until dupOrig.size.eql?(0)
@cards = newArr
end

def sort(order)
@cards.sort{ |x,y| order.index(y.value) <=> order.index(x.value) }
end
end

# a deck consists of a number of cards, depending on the type of game
class Deck
attr_reader :suits, :possValues

# the suits and possible values for the cards are passed in, after which the
# deck is generated.
def initialize(s, v)
@cards = CardSet.new
@suits = Array.new
@possValues= v

s.each{ |suit|
ns = Suit.new(suit)
@suits << ns
v.each{ |value|
c = Card.new(ns, value)
@cards << c
}
}
end

# number of cards in the suit
def size
return @cards.size
end

# get the first card from the deck
def getCard
return @cards.getCard
end

# shuffle the deck to randomize the cards
def shuffle!
@cards.shuffle!
end
end

# a Bridgegame consists of 4 players sitting n/e/s/w. A deck of cards is also
# needed and generated at initialization of the game.
class BridgeGame
attr_reader :north, :south, :east, :west, :suits, :values

# set the players and initialize the deck. If no playernames are given then
# default names are used
def initialize(n='north', e='east', s='south', w='west')
@suits = %w(spades hearts diamonds clubs)
@values = %w(A K Q J T 9 8 7 6 5 4 3 2)
@deck = Deck.new(@suits, @values)
@north=Player.new(n)
@south=Player.new(e)
@east=Player.new(s)
@west=Player.new(w)
@players = [@north, @east, @south, @west]
end

# return the cards that are still in the deck
def cardsInDeck
return @deck.size
end

# make sure every player gets his/her share of cards
def deal
@deck.shuffle!
while @deck.size >= 4
@players.each{ |player| player.cards << @deck.getCard }
end
end

# sort a list of cards by value (ignores the suit of cards)
def sortCards(cardArr)
if cardArr.size.eql?(0) then
rs = "-"
else
sorted = cardArr.sort!{ |a,b| @values.index(a.value) <=> @values.index(b.value) }
rs = sorted.inject(“”){ |tot,cur| tot << cur.value }
end
return rs
end

end
[/ruby]

The output of the unit tests:

bas@Librarian { ~/bridgehands }$ ruby bridgeTests.rb
Loaded suite bridgeTests
Started
....
Finished in 0.003154 seconds.

4 tests, 12 assertions, 0 failures, 0 errors
And the script that puts it together (i.e., uses the bridge libraries):

[ruby]
require ‘bridge’

bg=BridgeGame.new(north=”Bas”, south=”Theo”)
bg.deal

bg.suits.each{ |suit|
cards = bg.north.cardsOfSuit(suit)
print ” #{bg.sortCards(cards)} \n”
}
bg.suits.each{ |suit|
printf “%-10s %-10s\n”,
bg.sortCards(bg.east.cardsOfSuit(suit)),
bg.sortCards(bg.west.cardsOfSuit(suit))
}
bg.suits.each{ |suit|
cards = bg.south.cardsOfSuit(suit)
print ” #{bg.sortCards(cards)} \n”
}
[/ruby]

When this last bit of code is run you get something like:

bas@Librarian { ~/bridgehands }$ ruby deal.rb
        AQJ652
        92
        T53
        Q5
973                 4
K87653              AQJ
8                   J742
JT4                 A9876
        KT8
        T4
        AKQ96
        K32

which is just what I wanted! Perhaps having red hair helps! Now I can start working on implementing rules (i.e., north should have a 15-17 balanced hand) :)

This entry was posted in comp, general. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>