The three rules of Ruby Quiz:
1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.
2. Support Ruby Quiz by submitting ideas as often as you can:
3. Enjoy!
···
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
My wife and I love to play a card game called Lost Cities. It's an easy two
player game.
There are five "suits" representing locations in the world: Deserts, Oceans,
Mountains, Jungles, and Volcanoes. Each suit contains three "investment" cards
and one each of the numbers 2 through 10.
Eight cards are dealt to each player, then play alternates turns. On your turn,
you must do exactly two things: Play a card and draw a card, in that order.
When the last card is drawn from the deck, the game ends immediately.
Each play gets an "expedition" pile for each of the five suits, and the players
share five discard piles, again one for each suit. To play a card, you may add
it to your expedition pile for that suit or place it on the discard pile for
that suit.
Your expedition piles must go in order. You may only play a higher card than
the last one you played on that pile. Investment cards are low and you may play
up to all three of them, even though they are the same card. They must still
come before the number cards, of course.
You have two choices when drawing a card. You may take the top card from the
deck or the last card played on any of the five discard piles. You may not
however, discard a card and then draw it again in the same turn.
When the deck is exhausted, both player's scores are calculated based on the
expedition piles. High score wins. (Players generally play a few hands and
keep a running tally.)
An expeditions score is calculated by the following formula:
points = (total_of_all_number_cards - 20) * (1 + investment_cards_count) +
bonus_20_points_if_expedition_has_8_cards_or_more
For example, if you had two investment cards and the 6, 8, and 10 in the oceans
suit, that expedition is worth 12 points ((24 - 20) * (1 + 2) + 0). Expeditions
in which you didn't play a card don't count for or against you. They are
disregarded.
That's the whole game. Let's see a few rounds played out, as an example.
First, I'm shown my hand and I can select a card to play:
Deserts:
Opponent:
Discards:
You:
Oceans:
Opponent:
Discards:
You:
Mountains:
Opponent:
Discards:
You:
Jungles:
Opponent:
Discards:
You:
Volcanoes:
Opponent:
Discards:
You:
Deck: ############################################ (44)
Hand: InvD 2D 3D 5D 2O 5O 9J 5V
Score: 0 (You) vs. 0 (Opponent). Your play?
id
You play the InvD.
Then I am asked where I would like to draw from. I don't really have a choice
yet though, since the discard piles are empty:
Deserts:
Opponent:
Discards:
You: Inv (-40)
Oceans:
Opponent:
Discards:
You:
Mountains:
Opponent:
Discards:
You:
Jungles:
Opponent:
Discards:
You:
Volcanoes:
Opponent:
Discards:
You:
Deck: ############################################ (44)
Hand: 2D 3D 5D 2O 5O 9J 5V
Score: -40 (You) vs. 0 (Opponent). Draw from?
n
You draw a card from the deck.
Then my opponent gets a turn:
Your opponent plays the InvD.
Your opponent draws a card from the deck.
And I get another turn:
Deserts:
Opponent: Inv (-40)
Discards:
You: Inv (-40)
Oceans:
Opponent:
Discards:
You:
Mountains:
Opponent:
Discards:
You:
Jungles:
Opponent:
Discards:
You:
Volcanoes:
Opponent:
Discards:
You:
Deck: ########################################## (42)
Hand: 2D 3D 5D 2O 5O 6M 9J 5V
Score: -40 (You) vs. -40 (Opponent). Your play?
2d
You play the 2D.
Deserts:
Opponent: Inv (-40)
Discards:
You: Inv 2 (-36)
Oceans:
Opponent:
Discards:
You:
Mountains:
Opponent:
Discards:
You:
Jungles:
Opponent:
Discards:
You:
Volcanoes:
Opponent:
Discards:
You:
Deck: ########################################## (42)
Hand: 3D 5D 2O 5O 6M 9J 5V
Score: -36 (You) vs. -40 (Opponent). Draw from?
n
You draw a card from the deck.
We continue on like that until the deck is exhausted.
You can get the code I'm using above at:
http://rubyquiz.com/lost_cities.rb
That code functions as a trivial line-oriented client and server. To play a
card just feed it a card value and suit in the form (?:[i2-9]|10)[domjv]. Add a
"d" to the front of that if you wish to discard instead. To draw, just name a
pile or ask for a "n"ew card from the deck: [domjvn].
This week's Ruby Quiz? To build an AI for playing Lost Cities, of course!
You can tie into my server's very simple API buy defining a subclass of Player
with a show() and move() method. show() is called for each line of data the
server sends to you, and move() is called when the server expects a response.
Here's a very DumbPlayer to get you going:
#!/usr/local/bin/ruby -w
class DumbPlayer < Player
def initialize
@data = ""
@plays = nil
@discard = nil
end
def show( game_data )
if game_data =~ /^You (?:play|discard)/
@plays = nil
@discard = nil
end
@data << game_data
end
def move
if @data.include?("Draw from?")
draw_card
else
make_move
end
ensure
@data = ""
end
private
def draw_card
"n"
end
def make_move
if @plays.nil? and @data =~ /Hand: (.+?)\s*$/
@plays = $1.split.map { |card| card.sub(/Inv/, "I") }
@discard = "d#{@plays.first}"
end
if @plays.empty?
@discard
else
@plays.shift
end
end
end
If you save that as dumb_player.rb, you could play against it with something
like:
$ ruby lost_cities.rb 9016
... then in a different terminal ...
$ ruby lost_cities.rb localhost 9016 dumb_player.rb
Let the games begin!