Beispiel #1
0
    def auction_result(self, status, property, player, amount_paid):
        '''
        Called with the result of an auction. All players receive
        this notification.

        status is either AUCTION_SUCCEEDED or AUCTION_FAILED.

        If the auction succeeded, the property, the player who won
        the auction and the amount they paid are passed to the AI.

        If the auction failed, the player will be None and the
        amount paid will be 0.

        No response is required.
        '''
        if player is None:
            return

        if player.name != self.get_name():
            return

        if status == PlayerAIBase.Action.AUCTION_SUCCEEDED:
            Logger.log("# {0}: Property {1} won at auction".format(self.get_name(), property), Logger.INFO)
        else:
            Logger.log("# Property lost at auction", Logger.INFO)

        return
Beispiel #2
0
    def deal_proposed(self, game_state, player, deal_proposal):
        '''
        Called when a deal is proposed by another player.
        '''


        if len(deal_proposal.properties_wanted) > 0 and len(deal_proposal.properties_offered) == 0:
            (bid_price, ask_price) = self._calc_value_of_properties(game_state, player, deal_proposal.properties_wanted)

            if ask_price < self.amount_to_raise:
                ask_price = self.amount_to_raise

            Logger.log("# Accepted proposed deal of wanted properties {0} for {1}".format(str(deal_proposal.properties_wanted), ask_price))
            return DealResponse(
                action=DealResponse.Action.ACCEPT,
                minimum_cash_wanted = ask_price
                )

        if self.amount_to_raise > 0.0:
            return DealResponse(DealResponse.Action.REJECT)

        # We only accept deals for single properties wanted from us...
        if len(deal_proposal.properties_offered) > 0 and len(deal_proposal.properties_wanted) == 0:
            (bid_price, ask_price) = self._calc_value_of_properties(game_state, player, deal_proposal.properties_offered)
            if player.state.cash > bid_price + self.high_water_mark:
                Logger.log("# Accepted proposed deal of offered properties {0} for {1}".format(str(deal_proposal.properties_offered), bid_price))
                return DealResponse(
                    action=DealResponse.Action.ACCEPT,
                    maximum_cash_offered = bid_price
                    )



        return DealResponse(DealResponse.Action.REJECT)
Beispiel #3
0
    def unmortgage_properties(self, game_state, player):
        '''
        Called near the start of the player's turn to give them the
        opportunity to unmortgage properties.

        Unmortgaging costs half the face value plus 10%. Between deciding
        to unmortgage and money being taken the player will be given the
        opportunity to make deals or sell other properties. If after this
        they do not have enough money, the whole transaction will be aborted,
        and no properties will be unmortgaged and no money taken.

        Return a list of property names to unmortgage, like:
        [old_kent_road, bow_street]

        The properties should be Property objects.

        The default is to return an empty list, ie to do nothing.
        '''
        props_to_unmortgage = []
        if len(self.mortgaged_properties) > 0:
            cash_to_spend = player.state.cash - self.cash_reserve
            mortgaged = sorted(self.mortgaged_properties, key = lambda p: p.price)
            while cash_to_spend > 0.0 and len(mortgaged) > 0:
                mc = int(mortgaged[0].price * 0.5)
                cash_to_spend -= mc
                if cash_to_spend >= 0.0:
                    props_to_unmortgage.append(mortgaged[0])
                    mortgaged.pop(0)
            if len(props_to_unmortgage) > 0:
                Logger.log("# {0}: Unmortgaging: {1}".format(self.get_name(), str(props_to_unmortgage)), Logger.INFO)
        for i in range(0, len(props_to_unmortgage)):
            self.mortgaged_properties.remove(props_to_unmortgage[i])
        return props_to_unmortgage
Beispiel #4
0
    def start_of_turn(self, game_state, player):
        '''
        Called when an AI's turn starts. All AIs receive this notification.

        No response is required.
        '''
 

        self.num_turns += 1

        self.turn_properties_in_deal = set()

        self.deals_proposed_this_turn = []
        self.cash_spent_in_turn = 0

        self.behaviour_for_turn = BEHAVIOUR_NONE
        self.deal_proposals_for_turn = [] # used if behaviour is to sell properties
        self.propose_deal_turn_num = 0

        ( self.cash_reserve, self.high_water_mark) = self._calc_cash_reserve(game_state, player)

        if self.cash_reserve > player.state.cash:
            Logger.log("# {0}: SOT {1} - cash_reserve = {2}, HWM = {3}, Cash = {4}.".format(self.get_name(), self.num_turns, self.cash_reserve, self.high_water_mark, player.state.cash), Logger.INFO)
            if int(self.cash_reserve * CASH_RESERVE_FRACTION_SELL_TRIGGER) >= player.state.cash:
                self.behaviour_for_turn = BEHAVIOUR_SELL_PROPERTY

        self.amount_to_raise = 0.0


        Logger.log("# Start of Turn {0} - cash_reserve = {1}, HWM = {2}.".format(self.num_turns, self.cash_reserve, self.high_water_mark), Logger.INFO)

        return
Beispiel #5
0
    def property_offered_for_auction(self, game_state, player, property):
        '''
        Called when a property is put up for auction.

        Properties are auctioned when a player lands on an unowned square but does
        not want to buy it. All players take part in the auction, including the
        player who landed on the square.

        The property will be sold to the highest bidder using the eBay rule,
        ie, for £1 more than the second-highest bid.

        Return the amount you bid. To put in a bid this must be a positive integer.
        Zero means that you are not bidding (it does not mean that you are bidding
        zero).

        The default behaviour is not to bid.
        '''

        if player.ai is not self:
            Logger.log("# !!! ERROR player is NOT me in property_offered_for_auction")

        if self.amount_to_raise > 0.0:
            return 0.0

        if len(self.mortgaged_properties) > 0 and DONT_BID_ON_AUCTIONS_WITH_MORTAGED_PROPS:
            return 0

        price_to_bid = 0.0
        (bid_price, ask_price) = self._calc_value_of_properties(game_state, player, [ property ])
        if player.state.cash > bid_price + self.cash_reserve:
            price_to_bid = bid_price
        
        Logger.log("# Property being auctioned and bidding {0}".format(price_to_bid), Logger.INFO)

        return price_to_bid
Beispiel #6
0
 def landed_on_unowned_property(self,game_state,player,property):
     if self.want_property(game_state,player,property):
         if player.state.cash>(self.cashReserve+property.price):
             Logger.log(self.get_name()+" buying: "+property.name)
             return PlayerAIBase.Action.BUY
         else:
             Logger.log(self.get_name()+" can't buy: {0}, price: {1}, cash: {2}, reserve: {3}".format(property.name,property.price,player.state.cash,self.cashReserve,))
     return PlayerAIBase.Action.DO_NOT_BUY
Beispiel #7
0
    def money_taken(self, player, amount):
        '''
        Called when money has been taken from the player.

        No response is required.
        '''
        Logger.log("# Money taken : {0}".format(amount), Logger.INFO)
        return
Beispiel #8
0
    def player_landed_on_square(self, game_state, square, player):
        '''
        Called when a player lands on a square. All AIs receive this notification.

        No response is required.
        '''
        if player.ai != self:
            return

        Logger.log("# Landed on Square {0}".format(square), Logger.INFO)
        return
Beispiel #9
0
    def sell_houses(self, game_state, player):
        '''
        Gives the player the option to sell properties.

        This is called when any debt, fine or rent has to be paid. It is
        called just before mortgage_properties (below).

        Notes:
        - You cannot mortgage properties with houses on them, so if you
          plan to mortgage, make sure you sell all the houses first.

        - For each house sold you receive half the price that they were
          bought for.

        - Houses on a set must end up 'balanced', ie no property can have
          more than one more house than any other property in the set.

        Return a list of tuples of the streets and number of houses you
        want to sell. For example:
        [(old_kent_road, 1), (bow_street, 1)]

        The streets should be Property objects.

        The default is not to sell any houses.
        '''
        houses_to_sell = []
        if self.amount_to_raise > 0.0:
            money_generated = 0

            for prop_set in HOUSE_PROP_SET_SELL_ORDER:
                (num_houses, owned_prop_list) = self._get_owned_houses_in_property_set(game_state, player, prop_set)
                num_house_list = [ 0 for p in owned_prop_list ]
                houses_sold = 0
                while num_houses < houses_sold and money_generated < self.amount_to_raise:
                    for i in range(0, len(owned_prop_list)):
                        if num_house_list[i] < owned_prop_list[i].number_of_houses:
                            num_house_list[i] += 1
                            houses_sold += 1
                            money_generated += int(owned_prop_list[i].house_price / 2)
                if houses_sold > 0:
                    for i in range(0, len(owned_prop_list)):
                        if num_house_list[i] > 0:
                            houses_to_sell.append( (owned_prop_list[i], num_house_list[i], ))


            # update amount_to_raise
            self.amount_to_raise -= money_generated
            if self.amount_to_raise < 0:
                self.amount_to_raise = 0.0

        if len(houses_to_sell) > 0:
            Logger.log("# {0}: Selling the following houses: {1}".format(self.get_name(), str(houses_to_sell)), Logger.INFO)
            
        return houses_to_sell
Beispiel #10
0
    def landed_on_unowned_property(self, game_state, player, property):
        '''
        price the property / evaluate the risks of buying
        '''
        ret = PlayerAIBase.Action.DO_NOT_BUY
        act = "not buying"
        if player.state.cash > (self.cash_reserve + property.price):
            ret = PlayerAIBase.Action.BUY
            act = "buying"

        Logger.log("# Landed on unowned property and {0}".format(act), Logger.INFO)
        return ret
Beispiel #11
0
    def start_of_game(self):
        '''
        Called at the start of the game.

        No response is required.
        '''
        Logger.log("# Start of Game.", Logger.INFO)
        self.num_turns = 0
        self.num_get_out_of_jail_cards = 0
        self.amount_to_raise = 0.0
        self.mortgaged_properties = []

        return
Beispiel #12
0
    def mortgage_properties(self, game_state, player):
        '''
        Gives the player an option to mortgage properties.

        This is called before any debt is paid (house building, rent,
        tax, fines from cards etc).

        Notes:
        - You receive half the face value of each property mortgaged.

        - You cannot mortgage properties with houses on them.
          (The AI will have been given the option to sell houses before this
          function is called.)

        Return a list of properties to mortgage, for example:
        [bow_street, liverpool_street_station]

        The properties should be Property objects.

        Return an empty list if you do not want to mortgage anything.

        The default behaviour is not to mortgage anything.
        '''
        if DONT_MORTGAGE_ANY_PROPERTIES:
            return []

        if self.behaviour_for_turn == BEHAVIOUR_SELL_PROPERTY:
            Logger.log("{0}: Behaviour is to sell properties - Not Mortgaging!".format(self.get_name()), Logger.INFO)
            return []

        properties_to_mortage = []
        if self.amount_to_raise > 0.0:
            money_generated = 0
            board = game_state.board
            for sq in board.squares:
                if isinstance(sq, Property) and sq.owner == player and sq.is_mortgaged == False:
                    money_generated += int(sq.price / 2)
                    properties_to_mortage.append(sq)

                    if money_generated > self.amount_to_raise:
                        break
            self.amount_to_raise -= money_generated
            if self.amount_to_raise < 0.0:
                self.amount_to_raise = 0.0

        if len(properties_to_mortage) > 0:
            Logger.log("# {0}: Mortgaging the following properties: {1}".format(self.get_name(), str(properties_to_mortage)), Logger.INFO)
            self.mortgaged_properties.extend(properties_to_mortage)
            
        return properties_to_mortage
Beispiel #13
0
    def money_given(self, player, amount):
        '''
        Called when money has been given to the player.

        No response is required.
        '''
        if player.name != self.get_name():
            return

        if self.amount_to_raise > 0:
            self.amount_to_raise -= amount
            if self.amount_to_raise < 0:
                self.amount_to_raise = 0

            Logger.log("# {0}: Money given {1}, amount_to_raise: {2}".format(self.get_name(), amount, self.amount_to_raise), Logger.INFO)
        return
Beispiel #14
0
 def want_property(self,game_state,player,property):
     try:
         if len(player.state.properties)<self.propertyCountLower: # Get a 'critical mass' of properties for trading
             if player.board._name_to_index_map[property.name][0]>player.board._name_to_index_map["Jail"][0]:
                 return True
             else:
                 return False
         else:
             if str(property.property_set)!='Station' and str(property.property_set)!='Utility':
                 squareGroup=property.property_set.__str__()
                 if squareGroup!=None:
                     if squareGroup in self.targetSquareGroup:
                         return True
     except Exception as err:
         Logger.log("Exception thrown in 'landed_on_unowned_property': "+str(err))
     return False
Beispiel #15
0
    def landed_on_unowned_property(self, game_state, player, property):
        '''
        price the property / evaluate the risks of buying
        '''
        ret = PlayerAIBase.Action.DO_NOT_BUY
        act = "not buying"
        if player.state.cash > (self.cash_reserve + property.price):
            ret = PlayerAIBase.Action.BUY
            act = "buying"

            self.cash_spent_in_turn += property.price
            Logger.log("# {0}: Turn {1}, landed on unowned property and buying for {2}, cash = {3}".format(self.get_name(), self.num_turns, property.price, player.state.cash), Logger.INFO)


        Logger.log("# Landed on unowned property and {0}".format(act), Logger.INFO)
        return ret
Beispiel #16
0
    def deal_result(self, deal_info):
        '''
        Called when a proposed deal has finished. The players involved in
        the deal receive this notification.

        deal_info is a PlayerAIBase.DealInfo 'enum' giving indicating
        whether the deal succeeded, and if not why not.

        No response is required.
        '''
        if deal_info == PlayerAIBase.DealInfo.SUCCEEDED:
            Logger.log("# {0}: Deal Result: {1}".format(self.get_name(), 'SUCCEEDED'), Logger.INFO)
            if len(self.deal_proposals_for_turn) > 0:
                self.deal_proposals_for_turn = []
                self.behaviour_for_turn = BEHAVIOUR_NONE
            
        return
Beispiel #17
0
    def deal_proposed(self, game_state, player, deal_proposal):
        '''
        Called when a deal is proposed by another player.
        '''
        if len(deal_proposal.properties_wanted) > 0:
            for p in deal_proposal.properties_wanted:
                prop_info = None
                if p.name in self.properties_requested:
                    prop_info = self.properties_requested[p.name]
                else:
                    prop_info = PropertyRequestInfo(p, deal_proposal.proposed_by_player)
                    self.properties_requested[p.name] = prop_info
                prop_info.update_for_turn(self.num_turns)

        #Logger.log("##### DEAL_PROPOSED: player {0}".format(deal_proposal.proposed_by_player.name), Logger.INFO)
        if len(deal_proposal.properties_wanted) > 0 and len(deal_proposal.properties_offered) == 0:
            (bid_price, ask_price) = self._calc_value_of_properties(game_state, player, deal_proposal.properties_wanted)

            if ask_price < self.amount_to_raise:
                ask_price = self.amount_to_raise

            Logger.log("# Accepted proposed deal of wanted properties {0} for {1}".format(str(deal_proposal.properties_wanted), ask_price))
            return DealResponse(
                action=DealResponse.Action.ACCEPT,
                minimum_cash_wanted = ask_price
                )

        if self.amount_to_raise > 0.0:
            return DealResponse(DealResponse.Action.REJECT)

        # We only accept deals for single properties wanted from us...
        if len(deal_proposal.properties_offered) > 0 and len(deal_proposal.properties_wanted) == 0:
            (bid_price, ask_price) = self._calc_value_of_properties(game_state, player, deal_proposal.properties_offered)
            if player.state.cash > bid_price + self.high_water_mark:
                Logger.log("# Accepted proposed deal of offered properties {0} for {1}".format(str(deal_proposal.properties_offered), bid_price))
                return DealResponse(
                    action=DealResponse.Action.ACCEPT,
                    maximum_cash_offered = bid_price
                    )



        return DealResponse(DealResponse.Action.REJECT)
Beispiel #18
0
    def start_of_turn(self, game_state, player):
        '''
        Called when an AI's turn starts. All AIs receive this notification.

        No response is required.
        '''

        self.num_turns += 1

        self.turn_properties_in_deal = set()

        ( self.cash_reserve, self.high_water_mark) = self._calc_cash_reserve(game_state, player)

        self.amount_to_raise = 0.0


        Logger.log("# Start of Turn {0} - cash_reserve = {1}, HWM = {2}.".format(self.num_turns, self.cash_reserve, self.high_water_mark), Logger.INFO)

        return
Beispiel #19
0
    def money_will_be_taken(self, player, amount):
        '''
        Called shortly before money will be taken from the player.

        Before the money is taken, there will be an opportunity to
        make deals and/or mortgage properties. (This will be done via
        subsequent callbacks.)

        No response is required.

        if amount > player.state.cash:
          sell stuff
        '''
        if amount >= player.state.cash:
            self.amount_to_raise = amount - player.state.cash + 1

        Logger.log("# Money will be taken and amount to raise = {0}".format(self.amount_to_raise), Logger.INFO)

        return
Beispiel #20
0
    def money_will_be_taken(self, player, amount):
        '''
        Called shortly before money will be taken from the player.

        Before the money is taken, there will be an opportunity to
        make deals and/or mortgage properties. (This will be done via
        subsequent callbacks.)

        No response is required.

        if amount > player.state.cash:
          sell stuff
        '''
        if amount >= player.state.cash:
            self.amount_to_raise = amount - player.state.cash + 1

            Logger.log("# {0}: Turn {1} - Money will be taken and amount to raise = {2}".format(self.get_name(), self.num_turns, self.amount_to_raise), Logger.INFO)

            #if self.amount_to_raise >= 150:
            #    self.behaviour_for_turn = BEHAVIOUR_SELL_PROPERTY


        return
Beispiel #21
0
 def property_offered_for_auction(self,game_state,player,property):
     if self.want_property(game_state,player,property):
         bid=round(property.price*self.bidFactor) # Too simplistic
         if player.state.cash>(self.cashReserve+bid):
             Logger.log(self.get_name()+" bidding: "+property.name)
             return bid
         else:
             if player.state.cash>self.cashReserve:
                 Logger.log(self.get_name()+" bidding: "+property.name)
                 return player.state.cash-self.cashReserve # Bid what we can afford
             else:
                 Logger.log(self.get_name()+" can't bid: {0}, price: {1}, cash: {2}, reserve: {3}".format(property.name,property.price,player.state.cash,self.cashReserve,))
     return 0
Beispiel #22
0
 def start_of_turn(self,game_state,player):
     if player.name==self.get_name(): # Is it me?
         max_net_worth=0
         otherPlayers=[otherPlayer for otherPlayer in game_state.players if otherPlayer.name!=self.get_name()]
         for otherPlayer in otherPlayers:
             if otherPlayer.net_worth>max_net_worth:
                 max_net_worth=otherPlayer.net_worth
         Logger.log("Position={0}".format(player.net_worth-max_net_worth,))
         if player.net_worth>round(max_net_worth*self.positionFactor): # Am I in contention?
             if self.cashReserve<self.cashReserveUpper:
                 self.cashReserve+=self.cashReserveIncrement # Save for a rainy day!
         else:
             if self.cashReserve>self.cashReserveLower:
                 self.cashReserve-=self.cashReserveIncrement # Go for it!
         Logger.log("Reserve={0}".format(self.cashReserve,))
         ownedPropertyNames=[property.name for property in player.state.properties]
         Logger.log("Properties owned="+str(ownedPropertyNames))
         self.db.UpdateOwnership(ownedPropertyNames)
         self.proposedDealList=self.db.FindMissingProperties()
         self.proposedDealCount=0
         Logger.log("Properties wanted="+str(self.proposedDealList))
     return super(SnowboarderAI,self).start_of_turn(game_state,player)
Beispiel #23
0
    def _calc_value_of_properties(self, game_state, player, properties):
        # The value of the street is (roughly)
        # the number of players * expected_future_rent - expected_future_cost

        max_rent = 0.0
        min_rent = 0.0
        price = 0.0
        house_price_cost = 0.0

        min_value = 0.0

        for sq in properties:
            price += sq.price
            min_value += sq.mortgage_value

            num_props_owned = 0
            for op in sq.property_set.properties:
                if op.owner is not None and op.owner == player:
                    num_props_owned += 1

            prob_building = 0.1
            if num_props_owned > 0:
                prob_building = (0.20 * num_props_owned)

            if self.num_turns > self._average_life_of_bot() - 1:
                prob_building = 0.0
            else:
                prob_building *= (1.0 - float(self.num_turns / self._average_life_of_bot()))

            if isinstance(sq, Street):
                if len(sq.rents) > 0:
                    top_rent = sq.rents[len(sq.rents)-1]
                    max_rent += (sq.rents[0] + ((top_rent - sq.rents[0]) * prob_building))
                    min_rent += sq.rents[0]

                house_price_cost += (sq.house_price * ((5 - sq.number_of_houses) * prob_building))
                min_value += int(sq.house_price/2.0 * sq.number_of_houses)


            elif isinstance(sq, Utility):
                min_rent += 4.0
                max_rent += (4.0 + (6.0 * prob_building))
            elif isinstance(sq, Station):
                min_rent += 25.0
                max_rent += (25 + (175.0 * prob_building))

        remaining_turns = 500 - self.num_turns
        if remaining_turns < 0:
            # this shouldn't happen!
            remaining_turns = -remaining_turns

        # no houses
        min_expected_value = remaining_turns * (len(game_state.players)-1) * min_rent * PROBABILITY_SQUARE_LANDING_FACTOR_MIN

        max_expected_value = remaining_turns * (len(game_state.players)-1) * max_rent * PROBABILITY_SQUARE_LANDING_FACTOR_MAX - house_price_cost
        if max_expected_value < min_expected_value:
            max_expected_value = min_expected_value

        expected_value = (min_expected_value + max_expected_value) / 2.0

        factor = float(remaining_turns / 500.0)

        fair_price = (expected_value * factor) + (min_value * (1.0 - factor))

        Logger.log("### (minp, maxp, fair, min) = ({0}, {1}, {2}, {3})".format(min_expected_value, max_expected_value, fair_price, min_value), Logger.INFO)

        bid_price = min_expected_value
        ask_price = max_expected_value

        if bid_price < min_value:
            bid_price = min_value + 10
        
        if ask_price < min_value:
            ask_price = min_value * 2.0

        bid_price = int(bid_price)
        ask_price = int(ask_price)

        Logger.log("*** Calculated value for {0} properties of {1}, {2}".format(len(properties), bid_price, ask_price))
        return (bid_price, ask_price)
Beispiel #24
0
    def propose_deal(self, game_state, player):
        '''
        Called to allow the player to propose a deal.

        You return a DealProposal object.

        If you do not want to make a deal, return None.

        If you want to make a deal, you provide this information:
        - The player number of the player you are proposing the deal to
        - A list of properties offered
        - A list of properties wanted
        - Maximum cash offered as part of the deal
        - Minimum cash wanted as part of the deal.

        Properties offered and properties wanted are passed as lists of
        Property objects.

        If you offer money as part of the deal, set the cash wanted to zero
        and vice versa.

        Note that the cash limits will not be shown to the proposed-to player.
        When the deal is offered to them, they set their own limits for accepting
        the deal without seeing your limits. If the limits are acceptable to both
        players, the deal will be done at the halfway point.

        For example, Player1 proposes:
          Propose to: Player2
          Properties offered: Mayfair
          Properties wanted: (none)
          Maximum cash offered: 0
          Minimum cash wanted: 500

        Player2 accepts with these limits:
          Maximum cash offered: 1000
          Minimum cash wanted: 0

        The deal will be done with Player2 receiving Mayfair and paying £750
        to Player1.

        The only 'negotiation' is in the managing of cash along with the deal
        as discussed above. There is no negotiation about which properties are
        part of the deal. If a deal is rejected because it does not contain the
        right properties, another deal can be made at another time with different
        lists of properties.

        Example construction and return of a DealProposal object:
            return DealProposal(
                propose_to_player_number=2,
                properties_offered=[vine_street, bow_street],
                properties_wanted=[park_lane],
                maximum_cash_offered=200)

        The default is for no deal to be proposed.
        '''

        if not PROPOSE_DEALS:
            return None

        if self.num_turns < self.propose_deal_turn_min or self.num_turns > self.propose_deal_turn_max:
            return None

        if self.behaviour_for_turn == BEHAVIOUR_SELL_PROPERTY:
            self.propose_deal_turn_num += 1

            if self.propose_deal_turn_num == 1:
                self.deal_proposals_for_turn = []

                props_to_sell = []
                prop_sell_order = sorted(self.properties_requested.values(), key=operator.attrgetter('num_times'))
                for prop_info in prop_sell_order:
                    if not prop_info.property.is_mortgaged and prop_info.property.owner is player:
                        # if we own the set, put it at the back
                        if self._does_player_own_set(player, prop_info.property):
                            props_to_sell.append(prop_info)
                        else:
                            props_to_sell.insert(0, prop_info)

                # if we have no properties to sell, switch back to mortgage mode
                if len(props_to_sell) == 0:
                    self.behaviour_for_turn = BEHAVIOUR_NONE
                    return None

                if len(props_to_sell) == 1:
                    # generate 3 different deal proposals
                    # from offer price to amount_needed
                    prop_info = props_to_sell[0]

                    ( deal_proposal_max, deal_proposal_mid, deal_proposal_min ) = \
                        self._get_deal_proposals_for_property(game_state, player, prop_info.property, prop_info.player)
                    self.deal_proposals_for_turn.append(deal_proposal_max)
                    self.deal_proposals_for_turn.append(deal_proposal_mid)
                    self.deal_proposals_for_turn.append(deal_proposal_min)
                elif len(props_to_sell) == 2:
                    prop_info1 = props_to_sell[0]
                    prop_info2 = props_to_sell[1]

                    ( deal_proposal1_max, deal_proposal1_mid, deal_proposal1_min ) = \
                        self._get_deal_proposals_for_property(game_state, player, prop_info1.property, prop_info1.player)
                    ( deal_proposal2_max, deal_proposal2_mid, deal_proposal2_min ) = \
                        self._get_deal_proposals_for_property(game_state, player, prop_info2.property, prop_info2.player)
                    self.deal_proposals_for_turn.append(deal_proposal1_max)
                    self.deal_proposals_for_turn.append(deal_proposal1_mid)
                    self.deal_proposals_for_turn.append(deal_proposal2_mid)

                elif len(props_to_sell) >= 2:
                    prop_info1 = props_to_sell[0]
                    prop_info2 = props_to_sell[1]
                    prop_info3 = props_to_sell[2]

                    ( deal_proposal1_max, deal_proposal1_mid, deal_proposal1_min ) = \
                        self._get_deal_proposals_for_property(game_state, player, prop_info1.property, prop_info1.player)
                    ( deal_proposal2_max, deal_proposal2_mid, deal_proposal2_min ) = \
                        self._get_deal_proposals_for_property(game_state, player, prop_info2.property, prop_info2.player)
                    ( deal_proposal3_max, deal_proposal3_mid, deal_proposal3_min ) = \
                        self._get_deal_proposals_for_property(game_state, player, prop_info3.property, prop_info3.player)
                    self.deal_proposals_for_turn.append(deal_proposal1_mid)
                    self.deal_proposals_for_turn.append(deal_proposal2_mid)
                    self.deal_proposals_for_turn.append(deal_proposal3_mid)


            if len(self.deal_proposals_for_turn) > 0:
                deal_proposal = self.deal_proposals_for_turn.pop(0)

                if len(self.deal_proposals_for_turn) == 0:
                    self.behaviour_for_turn = BEHAVIOUR_NONE

                Logger.log("# {0}: Turn {1} - Selling property {2} for {3}".format(self.get_name(), self.num_turns, deal_proposal.properties_offered[0].name, deal_proposal.minimum_cash_wanted), Logger.INFO)

                return deal_proposal
            return None
            

        # TODO Potentially sell stuff if we need to
        if self.amount_to_raise > 0.0:
            return None

        if len(self.mortgaged_properties) > 0:
            return None

        properties_we_like = []
        if len(player.state.properties) == 0:
            # hell, we'll bid on anything!
            board = game_state.board
            for sq in board.squares:
                if isinstance(sq, Property) and sq.owner is not None and sq.owner != player:
                    properties_we_like.append(sq.name)
        else:

            # OK, pick out some good properties to bid on
            properties_we_like = set()
            for p in player.state.properties:
                properties_we_like.update(p.property_set.properties)

            properties_we_like = [ p.name for p in properties_we_like ]

        Logger.log("# Propose deal called!", Logger.INFO)

        deal_proposal = DealProposal()

        random.shuffle(properties_we_like)

        # We check to see if any of the properties we like is owned
        # by another player...
        for property_name in properties_we_like:
            property = game_state.board.get_square_by_name(property_name)
            if (property.owner is player or property.owner is None):
                # The property is either not owned, or owned by us...
                continue

            if property_name in self.turn_properties_in_deal:
                continue

            # The property is owned by another player, so we make them an
            # offer for it...
            (bid_price, ask_price) = self._calc_value_of_properties(game_state, player, [ property ])

            price_offered = bid_price
            if player.state.cash  > price_offered + self.high_water_mark:
                self.turn_properties_in_deal.add(property_name)

                return DealProposal(
                    properties_wanted=[property],
                    maximum_cash_offered=price_offered,
                    propose_to_player=property.owner)

        return None
Beispiel #25
0
 def game_over(self, winner, maximum_rounds_played):
     Logger.log("# GAME OVER at turn {0}".format(self.num_turns), Logger.INFO)
Beispiel #26
0
    def _calc_value_of_properties(self, game_state, player, properties):
        # The value of the street is (roughly)
        # the number of players * expected_future_rent - expected_future_cost

        max_rent = 0.0
        min_rent = 0.0
        price = 0.0
        house_price_cost = 0.0

        min_value = 0.0

        for sq in properties:
            price += sq.price
            min_value += sq.mortgage_value

            num_props_owned = 0
            for op in sq.property_set.properties:
                if op.owner is not None and op.owner == player:
                    num_props_owned += 1

            prob_building = 0.1
            if num_props_owned > 0:
                prob_building = (0.20 * num_props_owned)

            if self.num_turns > self._average_life_of_bot() - 1:
                prob_building = 0.0
            else:
                prob_building *= (
                    1.0 - float(self.num_turns / self._average_life_of_bot()))

            if isinstance(sq, Street):
                if len(sq.rents) > 0:
                    top_rent = sq.rents[len(sq.rents) - 1]
                    max_rent += (sq.rents[0] +
                                 ((top_rent - sq.rents[0]) * prob_building))
                    min_rent += sq.rents[0]

                house_price_cost += (sq.house_price * (
                    (5 - sq.number_of_houses) * prob_building))
                min_value += int(sq.house_price / 2.0 * sq.number_of_houses)

            elif isinstance(sq, Utility):
                min_rent += 4.0
                max_rent += (4.0 + (6.0 * prob_building))
            elif isinstance(sq, Station):
                min_rent += 25.0
                max_rent += (25 + (175.0 * prob_building))

        remaining_turns = 500 - self.num_turns
        if remaining_turns < 0:
            # this shouldn't happen!
            remaining_turns = -remaining_turns

        # no houses
        min_expected_value = remaining_turns * (
            len(game_state.players) -
            1) * min_rent * PROBABILITY_SQUARE_LANDING_FACTOR_MIN

        max_expected_value = remaining_turns * (
            len(game_state.players) - 1
        ) * max_rent * PROBABILITY_SQUARE_LANDING_FACTOR_MAX - house_price_cost
        if max_expected_value < min_expected_value:
            max_expected_value = min_expected_value

        expected_value = (min_expected_value + max_expected_value) / 2.0

        factor = float(remaining_turns / 500.0)

        fair_price = (expected_value * factor) + (min_value * (1.0 - factor))

        Logger.log(
            "### (minp, maxp, fair, min) = ({0}, {1}, {2}, {3})".format(
                min_expected_value, max_expected_value, fair_price, min_value),
            Logger.INFO)

        bid_price = min_expected_value
        ask_price = max_expected_value

        if bid_price < min_value:
            bid_price = min_value + 10

        if ask_price < min_value:
            ask_price = min_value * 2.0

        bid_price = int(bid_price)
        ask_price = int(ask_price)

        Logger.log(
            "*** Calculated value for {0} properties of {1}, {2}".format(
                len(properties), bid_price, ask_price))
        return (bid_price, ask_price)
Beispiel #27
0
    def propose_deal(self, game_state, player):
        '''
        Called to allow the player to propose a deal.

        You return a DealProposal object.

        If you do not want to make a deal, return None.

        If you want to make a deal, you provide this information:
        - The player number of the player you are proposing the deal to
        - A list of properties offered
        - A list of properties wanted
        - Maximum cash offered as part of the deal
        - Minimum cash wanted as part of the deal.

        Properties offered and properties wanted are passed as lists of
        Property objects.

        If you offer money as part of the deal, set the cash wanted to zero
        and vice versa.

        Note that the cash limits will not be shown to the proposed-to player.
        When the deal is offered to them, they set their own limits for accepting
        the deal without seeing your limits. If the limits are acceptable to both
        players, the deal will be done at the halfway point.

        For example, Player1 proposes:
          Propose to: Player2
          Properties offered: Mayfair
          Properties wanted: (none)
          Maximum cash offered: 0
          Minimum cash wanted: 500

        Player2 accepts with these limits:
          Maximum cash offered: 1000
          Minimum cash wanted: 0

        The deal will be done with Player2 receiving Mayfair and paying £750
        to Player1.

        The only 'negotiation' is in the managing of cash along with the deal
        as discussed above. There is no negotiation about which properties are
        part of the deal. If a deal is rejected because it does not contain the
        right properties, another deal can be made at another time with different
        lists of properties.

        Example construction and return of a DealProposal object:
            return DealProposal(
                propose_to_player_number=2,
                properties_offered=[vine_street, bow_street],
                properties_wanted=[park_lane],
                maximum_cash_offered=200)

        The default is for no deal to be proposed.
        '''

        if not PROPOSE_DEALS:
            return None

        if self.num_turns < self.propose_deal_turn_min or self.num_turns > self.propose_deal_turn_max:
            return None

        # TODO Potentially sell stuff if we need to
        if self.amount_to_raise > 0.0:
            return None

        if len(self.mortgaged_properties) > 0:
            return None

        properties_we_like = []
        if len(player.state.properties) == 0:
            # hell, we'll bid on anything!
            board = game_state.board
            for sq in board.squares:
                if isinstance(sq, Property) and sq.owner is not None and sq.owner != player:
                    properties_we_like.append(sq.name)
        else:

            # OK, pick out some good properties to bid on
            properties_we_like = set()
            for p in player.state.properties:
                properties_we_like.update(p.property_set.properties)

            properties_we_like = [ p.name for p in properties_we_like ]

        Logger.log("# Propose deal called!", Logger.INFO)

        deal_proposal = DealProposal()

        random.shuffle(properties_we_like)

        # We check to see if any of the properties we like is owned
        # by another player...
        for property_name in properties_we_like:
            property = game_state.board.get_square_by_name(property_name)
            if (property.owner is player or property.owner is None):
                # The property is either not owned, or owned by us...
                continue

            if property_name in self.turn_properties_in_deal:
                continue

            # The property is owned by another player, so we make them an
            # offer for it...
            (bid_price, ask_price) = self._calc_value_of_properties(game_state, player, [ property ])

            price_offered = bid_price
            if player.state.cash  > price_offered + self.high_water_mark:
                self.turn_properties_in_deal.add(property_name)

                return DealProposal(
                    properties_wanted=[property],
                    maximum_cash_offered=price_offered,
                    propose_to_player=property.owner)

        return None
Beispiel #28
0
    def build_houses(self, game_state, player):
        '''
        Called near the start of the player's turn to give the option of building houses.

        Return a list of tuples indicating which properties you want to build houses
        on and how many houses to build on each. For example:
        [(park_lane, 3), (mayfair, 4)]

        The properties should be Property objects.

        Return an empty list if you do not want to build.

        Notes:
        - You must own a whole set of unmortgaged properties before you can
          build houses on it.

        - You can build on multiple sets in one turn. Just specify all the streets
          and houses you want to build.

        - Build five houses on a property to have a "hotel".

        - You specify the _additional_ houses you will be building, not the
          total after building. For example, if Park Lane already has 3 houses
          and you specify (park_lane, 2) you will end up with 5
          houses (ie, a hotel).

        - Sets must end up with 'balanced' housing. No square in a set can
          have more than one more house than any other. If you request an
          unbalanced build, the whole transaction will be rolled back, even
          if it includes balanced building on other sets as well.

        - If you do not have (or cannot raise) enough money to build all the
          houses specified, the whole transaction will be rolled back. Between
          this function call and money being taken, you will have an opportunity
          to mortgage properties or make deals.

        The default behaviour is not to build.
        '''
        if self.amount_to_raise > 0.0:
            return []

        if len(self.mortgaged_properties) > 0:
            return []

        if player.state.cash < self.high_water_mark:
            return []

        # We find the first set we own that we can build on...
        houses_to_build = []
        for owned_set in player.state.owned_unmortgaged_sets:
            # We can't build on stations or utilities, or if the
            # set already has hotels on all the properties...
            if not owned_set.can_build_houses:
                continue

            # We see how much money we need for one house on each property...
            cost = owned_set.house_price * owned_set.number_of_properties
            if player.state.cash > (self.cash_reserve + cost):
                # We build one house on each property...
                houses_to_build = [(p, 1) for p in owned_set.properties]
                break

        if len(houses_to_build) > 0:
            Logger.log("# {0}: Building the following houses: {1}".format(self.get_name(), str(houses_to_build)), Logger.INFO)

        return houses_to_build
Beispiel #29
0
    def _old_calc_cash_reserve(self, game_state, player):

        all_rents = []

        for bd in range(0, Board.NUMBER_OF_SQUARES):
            sq = game_state.board.squares[bd]
            if isinstance(sq, Street):
                if sq.owner is not None and sq.owner is not self:
                    num_owned_properties += 1

                    # calculate the current rent
                    rent = sq.calculate_rent(None, player)

                    all_rents.append(rent)

                    # should maybe calculate the rent with 1 more house added...
                    Logger.log("Got rent of {0}".format(rent))
                    rent_prob = (rent * PROBS_TO_40[bd])
                    rents_times_probs += rent_prob
                    if bd > player.state.square + 2:
                        reserve += rent_prob

                        if player.state.square < go_to_jail_index:
                            jail_reserve += (0.15 * rent_prob)

            elif isinstance(sq, Station):
                if sq.owner is not None and sq.owner is not self:
                    num_owned_stations += 1

                    # calculate the current rent
                    rent = self._calc_rent_on_station(game_state, sq)

                    all_rents.append(rent)

                    # should maybe calculate the rent with 1 more house added...
                    Logger.log("Got station rent of {0}".format(rent))
                    rent_prob = (rent * PROBS_TO_40[bd])
                    rents_times_probs += rent_prob
                    if bd > player.state.square + 2:
                        reserve += rent_prob

                        if player.state.square < go_to_jail_index:
                            jail_reserve += (0.15 * rent_prob)

            elif isinstance(sq, Utility):
                if sq.owner is not None and sq.owner is not self:
                    num_owned_utilities += 1

                    # calculate the current rent
                    rent = self._calc_prob_rent_on_utility(game_state, sq)

                    all_rents.append(rent)

                    # should maybe calculate the rent with 1 more house added...
                    Logger.log("Got utility rent of {0}".format(rent))
                    rent_prob = (rent * PROBS_TO_40[bd])
                    rents_times_probs += rent_prob
                    if bd > player.state.square + 2:
                        reserve += rent_prob

                        if player.state.square < go_to_jail_index:
                            jail_reserve += (0.15 * rent_prob)
            elif isinstance(sq, Chance):
                chance_penalty = self._calc_chance_penalty(player)
                Logger.log("# calculated chance_penalty of {0}".format(chance_penalty))
                reserve += chance_penalty
        Logger.log("# calculated reserve of {0}, jail reserve = {1}".format(reserve, jail_reserve))

        all_rents.sort(reverse = True)

        top_rents_sum = 0
        for i in range(0, 6):
            if len(all_rents) > i:
                top_rents_sum += all_rents[i]
        Logger.log("# top_rents_sum = {0}".format(top_rents_sum))

        reserve += jail_reserve
        reserve += 30.0
        reserve = round(reserve)

        if top_rents_sum > reserve and top_rents_sum < 1000.0:
            reserve = top_rents_sum

        Logger.log("# Using reserve of {0}".format(reserve))

        return reserve
Beispiel #30
0
 def player_went_bankrupt(self, player):
     if player.name == self.get_name():
         Logger.log("# {0}: We went bankrupt at turn {1} - cash_reserve = {2}, HWM = {3}, Cash = {4}.".format(self.get_name(), self.num_turns, self.cash_reserve, self.high_water_mark, player.state.cash), Logger.INFO)
         #exit(0)
     #Logger.log("# Player {0} went bankrupt at turn {1}".format(player.name, self.num_turns), Logger.INFO)
     return
Beispiel #31
0
 def game_over(self, winner, maximum_rounds_played):
     self.sum_life_of_bot += self.num_turns
     self.num_games_played += 1
     Logger.log("# {0}: GAME OVER at turn {1}. Average life of bot is {2}".format(self.get_name(), self.num_turns, float(self.sum_life_of_bot / self.num_games_played)), Logger.INFO)