From: Ruby Quiz Date: 2005-10-14T21:34:40+09:00 Subject: [QUIZ] Lost Cities (#51) 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: http://www.rubyquiz.com/ 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!