I found this interesting blog post via CodingHorror: My Favorite Interview Question. In a nutshell, he talks about the object-oriented design challenges of designing the gam
I think you are taking the wrong path here.
...which probably means that you can use design patterns to model the rules of the game (see above).
I think this just shows that you don't really understand what design patterns are. The known Design Patterns are just names we give to recurrent situations when coding. In your everyday life, you never say "I woke up at 8am to go to 9am to place X, programming all day until 5pm, so they pay me by the end of the month". You say, "Today I've been off to work". You have the problem of wanting to earn money, and a recurrent solutions to that problem is going to work. So... we have a pattern here! Let's call it "Working" !
Design patterns are just a bunch of studied solutions to common problems. Each one of those solutions has an associated name (strategy, visitor, etc).
Coming back at
...which probably means that you can use design patterns to model the rules of the game
It doesn't mean you CAN USE design patterns to model the rules of the game, it means that whatever you do in your solution, it will probably fall onto some of the known design patterns. It is easier to think then of your solution as a set of interconnected patterns than having to describe everything from the ground up.
Here's how I would design Monopoly. I've taken the liberty of assuming a dynamically-typed language since that makes everything easier. Ruby specifically.
You have a simple Game
object that's mostly a wrapper around an Array
of size 40, plus some convenience methods. The Game
object also tracks the number of available houses
and hotels
and the two stacks of Chance and Community Chest cards. A few convenience methods like current_turn
and next_turn!
are provided — both return a Player
object; next_turn!
increments the turn index, wrapping to 0 if necessary.
All locations the player can land on must inherit from a superclass of Property
. The Property
class defines a few common things like rent
, owner
, set
, houses
, purchasable?
, and upgradeable?
. The rent
and owner
properties may be nil
. The set
property returns an Array
containing all properties within the group. The set
property may vary in size from 1 to 4. The houses
property represents a hotel as 5 'houses'.
The Game
object has an Array
of Player
objects, each with fields like position
(an integer from 0 to 39), money
(no upper bound — the bank technically never 'runs out of money'), get_out_of_jail_frees
, and in_jail?
(since position is insufficient for this). The Game
object also has an index to track whose turn it is.
Property-specific rules are all encoded within their respective subclasses. So, for instance, the implementation of rent
on a Railroad
would be:
def rent
owned_count = self.set.select { |rr| rr.owner == self.owner }.size
return 25 * 2 ** (owned_count - 1)
end
Chance and Community Chest cards can be simply implemented with a bunch of closures that takes a game and a player object as parameters. For instance:
# Second place in a beauty contest
COMMUNITY_CHEST_CARDS << lambda do |game, player|
player.money += 10
end
# Advance token to Boardwalk
CHANCE_CARDS << lambda do |game, player|
game.advance_token!(player, 39)
end
# Advance token to nearest railroad, pay double
CHANCE_CARDS << lambda do |game, player|
new_position = [5, 15, 25, 35].detect do |p|
p > player.position
end || 5
game.advance_token!(player, new_position)
# Pay rent again, no-op if unowned
game.properties[new_position].pay_rent!(player)
end
And so on. The advance_token!
method obviously handles things like passing go.
Obviously, there are more details — it's a fairly complicated game, but hopefully this gives you the right idea. It'd certainly be more than sufficient for an interview.
House rules could be switched on or off by adding a house_rules
Array
to the Game
object. This would allow the FreeParking
property to be implemented like this:
class Game
def house_rules
@house_rules ||= []
end
def kitty
# Initialize the kitty to $500.
@kitty ||= 500
end
def kitty=(new_kitty)
@kitty = new_kitty
end
end
class FreeParking < Property
def rent
if self.game.house_rules.include?(:free_parking_kitty)
# Give the player the contents of the kitty, and then reset it to zero.
return -(_, self.game.kitty = self.game.kitty, 0)[0]
else
return 0
end
end
end
I've never designed Monopoly rules (too easy, methinks), but I have dabbled in writing engines for other well-known games for personal pleasure and with the understanding that all of this is an academic exercise.
The two games I tried to model (and continue to try) are D&D and M:tG.
With D&D, the emphasis is on very good OO design - making classes and class hierarchies that make sense.
With M:tG, you basically realize that the straight OO paradigm is incomplete for this sort of thing. You end up then working with agents, event brokers, and creating really complicated rulesets.
It's all fairly meaningless unless you're a game designer. Good fun, though.