working through various bugs, computer is able to play a dozen turns against itself.
This commit is contained in:
@@ -12,3 +12,7 @@ class Argument(Exception):
|
|||||||
|
|
||||||
class DidntFind(Exception):
|
class DidntFind(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoOwner(Exception):
|
||||||
|
pass
|
||||||
|
|||||||
186
monopoly.py
186
monopoly.py
@@ -10,15 +10,14 @@ Along those lines, here's some prior art:
|
|||||||
http://www.tkcs-collins.com/truman/monopoly/monopoly.shtml
|
http://www.tkcs-collins.com/truman/monopoly/monopoly.shtml
|
||||||
|
|
||||||
|
|
||||||
# TODO: until_space_type -- evaluate this as a string
|
# TODO: maybe instead of all these classmethods, instances?
|
||||||
"""
|
"""
|
||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
from pprint import pprint
|
|
||||||
from random import shuffle, choice
|
from random import shuffle, choice
|
||||||
from typing import Type, NewType, Tuple, cast
|
from typing import Type, NewType, Tuple, cast
|
||||||
|
|
||||||
from exceptions import TooManyPlayers, NotEnough, DidntFind, Argument
|
from exceptions import TooManyPlayers, NotEnough, DidntFind, Argument, NoOwner
|
||||||
|
|
||||||
Doubles = NewType("Doubles", bool)
|
Doubles = NewType("Doubles", bool)
|
||||||
|
|
||||||
@@ -27,12 +26,15 @@ BACKUP_LANGUAGE = "English"
|
|||||||
|
|
||||||
|
|
||||||
class Space:
|
class Space:
|
||||||
pass
|
def __repr__(self):
|
||||||
|
if hasattr(self, "_name"):
|
||||||
|
return self._name
|
||||||
|
return self.__name__
|
||||||
|
|
||||||
|
|
||||||
class NothingHappensWhenYouLandOnItSpace(Space):
|
class NothingHappensWhenYouLandOnItSpace(Space):
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(self, player: "Player"):
|
def action(self, player: "Player", last_roll):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ class TaxSpace(Space):
|
|||||||
amount = None
|
amount = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player):
|
def action(cls, player, last_roll):
|
||||||
player.pay("Bank", cls.amount)
|
player.pay("Bank", cls.amount)
|
||||||
|
|
||||||
|
|
||||||
@@ -68,7 +70,7 @@ class IncomeTax(TaxSpace):
|
|||||||
|
|
||||||
class GoToJail(Space):
|
class GoToJail(Space):
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player):
|
def action(cls, player, last_roll):
|
||||||
player.go_to_jail()
|
player.go_to_jail()
|
||||||
|
|
||||||
|
|
||||||
@@ -76,10 +78,11 @@ class CardSpace(Space):
|
|||||||
deck = None
|
deck = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def action(cls, player):
|
def action(cls, player, last_roll):
|
||||||
|
# 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)
|
return card.action(player, last_roll)
|
||||||
|
|
||||||
|
|
||||||
class CommunityChest(CardSpace):
|
class CommunityChest(CardSpace):
|
||||||
@@ -97,10 +100,13 @@ class Card(ABC):
|
|||||||
cost = None
|
cost = None
|
||||||
keep = False
|
keep = False
|
||||||
|
|
||||||
@abstractmethod
|
@classmethod
|
||||||
def action(self, player: "Player"):
|
def action(self, player: "Player", last_roll):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.text
|
||||||
|
|
||||||
|
|
||||||
class ElectedPresidentCard(Card):
|
class ElectedPresidentCard(Card):
|
||||||
mandatory_action = True
|
mandatory_action = True
|
||||||
@@ -108,7 +114,8 @@ class ElectedPresidentCard(Card):
|
|||||||
"français": "Vous avez été elu president du conseil d'administration. Versez M50 à chaque joueur."
|
"français": "Vous avez été elu president du conseil d'administration. Versez M50 à chaque joueur."
|
||||||
}
|
}
|
||||||
|
|
||||||
def action(self, player):
|
@classmethod
|
||||||
|
def action(cls, player, last_roll):
|
||||||
for other_player in Game.game.active_players:
|
for other_player in Game.game.active_players:
|
||||||
if other_player != player:
|
if other_player != player:
|
||||||
player.pay(other_player, 50)
|
player.pay(other_player, 50)
|
||||||
@@ -120,7 +127,8 @@ class GetOutOfJailFreeCard(Card):
|
|||||||
"qu'elle soit utilisée ou vendue. "
|
"qu'elle soit utilisée ou vendue. "
|
||||||
}
|
}
|
||||||
|
|
||||||
def action(self, player: "Player"):
|
@classmethod
|
||||||
|
def action(cls, player: "Player", last_roll):
|
||||||
player.get_out_of_jail_free_card = True
|
player.get_out_of_jail_free_card = True
|
||||||
|
|
||||||
|
|
||||||
@@ -128,8 +136,9 @@ class AdvanceCard(Card):
|
|||||||
mandatory_action = True
|
mandatory_action = True
|
||||||
kwarg = {}
|
kwarg = {}
|
||||||
|
|
||||||
def action(self, player):
|
@classmethod
|
||||||
player.advance(**self.kwarg)
|
def action(cls, player, last_roll):
|
||||||
|
player.advance(**cls.kwarg)
|
||||||
|
|
||||||
|
|
||||||
class GoToJailCard(AdvanceCard):
|
class GoToJailCard(AdvanceCard):
|
||||||
@@ -168,7 +177,8 @@ class BuildingAndLoanMaturesCard(Card):
|
|||||||
"English": "Your building and loan matures. Collect M150.",
|
"English": "Your building and loan matures. Collect M150.",
|
||||||
}
|
}
|
||||||
|
|
||||||
def action(self, player):
|
@classmethod
|
||||||
|
def action(self, player, last_roll):
|
||||||
Bank.pay(player, 150)
|
Bank.pay(player, 150)
|
||||||
|
|
||||||
|
|
||||||
@@ -176,7 +186,8 @@ class SpeedingCard(Card):
|
|||||||
mandatory_action = True
|
mandatory_action = True
|
||||||
text = {"français": "Amende pour excès de vitesse. Payez M15."}
|
text = {"français": "Amende pour excès de vitesse. Payez M15."}
|
||||||
|
|
||||||
def action(self, player):
|
@classmethod
|
||||||
|
def action(cls, player, last_roll):
|
||||||
player.pay(Bank, 15)
|
player.pay(Bank, 15)
|
||||||
|
|
||||||
|
|
||||||
@@ -207,7 +218,8 @@ class ChanceDeck(Deck):
|
|||||||
|
|
||||||
|
|
||||||
class CommunityChestDeck(Deck):
|
class CommunityChestDeck(Deck):
|
||||||
deck = []
|
# TODO: remove these and add the readl ones!
|
||||||
|
deck = [GoToJailCard, SpeedingCard]
|
||||||
|
|
||||||
|
|
||||||
def shuffle_decks():
|
def shuffle_decks():
|
||||||
@@ -220,22 +232,42 @@ class Decision:
|
|||||||
|
|
||||||
|
|
||||||
class BuyDecision(Decision):
|
class BuyDecision(Decision):
|
||||||
def __init__(self, property: Type["Property"], player: "Player"):
|
def __init__(self, property: "Property", player: "Player"):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Property(Space):
|
class Property(Space):
|
||||||
mortgaged = False
|
mortgaged = False
|
||||||
|
owner = None
|
||||||
|
|
||||||
def __init__(self, _name):
|
def __init__(self, _name):
|
||||||
self._name = _name
|
self._name = _name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def action(cls, player: "Player", last_roll=None):
|
||||||
|
if not cls.owner:
|
||||||
|
return BuyDecision(cls, player)
|
||||||
|
if cls.mortgaged:
|
||||||
|
return
|
||||||
|
return player.pay(cls.owner, cls.calculate_rent(last_roll=last_roll))
|
||||||
|
|
||||||
|
def calculate_rent(self, last_roll):
|
||||||
|
if not self.owner:
|
||||||
|
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 = {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"
|
||||||
|
|
||||||
|
def calculate_rent(self, last_roll: int):
|
||||||
|
super().calculate_rent(last_roll)
|
||||||
|
if not last_roll:
|
||||||
|
return 10 * Player.roll_the_dice()[0]
|
||||||
|
return self.rent[self.owner.owns_x_of_type(self)](last_roll)
|
||||||
|
|
||||||
|
|
||||||
class Railroad(Property):
|
class Railroad(Property):
|
||||||
@@ -243,11 +275,14 @@ class Railroad(Property):
|
|||||||
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"
|
||||||
|
|
||||||
|
def calculate_rent(self, last_roll=None):
|
||||||
|
super().calculate_rent()
|
||||||
|
return self.rent[self.owner.owns_x_of_type(self)]
|
||||||
|
|
||||||
|
|
||||||
class BuildableProperty(Property):
|
class BuildableProperty(Property):
|
||||||
owner = None
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
_name,
|
_name,
|
||||||
@@ -263,16 +298,10 @@ class BuildableProperty(Property):
|
|||||||
self.rent = rent
|
self.rent = rent
|
||||||
self.house_and_hotel_cost = house_and_hotel_cost
|
self.house_and_hotel_cost = house_and_hotel_cost
|
||||||
self.color = color
|
self.color = color
|
||||||
|
self.type = color
|
||||||
self.mortgage_cost = mortgage_cost
|
self.mortgage_cost = mortgage_cost
|
||||||
self.unmortgage_cost = unmortgage_cost
|
self.unmortgage_cost = unmortgage_cost
|
||||||
|
self.buildings = None
|
||||||
def action(self, player: "Player"):
|
|
||||||
# TODO: implement on Property, then extend here
|
|
||||||
if self.owner:
|
|
||||||
if self.mortgaged:
|
|
||||||
return
|
|
||||||
player.pay(self.owner, self.calculate_rent())
|
|
||||||
return BuyDecision(self, player)
|
|
||||||
|
|
||||||
def mortgage(self, player: "Player"):
|
def mortgage(self, player: "Player"):
|
||||||
Bank.pay(player, self.mortgage_cost)
|
Bank.pay(player, self.mortgage_cost)
|
||||||
@@ -282,14 +311,15 @@ 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):
|
def calculate_rent(self, last_roll=None):
|
||||||
raise NotImplementedError
|
super().calculate_rent()
|
||||||
# if not self.owner:
|
if self.buildings:
|
||||||
# raise NoOwner
|
key = self.buildings
|
||||||
# if self.buildings:
|
elif self.owner.owns_all_type(self.type):
|
||||||
# key = self.buildings
|
key = "monopoly"
|
||||||
# else:
|
else:
|
||||||
# if self.owner.owns_all_type(self.)
|
key = 0
|
||||||
|
return self.rent[key]
|
||||||
|
|
||||||
|
|
||||||
class Board:
|
class Board:
|
||||||
@@ -604,19 +634,7 @@ def get_space_index(name):
|
|||||||
|
|
||||||
|
|
||||||
class EconomicActor:
|
class EconomicActor:
|
||||||
money = 0
|
pass
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def pay(cls, actor: Type["EconomicActor"], amount: int):
|
|
||||||
if amount > cls.money:
|
|
||||||
print(cls)
|
|
||||||
print("actor:", actor)
|
|
||||||
print("cls.money:", cls.money)
|
|
||||||
raise NotEnough
|
|
||||||
if isinstance(actor, str):
|
|
||||||
actor = eval(actor)
|
|
||||||
cls.money -= amount
|
|
||||||
actor.money += amount
|
|
||||||
|
|
||||||
|
|
||||||
class Bank(EconomicActor):
|
class Bank(EconomicActor):
|
||||||
@@ -624,6 +642,21 @@ class Bank(EconomicActor):
|
|||||||
NUM_HOUSES = 32
|
NUM_HOUSES = 32
|
||||||
NUM_HOTELS = 12
|
NUM_HOTELS = 12
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def pay(cls, actor: "EconomicActor", amount: int):
|
||||||
|
if isinstance(actor, str):
|
||||||
|
actor = eval(actor)
|
||||||
|
if amount > cls.money:
|
||||||
|
print("cls:", cls)
|
||||||
|
print("cls.money:", cls.money)
|
||||||
|
print(cls)
|
||||||
|
print("actor:", actor)
|
||||||
|
print("actor.money:", actor.money)
|
||||||
|
print(Game.games[0]._players)
|
||||||
|
raise NotEnough
|
||||||
|
cls.money -= amount
|
||||||
|
actor.money += amount
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_building(cls, type_, quantity):
|
def get_building(cls, type_, quantity):
|
||||||
if type_ not in ("house", "hotel"):
|
if type_ not in ("house", "hotel"):
|
||||||
@@ -636,15 +669,20 @@ class Bank(EconomicActor):
|
|||||||
|
|
||||||
|
|
||||||
def get_index_of_next_space_of_type(current_space_index, until_space_type):
|
def get_index_of_next_space_of_type(current_space_index, until_space_type):
|
||||||
|
print(until_space_type)
|
||||||
|
print(type(until_space_type))
|
||||||
space_indices_to_traverse = list(
|
space_indices_to_traverse = list(
|
||||||
range(current_space_index + 1, Board.NUM_SPACES)
|
range(current_space_index + 1, Board.NUM_SPACES)
|
||||||
) + list(range(current_space_index))
|
) + list(range(current_space_index))
|
||||||
for index in space_indices_to_traverse:
|
for index in space_indices_to_traverse:
|
||||||
|
if isinstance(until_space_type, str):
|
||||||
|
until_space_type = eval(until_space_type)
|
||||||
if isinstance(Board.spaces[index], until_space_type):
|
if isinstance(Board.spaces[index], until_space_type):
|
||||||
return index
|
return index
|
||||||
else:
|
else:
|
||||||
# for debugging TODO: delete
|
# for debugging TODO: delete
|
||||||
print(type(Board.spaces[index]))
|
print(type(Board.spaces[index]))
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
# for debugging TODO: delete
|
# for debugging TODO: delete
|
||||||
raise DidntFind
|
raise DidntFind
|
||||||
@@ -669,17 +707,36 @@ class Player(EconomicActor):
|
|||||||
get_out_of_jail_free_card = False
|
get_out_of_jail_free_card = False
|
||||||
go_again = False
|
go_again = False
|
||||||
current_space_index = get_space_index("Go")
|
current_space_index = get_space_index("Go")
|
||||||
|
money = 0
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
Bank.pay(self, 1_500)
|
Bank.pay(self, 1_500)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Player name='{self.name}' money={self.money}"
|
||||||
|
|
||||||
|
def pay(cls, actor: "EconomicActor", amount: int):
|
||||||
|
if isinstance(actor, str):
|
||||||
|
actor = eval(actor)
|
||||||
|
if amount > cls.money:
|
||||||
|
print("cls:", cls)
|
||||||
|
print("cls.money:", cls.money)
|
||||||
|
print(cls)
|
||||||
|
print("actor:", actor)
|
||||||
|
print("actor.money:", actor.money)
|
||||||
|
print(Game.games[0]._players)
|
||||||
|
raise NotEnough
|
||||||
|
cls.money -= amount
|
||||||
|
actor.money += amount
|
||||||
|
|
||||||
def take_a_turn(self):
|
def take_a_turn(self):
|
||||||
|
print(f"{self.name} taking a turn...")
|
||||||
if self.in_jail:
|
if self.in_jail:
|
||||||
return GetOutOfJailDecision(self)
|
return GetOutOfJailDecision(self)
|
||||||
num_spaces, doubles = self.roll_the_dice()
|
num_spaces, doubles = self.roll_the_dice()
|
||||||
self.go_again = doubles
|
self.go_again = doubles
|
||||||
self.advance(num_spaces)
|
self.advance(num_spaces, just_rolled=True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def roll_the_dice() -> Tuple[int, Doubles]:
|
def roll_the_dice() -> Tuple[int, Doubles]:
|
||||||
@@ -690,7 +747,12 @@ class Player(EconomicActor):
|
|||||||
return total, cast(Doubles, False)
|
return total, cast(Doubles, False)
|
||||||
|
|
||||||
def advance(
|
def advance(
|
||||||
self, num_spaces=None, space_index=None, until_space_type=None, pass_go=True
|
self,
|
||||||
|
num_spaces=None,
|
||||||
|
space_index=None,
|
||||||
|
until_space_type=None,
|
||||||
|
pass_go=True,
|
||||||
|
just_rolled=True,
|
||||||
):
|
):
|
||||||
new_space_index = None
|
new_space_index = None
|
||||||
check_args(num_spaces, space_index, until_space_type)
|
check_args(num_spaces, space_index, until_space_type)
|
||||||
@@ -701,7 +763,9 @@ class Player(EconomicActor):
|
|||||||
space_index = get_space_index(space_index)
|
space_index = get_space_index(space_index)
|
||||||
new_space_index = space_index
|
new_space_index = space_index
|
||||||
elif until_space_type:
|
elif until_space_type:
|
||||||
new_space_index = get_index_of_next_space_of_type(until_space_type)
|
new_space_index = get_index_of_next_space_of_type(
|
||||||
|
self.current_space_index, until_space_type
|
||||||
|
)
|
||||||
|
|
||||||
if pass_go and new_space_index >= Board.NUM_SPACES - 1:
|
if pass_go and new_space_index >= Board.NUM_SPACES - 1:
|
||||||
print("You passed go! Here's 200 Monopoly Dollars")
|
print("You passed go! Here's 200 Monopoly Dollars")
|
||||||
@@ -712,12 +776,15 @@ class Player(EconomicActor):
|
|||||||
self.money += 200
|
self.money += 200
|
||||||
|
|
||||||
self.current_space_index = new_space_index
|
self.current_space_index = new_space_index
|
||||||
self.do_action_of_current_space()
|
if just_rolled:
|
||||||
|
last_roll = num_spaces
|
||||||
|
else:
|
||||||
|
last_roll = None
|
||||||
|
self.do_action_of_current_space(last_roll=last_roll)
|
||||||
|
|
||||||
def do_action_of_current_space(self):
|
def do_action_of_current_space(self, last_roll=None):
|
||||||
space = Board.spaces[self.current_space_index]
|
space = Board.spaces[self.current_space_index]
|
||||||
print(space)
|
space.action(self, last_roll=last_roll)
|
||||||
space.action(self)
|
|
||||||
|
|
||||||
def go_to_jail(self):
|
def go_to_jail(self):
|
||||||
self.in_jail = True
|
self.in_jail = True
|
||||||
@@ -725,10 +792,10 @@ class Player(EconomicActor):
|
|||||||
|
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
game = None
|
games = []
|
||||||
|
|
||||||
def __init__(self, *player_names):
|
def __init__(self, *player_names):
|
||||||
self.game = self
|
self.games.append(self)
|
||||||
if len(player_names) > 8:
|
if len(player_names) > 8:
|
||||||
raise TooManyPlayers
|
raise TooManyPlayers
|
||||||
self._players = [Player(player_name) for player_name in player_names]
|
self._players = [Player(player_name) for player_name in player_names]
|
||||||
@@ -747,6 +814,7 @@ class Game:
|
|||||||
if not current_player.bankrupt:
|
if not current_player.bankrupt:
|
||||||
current_player.take_a_turn()
|
current_player.take_a_turn()
|
||||||
while current_player.go_again:
|
while current_player.go_again:
|
||||||
|
print(f"{current_player} got doubles, going again:")
|
||||||
current_player.take_a_turn()
|
current_player.take_a_turn()
|
||||||
self.end()
|
self.end()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user