working through various bugs, computer is able to play a dozen turns against itself.

This commit is contained in:
2020-05-21 01:15:14 +02:00
parent 0c23f99667
commit a12c3e78aa
2 changed files with 131 additions and 59 deletions

View File

@@ -12,3 +12,7 @@ class Argument(Exception):
class DidntFind(Exception):
pass
class NoOwner(Exception):
pass

View File

@@ -10,15 +10,14 @@ Along those lines, here's some prior art:
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 itertools import cycle
from pprint import pprint
from random import shuffle, choice
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)
@@ -27,12 +26,15 @@ BACKUP_LANGUAGE = "English"
class Space:
pass
def __repr__(self):
if hasattr(self, "_name"):
return self._name
return self.__name__
class NothingHappensWhenYouLandOnItSpace(Space):
@classmethod
def action(self, player: "Player"):
def action(self, player: "Player", last_roll):
pass
@@ -52,7 +54,7 @@ class TaxSpace(Space):
amount = None
@classmethod
def action(cls, player):
def action(cls, player, last_roll):
player.pay("Bank", cls.amount)
@@ -68,7 +70,7 @@ class IncomeTax(TaxSpace):
class GoToJail(Space):
@classmethod
def action(cls, player):
def action(cls, player, last_roll):
player.go_to_jail()
@@ -76,10 +78,11 @@ class CardSpace(Space):
deck = None
@classmethod
def action(cls, player):
def action(cls, player, last_roll):
# for lazy loading to avoid circular imports (?)
deck = eval(cls.deck)
card = deck.get_card()
return card.action(player)
return card.action(player, last_roll)
class CommunityChest(CardSpace):
@@ -97,10 +100,13 @@ class Card(ABC):
cost = None
keep = False
@abstractmethod
def action(self, player: "Player"):
@classmethod
def action(self, player: "Player", last_roll):
raise NotImplementedError
def __repr__(self):
return self.text
class ElectedPresidentCard(Card):
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."
}
def action(self, player):
@classmethod
def action(cls, player, last_roll):
for other_player in Game.game.active_players:
if other_player != player:
player.pay(other_player, 50)
@@ -120,7 +127,8 @@ class GetOutOfJailFreeCard(Card):
"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
@@ -128,8 +136,9 @@ class AdvanceCard(Card):
mandatory_action = True
kwarg = {}
def action(self, player):
player.advance(**self.kwarg)
@classmethod
def action(cls, player, last_roll):
player.advance(**cls.kwarg)
class GoToJailCard(AdvanceCard):
@@ -168,7 +177,8 @@ class BuildingAndLoanMaturesCard(Card):
"English": "Your building and loan matures. Collect M150.",
}
def action(self, player):
@classmethod
def action(self, player, last_roll):
Bank.pay(player, 150)
@@ -176,7 +186,8 @@ class SpeedingCard(Card):
mandatory_action = True
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)
@@ -207,7 +218,8 @@ class ChanceDeck(Deck):
class CommunityChestDeck(Deck):
deck = []
# TODO: remove these and add the readl ones!
deck = [GoToJailCard, SpeedingCard]
def shuffle_decks():
@@ -220,22 +232,42 @@ class Decision:
class BuyDecision(Decision):
def __init__(self, property: Type["Property"], player: "Player"):
def __init__(self, property: "Property", player: "Player"):
pass
class Property(Space):
mortgaged = False
owner = None
def __init__(self, _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):
cost = 200
rent = {1: lambda dice_total: dice_total * 4, 2: lambda dice_total: dice_total * 10}
mortgage_cost = 75
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):
@@ -243,11 +275,14 @@ class Railroad(Property):
rent = ({1: 25, 2: 50, 3: 100, 4: 200},)
mortgage_cost = 100
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):
owner = None
def __init__(
self,
_name,
@@ -263,16 +298,10 @@ class BuildableProperty(Property):
self.rent = rent
self.house_and_hotel_cost = house_and_hotel_cost
self.color = color
self.type = color
self.mortgage_cost = mortgage_cost
self.unmortgage_cost = unmortgage_cost
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)
self.buildings = None
def mortgage(self, player: "Player"):
Bank.pay(player, self.mortgage_cost)
@@ -282,14 +311,15 @@ class BuildableProperty(Property):
player.pay(Bank, self.unmortgage_cost)
self.mortgaged = False
def calculate_rent(self):
raise NotImplementedError
# if not self.owner:
# raise NoOwner
# if self.buildings:
# key = self.buildings
# else:
# if self.owner.owns_all_type(self.)
def calculate_rent(self, last_roll=None):
super().calculate_rent()
if self.buildings:
key = self.buildings
elif self.owner.owns_all_type(self.type):
key = "monopoly"
else:
key = 0
return self.rent[key]
class Board:
@@ -604,19 +634,7 @@ def get_space_index(name):
class EconomicActor:
money = 0
@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
pass
class Bank(EconomicActor):
@@ -624,6 +642,21 @@ class Bank(EconomicActor):
NUM_HOUSES = 32
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
def get_building(cls, type_, quantity):
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):
print(until_space_type)
print(type(until_space_type))
space_indices_to_traverse = list(
range(current_space_index + 1, Board.NUM_SPACES)
) + list(range(current_space_index))
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):
return index
else:
# for debugging TODO: delete
print(type(Board.spaces[index]))
pass
else:
# for debugging TODO: delete
raise DidntFind
@@ -669,17 +707,36 @@ class Player(EconomicActor):
get_out_of_jail_free_card = False
go_again = False
current_space_index = get_space_index("Go")
money = 0
def __init__(self, name):
self.name = name
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):
print(f"{self.name} taking a turn...")
if self.in_jail:
return GetOutOfJailDecision(self)
num_spaces, doubles = self.roll_the_dice()
self.go_again = doubles
self.advance(num_spaces)
self.advance(num_spaces, just_rolled=True)
@staticmethod
def roll_the_dice() -> Tuple[int, Doubles]:
@@ -690,7 +747,12 @@ class Player(EconomicActor):
return total, cast(Doubles, False)
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
check_args(num_spaces, space_index, until_space_type)
@@ -701,7 +763,9 @@ class Player(EconomicActor):
space_index = get_space_index(space_index)
new_space_index = space_index
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:
print("You passed go! Here's 200 Monopoly Dollars")
@@ -712,12 +776,15 @@ class Player(EconomicActor):
self.money += 200
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]
print(space)
space.action(self)
space.action(self, last_roll=last_roll)
def go_to_jail(self):
self.in_jail = True
@@ -725,10 +792,10 @@ class Player(EconomicActor):
class Game:
game = None
games = []
def __init__(self, *player_names):
self.game = self
self.games.append(self)
if len(player_names) > 8:
raise TooManyPlayers
self._players = [Player(player_name) for player_name in player_names]
@@ -747,6 +814,7 @@ class Game:
if not current_player.bankrupt:
current_player.take_a_turn()
while current_player.go_again:
print(f"{current_player} got doubles, going again:")
current_player.take_a_turn()
self.end()