Class: Monotony::Game

Inherits:
Object
  • Object
show all
Defined in:
lib/monotony/game.rb

Overview

Contains the main game engine logic.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts) ⇒ String

Creates a new Monopoly game

Parameters:

  • opts (Hash)

    Game configuration options

Options Hash (opts):

  • :free_parking_balance (Integer)

    The amount of money stored on Free Parking at the beginning of the game (unofficial game feature in widespread use).

  • :bank_balance (Integer)

    Starting balance of the bank.

  • :max_turns_in_jail (Integer)

    The maximum number of turns a player may spend in jail before being forced to pay a fine to leave.

  • :go_amount (Integer)

    The amount of money given to a player when they pass GO.

  • :num_dice (Integer)

    The number of dice a player will roll on their turn.

  • :die_size (Integer)

    The number of sides per dice.

  • :num_houses (Integer)

    The total number of houses available to be purchased.

  • :num_hotels (Integer)

    The total number of hotels available to be purchased

  • :starting_currency (Integer)

    The amount of currency given to each player at the start of the game.

  • :players (Integer, Array<Player>)

    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.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 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

Instance Attribute Details

#available_propertiesArray<Purchasable>

Returns properties yet to be purchased by players.

Returns:

  • (Array<Purchasable>)

    properties yet to be purchased by players.



14
15
16
# File 'lib/monotony/game.rb', line 14

def available_properties
  @available_properties
end

#bank_balanceObject

Returns the value of attribute bank_balance.



10
11
12
# File 'lib/monotony/game.rb', line 10

def bank_balance
  @bank_balance
end

#boardArray<Square>

Returns the current game board.

Returns:

  • (Array<Square>)

    the current game board.



7
8
9
# File 'lib/monotony/game.rb', line 7

def board
  @board
end

#chanceString

Draws a chance card from the pile. If the pile is empty, resets and reshuffles the deck.

Returns:

  • (String)

    a chance string



157
158
159
# File 'lib/monotony/game.rb', line 157

def chance
  @chance
end

#community_chestString

Draws a community chest card from the pile. If the pile is empty, resets and reshuffles the deck.

Returns:

  • (String)

    a community chest string



195
196
197
# File 'lib/monotony/game.rb', line 195

def community_chest
  @community_chest
end

#completedBoolean

Returns whether or not the game has been completed.

Returns:

  • (Boolean)

    whether or not the game has been completed.



12
13
14
# File 'lib/monotony/game.rb', line 12

def completed
  @completed
end

#die_sizeObject

Returns the value of attribute die_size.



10
11
12
# File 'lib/monotony/game.rb', line 10

def die_size
  @die_size
end

#free_parking_balanceObject

Returns the value of attribute free_parking_balance.



10
11
12
# File 'lib/monotony/game.rb', line 10

def free_parking_balance
  @free_parking_balance
end

#go_amountObject

Returns the value of attribute go_amount.



10
11
12
# File 'lib/monotony/game.rb', line 10

def go_amount
  @go_amount
end

#hitsHash

Returns a hash containing the number of times each Square has been landed on.

Returns:

  • (Hash)

    Returns a hash containing the number of times each Square has been landed on.



5
6
7
# File 'lib/monotony/game.rb', line 5

def hits
  @hits
end

#last_rollObject

Returns the value of attribute last_roll.



10
11
12
# File 'lib/monotony/game.rb', line 10

def last_roll
  @last_roll
end

#max_turns_in_jailObject

Returns the value of attribute max_turns_in_jail.



10
11
12
# File 'lib/monotony/game.rb', line 10

def max_turns_in_jail
  @max_turns_in_jail
end

#num_diceObject

Returns the value of attribute num_dice.



10
11
12
# File 'lib/monotony/game.rb', line 10

def num_dice
  @num_dice
end

#num_hotelsObject

Returns the value of attribute num_hotels.



10
11
12
# File 'lib/monotony/game.rb', line 10

def num_hotels
  @num_hotels
end

#num_housesObject

Returns the value of attribute num_houses.



10
11
12
# File 'lib/monotony/game.rb', line 10

def num_houses
  @num_houses
end

#player_starting_balanceObject

Returns the value of attribute player_starting_balance.



10
11
12
# File 'lib/monotony/game.rb', line 10

def player_starting_balance
  @player_starting_balance
end

#playersBoolean

Returns players registered to the game.

Returns:

  • (Boolean)

    players registered to the game.



9
10
11
# File 'lib/monotony/game.rb', line 9

def players
  @players
end

#starting_currencyObject

Returns the value of attribute starting_currency.



10
11
12
# File 'lib/monotony/game.rb', line 10

def starting_currency
  @starting_currency
end

#turnInteger

Returns the current turn number.

Returns:

  • (Integer)

    the current turn number.



17
18
19
# File 'lib/monotony/game.rb', line 17

def turn
  @turn
end

Instance Method Details

#active_playersArray<Player>

Returns an array of players who have not yet been eliminated from the game.

Returns:

  • (Array<Player>)

    an array of players who have not yet been eliminated from the game.



163
164
165
# File 'lib/monotony/game.rb', line 163

def active_players
	@players.reject{ |p| p.is_out? }
end

#all_sets_ownedArray<Symbol>

Returns the names of completed property sets currently owned by players.

Returns:

  • (Array<Symbol>)

    the names of completed property sets currently owned by players



93
94
95
# 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) ⇒ Boolean

Transfers money from the bank to a player. If the bank does not have sufficient funds, transfers as much as possible.

Returns:

  • (Boolean)

    whether or not the bank had sufficient cash to pay the player the desired amount.



169
170
171
172
173
174
175
176
177
178
179
180
181
# 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) ⇒ Integer

Pays the contents of the free parking square to a player.

Returns:

  • (Integer)

    the amount of money given to the player.



185
186
187
188
189
190
191
# 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) ⇒ self

Play through a given number of turns of the game as configured.

Examples:

Play through an entire game

game = Monotony::Game.new
game.play

Play through 10 turns

game.play(10)

Parameters:

  • turns (Integer) (defaults to: 100000)

    the number of turns of the game to play through.

Returns:

  • (self)


214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# 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) ⇒ Array<Player>

Add a player to the game.

Returns:

  • (Array<Player>)

    an array of active players.



202
203
204
# File 'lib/monotony/game.rb', line 202

def register_player(player)
	@players << player
end

#summaryString

Produces a colourful ASCII representation of the state of the game board to standard output. The string produced contains ANSI colours.

Examples:

Show a summary of a game in progress

game.summary

Returns:

  • (String)

    game summary.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# 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