def test_get_rent_by_name_railroads(self): # Resetting property instance Properties.properties = _init_properties() railroad_1 = Properties.get_property_by_name('Reading Railroad') railroad_2 = Properties.get_property_by_name('Pennsylvania Railroad') railroad_3 = Properties.get_property_by_name('B. & O. Railroad') railroad_4 = Properties.get_property_by_name('Short Line') self.assertEqual(Properties.get_rent(property_name='Reading Railroad'), 0, 'Bank owns property, rent should be 0') # Note: DO NOT JUST MANUALLY CHANGE OWNER IN ANY OTHER INSTANCE BESIDES TESTING! railroad_1.property_owner = 'Car' self.assertEqual(Properties.get_rent(property_name='Reading Railroad'), 25, 'One railroad owned, rent should be 25') railroad_2.property_owner = 'Car' self.assertEqual(Properties.get_rent(property_name='Reading Railroad'), 50, 'Two railroads owned, rent should be 50') railroad_3.property_owner = 'Car' self.assertEqual(Properties.get_rent(property_name='Reading Railroad'), 100, 'Three railroads owned, rent should be 100') railroad_4.property_owner = 'Car' self.assertEqual(Properties.get_rent(property_name='Reading Railroad'), 200, 'Three railroads owned, rent should be 200')
def __str__(self): properties = ', '.join( Properties.get_property_names_by_owner(self.game_piece)) if properties: return f'{self.game_piece}: ${self.cash} cash, owns properties {properties}.' else: return f'{self.game_piece}: ${self.cash} cash, owns no properties.'
def _get_property_repair_amount(self, card_type): # Here, we need 'Chance' or 'Community Chest' for card type, as they drive different values. amount_to_pay = 0 price_per_house = 0 price_per_hotel = 0 if card_type == 'Chance': # For each house pay $25, for each hotel (num_houses = 5) pay $100 price_per_house = 25 price_per_hotel = 100 if card_type == 'Community Chest': # For each house pay $40, for each hotel (num_houses = 5) pay $115 price_per_house = 40 price_per_hotel = 115 properties_owned = Properties.get_properties_by_owner( owner_name=self.game_piece, tile_type=TileType.PROPERTY) for p in properties_owned: if 0 < p.num_houses < 5: amount_to_pay += price_per_house * p.num_houses if p.num_houses == 5: amount_to_pay += price_per_hotel return amount_to_pay
def _pay_income_tax(self): # Player shall pay 10% of total assets or $200, whichever is less. props = Properties.get_properties_by_owner(owner_name=self.game_piece) # Get all property values sum_prop_values = 0 sum_house_values = 0 for prop in props: # Get all the property values if prop.is_mortgaged: sum_prop_values += prop.mortgage_value else: sum_prop_values += prop.property_value # For each property, get all house values if prop.tile_type == TileType.PROPERTY: sum_house_values += prop.num_houses * prop.cost_per_house # Add all assets together sum_assets = sum_prop_values + sum_house_values + self.cash # Get 10% of assets sum_assets = int(sum_assets * 0.1) # If 10% of the assets < 200, pay assets if sum_assets < 200: amount_paid = self._pay_money(sum_assets) else: amount_paid = self._pay_money(200) return amount_paid
def test_get_property_by_name(self): # Resetting property instance Properties.properties = _init_properties() prop = Properties.get_property_by_name('Boardwalk') self.assertEqual(prop.name, 'Boardwalk', 'Property name should be Boardwalk') self.assertIsInstance(prop, TileProperty, 'Should be TileProperty class')
def test_get_rent_by_name_color_properties(self): # Resetting property instance Properties.properties = _init_properties() boardwalk = Properties.get_property_by_name('Boardwalk') park_place = Properties.get_property_by_name('Park Place') self.assertEqual(Properties.get_rent(property_name='Boardwalk'), 0, 'Bank owns property, rent should be 0') # Note: DO NOT JUST MANUALLY CHANGE OWNER IN ANY OTHER INSTANCE BESIDES TESTING! boardwalk.property_owner = 'Battleship' self.assertEqual(Properties.get_rent(property_name='Boardwalk'), 50, 'Boardwalk is owned, rent should be 50') park_place.property_owner = 'Battleship' self.assertEqual(Properties.get_rent(property_name='Boardwalk'), 100, 'All properties of same color are owned, rent should be 100') boardwalk.num_houses = 1 self.assertEqual(Properties.get_rent(property_name='Boardwalk'), 200, 'Boardwalk rent with 1 house should be 200') self.assertEqual(Properties.get_rent(property_name='Park Place'), 70, 'Park Place rent should be 70 which is doubled still')
def test_get_rent_by_name_utilities(self): # Resetting property instance Properties.properties = _init_properties() utility_1 = Properties.get_property_by_name('Electric Company') utility_2 = Properties.get_property_by_name('Water Works') self.assertEqual(Properties.get_rent(property_name='Electric Company', dice_roll=12), 0, 'Bank owns property, rent should be 0') # Note: DO NOT JUST MANUALLY CHANGE OWNER IN ANY OTHER INSTANCE BESIDES TESTING! utility_1.property_owner = 'Iron' self.assertEqual(Properties.get_rent(property_name='Electric Company', dice_roll=12), 48, 'Dice roll 12, one utility owned, 12 * 4 = 48') utility_2.property_owner = 'Iron' self.assertEqual(Properties.get_rent(property_name='Electric Company', dice_roll=12), 120, 'Dice roll 12, two utilities owned, 12 * 10 = 120')
def test_property_repair(self): # Resetting property instance Properties.properties = _init_properties() # Resetting game pieces Player._game_pieces = [ 'battleship', 'dog', 'iron', 'shoe', 'thimble', 'top hat', 'wheelbarrow' ] self.player = Player() # Some shopping money self.player.action(Actions.RECEIVE_MONEY, 100000) self.player.action(Actions.BUY_PROPERTY, 'Boardwalk') self.player.action(Actions.BUY_PROPERTY, 'Park Place') self.player.action(Actions.BUY_HOUSE, 'Boardwalk') self.player.action(Actions.BUY_HOUSE, 'Park Place') self.player.action(Actions.BUY_HOUSE, 'Boardwalk') self.player.action(Actions.BUY_HOUSE, 'Park Place') self.player.action(Actions.BUY_HOUSE, 'Boardwalk') self.player.action(Actions.BUY_HOUSE, 'Park Place') self.player.action(Actions.BUY_HOUSE, 'Boardwalk') self.player.action(Actions.BUY_HOUSE, 'Park Place') self.player.action(Actions.BUY_HOUSE, 'Boardwalk') self.assertEqual( Properties.get_property_by_name('Boardwalk').num_houses, 5, 'Boardwalk should be hotel status') self.assertEqual( Properties.get_property_by_name('Park Place').num_houses, 4, 'Park Place should have 4 houses') # Chance: 4 houses = 25 per house = 100, 1 hotel = 100, total = $200 # CC: 4 houses = 40 per house = 160, 1 hotel = 115, total = $275 repair_amount = self.player.action(Actions.PROPERTY_REPAIR, 'Chance') self.assertEqual(repair_amount, 200, 'Chance repairs should be 200') repair_amount = self.player.action(Actions.PROPERTY_REPAIR, 'Community Chest') self.assertEqual(repair_amount, 275, 'Community Chest repairs should be 275')
def _pay_money(self, amount): if self.cash > amount: # We have enough cash to pay. self.cash -= amount logging.info( f'{self.game_piece} paid ${amount}, now has ${self.cash}') return amount # If we reached this far, it means there's not enough cash to pay. # Find if there are any houses to sell properties_owned = Properties.get_properties_by_owner(self.game_piece) for p in properties_owned: # Check if we got enough cash from selling houses. if self.cash > amount: break if p.num_houses > 0: # Found a house. We'll need to make sure we're keeping houses evenly distributed across the group. properties_in_same_color = [ g for g in properties_owned if g.color == p.color ] # Start with the property with the most houses properties_in_same_color.sort(key=lambda l: l.num_houses, reverse=True) # Sell some properties for i in properties_in_same_color: self.cash += i.mortgage_house() # Check after each house sold to see if we got enough cash if self.cash > amount: break # See if we can bow out of this action after selling houses if self.cash > amount: self.cash -= amount return amount # If we reached this far, it means we didn't have enough houses to sell. Start mortgaging properties. for p in properties_owned: if self.cash > amount: break self.cash += p.mortgage_property() # Finally see if we have enough money to pay up. # If we have enough cash, return the full amount. # If there wasn't enough, just send whatever cash is available. # This will indicate the player is out and should be removed from the game. if self.cash > amount: self.cash -= amount return amount else: available_cash = self.cash self.cash = 0 return available_cash
def test_pay_money(self): # Resetting property instance Properties.properties = _init_properties() # Resetting game pieces Player._game_pieces = [ 'battleship', 'dog', 'iron', 'shoe', 'thimble', 'top hat', 'wheelbarrow' ] self.player = Player() self.player.action(Actions.BUY_PROPERTY, 'Boardwalk') self.player.action(Actions.BUY_PROPERTY, 'Park Place') # Should have 750 at this point self.player.action(Actions.BUY_HOUSE, 'Boardwalk') self.assertEqual( self.player.cash, 550, 'Started at 750, bought a house for 200, should be 550 cash') self.assertEqual( Properties.get_property_by_name('Boardwalk').num_houses, 1, 'Num houses should be 1') # Paying 1 dollar more than we have in cash. Will have to sell house. self.player.action(Actions.PAY_MONEY, 551) self.assertEqual(self.player.cash, 99, 'Had 1300, sell house for 100, left with 99') self.assertEqual( Properties.get_property_by_name('Boardwalk').num_houses, 0, 'Num houses should be 0') # cash = 99 # mortgage Park Place = 350 / 2 = 175 # mortgage Boardwalk = 400 / 2 = 200 # total = $474 money = self.player.action(Actions.PAY_MONEY, 1000) self.assertEqual( money, 474, 'Player did not have enough money. The function returned amount of cash on hand' )
def test_buy_properties(self): # Resetting property instance Properties.properties = _init_properties() # Resetting game pieces Player._game_pieces = [ 'battleship', 'dog', 'iron', 'shoe', 'thimble', 'top hat', 'wheelbarrow' ] self.player = Player() self.player.action(Actions.BUY_PROPERTY, 'Boardwalk') self.assertEqual(self.player.cash, 1100, 'Bought Boardwalk, should have 1100 leftover') boardwalk = Properties.get_property_by_name('Boardwalk') self.assertEqual(boardwalk.property_owner, self.player.game_piece, 'New owner should be player')
def _buy_property(self, property_name): logging.info( f'{self.game_piece} is attempting to buy the property {property_name}.' ) property_to_buy = Properties.get_property_by_name(property_name) current_owner = property_to_buy.property_owner if self.cash > property_to_buy.property_value and current_owner == 'Bank': self.cash -= property_to_buy.property_value property_to_buy.update_property_owner(self.game_piece) logging.info( f'{self.game_piece} bought property {property_name} ' f'for ${property_to_buy.property_value}, has ${self.cash} left.' ) return True logging.info( f'{self.game_piece} did not buy property {property_name}, has ${self.cash} left.' ) return False
def test_buy_houses(self): # Resetting property instance Properties.properties = _init_properties() # Resetting game pieces Player._game_pieces = [ 'battleship', 'dog', 'iron', 'shoe', 'thimble', 'top hat', 'wheelbarrow' ] self.player = Player() self.player.action(Actions.BUY_PROPERTY, 'Boardwalk') self.player.action(Actions.BUY_PROPERTY, 'Park Place') # Should have 750 cash at this point self.player.action(Actions.BUY_HOUSE, 'Boardwalk') self.assertEqual( Properties.get_property_by_name('Boardwalk').num_houses, 1, 'Got a house on Boardwalk') self.assertEqual(self.player.cash, 550, 'Had 750, house cost 200, should now have 550')
def player_action(self, tile): current_player = self.get_current_player() curr = current_player.game_piece logging.info(f'{curr} landed on tile {tile[0]}.') if tile[1] in [TileType.PROPERTY, TileType.UTILITY, TileType.RAILROAD]: property_owner = Properties.get_property_owner_by_name(tile[0]) if property_owner == curr: # Player already owns property. Do nothing. logging.info( f'{curr} already owns property {tile[0]}. No action taken.' ) return if property_owner == 'Bank': # nobody owns property. Player can choose to buy or auction # TODO: buy or auction did_player_buy_property = current_player.action( Actions.BUY_PROPERTY, tile[0]) if not did_player_buy_property: # Chose not to buy house, or not enough money to buy house. Auction it! # TODO: Auction house return return # If we're here, that means another player owns the property and current player needs to pay rent. landlord_player = self.get_player(property_owner) amount_owed = Properties.get_rent(tile[0], self.dice) logging.info( f'{curr} owes {landlord_player.game_piece} ${amount_owed}') amount_paid = current_player.action(Actions.PAY_MONEY, amount_owed) landlord_player.action(Actions.RECEIVE_MONEY, amount_paid) if amount_owed != amount_paid: # Player could not pay the amount owed and has gone bankrupt. self.remove_player_from_game(curr) else: logging.info( f'{curr} has paid the full ${amount_paid}, has ${current_player.cash} left.' ) if tile[1] == TileType.LUXURY_TAX: logging.info(f'{curr} owes $75 luxury tax.') amount_owed = 75 amount_paid = current_player.action(Actions.PAY_MONEY, amount_owed) if amount_paid != amount_owed: # Player could not pay the amount owed and has gone bankrupt. self.remove_player_from_game(curr) else: logging.info( f'{curr} has paid the full ${amount_paid}, has ${current_player.cash} left.' ) if tile[1] == TileType.INCOME_TAX: amount_paid = current_player.action(Actions.PAY_INCOME_TAX, None) logging.info( f'{curr} has paid income tax of {amount_paid}, has ${current_player.cash} left.' ) if tile[1] == TileType.GO_TO_JAIL: current_player.action(Actions.MOVE_DIRECTLY_TO_TILE_DO_NOT_PASS_GO, 10) logging.info(f'{curr} will go directly to jail.') if tile[1] == TileType.CHANCE: # TODO: implement Actions.PAY_MONEY_TO_EACH_PLAYER and Actions.RECEIVE_MONEY_FROM_EACH_PLAYER chance_card = self.board.chance_cards.draw() logging.info(f'{curr} drew Chance card: {chance_card.name}') current_player.action(chance_card.action, chance_card.amount) if tile[1] == TileType.COMMUNITY_CHEST: cc_card = self.board.community_chest_cards.draw() logging.info(f'{curr} drew Community Chest card: {cc_card.name}') current_player.action(cc_card.action, cc_card.amount) # GO, Jail (Just Visiting), and Free Parking do nothing. if tile[1] in [TileType.GO, TileType.JAIL, TileType.FREE_PARKING]: logging.info(f'{curr} takes no action.') return
def _buy_house(self, property_name): logging.info( f'{self.game_piece} is attempting to buy a house on {property_name}.' ) properties_owned = Properties.get_properties_by_owner( self.game_piece, TileType.PROPERTY) if properties_owned: if property_name not in [p.name for p in properties_owned]: e = 'Property' + property_name + ' not owned by player.' logging.error(e) raise Exception(e) # Get the property to put a house on. prop = Properties.get_property_by_name(property_name) # Make sure we're dealing with the right type of property first... if prop.tile_type != TileType.PROPERTY: return 0 # See if player can afford to buy a house. if self.cash < prop.cost_per_house: logging.warning( f'{self.game_piece} could not afford a house on {property_name}. ' f'Still has {prop.num_houses} houses.') return prop.num_houses # Verifying player owns all the properties of that color before buying houses. properties_owned_in_same_color = Properties.get_properties_by_owner( owner_name=self.game_piece, tile_type=prop.tile_type, color=prop.color) prop_colors = Properties.get_properties_by_color(prop.color) if len(properties_owned_in_same_color) != len(prop_colors): logging.warning( f'{self.game_piece} does not own all properties of the same color ' f'and cannot purchase a house. Still has {prop.num_houses} houses.' ) return prop.num_houses # We also need to verify we don't own too many houses on one property. min_houses_in_same_color = min( [p.num_houses for p in properties_owned_in_same_color]) if prop.num_houses > min_houses_in_same_color: logging.warning( f'{self.game_piece} cannot put a house on this property ' f'before putting houses on the other properties first.') return prop.num_houses # If we made it this far, we are eligible to purchase a house on this property. # prop.add_house() will make sure we don't add more than 5 properties. house_count = prop.add_house() if house_count != -1: self.cash -= prop.cost_per_house logging.info( f'{self.game_piece} added a house to' f' property {property_name} and now has {prop.num_houses} ' f'houses for {prop.cost_per_house}, has ${self.cash} left.') else: logging.warning( f'{self.game_piece} tried adding a house but already has too many.' ) return prop.num_houses