class Monotony::Game

Contains the main game engine logic.

Attributes

available_properties[RW]

@return [Array<Purchasable>] properties yet to be purchased by players.

bank_balance[RW]
board[RW]

@return [Array<Square>] the current game board.

chance[RW]
community_chest[RW]
completed[RW]

@return [Boolean] whether or not the game has been completed.

die_size[RW]
free_parking_balance[RW]
go_amount[RW]
hits[RW]

@return [Hash] Returns a hash containing the number of times each Square has been landed on.

last_roll[RW]
max_turns_in_jail[RW]
num_dice[RW]
num_hotels[RW]
num_houses[RW]
player_starting_balance[RW]
players[RW]

@return [Boolean] players registered to the game.

starting_currency[RW]
turn[RW]

@return [Integer] the current turn number.

Public Class Methods

new(opts) click to toggle source

Creates a new Monopoly game @param [Hash] opts Game configuration options @option opts [Integer] :free_parking_balance The amount of money stored on Free Parking at the beginning of the game (unofficial game feature in widespread use). @option opts [Integer] :bank_balance Starting balance of the bank. @option opts [Integer] :max_turns_in_jail The maximum number of turns a player may spend in jail before being forced to pay a fine to leave. @option opts [Integer] :go_amount The amount of money given to a player when they pass GO. @option opts [Integer] :num_dice The number of dice a player will roll on their turn. @option opts [Integer] :die_size The number of sides per dice. @option opts [Integer] :num_houses The total number of houses available to be purchased. @option opts [Integer] :num_hotels The total number of hotels available to be purchased @option opts [Integer] :starting_currency The amount of currency given to each player at the start of the game. @option opts [Integer, Array<Player>] :players If an array of Player objects are given, then add them to the game. If an integer is given, generate that number of players with default options, and add those to the game instead. @return [String] the object converted into the expected format.

# File lib/monotony/game.rb, line 32
def initialize(opts)
        opts = {
                free_parking_balance: 0,
                bank_balance: 12755,
                max_turns_in_jail: 3,
                go_amount: 200,
                num_dice: 2,
                die_size: 6,
                num_houses: 48,
                num_hotels: 12,
                starting_currency: 1500,
                players: 4,
                variant: Monotony::DefaultLayout
        }.merge(opts)

        random_player_names = %w{Andy Brian Katie Cathy Tine Jody James Ryan Lucy Pierre George Gregor Tracy Lia Andoni Ralph San}

        @board = opts[:variant]::BOARD
        @chance_all = opts[:variant]::CHANCE
        @community_chest_all = opts[:variant]::COMMUNITY_CHEST

        @hits = {}
        @turn = 0
        @bank_balance = opts[:bank_balance] 
        @free_parking_balance = opts[:free_parking_balance]
        @max_turns_in_jail = opts[:max_turns_in_jail]
        @last_roll = 0
        @go_amount = opts[:go_amount]
        @initial_board = @board
        @available_properties = @board
        @chance = @chance_all.shuffle
        @community_chest = @community_chest_all.shuffle
        @num_dice = opts[:num_dice]
        @num_houses = opts[:num_houses] 
        @num_hotels = opts[:num_hotels] 
        @die_size = opts[:die_size]
        @starting_currency = opts[:starting_currency]
        @variant = opts[:variant]

        case opts[:players]
                when Integer
                        @players = []
                        opts[:players].times do
                                @players << Monotony::Player.new(name: random_player_names.sample)
                        end
                when Array
                        @players = opts[:players]
        end
        @completed = false
        @board.each do |square|
                @hits[square] = 0
        end
        @players.each do |player|
                player.board = @board.clone
                player.currency = opts[:starting_currency]
                player.game = self
        end
        self
end

Public Instance Methods

active_players() click to toggle source

@return [Array<Player>] an array of players who have not yet been eliminated from the game.

# File lib/monotony/game.rb, line 163
def active_players
        @players.reject{ |p| p.is_out? }
end
all_sets_owned() click to toggle source

@return [Array<Symbol>] the names of completed property sets currently owned by players

# File lib/monotony/game.rb, line 93
def all_sets_owned
        @board.select{ |p| p.is_a? BasicProperty }.select { |p| p.set_owned? }.group_by { |p| p.set }.keys
end
pay_player(player, amount, reason = nil) click to toggle source

Transfers money from the bank to a player. If the bank does not have sufficient funds, transfers as much as possible. @return [Boolean] whether or not the bank had sufficient cash to pay the player the desired amount.

# File lib/monotony/game.rb, line 169
def pay_player(player, amount, reason = nil)
        if @bank_balance > amount
                @bank_balance = @bank_balance - amount
                player.currency = player.currency + amount
                puts '[%s] Received £%d from bank%s (balance: £%d, bank balance: £%d)' % [ player.name, amount, (reason ? ' for %s' % reason : '' ), player.currency, bank_balance ]
                true
        else
                player.currency = player.currency + bank_balance
                puts '[%s] Unable to receive £%d from bank! Received £%d instead (balance: £%d)' % [ player.name, amount, bank_balance, player.currency ]
                @bank_balance = 0
                false
        end
end
payout_free_parking(player) click to toggle source

Pays the contents of the free parking square to a player. @return [Integer] the amount of money given to the player.

# File lib/monotony/game.rb, line 185
def payout_free_parking(player)
        payout = @free_parking_balance
        player.currency = player.currency + payout
        puts '[%s] Landed on free parking! £%d treasure found' % [player.name, @free_parking_balance] unless @free_parking_balance == 0
        @free_parking_balance = 0
        payout
end
play(turns = 100000) click to toggle source

Play through a given number of turns of the game as configured. @param [Integer] turns the number of turns of the game to play through. @return [self] @example Play through an entire game

game = Monotony::Game.new
game.play

@example Play through 10 turns

game.play(10)
# File lib/monotony/game.rb, line 214
def play(turns = 100000)
        if @completed
                puts 'Game is complete!'
                return false
        end
        turns.to_i.times do
                @turn = @turn + 1
                puts '- Turn %d begins!' % @turn
                @players.each do |turn|
                        if turn.is_out?
                                puts '[%s] Is sitting out' % turn.name
                                next
                        end
                                puts '[%s] Go begins on %s (balance: £%d)' % [ turn.name , turn.current_square.name, turn.currency ]

                        turn.properties.each do |property|
                                case property
                                when Station
                                        if property.is_mortgaged?
                                                turn.behaviour[:unmortgage_possible].call(self, turn, property) if turn.currency > property.cost
                                        end
                                when Utility
                                        if property.is_mortgaged?
                                                turn.behaviour[:unmortgage_possible].call(self, turn, property) if turn.currency > property.cost
                                        end
                                when BasicProperty
                                        if property.is_mortgaged?
                                                turn.behaviour[:unmortgage_possible].call(self, turn, property) if turn.currency > property.cost
                                        else
                                                if property.set_owned?
                                                        case property.num_houses
                                                        when 0..3
                                                                turn.behaviour[:houses_available].call(self, turn, property) unless property.num_hotels > 0
                                                        when 4
                                                                turn.behaviour[:hotel_available].call(self, turn, property)
                                                        end
                                                end
                                        end
                                end
                        end

                        turn.behaviour[:trade_possible].call(self, turn) if not turn.properties.empty?
                        turn.behaviour[:use_jail_card].call(self, turn) if turn.in_jail? and turn.jail_free_cards > 0

                        result = turn.roll
                        double = (result.uniq.length == 1)

                        move_total = result.inject(:+)
                        @last_roll = move_total


                        puts '[%s] Rolled %s (total: %d)' % [ turn.name, result.join(', '), move_total ]
                        puts '[%s] Rolled a double' % turn.name if double

                        if turn.in_jail?
                                if double
                                        puts '[%s] Got out of jail! (rolled double)' % turn.name
                                        turn.in_jail = false
                                else
                                        turn.turns_in_jail = turn.turns_in_jail + 1
                                        puts '[%s] Is still in jail (turn %d)' % [ turn.name, turn.turns_in_jail ]
                                        if turn.turns_in_jail >= @max_turns_in_jail
                                                turn.in_jail = false
                                                turn.pay(:free_parking, 50)
                                                puts '[%s] Got out of jail (paid out)' % turn.name
                                        else 
                                                next
                                        end
                                end
                        end

                        square = turn.move(move_total)

                        puts '[%s] Moved to %s' % [ turn.name, square.name ]
                        square.action.call(self, square.owner, turn, square)

                        puts '[%s] Next throw' % turn.name if double
                        redo if double
                        puts '[%s] Ended go on %s (balance: £%d)' % [ turn.name, turn.current_square.name, turn.currency ]
                end

                still_in = @players.reject{ |p| p.is_out? }
                if active_players.count == 1
                        winner = still_in.first
                        puts '[%s] Won the game! Final balance: £%d, Property: %s' % [ winner.name, winner.currency, winner.properties.collect {|p| p.name} ]
                        @completed = true
                        break
                end
        end
        self
end
register_player(player) click to toggle source

Add a player to the game. @return [Array<Player>] an array of active players.

# File lib/monotony/game.rb, line 202
def register_player(player)
        @players << player
end
summary() click to toggle source

Produces a colourful ASCII representation of the state of the game board to standard output. The string produced contains ANSI colours. @return [String] game summary. @example Show a summary of a game in progress

game.summary
# File lib/monotony/game.rb, line 102
def summary
        summary = Array.new(6) { '' }
                position = 0
                header = ''

                worth = @players.collect { |p| '%s: £%d' % [ p.name, p.currency ]}
                header << 'Balances: %s' % worth.join(', ')
                puts header
                puts

                @board.each do |property|
                        if position % 10 == 0 and position > 0
                                puts summary.collect! { |s| s << "\n" }
                                puts

                                summary.collect! { '' }
                        end
                        if property.owner
                                owner_string = ( '%7s ' % property.owner.name ).colorize(:color => :black, :background => :light_white)
                        else
                                owner_string = '        '.colorize(:background => :white)
                        end

                        summary[0] << ( '%-11s' % property.name[0..9] ).colorize(:color => :light_white, :background => property.colour) + ' '.colorize(:color => :default)
                        summary[1] << ( '%11s' % ' ' ).colorize(:color => :light_white, :background => :white) + ' '.colorize(:color => :default)
                        summary[3] << ( '%11s' % ' ' ).colorize(:color => :light_white, :background => :white) + ' '.colorize(:color => :default)
                        summary[4] << ( '%3s' % ' ' ).colorize(:color => :light_white, :background => :white) + owner_string + ' '.colorize(:color => :default)

                        this_space = @players.select { |p| p.current_square == property }                          
                        if this_space.length > 0
                                        summary[2] << ' '.colorize(:background => :white) + ( ' %-6s ' % this_space.collect(&:name).first.upcase ).colorize(:color => :black, :background => :white)
                                        summary[2] << ( ' ' * 2 ) .colorize(:background => :white) + ' '.colorize(:color => :default)
                        else
                                summary[2] << ( '%11s' % ' ' ).colorize(:color => :light_white, :background => :white) + ' '.colorize(:color => :default)
                        end

                        if property.is_a? PurchasableProperty and property.is_mortgaged?
                                summary[5] << ' MORTGAGED '.colorize(:background => :light_black, :color => :white) + ' '.colorize(:color => :default)
                        elsif property.is_a? BasicProperty
                                summary[5] << property.display_house_ascii
                        else
                                summary[5] << ( '%11s' % ' ' ).colorize(:background => :white) + ' '.colorize(:color => :default)
                        end

                        position = position + 1
                end

                summary_out = summary.collect! { |s| s << "\n" }
                puts summary_out
                puts
                return summary_out
        end