...
This commit is contained in:
115
monopoly.py
115
monopoly.py
@@ -12,12 +12,13 @@ http://www.tkcs-collins.com/truman/monopoly/monopoly.shtml
|
|||||||
|
|
||||||
# TODO: maybe instead of all these classmethods, instances?
|
# TODO: maybe instead of all these classmethods, instances?
|
||||||
# TODO: something more graceful than Game.games[0]
|
# TODO: something more graceful than Game.games[0]
|
||||||
|
# TODO: store LAST_ROLL in a global constant instead of passing it around to all the `action` methods
|
||||||
"""
|
"""
|
||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from pprint import pprint
|
|
||||||
from random import shuffle, choice
|
from random import shuffle, choice
|
||||||
|
from time import sleep
|
||||||
from typing import Type, NewType, Tuple, cast, List
|
from typing import Type, NewType, Tuple, cast, List
|
||||||
|
|
||||||
from exceptions import TooManyPlayers, NotEnough, DidntFind, Argument, NoOwner
|
from exceptions import TooManyPlayers, NotEnough, DidntFind, Argument, NoOwner
|
||||||
@@ -37,7 +38,7 @@ class Space:
|
|||||||
|
|
||||||
class NothingHappensWhenYouLandOnItSpace(Space):
|
class NothingHappensWhenYouLandOnItSpace(Space):
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(self, player: "Player", last_roll):
|
def action(self, player: "Player", _):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ class TaxSpace(Space):
|
|||||||
amount = None
|
amount = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player, last_roll):
|
def action(cls, player, _):
|
||||||
player.pay("Bank", cls.amount)
|
player.pay("Bank", cls.amount)
|
||||||
|
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ class IncomeTax(TaxSpace):
|
|||||||
|
|
||||||
class GoToJail(Space):
|
class GoToJail(Space):
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player, last_roll):
|
def action(cls, player, _):
|
||||||
# player.go_to_jail()
|
# player.go_to_jail()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -82,11 +83,11 @@ class CardSpace(Space):
|
|||||||
deck = None
|
deck = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player, last_roll):
|
def action(cls, player, _):
|
||||||
# for lazy loading to avoid circular imports (?)
|
# for lazy loading to avoid circular imports (?)
|
||||||
deck = eval(cls.deck)
|
deck = eval(cls.deck)
|
||||||
card = deck.get_card()
|
card = deck.get_card()
|
||||||
return card.action(player, last_roll)
|
return card.action(player, _)
|
||||||
|
|
||||||
|
|
||||||
class CommunityChest(CardSpace):
|
class CommunityChest(CardSpace):
|
||||||
@@ -105,7 +106,7 @@ class Card(ABC):
|
|||||||
keep = False
|
keep = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(self, player: "Player", last_roll):
|
def action(self, player: "Player", _):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -119,7 +120,7 @@ class ElectedPresidentCard(Card):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player, last_roll):
|
def action(cls, player, _):
|
||||||
for other_player in Game.games[0].active_players:
|
for other_player in Game.games[0].active_players:
|
||||||
if other_player != player:
|
if other_player != player:
|
||||||
player.pay(other_player, 50)
|
player.pay(other_player, 50)
|
||||||
@@ -132,7 +133,7 @@ class GetOutOfJailFreeCard(Card):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player: "Player", last_roll):
|
def action(cls, player: "Player", _):
|
||||||
player.get_out_of_jail_free_card = True
|
player.get_out_of_jail_free_card = True
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +142,7 @@ class AdvanceCard(Card):
|
|||||||
kwarg = {}
|
kwarg = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player, last_roll):
|
def action(cls, player, _):
|
||||||
player.advance(**cls.kwarg)
|
player.advance(**cls.kwarg)
|
||||||
|
|
||||||
|
|
||||||
@@ -182,7 +183,7 @@ class BuildingAndLoanMaturesCard(Card):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(self, player, last_roll):
|
def action(self, player, _):
|
||||||
Bank.pay(player, 150)
|
Bank.pay(player, 150)
|
||||||
|
|
||||||
|
|
||||||
@@ -191,7 +192,7 @@ class SpeedingCard(Card):
|
|||||||
text = {"français": "Amende pour excès de vitesse. Payez M15."}
|
text = {"français": "Amende pour excès de vitesse. Payez M15."}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player, last_roll):
|
def action(cls, player, _):
|
||||||
player.pay(Bank, 15)
|
player.pay(Bank, 15)
|
||||||
|
|
||||||
|
|
||||||
@@ -231,11 +232,10 @@ def shuffle_decks():
|
|||||||
deck.shuffle()
|
deck.shuffle()
|
||||||
|
|
||||||
|
|
||||||
def buy_decision(property: Type["Property"], player: "Player"):
|
def buy_decision(property: "Property", player: "Player"):
|
||||||
if property.cost > player.assets:
|
if property.cost > player.assets:
|
||||||
print(f"{player} doesn't have enough to buy {property}.")
|
print(f"{player} doesn't have enough to buy {property}.")
|
||||||
if Game.games[0].buy_decision_algorithm(property, player):
|
return Game.games[0].buy_decision_algorithm(property, player)
|
||||||
player.buy(property)
|
|
||||||
|
|
||||||
|
|
||||||
class Decision:
|
class Decision:
|
||||||
@@ -253,28 +253,39 @@ class Property(Space):
|
|||||||
self._name = _name
|
self._name = _name
|
||||||
self.instances.append(self)
|
self.instances.append(self)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if hasattr(self, "_name"):
|
||||||
|
return self._name
|
||||||
|
return str(self.__class__)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def instances_by_type(cls):
|
def instances_by_type(cls):
|
||||||
ibt = defaultdict(list)
|
ibt = defaultdict(list)
|
||||||
for i in cls.instances:
|
for i in cls.instances:
|
||||||
print(i)
|
|
||||||
ibt[i.type].append(i)
|
ibt[i.type].append(i)
|
||||||
|
return ibt
|
||||||
|
|
||||||
def action(self, player: "Player", last_roll=None):
|
def action(self, player: "Player", last_roll=None):
|
||||||
if not self.owner:
|
if not self.owner:
|
||||||
return buy_decision(self, player)
|
buy = buy_decision(self, player)
|
||||||
|
if buy:
|
||||||
|
return player.buy(self)
|
||||||
if self.mortgaged:
|
if self.mortgaged:
|
||||||
return
|
return
|
||||||
return player.pay(self.owner, self.calculate_rent(last_roll=last_roll))
|
return player.pay(self.owner, self.calculate_rent(last_roll))
|
||||||
|
|
||||||
def calculate_rent(self, last_roll):
|
def calculate_rent(self, _):
|
||||||
if not self.owner:
|
if not self.owner:
|
||||||
raise NoOwner
|
raise NoOwner
|
||||||
|
|
||||||
|
|
||||||
class Utility(Property):
|
class Utility(Property):
|
||||||
cost = 200
|
cost = 200
|
||||||
rent = {1: lambda dice_total: dice_total * 4, 2: lambda dice_total: dice_total * 10}
|
rent = {
|
||||||
|
0: lambda _: 0,
|
||||||
|
1: lambda dice_total: dice_total * 4,
|
||||||
|
2: lambda dice_total: dice_total * 10,
|
||||||
|
}
|
||||||
mortgage_cost = 75
|
mortgage_cost = 75
|
||||||
unmortgage_cost = 83
|
unmortgage_cost = 83
|
||||||
type = "utility"
|
type = "utility"
|
||||||
@@ -288,14 +299,17 @@ class Utility(Property):
|
|||||||
|
|
||||||
class Railroad(Property):
|
class Railroad(Property):
|
||||||
cost = 200
|
cost = 200
|
||||||
rent = ({1: 25, 2: 50, 3: 100, 4: 200},)
|
rent = {1: 25, 2: 50, 3: 100, 4: 200}
|
||||||
mortgage_cost = 100
|
mortgage_cost = 100
|
||||||
unmortgage_cost = 110
|
unmortgage_cost = 110
|
||||||
type = "railroad"
|
type = "railroad"
|
||||||
|
|
||||||
def calculate_rent(self, last_roll=None):
|
def calculate_rent(self, _):
|
||||||
super().calculate_rent()
|
super().calculate_rent(_)
|
||||||
return self.rent[self.owner.owns_x_of_type(self)]
|
owns_x_of_type = self.owner.owns_x_of_type(self)
|
||||||
|
if not owns_x_of_type:
|
||||||
|
return 0
|
||||||
|
return self.rent[owns_x_of_type]
|
||||||
|
|
||||||
|
|
||||||
class BuildableProperty(Property):
|
class BuildableProperty(Property):
|
||||||
@@ -327,8 +341,8 @@ class BuildableProperty(Property):
|
|||||||
player.pay(Bank, self.unmortgage_cost)
|
player.pay(Bank, self.unmortgage_cost)
|
||||||
self.mortgaged = False
|
self.mortgaged = False
|
||||||
|
|
||||||
def calculate_rent(self, last_roll=None):
|
def calculate_rent(self, _):
|
||||||
super().calculate_rent(last_roll)
|
super().calculate_rent(_)
|
||||||
if self.buildings:
|
if self.buildings:
|
||||||
key = self.buildings
|
key = self.buildings
|
||||||
elif self.owner.owns_all_type(self.type):
|
elif self.owner.owns_all_type(self.type):
|
||||||
@@ -648,10 +662,12 @@ def get_space_index(name):
|
|||||||
|
|
||||||
|
|
||||||
class EconomicActor:
|
class EconomicActor:
|
||||||
pass
|
def __repr__(self):
|
||||||
|
return f"<{self.name} money=${self.money}>"
|
||||||
|
|
||||||
|
|
||||||
class Bank(EconomicActor):
|
class Bank(EconomicActor):
|
||||||
|
name = "Bank"
|
||||||
money = 20_580
|
money = 20_580
|
||||||
NUM_HOUSES = 32
|
NUM_HOUSES = 32
|
||||||
NUM_HOTELS = 12
|
NUM_HOTELS = 12
|
||||||
@@ -715,20 +731,25 @@ class Player(EconomicActor):
|
|||||||
self.name = name
|
self.name = name
|
||||||
Bank.pay(self, 1_500)
|
Bank.pay(self, 1_500)
|
||||||
|
|
||||||
def __repr__(self):
|
def pay(self, actor: Type["EconomicActor"], amount: int):
|
||||||
return f"<Player name='{self.name}' money={self.money}"
|
|
||||||
|
|
||||||
def pay(cls, actor: Type["EconomicActor"], amount: int):
|
|
||||||
if isinstance(actor, str):
|
if isinstance(actor, str):
|
||||||
actor = eval(actor)
|
actor = eval(actor)
|
||||||
if amount > cls.money:
|
print(actor, amount, self.money)
|
||||||
|
if amount > self.money:
|
||||||
raise NotEnough
|
raise NotEnough
|
||||||
cls.money -= amount
|
print(f"{self} is paying {actor} ${amount}")
|
||||||
|
self.money -= amount
|
||||||
actor.money += amount
|
actor.money += amount
|
||||||
|
|
||||||
def buy(self, property: Type["Property"], from_=Bank, cost=None):
|
def buy(self, property_: "Property", from_=Bank, cost=None):
|
||||||
property.owner = self
|
try:
|
||||||
self.pay(from_, cost or property.cost)
|
self.pay(from_, cost or property_.cost)
|
||||||
|
except NotEnough:
|
||||||
|
print(f"{self.name} does not have enough to buy {property_._name}")
|
||||||
|
return
|
||||||
|
property_.owner = self
|
||||||
|
print(f"{self.name} bought {property_._name}")
|
||||||
|
sleep(0.2)
|
||||||
|
|
||||||
def take_a_turn(self):
|
def take_a_turn(self):
|
||||||
if self.in_jail:
|
if self.in_jail:
|
||||||
@@ -742,7 +763,10 @@ class Player(EconomicActor):
|
|||||||
self.advance(num_spaces, just_rolled=True)
|
self.advance(num_spaces, just_rolled=True)
|
||||||
|
|
||||||
def owns_x_of_type(self, type_):
|
def owns_x_of_type(self, type_):
|
||||||
return len(self.properties_by_type.get(type_))
|
properties_of_this_type = self.properties_by_type.get(type_)
|
||||||
|
if properties_of_this_type is None:
|
||||||
|
return 0
|
||||||
|
return len(properties_of_this_type)
|
||||||
|
|
||||||
def owns_all_type(self, type_):
|
def owns_all_type(self, type_):
|
||||||
num_of_type = len(Property.instances_by_type()[type])
|
num_of_type = len(Property.instances_by_type()[type])
|
||||||
@@ -805,15 +829,23 @@ class Player(EconomicActor):
|
|||||||
self.money += 200
|
self.money += 200
|
||||||
|
|
||||||
self.current_space_index = new_space_index
|
self.current_space_index = new_space_index
|
||||||
|
|
||||||
if just_rolled:
|
if just_rolled:
|
||||||
last_roll = num_spaces
|
last_roll = num_spaces
|
||||||
else:
|
else:
|
||||||
last_roll = None
|
last_roll = None
|
||||||
|
|
||||||
|
try:
|
||||||
self.do_action_of_current_space(last_roll=last_roll)
|
self.do_action_of_current_space(last_roll=last_roll)
|
||||||
|
except NotEnough:
|
||||||
|
# TODO: is this always right?
|
||||||
|
# TODO: eventually make deals and mortgage prtoperties to avoid bankruptcy
|
||||||
|
print(f"{self.name} went bankrupt!")
|
||||||
|
self.bankrupt = True
|
||||||
|
|
||||||
def do_action_of_current_space(self, last_roll=None):
|
def do_action_of_current_space(self, last_roll=None):
|
||||||
space = Board.spaces[self.current_space_index]
|
space = Board.spaces[self.current_space_index]
|
||||||
space.action(self, last_roll=last_roll)
|
space.action(self, last_roll)
|
||||||
|
|
||||||
def go_to_jail(self):
|
def go_to_jail(self):
|
||||||
self.in_jail = True
|
self.in_jail = True
|
||||||
@@ -822,6 +854,7 @@ class Player(EconomicActor):
|
|||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
games = []
|
games = []
|
||||||
|
rounds = 0
|
||||||
|
|
||||||
def __init__(self, *player_names, buy_decision_algorithm=None):
|
def __init__(self, *player_names, buy_decision_algorithm=None):
|
||||||
def return_true(_, __):
|
def return_true(_, __):
|
||||||
@@ -848,10 +881,14 @@ class Game:
|
|||||||
current_player.take_a_turn()
|
current_player.take_a_turn()
|
||||||
while current_player.go_again:
|
while current_player.go_again:
|
||||||
current_player.take_a_turn()
|
current_player.take_a_turn()
|
||||||
|
self.rounds += 1
|
||||||
|
if self.rounds > 500:
|
||||||
|
break
|
||||||
self.end()
|
self.end()
|
||||||
|
|
||||||
def end(self):
|
def end(self):
|
||||||
print(self.active_players[0], "is the winner!")
|
print(self.active_players[0], "is the winner!")
|
||||||
|
print(f"It took {self.rounds} rounds.")
|
||||||
|
|
||||||
|
|
||||||
game = Game("Bot", "Ro", buy_decision_algorithm=None)
|
game = Game("Bot", "Ro", "Francis", buy_decision_algorithm=None)
|
||||||
|
|||||||
Reference in New Issue
Block a user