Skip to main content
Stack Overflow
  1. About
  2. For Teams

Return to Question

Post Locked by Thom A
Notice added Content dispute by Thom A
Rollback to Revision 22
Source Link
trincot
  • 357.3k
  • 38
  • 282
  • 340

Quis gravida magna mi How to implement a libero. Fusce vulputaterecursive distance function for Hex game AI evaluation?

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit mauris, sed placerat ipsum urna sed risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla facilisi. Etiam feugiat, ligula at consequat rhoncus, sem nunc gravida justo, quis eleifend arcu risus nec sapien. Aliquam erat volutpat. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, Quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.

I'm working on a Hex game AI that uses the alpha-beta pruning algorithm, and as part of evaluating the board state.

More detailed information about this distance metric can be found in this paper (see pages 14–17).

To simplify the problem and avoid unnecessary details, I’ve reduced the graph to a minimal example that isolates the issue.

the main problem is figuring out how to to calculate a specific distance metric between two cells for use in the evaluation function.

Conceptually, the problem comes down to calculating the distance between two cells (u and v) on a graph. I believe it's somewhat similar to BFS, DFS, or Dijkstra’s algorithm — but implemented recursively.

The Graph and Neighborhood Concept

Here is an example visualization of the graph, where I want to compute the distance from the node u to the node (4, 1):

Graph showing the structure for calculating distance from u to (4,1)

In this graph, each node is a cell on the Hex board, and connections represent valid moves depending on the board state.

To compute the distance, there are two main components:


1. Neighborhood of a Cell u (N(u))

This neighborhood is dynamic and not the standard definition from graph theory. It changes depending on the current board state.

Here’s a visualization where the shaded cells represent N(u) — the neighborhood of node u:

Shaded example showing dynamic neighbors of u

The neighborhood includes:

  • The immediate empty neighbors of u.
  • The neighbors of other friendly stones (connected to u) and the virtual nodes (edges of the board).
  • The neighbors of all connected chains that u is part of.

2. The Distance Function d(u, v)

Here is the high-level structure of the recursive distance function I’m trying to implement:

Recursive distance function diagram

The function c_k(u, v) returns the number of cells in the neighborhood of u whose distance to v is less than k:

Diagram showing the working of c_k(u, v)


Problem Example

Here’s a simple example where the function is called as:

d(u, (4, 1))

With the function defined as described above, the expected output is:

6

Why is the distance 6? I’m quoting from the paper:

"In that figure (see the last picture below), it shows the distancemetric between each unoccupied cell and the upper-left edge piece (the top-left black node) for the position in our running example. Notice that the cell with distancemetric 6 in the bottom corner of the last picture has a neighbourhood that includes the bottom-right black edge piece as well as the cell with distancemetric 5 further up on the right."


Code Implementation

Here’s my current code for the Hex board, graph construction, neighborhood extraction, and the distanceMetric function:

import math
SMALLBOARD = 5
def create_hex_graph(N):
 graph = {}
 directions = [(1, 0), (1, -1), (0, 1), (0, -1), (-1, 1), (-1, 0)]
 for r in range(N):
 for c in range(N):
 neighbors = []
 for dr, dc in directions:
 nr, nc = r + dr, c + dc
 if 0 <= nr < N and 0 <= nc < N:
 neighbors.append((nr, nc))
 graph[(r, c)] = neighbors
 graph['top'] = [(0, c) for c in range(N)]
 graph['bottom'] = [(N - 1, c) for c in range(N)]
 graph['left'] = [(r, 0) for r in range(N)]
 graph['right'] = [(r, N - 1) for r in range(N)]
 for c in range(N):
 graph[(0, c)].append('top')
 graph[(N - 1, c)].append('bottom')
 for r in range(N):
 graph[(r, 0)].append('left')
 graph[(r, N - 1)].append('right')
 return graph
class HexBoard:
 def __init__(self, size=SMALLBOARD):
 self.size = size
 self.board = [['.' for _ in range(size)] for _ in range(size)]
 def display(self):
 for i, row in enumerate(self.board):
 print(' ' * i + ' '.join(row))
 print()
 def get_valid_moves(self):
 return [(r, c) for r in range(self.size) for c in range(self.size) if self.board[r][c] == '.']
 def invalid_moves(self, row, col):
 return not (0 <= row < self.size and 0 <= col < self.size) or self.board[row][col] != '.'
 def make_move(self, row, col, player):
 if self.invalid_moves(row, col):
 print("invalid move!")
 return False
 self.board[row][col] = player
 return True
 def reset(self):
 self.board = [['.' for _ in range(self.size)] for _ in range(self.size)]
def filtered_board(board: HexBoard, player_symbol: str) -> dict:
 opponent = 'O' if player_symbol == 'X' else 'X'
 N = board.size
 full_graph = create_hex_graph(N)
 opponent_cells = {(r, c) for r in range(N) for c in range(N) if board.board[r][c] == opponent}
 filtered_graph = {}
 for vertex, neighbors in full_graph.items():
 if vertex in opponent_cells:
 continue
 filtered_neighbors = [n for n in neighbors if n not in opponent_cells]
 filtered_graph[vertex] = filtered_neighbors
 return filtered_graph
def chainfromFilteredBoard(board: HexBoard, player: str) -> list[set]:
 filtered_graph = filtered_board(board, player)
 visited = set()
 chains = []
 player_cells = {(r, c) for r in range(board.size) for c in range(board.size) if board.board[r][c] == player}
 relevant_virtual_nodes = ['top', 'bottom'] if player == 'X' else ['left', 'right']
 def dfs(start):
 stack = [start]
 current_chain = set()
 while stack:
 node = stack.pop()
 if node in visited:
 continue
 visited.add(node)
 current_chain.add(node)
 for neighbor in filtered_graph.get(node, []):
 if (neighbor in player_cells or neighbor in relevant_virtual_nodes) and neighbor not in visited:
 stack.append(neighbor)
 return current_chain
 for cell in player_cells.union(set(relevant_virtual_nodes)):
 if cell not in visited:
 chains.append(dfs(cell))
 return chains
def neighborhood(u, player: str, board: HexBoard) -> set:
 filtered_graph = filtered_board(board, player)
 neighbors_set = set()
 for neighbor in filtered_graph.get(u, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 player_chains = chainfromFilteredBoard(board, player)
 for ch in player_chains:
 if any(u in filtered_graph.get(node, []) for node in ch):
 for node in ch:
 for neighbor in filtered_graph.get(node, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 neighbors_set.discard(u)
 return neighbors_set
def distancemetric(u, v, player, board, visited=None, memo=None):
 if visited is None:
 visited = set()
 if memo is None:
 memo = {}
 if (u, v) in memo:
 return memo[(u, v)]
 if u == v:
 return 0
 if u in visited:
 return math.inf
 visited.add(u)
 neighbors_u = neighborhood(u, player, board)
 if v in neighbors_u:
 memo[(u, v)] = 1
 visited.remove(u)
 return 1
 max_k = board.size * 2 + 10
 for k in range(2, max_k):
 count = 0
 for w in neighbors_u:
 dist_wv = distancemetric(w, v, player, board, visited, memo)
 if dist_wv < k:
 count += 1
 if count >= 2:
 memo[(u, v)] = k
 visited.remove(u)
 return k
 memo[(u, v)] = math.inf
 visited.remove(u)
 return math.inf
# ---- Example Usage ----
if __name__ == "__main__":
 board = HexBoard(size=5)
 board.make_move(0, 3, 'X')
 board.make_move(2, 1, 'X')
 board.make_move(2, 2, 'X')
 board.make_move(3, 1, 'O')
 board.make_move(4, 2, 'O')
 board.display()
 u = 'top'
 v = (4, 1)
 player = 'X'
 dist = distancemetric(u, v, player, board)
 print(f"distancemetric({v}, {u}) = {dist}")

What’s Going Wrong?

My distanceMetric function calculates the distance to some target nodes incorrectly and and I believe my recursive approach might be fundamentally wrong

I am able to compute the distance to any target node, but the inconsistencies make it hard to trust the metric during evaluation.

Here is a visualization of the expected distances from u to every other node on the board (these are correct and by the paper verified):

Expected distances from u to every node

Quis gravida magna mi a libero. Fusce vulputate

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit mauris, sed placerat ipsum urna sed risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla facilisi. Etiam feugiat, ligula at consequat rhoncus, sem nunc gravida justo, quis eleifend arcu risus nec sapien. Aliquam erat volutpat. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, Quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.

How to implement a recursive distance function for Hex game AI evaluation?

I'm working on a Hex game AI that uses the alpha-beta pruning algorithm, and as part of evaluating the board state.

More detailed information about this distance metric can be found in this paper (see pages 14–17).

To simplify the problem and avoid unnecessary details, I’ve reduced the graph to a minimal example that isolates the issue.

the main problem is figuring out how to to calculate a specific distance metric between two cells for use in the evaluation function.

Conceptually, the problem comes down to calculating the distance between two cells (u and v) on a graph. I believe it's somewhat similar to BFS, DFS, or Dijkstra’s algorithm — but implemented recursively.

The Graph and Neighborhood Concept

Here is an example visualization of the graph, where I want to compute the distance from the node u to the node (4, 1):

Graph showing the structure for calculating distance from u to (4,1)

In this graph, each node is a cell on the Hex board, and connections represent valid moves depending on the board state.

To compute the distance, there are two main components:


1. Neighborhood of a Cell u (N(u))

This neighborhood is dynamic and not the standard definition from graph theory. It changes depending on the current board state.

Here’s a visualization where the shaded cells represent N(u) — the neighborhood of node u:

Shaded example showing dynamic neighbors of u

The neighborhood includes:

  • The immediate empty neighbors of u.
  • The neighbors of other friendly stones (connected to u) and the virtual nodes (edges of the board).
  • The neighbors of all connected chains that u is part of.

2. The Distance Function d(u, v)

Here is the high-level structure of the recursive distance function I’m trying to implement:

Recursive distance function diagram

The function c_k(u, v) returns the number of cells in the neighborhood of u whose distance to v is less than k:

Diagram showing the working of c_k(u, v)


Problem Example

Here’s a simple example where the function is called as:

d(u, (4, 1))

With the function defined as described above, the expected output is:

6

Why is the distance 6? I’m quoting from the paper:

"In that figure (see the last picture below), it shows the distancemetric between each unoccupied cell and the upper-left edge piece (the top-left black node) for the position in our running example. Notice that the cell with distancemetric 6 in the bottom corner of the last picture has a neighbourhood that includes the bottom-right black edge piece as well as the cell with distancemetric 5 further up on the right."


Code Implementation

Here’s my current code for the Hex board, graph construction, neighborhood extraction, and the distanceMetric function:

import math
SMALLBOARD = 5
def create_hex_graph(N):
 graph = {}
 directions = [(1, 0), (1, -1), (0, 1), (0, -1), (-1, 1), (-1, 0)]
 for r in range(N):
 for c in range(N):
 neighbors = []
 for dr, dc in directions:
 nr, nc = r + dr, c + dc
 if 0 <= nr < N and 0 <= nc < N:
 neighbors.append((nr, nc))
 graph[(r, c)] = neighbors
 graph['top'] = [(0, c) for c in range(N)]
 graph['bottom'] = [(N - 1, c) for c in range(N)]
 graph['left'] = [(r, 0) for r in range(N)]
 graph['right'] = [(r, N - 1) for r in range(N)]
 for c in range(N):
 graph[(0, c)].append('top')
 graph[(N - 1, c)].append('bottom')
 for r in range(N):
 graph[(r, 0)].append('left')
 graph[(r, N - 1)].append('right')
 return graph
class HexBoard:
 def __init__(self, size=SMALLBOARD):
 self.size = size
 self.board = [['.' for _ in range(size)] for _ in range(size)]
 def display(self):
 for i, row in enumerate(self.board):
 print(' ' * i + ' '.join(row))
 print()
 def get_valid_moves(self):
 return [(r, c) for r in range(self.size) for c in range(self.size) if self.board[r][c] == '.']
 def invalid_moves(self, row, col):
 return not (0 <= row < self.size and 0 <= col < self.size) or self.board[row][col] != '.'
 def make_move(self, row, col, player):
 if self.invalid_moves(row, col):
 print("invalid move!")
 return False
 self.board[row][col] = player
 return True
 def reset(self):
 self.board = [['.' for _ in range(self.size)] for _ in range(self.size)]
def filtered_board(board: HexBoard, player_symbol: str) -> dict:
 opponent = 'O' if player_symbol == 'X' else 'X'
 N = board.size
 full_graph = create_hex_graph(N)
 opponent_cells = {(r, c) for r in range(N) for c in range(N) if board.board[r][c] == opponent}
 filtered_graph = {}
 for vertex, neighbors in full_graph.items():
 if vertex in opponent_cells:
 continue
 filtered_neighbors = [n for n in neighbors if n not in opponent_cells]
 filtered_graph[vertex] = filtered_neighbors
 return filtered_graph
def chainfromFilteredBoard(board: HexBoard, player: str) -> list[set]:
 filtered_graph = filtered_board(board, player)
 visited = set()
 chains = []
 player_cells = {(r, c) for r in range(board.size) for c in range(board.size) if board.board[r][c] == player}
 relevant_virtual_nodes = ['top', 'bottom'] if player == 'X' else ['left', 'right']
 def dfs(start):
 stack = [start]
 current_chain = set()
 while stack:
 node = stack.pop()
 if node in visited:
 continue
 visited.add(node)
 current_chain.add(node)
 for neighbor in filtered_graph.get(node, []):
 if (neighbor in player_cells or neighbor in relevant_virtual_nodes) and neighbor not in visited:
 stack.append(neighbor)
 return current_chain
 for cell in player_cells.union(set(relevant_virtual_nodes)):
 if cell not in visited:
 chains.append(dfs(cell))
 return chains
def neighborhood(u, player: str, board: HexBoard) -> set:
 filtered_graph = filtered_board(board, player)
 neighbors_set = set()
 for neighbor in filtered_graph.get(u, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 player_chains = chainfromFilteredBoard(board, player)
 for ch in player_chains:
 if any(u in filtered_graph.get(node, []) for node in ch):
 for node in ch:
 for neighbor in filtered_graph.get(node, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 neighbors_set.discard(u)
 return neighbors_set
def distancemetric(u, v, player, board, visited=None, memo=None):
 if visited is None:
 visited = set()
 if memo is None:
 memo = {}
 if (u, v) in memo:
 return memo[(u, v)]
 if u == v:
 return 0
 if u in visited:
 return math.inf
 visited.add(u)
 neighbors_u = neighborhood(u, player, board)
 if v in neighbors_u:
 memo[(u, v)] = 1
 visited.remove(u)
 return 1
 max_k = board.size * 2 + 10
 for k in range(2, max_k):
 count = 0
 for w in neighbors_u:
 dist_wv = distancemetric(w, v, player, board, visited, memo)
 if dist_wv < k:
 count += 1
 if count >= 2:
 memo[(u, v)] = k
 visited.remove(u)
 return k
 memo[(u, v)] = math.inf
 visited.remove(u)
 return math.inf
# ---- Example Usage ----
if __name__ == "__main__":
 board = HexBoard(size=5)
 board.make_move(0, 3, 'X')
 board.make_move(2, 1, 'X')
 board.make_move(2, 2, 'X')
 board.make_move(3, 1, 'O')
 board.make_move(4, 2, 'O')
 board.display()
 u = 'top'
 v = (4, 1)
 player = 'X'
 dist = distancemetric(u, v, player, board)
 print(f"distancemetric({v}, {u}) = {dist}")

What’s Going Wrong?

My distanceMetric function calculates the distance to some target nodes incorrectly and and I believe my recursive approach might be fundamentally wrong

I am able to compute the distance to any target node, but the inconsistencies make it hard to trust the metric during evaluation.

Here is a visualization of the expected distances from u to every other node on the board (these are correct and by the paper verified):

Expected distances from u to every node

Rollback to Revision 21
Source Link

How to implement Quis gravida magna mi a recursive distance function for Hex game AI evaluation?libero. Fusce vulputate

I'm working on a Hex game AI that uses the alpha-beta pruning algorithm, and as part of evaluating the board state.

More detailed information about this distance metric can be found in this paper (see pages 14–17).

To simplify the problem and avoid unnecessary details, I’ve reduced the graph to a minimal example that isolates the issue.

the main problem is figuring out how to to calculate a specific distance metric between two cells for use in the evaluation function.

Conceptually, the problem comes down to calculating the distance between two cells (u and v) on a graph. I believe it's somewhat similar to BFS, DFS, or Dijkstra’s algorithm — but implemented recursively.

The Graph and Neighborhood Concept

Here is an example visualization of the graph, where I want to compute the distance from the node u to the node (4, 1):

Graph showing the structure for calculating distance from u to (4,1)

In this graph, each node is a cell on the Hex board, and connections represent valid moves depending on the board state.

To compute the distance, there are two main components:


1. Neighborhood of a Cell u (N(u))

This neighborhood is dynamic and not the standard definition from graph theory. It changes depending on the current board state.

Here’s a visualization where the shaded cells represent N(u) — the neighborhood of node u:

Shaded example showing dynamic neighbors of u

The neighborhood includes:

  • The immediate empty neighbors of u.
  • The neighbors of other friendly stones (connected to u) and the virtual nodes (edges of the board).
  • The neighbors of all connected chains that u is part of.

2. The Distance Function d(u, v)

Here is the high-level structure of the recursive distance function I’m trying to implement:

Recursive distance function diagram

The function c_k(u, v) returns the number of cells in the neighborhood of u whose distance to v is less than k:

Diagram showing the working of c_k(u, v)


Problem Example

Here’s a simple example where the function is called as:

d(u, (4, 1))

With the function defined as described above, the expected output is:

6

Why is the distance 6? I’m quoting from the paper:

"In that figure (see the last picture below), it shows the distancemetric between each unoccupied cell and the upper-left edge piece (the top-left black node) for the position in our running example. Notice that the cell with distancemetric 6 in the bottom corner of the last picture has a neighbourhood that includes the bottom-right black edge piece as well as the cell with distancemetric 5 further up on the right."


Code Implementation

Here’s my current code for the Hex board, graph construction, neighborhood extraction, and the distanceMetric function:

import math
SMALLBOARD = 5
def create_hex_graph(N):
 graph = {}
 directions = [(1, 0), (1, -1), (0, 1), (0, -1), (-1, 1), (-1, 0)]
 for r in range(N):
 for c in range(N):
 neighbors = []
 for dr, dc in directions:
 nr, nc = r + dr, c + dc
 if 0 <= nr < N and 0 <= nc < N:
 neighbors.append((nr, nc))
 graph[(r, c)] = neighbors
 graph['top'] = [(0, c) for c in range(N)]
 graph['bottom'] = [(N - 1, c) for c in range(N)]
 graph['left'] = [(r, 0) for r in range(N)]
 graph['right'] = [(r, N - 1) for r in range(N)]
 for c in range(N):
 graph[(0, c)].append('top')
 graph[(N - 1, c)].append('bottom')
 for r in range(N):
 graph[(r, 0)].append('left')
 graph[(r, N - 1)].append('right')
 return graph
class HexBoard:
 def __init__(self, size=SMALLBOARD):
 self.size = size
 self.board = [['.' for _ in range(size)] for _ in range(size)]
 def display(self):
 for i, row in enumerate(self.board):
 print(' ' * i + ' '.join(row))
 print()
 def get_valid_moves(self):
 return [(r, c) for r in range(self.size) for c in range(self.size) if self.board[r][c] == '.']
 def invalid_moves(self, row, col):
 return not (0 <= row < self.size and 0 <= col < self.size) or self.board[row][col] != '.'
 def make_move(self, row, col, player):
 if self.invalid_moves(row, col):
 print("invalid move!")
 return False
 self.board[row][col] = player
 return True
 def reset(self):
 self.board = [['.' for _ in range(self.size)] for _ in range(self.size)]
def filtered_board(board: HexBoard, player_symbol: str) -> dict:
 opponent = 'O' if player_symbol == 'X' else 'X'
 N = board.size
 full_graph = create_hex_graph(N)
 opponent_cells = {(r, c) for r in range(N) for c in range(N) if board.board[r][c] == opponent}
 filtered_graph = {}
 for vertex, neighbors in full_graph.items():
 if vertex in opponent_cells:
 continue
 filtered_neighbors = [n for n in neighbors if n not in opponent_cells]
 filtered_graph[vertex] = filtered_neighbors
 return filtered_graph
def chainfromFilteredBoard(board: HexBoard, player: str) -> list[set]:
 filtered_graph = filtered_board(board, player)
 visited = set()
 chains = []
 player_cells = {(r, c) for r in range(board.size) for c in range(board.size) if board.board[r][c] == player}
 relevant_virtual_nodes = ['top', 'bottom'] if player == 'X' else ['left', 'right']
 def dfs(start):
 stack = [start]
 current_chain = set()
 while stack:
 node = stack.pop()
 if node in visited:
 continue
 visited.add(node)
 current_chain.add(node)
 for neighbor in filtered_graph.get(node, []):
 if (neighbor in player_cells or neighbor in relevant_virtual_nodes) and neighbor not in visited:
 stack.append(neighbor)
 return current_chain
 for cell in player_cells.union(set(relevant_virtual_nodes)):
 if cell not in visited:
 chains.append(dfs(cell))
 return chains
def neighborhood(u, player: str, board: HexBoard) -> set:
 filtered_graph = filtered_board(board, player)
 neighbors_set = set()
 for neighbor in filtered_graph.get(u, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 player_chains = chainfromFilteredBoard(board, player)
 for ch in player_chains:
 if any(u in filtered_graph.get(node, []) for node in ch):
 for node in ch:
 for neighbor in filtered_graph.get(node, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 neighbors_set.discard(u)
 return neighbors_set
def distancemetric(u, v, player, board, visited=None, memo=None):
 if visited is None:
 visited = set()
 if memo is None:
 memo = {}
 if (u, v) in memo:
 return memo[(u, v)]
 if u == v:
 return 0
 if u in visited:
 return math.inf
 visited.add(u)
 neighbors_u = neighborhood(u, player, board)
 if v in neighbors_u:
 memo[(u, v)] = 1
 visited.remove(u)
 return 1
 max_k = board.size * 2 + 10
 for k in range(2, max_k):
 count = 0
 for w in neighbors_u:
 dist_wv = distancemetric(w, v, player, board, visited, memo)
 if dist_wv < k:
 count += 1
 if count >= 2:
 memo[(u, v)] = k
 visited.remove(u)
 return k
 memo[(u, v)] = math.inf
 visited.remove(u)
 return math.inf
# ---- Example Usage ----
if __name__ == "__main__":
 board = HexBoard(size=5)
 board.make_move(0, 3, 'X')
 board.make_move(2, 1, 'X')
 board.make_move(2, 2, 'X')
 board.make_move(3, 1, 'O')
 board.make_move(4, 2, 'O')
 board.display()
 u = 'top'
 v = (4, 1)
 player = 'X'
 dist = distancemetric(u, v, player, board)
 print(f"distancemetric({v}, {u}) = {dist}")

What’s Going Wrong?

My distanceMetric function calculates the distance to some target nodes incorrectly and and I believe my recursive approach might be fundamentally wrong

I am able to compute the distance to any target node, but the inconsistencies make it hard to trust the metric during evaluation.

Here is a visualization of the expected distances from u to every other node on the board (these are correct and by the paper verified):

Expected distances from u to every node

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit mauris, sed placerat ipsum urna sed risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla facilisi. Etiam feugiat, ligula at consequat rhoncus, sem nunc gravida justo, quis eleifend arcu risus nec sapien. Aliquam erat volutpat. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, Quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.

How to implement a recursive distance function for Hex game AI evaluation?

I'm working on a Hex game AI that uses the alpha-beta pruning algorithm, and as part of evaluating the board state.

More detailed information about this distance metric can be found in this paper (see pages 14–17).

To simplify the problem and avoid unnecessary details, I’ve reduced the graph to a minimal example that isolates the issue.

the main problem is figuring out how to to calculate a specific distance metric between two cells for use in the evaluation function.

Conceptually, the problem comes down to calculating the distance between two cells (u and v) on a graph. I believe it's somewhat similar to BFS, DFS, or Dijkstra’s algorithm — but implemented recursively.

The Graph and Neighborhood Concept

Here is an example visualization of the graph, where I want to compute the distance from the node u to the node (4, 1):

Graph showing the structure for calculating distance from u to (4,1)

In this graph, each node is a cell on the Hex board, and connections represent valid moves depending on the board state.

To compute the distance, there are two main components:


1. Neighborhood of a Cell u (N(u))

This neighborhood is dynamic and not the standard definition from graph theory. It changes depending on the current board state.

Here’s a visualization where the shaded cells represent N(u) — the neighborhood of node u:

Shaded example showing dynamic neighbors of u

The neighborhood includes:

  • The immediate empty neighbors of u.
  • The neighbors of other friendly stones (connected to u) and the virtual nodes (edges of the board).
  • The neighbors of all connected chains that u is part of.

2. The Distance Function d(u, v)

Here is the high-level structure of the recursive distance function I’m trying to implement:

Recursive distance function diagram

The function c_k(u, v) returns the number of cells in the neighborhood of u whose distance to v is less than k:

Diagram showing the working of c_k(u, v)


Problem Example

Here’s a simple example where the function is called as:

d(u, (4, 1))

With the function defined as described above, the expected output is:

6

Why is the distance 6? I’m quoting from the paper:

"In that figure (see the last picture below), it shows the distancemetric between each unoccupied cell and the upper-left edge piece (the top-left black node) for the position in our running example. Notice that the cell with distancemetric 6 in the bottom corner of the last picture has a neighbourhood that includes the bottom-right black edge piece as well as the cell with distancemetric 5 further up on the right."


Code Implementation

Here’s my current code for the Hex board, graph construction, neighborhood extraction, and the distanceMetric function:

import math
SMALLBOARD = 5
def create_hex_graph(N):
 graph = {}
 directions = [(1, 0), (1, -1), (0, 1), (0, -1), (-1, 1), (-1, 0)]
 for r in range(N):
 for c in range(N):
 neighbors = []
 for dr, dc in directions:
 nr, nc = r + dr, c + dc
 if 0 <= nr < N and 0 <= nc < N:
 neighbors.append((nr, nc))
 graph[(r, c)] = neighbors
 graph['top'] = [(0, c) for c in range(N)]
 graph['bottom'] = [(N - 1, c) for c in range(N)]
 graph['left'] = [(r, 0) for r in range(N)]
 graph['right'] = [(r, N - 1) for r in range(N)]
 for c in range(N):
 graph[(0, c)].append('top')
 graph[(N - 1, c)].append('bottom')
 for r in range(N):
 graph[(r, 0)].append('left')
 graph[(r, N - 1)].append('right')
 return graph
class HexBoard:
 def __init__(self, size=SMALLBOARD):
 self.size = size
 self.board = [['.' for _ in range(size)] for _ in range(size)]
 def display(self):
 for i, row in enumerate(self.board):
 print(' ' * i + ' '.join(row))
 print()
 def get_valid_moves(self):
 return [(r, c) for r in range(self.size) for c in range(self.size) if self.board[r][c] == '.']
 def invalid_moves(self, row, col):
 return not (0 <= row < self.size and 0 <= col < self.size) or self.board[row][col] != '.'
 def make_move(self, row, col, player):
 if self.invalid_moves(row, col):
 print("invalid move!")
 return False
 self.board[row][col] = player
 return True
 def reset(self):
 self.board = [['.' for _ in range(self.size)] for _ in range(self.size)]
def filtered_board(board: HexBoard, player_symbol: str) -> dict:
 opponent = 'O' if player_symbol == 'X' else 'X'
 N = board.size
 full_graph = create_hex_graph(N)
 opponent_cells = {(r, c) for r in range(N) for c in range(N) if board.board[r][c] == opponent}
 filtered_graph = {}
 for vertex, neighbors in full_graph.items():
 if vertex in opponent_cells:
 continue
 filtered_neighbors = [n for n in neighbors if n not in opponent_cells]
 filtered_graph[vertex] = filtered_neighbors
 return filtered_graph
def chainfromFilteredBoard(board: HexBoard, player: str) -> list[set]:
 filtered_graph = filtered_board(board, player)
 visited = set()
 chains = []
 player_cells = {(r, c) for r in range(board.size) for c in range(board.size) if board.board[r][c] == player}
 relevant_virtual_nodes = ['top', 'bottom'] if player == 'X' else ['left', 'right']
 def dfs(start):
 stack = [start]
 current_chain = set()
 while stack:
 node = stack.pop()
 if node in visited:
 continue
 visited.add(node)
 current_chain.add(node)
 for neighbor in filtered_graph.get(node, []):
 if (neighbor in player_cells or neighbor in relevant_virtual_nodes) and neighbor not in visited:
 stack.append(neighbor)
 return current_chain
 for cell in player_cells.union(set(relevant_virtual_nodes)):
 if cell not in visited:
 chains.append(dfs(cell))
 return chains
def neighborhood(u, player: str, board: HexBoard) -> set:
 filtered_graph = filtered_board(board, player)
 neighbors_set = set()
 for neighbor in filtered_graph.get(u, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 player_chains = chainfromFilteredBoard(board, player)
 for ch in player_chains:
 if any(u in filtered_graph.get(node, []) for node in ch):
 for node in ch:
 for neighbor in filtered_graph.get(node, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 neighbors_set.discard(u)
 return neighbors_set
def distancemetric(u, v, player, board, visited=None, memo=None):
 if visited is None:
 visited = set()
 if memo is None:
 memo = {}
 if (u, v) in memo:
 return memo[(u, v)]
 if u == v:
 return 0
 if u in visited:
 return math.inf
 visited.add(u)
 neighbors_u = neighborhood(u, player, board)
 if v in neighbors_u:
 memo[(u, v)] = 1
 visited.remove(u)
 return 1
 max_k = board.size * 2 + 10
 for k in range(2, max_k):
 count = 0
 for w in neighbors_u:
 dist_wv = distancemetric(w, v, player, board, visited, memo)
 if dist_wv < k:
 count += 1
 if count >= 2:
 memo[(u, v)] = k
 visited.remove(u)
 return k
 memo[(u, v)] = math.inf
 visited.remove(u)
 return math.inf
# ---- Example Usage ----
if __name__ == "__main__":
 board = HexBoard(size=5)
 board.make_move(0, 3, 'X')
 board.make_move(2, 1, 'X')
 board.make_move(2, 2, 'X')
 board.make_move(3, 1, 'O')
 board.make_move(4, 2, 'O')
 board.display()
 u = 'top'
 v = (4, 1)
 player = 'X'
 dist = distancemetric(u, v, player, board)
 print(f"distancemetric({v}, {u}) = {dist}")

What’s Going Wrong?

My distanceMetric function calculates the distance to some target nodes incorrectly and and I believe my recursive approach might be fundamentally wrong

I am able to compute the distance to any target node, but the inconsistencies make it hard to trust the metric during evaluation.

Here is a visualization of the expected distances from u to every other node on the board (these are correct and by the paper verified):

Expected distances from u to every node

Quis gravida magna mi a libero. Fusce vulputate

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit mauris, sed placerat ipsum urna sed risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla facilisi. Etiam feugiat, ligula at consequat rhoncus, sem nunc gravida justo, quis eleifend arcu risus nec sapien. Aliquam erat volutpat. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, Quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.

Rollback to Revision 20
Source Link
khelwood
  • 59.7k
  • 14
  • 91
  • 116

Quis gravida magna mi How to implement a libero. Fusce vulputaterecursive distance function for Hex game AI evaluation?

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit mauris, sed placerat ipsum urna sed risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla facilisi. Etiam feugiat, ligula at consequat rhoncus, sem nunc gravida justo, quis eleifend arcu risus nec sapien. Aliquam erat volutpat. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, Quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.

I'm working on a Hex game AI that uses the alpha-beta pruning algorithm, and as part of evaluating the board state.

More detailed information about this distance metric can be found in this paper (see pages 14–17).

To simplify the problem and avoid unnecessary details, I’ve reduced the graph to a minimal example that isolates the issue.

the main problem is figuring out how to to calculate a specific distance metric between two cells for use in the evaluation function.

Conceptually, the problem comes down to calculating the distance between two cells (u and v) on a graph. I believe it's somewhat similar to BFS, DFS, or Dijkstra’s algorithm — but implemented recursively.

The Graph and Neighborhood Concept

Here is an example visualization of the graph, where I want to compute the distance from the node u to the node (4, 1):

Graph showing the structure for calculating distance from u to (4,1)

In this graph, each node is a cell on the Hex board, and connections represent valid moves depending on the board state.

To compute the distance, there are two main components:


1. Neighborhood of a Cell u (N(u))

This neighborhood is dynamic and not the standard definition from graph theory. It changes depending on the current board state.

Here’s a visualization where the shaded cells represent N(u) — the neighborhood of node u:

Shaded example showing dynamic neighbors of u

The neighborhood includes:

  • The immediate empty neighbors of u.
  • The neighbors of other friendly stones (connected to u) and the virtual nodes (edges of the board).
  • The neighbors of all connected chains that u is part of.

2. The Distance Function d(u, v)

Here is the high-level structure of the recursive distance function I’m trying to implement:

Recursive distance function diagram

The function c_k(u, v) returns the number of cells in the neighborhood of u whose distance to v is less than k:

Diagram showing the working of c_k(u, v)


Problem Example

Here’s a simple example where the function is called as:

d(u, (4, 1))

With the function defined as described above, the expected output is:

6

Why is the distance 6? I’m quoting from the paper:

"In that figure (see the last picture below), it shows the distancemetric between each unoccupied cell and the upper-left edge piece (the top-left black node) for the position in our running example. Notice that the cell with distancemetric 6 in the bottom corner of the last picture has a neighbourhood that includes the bottom-right black edge piece as well as the cell with distancemetric 5 further up on the right."


Code Implementation

Here’s my current code for the Hex board, graph construction, neighborhood extraction, and the distanceMetric function:

import math
SMALLBOARD = 5
def create_hex_graph(N):
 graph = {}
 directions = [(1, 0), (1, -1), (0, 1), (0, -1), (-1, 1), (-1, 0)]
 for r in range(N):
 for c in range(N):
 neighbors = []
 for dr, dc in directions:
 nr, nc = r + dr, c + dc
 if 0 <= nr < N and 0 <= nc < N:
 neighbors.append((nr, nc))
 graph[(r, c)] = neighbors
 graph['top'] = [(0, c) for c in range(N)]
 graph['bottom'] = [(N - 1, c) for c in range(N)]
 graph['left'] = [(r, 0) for r in range(N)]
 graph['right'] = [(r, N - 1) for r in range(N)]
 for c in range(N):
 graph[(0, c)].append('top')
 graph[(N - 1, c)].append('bottom')
 for r in range(N):
 graph[(r, 0)].append('left')
 graph[(r, N - 1)].append('right')
 return graph
class HexBoard:
 def __init__(self, size=SMALLBOARD):
 self.size = size
 self.board = [['.' for _ in range(size)] for _ in range(size)]
 def display(self):
 for i, row in enumerate(self.board):
 print(' ' * i + ' '.join(row))
 print()
 def get_valid_moves(self):
 return [(r, c) for r in range(self.size) for c in range(self.size) if self.board[r][c] == '.']
 def invalid_moves(self, row, col):
 return not (0 <= row < self.size and 0 <= col < self.size) or self.board[row][col] != '.'
 def make_move(self, row, col, player):
 if self.invalid_moves(row, col):
 print("invalid move!")
 return False
 self.board[row][col] = player
 return True
 def reset(self):
 self.board = [['.' for _ in range(self.size)] for _ in range(self.size)]
def filtered_board(board: HexBoard, player_symbol: str) -> dict:
 opponent = 'O' if player_symbol == 'X' else 'X'
 N = board.size
 full_graph = create_hex_graph(N)
 opponent_cells = {(r, c) for r in range(N) for c in range(N) if board.board[r][c] == opponent}
 filtered_graph = {}
 for vertex, neighbors in full_graph.items():
 if vertex in opponent_cells:
 continue
 filtered_neighbors = [n for n in neighbors if n not in opponent_cells]
 filtered_graph[vertex] = filtered_neighbors
 return filtered_graph
def chainfromFilteredBoard(board: HexBoard, player: str) -> list[set]:
 filtered_graph = filtered_board(board, player)
 visited = set()
 chains = []
 player_cells = {(r, c) for r in range(board.size) for c in range(board.size) if board.board[r][c] == player}
 relevant_virtual_nodes = ['top', 'bottom'] if player == 'X' else ['left', 'right']
 def dfs(start):
 stack = [start]
 current_chain = set()
 while stack:
 node = stack.pop()
 if node in visited:
 continue
 visited.add(node)
 current_chain.add(node)
 for neighbor in filtered_graph.get(node, []):
 if (neighbor in player_cells or neighbor in relevant_virtual_nodes) and neighbor not in visited:
 stack.append(neighbor)
 return current_chain
 for cell in player_cells.union(set(relevant_virtual_nodes)):
 if cell not in visited:
 chains.append(dfs(cell))
 return chains
def neighborhood(u, player: str, board: HexBoard) -> set:
 filtered_graph = filtered_board(board, player)
 neighbors_set = set()
 for neighbor in filtered_graph.get(u, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 player_chains = chainfromFilteredBoard(board, player)
 for ch in player_chains:
 if any(u in filtered_graph.get(node, []) for node in ch):
 for node in ch:
 for neighbor in filtered_graph.get(node, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 neighbors_set.discard(u)
 return neighbors_set
def distancemetric(u, v, player, board, visited=None, memo=None):
 if visited is None:
 visited = set()
 if memo is None:
 memo = {}
 if (u, v) in memo:
 return memo[(u, v)]
 if u == v:
 return 0
 if u in visited:
 return math.inf
 visited.add(u)
 neighbors_u = neighborhood(u, player, board)
 if v in neighbors_u:
 memo[(u, v)] = 1
 visited.remove(u)
 return 1
 max_k = board.size * 2 + 10
 for k in range(2, max_k):
 count = 0
 for w in neighbors_u:
 dist_wv = distancemetric(w, v, player, board, visited, memo)
 if dist_wv < k:
 count += 1
 if count >= 2:
 memo[(u, v)] = k
 visited.remove(u)
 return k
 memo[(u, v)] = math.inf
 visited.remove(u)
 return math.inf
# ---- Example Usage ----
if __name__ == "__main__":
 board = HexBoard(size=5)
 board.make_move(0, 3, 'X')
 board.make_move(2, 1, 'X')
 board.make_move(2, 2, 'X')
 board.make_move(3, 1, 'O')
 board.make_move(4, 2, 'O')
 board.display()
 u = 'top'
 v = (4, 1)
 player = 'X'
 dist = distancemetric(u, v, player, board)
 print(f"distancemetric({v}, {u}) = {dist}")

What’s Going Wrong?

My distanceMetric function calculates the distance to some target nodes incorrectly and and I believe my recursive approach might be fundamentally wrong

I am able to compute the distance to any target node, but the inconsistencies make it hard to trust the metric during evaluation.

Here is a visualization of the expected distances from u to every other node on the board (these are correct and by the paper verified):

Expected distances from u to every node

Quis gravida magna mi a libero. Fusce vulputate

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit mauris, sed placerat ipsum urna sed risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla facilisi. Etiam feugiat, ligula at consequat rhoncus, sem nunc gravida justo, quis eleifend arcu risus nec sapien. Aliquam erat volutpat. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, Quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.

How to implement a recursive distance function for Hex game AI evaluation?

I'm working on a Hex game AI that uses the alpha-beta pruning algorithm, and as part of evaluating the board state.

More detailed information about this distance metric can be found in this paper (see pages 14–17).

To simplify the problem and avoid unnecessary details, I’ve reduced the graph to a minimal example that isolates the issue.

the main problem is figuring out how to to calculate a specific distance metric between two cells for use in the evaluation function.

Conceptually, the problem comes down to calculating the distance between two cells (u and v) on a graph. I believe it's somewhat similar to BFS, DFS, or Dijkstra’s algorithm — but implemented recursively.

The Graph and Neighborhood Concept

Here is an example visualization of the graph, where I want to compute the distance from the node u to the node (4, 1):

Graph showing the structure for calculating distance from u to (4,1)

In this graph, each node is a cell on the Hex board, and connections represent valid moves depending on the board state.

To compute the distance, there are two main components:


1. Neighborhood of a Cell u (N(u))

This neighborhood is dynamic and not the standard definition from graph theory. It changes depending on the current board state.

Here’s a visualization where the shaded cells represent N(u) — the neighborhood of node u:

Shaded example showing dynamic neighbors of u

The neighborhood includes:

  • The immediate empty neighbors of u.
  • The neighbors of other friendly stones (connected to u) and the virtual nodes (edges of the board).
  • The neighbors of all connected chains that u is part of.

2. The Distance Function d(u, v)

Here is the high-level structure of the recursive distance function I’m trying to implement:

Recursive distance function diagram

The function c_k(u, v) returns the number of cells in the neighborhood of u whose distance to v is less than k:

Diagram showing the working of c_k(u, v)


Problem Example

Here’s a simple example where the function is called as:

d(u, (4, 1))

With the function defined as described above, the expected output is:

6

Why is the distance 6? I’m quoting from the paper:

"In that figure (see the last picture below), it shows the distancemetric between each unoccupied cell and the upper-left edge piece (the top-left black node) for the position in our running example. Notice that the cell with distancemetric 6 in the bottom corner of the last picture has a neighbourhood that includes the bottom-right black edge piece as well as the cell with distancemetric 5 further up on the right."


Code Implementation

Here’s my current code for the Hex board, graph construction, neighborhood extraction, and the distanceMetric function:

import math
SMALLBOARD = 5
def create_hex_graph(N):
 graph = {}
 directions = [(1, 0), (1, -1), (0, 1), (0, -1), (-1, 1), (-1, 0)]
 for r in range(N):
 for c in range(N):
 neighbors = []
 for dr, dc in directions:
 nr, nc = r + dr, c + dc
 if 0 <= nr < N and 0 <= nc < N:
 neighbors.append((nr, nc))
 graph[(r, c)] = neighbors
 graph['top'] = [(0, c) for c in range(N)]
 graph['bottom'] = [(N - 1, c) for c in range(N)]
 graph['left'] = [(r, 0) for r in range(N)]
 graph['right'] = [(r, N - 1) for r in range(N)]
 for c in range(N):
 graph[(0, c)].append('top')
 graph[(N - 1, c)].append('bottom')
 for r in range(N):
 graph[(r, 0)].append('left')
 graph[(r, N - 1)].append('right')
 return graph
class HexBoard:
 def __init__(self, size=SMALLBOARD):
 self.size = size
 self.board = [['.' for _ in range(size)] for _ in range(size)]
 def display(self):
 for i, row in enumerate(self.board):
 print(' ' * i + ' '.join(row))
 print()
 def get_valid_moves(self):
 return [(r, c) for r in range(self.size) for c in range(self.size) if self.board[r][c] == '.']
 def invalid_moves(self, row, col):
 return not (0 <= row < self.size and 0 <= col < self.size) or self.board[row][col] != '.'
 def make_move(self, row, col, player):
 if self.invalid_moves(row, col):
 print("invalid move!")
 return False
 self.board[row][col] = player
 return True
 def reset(self):
 self.board = [['.' for _ in range(self.size)] for _ in range(self.size)]
def filtered_board(board: HexBoard, player_symbol: str) -> dict:
 opponent = 'O' if player_symbol == 'X' else 'X'
 N = board.size
 full_graph = create_hex_graph(N)
 opponent_cells = {(r, c) for r in range(N) for c in range(N) if board.board[r][c] == opponent}
 filtered_graph = {}
 for vertex, neighbors in full_graph.items():
 if vertex in opponent_cells:
 continue
 filtered_neighbors = [n for n in neighbors if n not in opponent_cells]
 filtered_graph[vertex] = filtered_neighbors
 return filtered_graph
def chainfromFilteredBoard(board: HexBoard, player: str) -> list[set]:
 filtered_graph = filtered_board(board, player)
 visited = set()
 chains = []
 player_cells = {(r, c) for r in range(board.size) for c in range(board.size) if board.board[r][c] == player}
 relevant_virtual_nodes = ['top', 'bottom'] if player == 'X' else ['left', 'right']
 def dfs(start):
 stack = [start]
 current_chain = set()
 while stack:
 node = stack.pop()
 if node in visited:
 continue
 visited.add(node)
 current_chain.add(node)
 for neighbor in filtered_graph.get(node, []):
 if (neighbor in player_cells or neighbor in relevant_virtual_nodes) and neighbor not in visited:
 stack.append(neighbor)
 return current_chain
 for cell in player_cells.union(set(relevant_virtual_nodes)):
 if cell not in visited:
 chains.append(dfs(cell))
 return chains
def neighborhood(u, player: str, board: HexBoard) -> set:
 filtered_graph = filtered_board(board, player)
 neighbors_set = set()
 for neighbor in filtered_graph.get(u, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 player_chains = chainfromFilteredBoard(board, player)
 for ch in player_chains:
 if any(u in filtered_graph.get(node, []) for node in ch):
 for node in ch:
 for neighbor in filtered_graph.get(node, []):
 if isinstance(neighbor, tuple):
 if board.board[neighbor[0]][neighbor[1]] == '.':
 neighbors_set.add(neighbor)
 else:
 if player == 'X' and neighbor in ['top', 'bottom']:
 neighbors_set.add(neighbor)
 elif player == 'O' and neighbor in ['left', 'right']:
 neighbors_set.add(neighbor)
 neighbors_set.discard(u)
 return neighbors_set
def distancemetric(u, v, player, board, visited=None, memo=None):
 if visited is None:
 visited = set()
 if memo is None:
 memo = {}
 if (u, v) in memo:
 return memo[(u, v)]
 if u == v:
 return 0
 if u in visited:
 return math.inf
 visited.add(u)
 neighbors_u = neighborhood(u, player, board)
 if v in neighbors_u:
 memo[(u, v)] = 1
 visited.remove(u)
 return 1
 max_k = board.size * 2 + 10
 for k in range(2, max_k):
 count = 0
 for w in neighbors_u:
 dist_wv = distancemetric(w, v, player, board, visited, memo)
 if dist_wv < k:
 count += 1
 if count >= 2:
 memo[(u, v)] = k
 visited.remove(u)
 return k
 memo[(u, v)] = math.inf
 visited.remove(u)
 return math.inf
# ---- Example Usage ----
if __name__ == "__main__":
 board = HexBoard(size=5)
 board.make_move(0, 3, 'X')
 board.make_move(2, 1, 'X')
 board.make_move(2, 2, 'X')
 board.make_move(3, 1, 'O')
 board.make_move(4, 2, 'O')
 board.display()
 u = 'top'
 v = (4, 1)
 player = 'X'
 dist = distancemetric(u, v, player, board)
 print(f"distancemetric({v}, {u}) = {dist}")

What’s Going Wrong?

My distanceMetric function calculates the distance to some target nodes incorrectly and and I believe my recursive approach might be fundamentally wrong

I am able to compute the distance to any target node, but the inconsistencies make it hard to trust the metric during evaluation.

Here is a visualization of the expected distances from u to every other node on the board (these are correct and by the paper verified):

Expected distances from u to every node

edited tags
Source Link
Loading
Rollback to Revision 18
Source Link
trincot
  • 357.3k
  • 38
  • 282
  • 340
Loading
Rollback to Revision 15 - someone violates my Question
Source Link
Loading
Rollback to Revision 16
Source Link
trincot
  • 357.3k
  • 38
  • 282
  • 340
Loading
Rollback to Revision 15
Source Link
Loading
Rollback to Revision 14
Source Link
m.raynal
  • 3.2k
  • 3
  • 26
  • 44
Loading
Rollback to Revision 7 - ERROR QUESTION
Source Link
Loading
Restored broken image tag
Source Link
trincot
  • 357.3k
  • 38
  • 282
  • 340
Loading
deleted 557 characters in body
Source Link
Loading
Rollback to Revision 10
Source Link
trincot
  • 357.3k
  • 38
  • 282
  • 340
Loading
Rollback to Revision 9
Source Link
Loading
Notice removed Content dispute by Community Bot
Post Unlocked by Community Bot
Post Locked by Dharman
Notice added Content dispute by Dharman
Rollback to Revision 8
Source Link
trincot
  • 357.3k
  • 38
  • 282
  • 340
Loading
Rollback to Revision 7
Source Link
Loading
Rollback to Revision 6
Source Link
trincot
  • 357.3k
  • 38
  • 282
  • 340
Loading
deleted 7397 characters in body; edited title
Source Link
Loading
I tried another test case with the same function from the answer, but it leads to an error.
Source Link
Loading
1
2
lang-py

AltStyle によって変換されたページ (->オリジナル) /