'Why my connect4 minimax doesn't work properly?
Can you please explain to me why my AI can play properly, until player has 3 in a row in sixth or seventh column then my AI refuses to block him. And another problem is in a position when my AI can win in 1 move, but he will rather choose a longer game. Can anyone help me please.
Returns a value for a winning move
def utility(board):
if terminal(board):
if winning_move(board, X):
return 1000000
elif winning_move(board, O):
return -10000
else:
return 0
Calculates the score of position when depth = 0
def compute_score(block, board, col, row):
score = 0
if block.count(X) == 4 and block.count("-") == 0:
score += 100000
elif block.count(X) == 3 and block.count("-") == 1:
score += 10
elif block.count(X) == 2 and block.count("-") == 2:
score += 3
if block.count(O) == 4 and block.count("-") == 0:
score -= 1000000
elif block.count(O) == 3 and block.count("-") == 1:
score -= 6
elif block.count(O) == 2 and block.count("-") == 2:
score -= 2
return score
Gives value to every possible "four block"
def score_position(board):
score = 0
mid_section = []
#increases score because playing in middle is usually good move
for col in range(5):
mid_section.append(board[col][x // 2])
score += mid_section.count(X) * 3
#claculates score of all horizontal 4 in a rows
for row in range(y):
for col in range(x - 3):
block = board[col: col + 4]
score += compute_score(block, board, col, row)
#claculates score of all vertical 4 in a rows
for col in range(x):
for row in range(y - 3):
block = [board[row][col], board[row + 1][col], board[row + 2][col], board[row + 3][col]]
score += compute_score(block, board, col, row)
#claculates score of all diagonals from left bottom to right top 4 in a rows
for row in range(y - 3):
for col in range(x - 3):
block = [board[row][col], board[row + 1][col + 1], board[row + 2][col + 2], board[row + 3][col + 3]]
score += compute_score(block, board, col, row)
#claculates score of all diagonals from right bottom to left top 4 in a rows
for row in range(y - 3):
for col in range(x - 1, 2, -1):
block = [board[row][col], board[row + 1][col - 1], board[row + 2][col - 2], board[row + 3][col - 3]]
score += compute_score(block, board, col, row)
return score
The minimax algorithm (if you want to know more about this algorithm check this video - https://www.youtube.com/watch?v=l-hh51ncgDI)
def minimax(board, depth):
#if terminal(board):
# return None
best_move = None
alpha = -math.inf
beta = math.inf
if player(board) == X:
if board == initial_state():
best_move = x//2
return best_move
best_score = -math.inf
for action in actions(board):
score = minimaze(result(board, action), alpha, beta, depth)
alpha = max(alpha, score)
if score > best_score:
best_score = score
best_move = action
return best_move
else:
best_score = math.inf
for action in actions(board):
score = maximaze(result(board, action), alpha, beta, depth)
beta = min(beta, score)
if score < best_score:
best_score = score
best_move = action
print(best_move)
return best_move
The minimazing player (returns the best player move four the specific position)
def minimaze(board, alpha, beta, depth):
if terminal(board):
return utility(board)
elif depth <= 0:
return score_position(board)
v = math.inf
for action in actions(board):
v = min(v, maximaze(result(board, action), alpha, beta, depth - 1))
beta = min(beta, v)
return v
The maximazing AI (returns the best AI move four the specific position)
def maximaze(board, alpha, beta, depth):
if terminal(board):
return utility(board)
elif depth <= 0:
return score_position(board)
v = -math.inf
for action in actions(board):
v = max(v, minimaze(result(board, action), alpha, beta, depth - 1))
alpha = max(alpha, v)
return v
Solution 1:[1]
I have been working on the exact same project recently :)
For your second problem, to make the AI win as fast as it can you should change your score system.
- When the game hasn't over, You can use your
score_positionfunction. - When the game is over (some player had won the game), Give score based on how many turns passed in the game. But also indicate that the game had over.
You can do that by changing the score addition when there are four pieces in a row:
if block.count(X) == 4 and block.count("-") == 0:
score += 100000 * (places_in_board - turns_played + 1)
if block.count(O) == 4 and block.count("-") == 0:
score -= 1000000 * (places_in_board - turns_played + 1)
Where places_in_board is the number of places of your board and turns_played is the number of turns that had passed in the game.
Notice that as you win the game in less turns, the score you get is higher.
This way the AI would 'prefer' to take the short way to a win- The score it would get just will be greater.
AND, as a side effect you get another improvement to your AI!!
If the AI got somehow into a situation which it will always lose at (if the opponent plays perfectly), It would prefer to take the longest way to its lost. Why? Because in the 'longest lose' turns_played will be greater, so
score -= 1000000 * (places_in_board - turns_left + 1)
will reduce less to the score. The score would be higher so the AI would prefer to take that 'long lose'.
Here is link for my connect4 AI if you need some reference: https://github.com/YotamZaiger1/connect_4_AI
My scoring function is in Board file, called "state_value".
Hope I helped, good luck!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Y_Z |
