Esempio n. 1
0
 def test_init(self):
     cf = ConnectFour()
     self.assertEqual(cf.get_number_of_discs(), 0)
     self.assertEqual(cf.get_current_player(), 0)
     self.assertIsNone(cf.get_winner())
     self.assertEqual(ConnectFour.width, 7)
     self.assertEqual(ConnectFour.height, 6)
Esempio n. 2
0
def string_list_to_connect_four(string_list):
    symbol_to_player = {"X": 0, "O": 1}
    cf = ConnectFour()
    for y in range(ConnectFour.height):
        line = string_list[ConnectFour.height - 1 - y]
        for x in range(ConnectFour.width):
            symbol = line[x]
            try:
                cf.discs[x][y] = symbol_to_player[symbol]
            except KeyError:
                pass
    return cf
Esempio n. 3
0
def count_unique_states_at_each_depth(db):
    heuristic_class = Heuristic100
    state_to_node_prev_depth = {}
    state_to_node = {}
    depth = None
    for row in db.get_fringe():
        state = row[0]
        depth = row[1] + 1
        cf = ConnectFour.create_from_string(state)
        node = SearchNode(cf, heuristic_class)
        state_to_node_prev_depth[state] = node
    if depth is None:
        depth = 0
    else:
        print("Fetched fringe from database. Depth={d} States={s}".format(
            d=depth - 1, s=len(state_to_node_prev_depth)))
    while depth <= 6:
        n_states = 0
        if depth == 0:
            node = SearchNode(ConnectFour(), heuristic_class)
            state_to_node = {node.get_state(): node}
        else:
            for node in state_to_node_prev_depth.values():
                for successor in node.get_successors():
                    n_states += 1
                    state = successor.get_state()
                    state_to_node[state] = successor
        n_solved_states = 0
        for (state, node) in state_to_node.items():
            if abs(node.get_heuristic_value()
                   ) >= Heuristic.heuristic_value_win_threshold:
                n_solved_states += 1
                db.set_value_of_state(state, node.get_heuristic_value())
        print(
            "Depth {d} States: {s:,} Unique states: {us:,} ({ss:,} solved) ({max:,} max)"
            .format(d=depth,
                    s=n_states,
                    us=len(state_to_node),
                    ss=n_solved_states,
                    max=7**depth))
        unsolved_states = [
            state for (state, node) in state_to_node.items()
            if abs(node.get_heuristic_value()) <
            Heuristic.heuristic_value_win_threshold
        ]
        db.add_fringe(unsolved_states, depth)
        state_to_node_prev_depth = state_to_node
        state_to_node = {}
        depth += 1
Esempio n. 4
0
 def test_play_game(self):
     # .......
     # .......
     # ..?....
     # ..X....
     # ..XOOO.
     # .OXXXO.
     positions = [(2, 0), (1, 0), (3, 0), (3, 1), (4, 0), (5, 0), (2, 1),
                  (4, 1), (2, 2), (5, 1)]
     cf = ConnectFour()
     for i in range(len(positions)):
         (x, y) = positions[i]
         cf.place_disc(x)
         self.assertEqual(cf.get_number_of_discs(), i + 1)
         self.assertEqual(cf.discs[x][y], i % 2)
         self.assertEqual(cf.get_current_player(), (i + 1) % 2)
         self.assertIsNone(cf.get_winner())
     cf.place_disc(2)
     self.assertEqual(cf.get_winner(), 0)
Esempio n. 5
0
def print_database(db):
    cursor = db.execute_sql("select * from connectfour")
    i = 0
    for (state, value, move) in cursor:  # @UnusedVariable
        print("Row:", i, "State:", state, "Value:", value)
        cf = ConnectFour.create_from_string(state)
        print(cf.to_human_readable_string())
        i += 1
        if i == 1000:
            break
Esempio n. 6
0
def do_alphabeta(db):
    initial_node = SearchNode(ConnectFour(), Heuristic100)
    max_depth = 6
    for depth in range(1, max_depth + 1):
        #alphabeta = AlphaBeta(db_connection=db)
        alphabeta = AlphaBeta()
        (node, value) = alphabeta.alphabeta(initial_node, depth, True)
        print(
            "Depth: {d}  Nodes created: {c}  Nodes evaluated: {e}  Best move value: {v}"
            .format(d=depth,
                    c=alphabeta.n_created_nodes,
                    e=alphabeta.n_evaluated_nodes,
                    v=value))
        print(node.cf.to_human_readable_string())
Esempio n. 7
0
def play_game(db):
    cf = ConnectFour()
    while not cf.is_game_over():
        move = get_next_move(cf)
        cf = ConnectFour(cf, move)
        print("Best move:", move)
        print("New game state:")
        print(cf.to_human_readable_string())
    winner = cf.get_winner()
    if winner is not None:
        print(winner, "wins")
    else:
        print("It's a tie")
Esempio n. 8
0
 def get_successors(self):
     if self.cf.get_winner() is not None:
         return []
     successors = []
     for i in range(ConnectFour.width):
         try:
             successor_cf = ConnectFour(self.cf, i)
             successors.append(
                 SearchNode(successor_cf, self.heuristic_class))
         except ValueError:
             pass
     random.shuffle(successors)
     successors.sort(key=lambda x: x.get_heuristic_value(),
                     reverse=self.cf.get_current_player() == 0)
     return successors
Esempio n. 9
0
 def test_create_from_string(self):
     # .......
     # .......
     # ..X....
     # ..X....
     # ..XOOO.
     # .OXXXO.
     cf_as_string = "01031006060701"
     cf = ConnectFour.create_from_string(cf_as_string)
     self.assertEqual(cf.get_number_of_discs(), 11)
     self.assertEqual(cf.discs[1][0], 1)
     self.assertEqual(cf.discs[2][3], 0)
     self.assertIsNone(cf.discs[3][2])
     self.assertEqual(cf.get_current_player(), 1)
     self.assertEqual(cf.get_winner(), 0)
     self.assertEqual(cf.to_string(), cf_as_string)
Esempio n. 10
0
 def test_place_disc(self):
     cf = ConnectFour()
     for i in range(ConnectFour.height):  # @UnusedVariable
         cf.place_disc(4)
     cf.place_disc(3)
     try:
         cf.place_disc(4)
         self.fail("Expected ValueError")
     except ValueError:
         pass
     try:
         cf.place_disc(-1)
         self.fail("Expected ValueError")
     except ValueError:
         pass
     try:
         cf.place_disc(ConnectFour.width)
         self.fail("Expected ValueError")
     except ValueError:
         pass
Esempio n. 11
0
	def get_heuristic_value(self):
		winner = self.cf.get_winner()
		if winner is not None:
			return self.get_heuristic_value_for_win(winner, 0)
		if self.cf.get_number_of_discs() == ConnectFour.width * ConnectFour.height:
			return 0
		
		threats = self.get_threats()
		immediate_threats = [self.get_immediate_threats(x) for x in threats]
		current_player = self.cf.get_current_player()
		opponent = (current_player+1) % 2
		
		# If current player has an immediate threat, current player will win in 1 move.
		if len(immediate_threats[current_player]) > 0:
			#print("Current player has an immediate threat. Will win in 1 move.")
			return self.get_heuristic_value_for_win(current_player, 1)
		
		# If opponent has more than one immediate threat, opponent will win in 2 moves.
		if len(immediate_threats[opponent]) > 1:
			#print("Opponent has more than 1 immediate threat and will win in 2 moves.")
			return self.get_heuristic_value_for_win(opponent, 2)
		
		# If opponent has exactly one immediate threat, place a disc in that column,
		# and get heuristic value for that state.
		if len(immediate_threats[opponent]) == 1:
			#print("Opponent has 1 immediate threat. Return the heuristic value of the " \
			#		+ "state resulting from placing a disc in that column.")
			cf_successor = ConnectFour(self.cf)
			column_of_threat = next(iter(immediate_threats[opponent]))[0]
			cf_successor.place_disc(column_of_threat)
			return Heuristic100(cf_successor).get_heuristic_value()
		
		if all(len(x) == 0 for x in threats):
			# Neither player has any threats.
			# Player with the most central discs has advantage.
			return sum([
				abs(ConnectFour.width//2 - x) * (-1 if self.cf.discs[x][y] == 0 else 1)
				for x in range(ConnectFour.width) for y in range(ConnectFour.height)
				if self.cf.discs[x][y] is not None
			])
		
		# Determine which player has advantage, based on zugzwang.
		# Which positions are playable for both players?
		# Take turns and place disc in those positions as long as possible.
		# After that, it's either:
		# - Player has to play a disc where opponent has an active threat (and loses)
		# - Player has to sacrifice own threat
		open_positions = self.get_open_positions()
		while True:
			if len(open_positions) == 0:
				return 100 * (len(threats[0]) - len(threats[1]))
			playable_positions = []
			for player in [0, 1]:
				playable_positions.append({
					(x,y) for (x,y) in open_positions
					if (y == 0 or (x,y-1) not in open_positions) \
							and (x,y+1) not in threats[(player+1)%2]
				})
			if len(playable_positions[current_player]) == 0:
				return 1000 * (-1 if current_player == 0 else 1)
			playable_positions_common = playable_positions[0] & playable_positions[1]
			if len(playable_positions_common) > 1:
				open_positions.remove(playable_positions_common.pop())
				current_player = (current_player+1) % 2
				continue
			# Current player has to sacrifice own threat.
			# Look for position that changes zugzwang.
			zugswang_position = next((
				(x,y) for (x,y) in playable_positions[current_player]
				if (ConnectFour.height-y) % 2 == 0
			), None)
			if zugswang_position is not None:
				open_positions.remove(zugswang_position)
				current_player = (current_player+1) % 2
				continue
			open_positions.remove(playable_positions[current_player].pop())
			current_player = (current_player+1) % 2