Пример #1
0
	def _is_card_between_values(card, center_card, pay_off):
		" returns 1 if cards value is between center_card and pay_off card, 0 otherwise "
		# get the value of the card if we have a string of the card
		if type(card) in [str, unicode]:
			card = Card.to_numeric_value(card)
		if type(center_card) in [str, unicode]:
			center_card = Card.to_numeric_value(center_card)
		if type(pay_off) in [str, unicode]:
			pay_off = Card.to_numeric_value(pay_off)
		# if center is above pay_off, move center
		if center_card > pay_off:
			center_card -= 11
		return (center_card < card < pay_off)
Пример #2
0
	def _distance_between_values(pile_card, play_card):
		" find the distance between the pile card, and the play card values"
		# make sure we have numeric values
		if type(pile_card) in [str, unicode]:
			pile_card = Card.to_numeric_value(pile_card)
		if type(play_card) in [str, unicode]:
			play_card = Card.to_numeric_value(play_card)

		if pile_card == None:
			return play_card
		if play_card > pile_card:
			return play_card - pile_card
		if play_card == pile_card:
			return 11
		return 11 - pile_card + play_card
Пример #3
0
	def _successors(self, node):
		"""
		Return a list of successor nodes for the current node. Each node is a valid move.
		"""
		log.debug("Generating successors for %s" % node)
		node_list = []

		# opponent plays card on center
		if node.player == StateNode.OTHER or (node.action and node.action.to_pile == DISCARD):
			swap_player = (node.player == StateNode.SELF)
			for pile_name, pile_len in [(PAY_OFF,1), (DISCARD,4)]:
				for pile_id in range(pile_len):
					for action in self._get_center_move_from(node.state, pile_name, pile_id, swap_player):
						new_state = ComputerPlayer._new_state_from_action(node.state, action, swap_player)
						new_node = StateNode(new_state, action, node, player=StateNode.OTHER)
						node_list.append(new_node)
			return node_list

		# moves to center
		for pile_name, pile_len in [(HAND,1), (PAY_OFF,1), (DISCARD,4)]:
			for pile_id in range(pile_len):
				for action in self._get_center_move_from(node.state, pile_name, pile_id):
					new_state = ComputerPlayer._new_state_from_action(node.state, action)
					new_node = StateNode(new_state, action, node)
					node_list.append(new_node)

		# moves to discard
		for card in node.state.get_player()[HAND]:
			# can't discard kings
			if Card.to_numeric_value(card) == 13:
				continue
			# only create moves for different discard pile states
			discard_pile_values = []
			discard_pile_ids = []
			for i in range(len(node.state.get_player()[DISCARD])):
				value = None
				if len(node.state.get_player()[DISCARD][i]):
					value = Card.to_numeric_value(node.state.get_player()[DISCARD][i][-1])
				if value not in discard_pile_values:
					discard_pile_values.append(value)
					discard_pile_ids.append(i)
			# find the moves
			for pile_id in discard_pile_ids: 
				action = PlayerMove(card, from_pile=HAND, to_pile=DISCARD, to_id=pile_id)
				new_state = ComputerPlayer._new_state_from_action(node.state, action)
				new_node = StateNode(new_state, action, node)
				node_list.append(new_node)
		return node_list
Пример #4
0
	def place_card(self, player_move):
		"""
		Place a card onto a stack. Inputs are:
		card is the card to be placed
		from_location is a tuple of the pile (HAND, DISCARD or PAY_OFF) and the id of the pile
		to_location is a tuple of the pile (DISCARD or CENTER), and the id of the pile

		Returns the id of the player who gets to play next.
		"""
		card = player_move.card
		if not Card.is_valid(card):
			raise InvalidMove("Unknown card %s." % (card))
		if player_move.from_pile not in (HAND, DISCARD, PAY_OFF):
			raise InvalidMove("from_location incorrect: %s " % (player_move.from_pile))
		if player_move.to_pile not in (DISCARD, CENTER) or player_move.to_id < 0 \
				or player_move.to_id >= self.NUM_STACKS:
			raise InvalidMove("to_location incorrect: %s." % (player_move.to_pile))
		if player_move.to_pile == CENTER and \
				not self.can_place_card_in_center(self.center_stacks[player_move.to_id], card):
			raise InvalidMove("Can not place card(%s) on center stack %s." % (card, player_move.to_id))
		
		# can not discard kings
		if card[0] == 'K' and player_move.to_pile == DISCARD:
			raise InvalidMove("Can not DISCARD card(%s)" % (card))

		# can not move from discard to discard
		if player_move.to_pile == player_move.from_pile:
			raise InvalidMove("Can not move to same pile %s" % player_move.to_pile)

		# remove it from old location
		player = self.players[self.active_player]
		if player_move.from_pile == HAND and card in player[HAND]:
			player[HAND].remove(card)
		elif player_move.from_pile == DISCARD and card in player[DISCARD][player_move.from_id]:
			player[DISCARD][player_move.from_id].remove(card)
		elif player_move.from_pile == PAY_OFF and card == player[PAY_OFF][-1]:
			if player_move.to_pile != CENTER:
				raise InvalidMove("Can not move PAY_OFF to %s" % player_move.to_pile)
			else:
				player[PAY_OFF].pop()
		else:
			raise InvalidMove("Could not find card(%s) in %s." % (card, player_move.from_pile))

		# place it in new location
		if player_move.to_pile == CENTER:
			self.center_stacks[player_move.to_id].append(card)
		elif player_move.to_pile == DISCARD:
			player[DISCARD][player_move.to_id].append(card)
Пример #5
0
	def can_place_card_in_center(cls, pile, card):
		"""
		Determins if card can be played on the center stack pile. 
		Returns true if it can be placed, false otherwise.
		"""
		value = card[0]
		# king can be placed on anything
		if value == 'K':
			return True
		# ace can be played on empty piles
		if value == 'A':
			return (len(pile) == 0)

		# convert letters into numeric values
		value = Card.to_numeric_value(card)
		top_value = len(pile)
		return (value - top_value == 1)
Пример #6
0
	def _utility(self, node):
		""" 
		Calculate the utility value for this state node.
		"""
		ALL = 'all'
		# FIXME: Balance points so that placing on center only when necesarry (discard full, or no closer to po for op)
		points = {
			# to discard pile
			DISCARD: (10, {
				'on_same': 50,			# Discard on same value card
				'on_empty': 30,			# Discard on empty pile
				'common_in_hand': 10,	# Each time the discard card occures in the hard
				'least_essential': 5,	# Discard least essential card
				'bury_least': 2,		# Discard buries the least essential card
			}),
			# to center
			CENTER: (0, {
				'pay_off': 1000,		# Play the pay off card
			}),
			# from hand
			HAND: (0, {
				'empty_hand': 120,		# Empty hand without a discard
			}),
			# All moves
			ALL: {
				'op_dist_po': 30,		# Each point away the closest center is from opponents pay off (max *12)
			},
			# Opponent play
			StateNode.OTHER: (0, {
				'from_discard': -10,	# Opponent plays from discard
				'from_pay_off': -1000,	# Opponent plays pay_off card
			})
		}

		# shortcut vars
		center_values = []
		for pile in node.state.center_stacks:
			center_values.append(len(pile))
		if node.player == StateNode.SELF:
			myself = node.state.get_player()
			other = node.state.get_player(True)
		else:
			myself = node.state.get_player(True)
			other = node.state.get_player()


		value = 0
		# each point away the closest center is from opponents pay off
		if len(other[PAY_OFF]):
			value += points[ALL]['op_dist_po'] * \
					self._find_min_center_distance(center_values, other[PAY_OFF][-1])
		if node.player == StateNode.SELF:

			if node.action.to_pile == DISCARD:
				value += points[DISCARD][0]
				# discard on empty pile
				if len(myself[DISCARD][node.action.to_id]) == 1:
					value += points[DISCARD][1]['on_empty']
				# discard on same value card
				if len(myself[DISCARD][node.action.to_id]) > 1 \
						and Card.to_numeric_value(myself[DISCARD][node.action.to_id][-1]) == \
						Card.to_numeric_value(myself[DISCARD][node.action.to_id][-2]):
					value += points[DISCARD][1]['on_same']
				# each time the discard cards value ocurs in the hand
				value += points[DISCARD][1]['common_in_hand'] * \
						map(lambda c: Card.to_numeric_value(c), myself[HAND]).count(
						Card.to_numeric_value(node.action.card))
				# discard least essential card
				if 0 == self._find_least_essential_card(
						center_values, [node.action.card] + myself[HAND], myself[PAY_OFF][-1]):
					value += points[DISCARD][1]['least_essential']
				# discard buries least essential card
				if len(myself[DISCARD][node.action.to_id]) >= 1:
					discard_piles = self._build_pre_play_discard_piles(node)
					if node.action.to_id == self._find_least_essential_card(center_values,
							discard_piles, myself[PAY_OFF][-1]):
						value += points[DISCARD][1]['bury_least']

			elif node.action.to_pile == CENTER:
				value += points[CENTER][0]
				# pay off played
				if node.action.from_pile == PAY_OFF:
					value += points[CENTER][1]['pay_off']

			if node.action.from_pile == HAND:
				value += points[HAND][0]
				# empty hand without a discard
				if len(myself[HAND]) == 0 and node.action.to_pile != DISCARD:
					value += points[HAND][1]['empty_hand']

		# opponents plays
		else:
			value += points[StateNode.OTHER][0]
			if node.action.from_pile == PAY_OFF:
				value += points[StateNode.OTHER][1]['from_pay_off']
			if node.action.from_pile == DISCARD:
				value += points[StateNode.OTHER][1]['from_discard']

		# cumulative utils
		log.debug("Util %d " % (value))
		return value + node.parent_node.util_value
Пример #7
0
    def run(self):
        " Lets go. "
        # find out which player goes first
        if Card.to_numeric_value(self.model.players[0][PAY_OFF][-1]) > Card.to_numeric_value(
            self.model.players[1][PAY_OFF][-1]
        ):
            self.model.active_player = 0
        else:
            self.model.active_player = 1

        prev_active = None
        while True:
            active_player = self.model.active_player
            other_player = int(not self.model.active_player)

            # special case for both human players, blank the screen if it's a new players turn
            if (
                type(self.players[active_player]) == type(self.players[other_player]) == HumanPlayer
                and prev_active != active_player
            ):
                self.view.wait_screen()
                prev_active = active_player

                # draw the board for the human player(s)
            if type(self.players[active_player]) == HumanPlayer:
                self.view.draw_board(active_player)
            elif type(self.players[other_player]) == HumanPlayer:
                self.view.draw_board(other_player)

                # get the next move
            if type(self.players[active_player]) == HumanPlayer:
                player_move = self.players[active_player].play_card(self.view.select_group, self.view.target_group)
            else:
                game_state = self.model.build_view_for_player()
                player_move = self.players[active_player].play_card(game_state)

            if player_move == None:
                log.warn("Got None move. Ending")
                return

                # play the move
            try:
                self.model.place_card(player_move)
            except InvalidMove, inv:
                self.view.show_error(inv)
                continue
                # check for win
            if self.model.is_won():
                self.view.game_over()
                return
                # mix completed stacks back in
            self.model.mix_into_stock()

            # fill player hand if empty, and not a discard
            hand = self.model.players[active_player][HAND]
            if player_move.to_pile != DISCARD and len(hand) == 0:
                self.model.fill_hand()

                # swap players if move was a discard
            if player_move.to_pile == DISCARD:
                # tell view and model to end the round
                self.model.swap_players()
                self.model.fill_hand()