On Fri, Jan 16, 2009 at 7:48 AM, Matthew Moss <matt / moss.name> wrote:

> ## Monopoly Walker
>
>
> Your task this week is to simulate players walking about a Monopoly board.
> You are not implementing the whole game; rather, you are to simulate and
> track just the players' movement. Throw dice, move tokens. Keep track of the
> properties where players land and how often.

My solution is pretty straightforward, I have a Property class and a
Board class. The board holds an array of properties. The interesting
thing comes when some properties have a special move, like "Advance to
St. Charles Place". I used some Procs to keep track of the extra move
behavior. The simulation part involves throwing the dice, then
following all moves until the token stays put (sometimes you can land
on Chance, go back three spaces, land on Community Chest, then advance
to GO).

The first argument to the program states the number of dice throws to
simulate, if none is given 100000 is used.

# Ruby Quiz #188
# Monopoly Walker
# Daniel Moore

# Get some dice action going on
class Fixnum
  def d(sides)
    sum = 0
    self.times {sum += Kernel.rand(sides) + 1}
    return sum
  end
end

# Cells keep track of their own hit count and any crazy bonus moves
class Property
  @@property_count = 0
  attr_accessor :count

  def initialize(name, block)
    @count = 0
    @name = name.gsub('_', ' ')
    @position = @@property_count
    @@property_count += 1
    @move_block = block
  end

  # Record that the token landed on this cell
  # Return any bonus move (new location)
  def land
    @count += 1
    # Sometimes cells have a bonus move, this returns
    # the new location, could be the same if no bonus move.
    @move_block.call(@position)
  end

  # Print out this cells name and count
  def to_s
    "(#{"%02d" % @position}) #{@name}#{spacing}- #{@count}"
  end

  # Arbitrary spacing to format the output cleanly
  def spacing
    s = " "
    (21 - @name.size).times do
      s += " "
    end
    s
  end
end

class Board
  PROPERTY_NAMES = %w[GO Mediterranean Community_Chest Baltic
Income_Tax Reading_Railroad Oriental Chance Vermont Connecticut
    Jail/Just_Visiting St._Charles_Place Electric_Company States
Virginia Pennsylvania_Railroad St._James_Place Community_Chest
Tennessee New_York
    Free_Parking Kentucky Chance Indiana Illinois B&O_Railroad
Atlantic Ventnor Water_Works Marvin_Gardins
    Go_To_Jail Pacific North_Carolina Community_Chest Pennsylvania
Short_Line_Railroad Chance Park_Place Luxury_Tax Boardwalk]

  # Some Board positions
  GO_POSITION = 0
  READING_POSITION = 5
  JAIL_POSITION = 10
  ST_CHARLES_POSITION = 11
  ELECTRIC_COMPANY_POSITION = 12
  ILLINOIS_POSITION = 24
  WATER_WORKS_POSITION = 28
  BOARDWALK_POSITION = 39

  CHANCE_CARDS = 15
  COMMUNITY_CHEST_CARDS = 16
  BOARD_SIZE = 40

  COMMUNITY_CHEST_EFFECT = Proc.new do |cur_pos|
    # Simulate 16 card Community chest deck
    case Kernel.rand(COMMUNITY_CHEST_CARDS)
    when 0
      GO_POSITION
    when 1
      JAIL_POSITION
    else
      # This card does not have an effect on position
      cur_pos
    end
  end

  CHANCE_EFFECT = Proc.new do |cur_pos|
    case Kernel.rand(CHANCE_CARDS)
    when 0
      GO_POSITION
    when 1
      ILLINOIS_POSITION
    when 2
      # Nearest Utility
      if (cur_pos >= WATER_WORKS_POSITION) || (cur_pos <
ELECTRIC_COMPANY_POSITION)
        ELECTRIC_COMPANY_POSITION
      else
        WATER_WORKS_POSITION
      end
    when 3..4
      # Nearest Railroad
      case cur_pos
      when 5..14
        15
      when 15..24
        25
      when 25..34
        35
      else
        READING_POSITION
      end
    when 5
      ST_CHARLES_POSITION
    when 6
      # Go back three spaces
      cur_pos - 3
    when 7
      JAIL_POSITION
    when 8
      READING_POSITION
    when 9
      BOARDWALK_POSITION
    else
      # This card does not have an effect on position
      cur_pos
    end
  end

  # Roll 2d6
  def roll
    2.d 6
  end

  def initialize
    # Stay put Proc, used in most regular cells
    stay_put = Proc.new {|cur_pos| cur_pos}

    proc_for_name = {
      "Community_Chest" => COMMUNITY_CHEST_EFFECT,
      "Chance" => CHANCE_EFFECT,
      "Go_To_Jail" => Proc.new do |cur_pos|
        JAIL_POSITION
      end
    }

    @properties = PROPERTY_NAMES.map do |name|
      # Create the property and give it it's bonus move behavior proc
      Property.new(name, proc_for_name[name] || stay_put)
    end
  end

  def simulate(moves)
    @moves = moves
    position = 0

    @moves.times do
      position += roll

      # Land on the properties and keep following the cards until we stay put
      while( position != (new_position = (@properties[position %
BOARD_SIZE]).land) ) do
        position = new_position
        # Track the extra moves
        @moves += 1
      end
    end
  end

  # Displays the results of the simulation
  # Permanently alters the cells making them unsuitable for
  # further simulation.
  def display
    puts "Total hits in board order after #{@moves} turns:"
    puts @properties
    puts "------"

    puts "Sorted by Relative Frequency: "
    # Sort and display results
    @properties.sort{|a, b| b.count <=> a.count }.each do |property|
      property.count = "%1.4f%" % (property.count * 100 / @moves.to_f)
      puts property
    end
    puts "------"
  end
end

board = Board.new

board.simulate((ARGV[0] || 100000).to_i)
board.display


------
Sorted by Relative Frequency:
(10) Jail/Just Visiting    - 5.0612%
(00) GO                    - 4.4737%
(05) Reading Railroad      - 3.8067%
(01) Mediterranean         - 3.5400%
(03) Baltic                - 3.3674%
(04) Income Tax            - 3.3528%
(02) Community Chest       - 3.2069%
(06) Oriental              - 2.7579%
(24) Illinois              - 2.6526%
(19) New York              - 2.5675%
(25) B&O Railroad          - 2.5140%
(08) Vermont               - 2.4062%
(17) Community Chest       - 2.3746%
(21) Kentucky              - 2.3462%
(18) Tennessee             - 2.3341%
(16) St. James Place       - 2.3292%
(20) Free Parking          - 2.3227%
(22) Chance                - 2.3154%
(11) St. Charles Place     - 2.3130%
(09) Connecticut           - 2.3090%
(15) Pennsylvania Railroad - 2.3057%
(28) Water Works           - 2.3033%
(26) Atlantic              - 2.2676%
(31) Pacific               - 2.1947%
(39) Boardwalk             - 2.1923%
(33) Community Chest       - 2.1898%
(30) Go To Jail            - 2.1817%
(12) Electric Company      - 2.1525%
(23) Indiana               - 2.1404%
(07) Chance                - 2.1193%
(32) North Carolina        - 2.1145%
(29) Marvin Gardins        - 2.1023%
(27) Ventnor               - 2.0861%
(34) Pennsylvania          - 2.0610%
(14) Virginia              - 2.0350%
(35) Short Line Railroad   - 2.0026%
(13) States                - 1.8964%
(36) Chance                - 1.8778%
(38) Luxury Tax            - 1.7376%
(37) Park Place            - 1.6882%
------

-- 
-Daniel
http://strd6.com