import re # Define patterns using regular expressions patterns = { 'five': re.compile('11111'), 'block_five': re.compile('211111|111112'), 'four': re.compile('011110'), 'block_four': re.compile('10111|11011|11101|211110|211101|211011|210111|011112|101112|110112|111012'), 'three': re.compile('011100|011010|010110|001110'), 'block_three': re.compile('211100|211010|210110|001112|010112|011012'), 'two': re.compile('001100|011000|000110|010100|001010'), } # Define shapes with associated scores shapes = { 'FIVE': 5, 'BLOCK_FIVE': 50, 'FOUR': 4, 'FOUR_FOUR': 44, # Double four 'FOUR_THREE': 43, # Four with an open three 'THREE_THREE': 33, # Double three 'BLOCK_FOUR': 40, 'THREE': 3, 'BLOCK_THREE': 30, 'TWO_TWO': 22, # Double two 'TWO': 2, 'NONE': 0 } # Initialize a performance record performance = { 'five': 0, 'block_five': 0, 'four': 0, 'block_four': 0, 'three': 0, 'block_three': 0, 'two': 0, 'none': 0, 'total': 0 } # Function to detect shapes on the board def get_shape(board, x, y, offset_x, offset_y, role): """ Detect shape at a given board position. :param board: The game board. :param x: X-coordinate. :param y: Y-coordinate. :param offset_x: X-direction offset for scanning. :param offset_y: Y-direction offset for scanning. :param role: Current player's role. :return: A tuple of shape, self count, opponent count, and empty count. """ opponent = -role empty_count = 0 self_count = 1 opponent_count = 0 shape = shapes['NONE'] # Skip empty nodes if ( board[x + offset_x + 1][y + offset_y + 1] == 0 and board[x - offset_x + 1][y - offset_y + 1] == 0 and board[x + 2 * offset_x + 1][y + 2 * offset_y + 1] == 0 and board[x - 2 * offset_x + 1][y - 2 * offset_y + 1] == 0 ): return [0, self_count, opponent_count, empty_count] # Check for 'two' pattern for i in range(-3, 4): if i == 0: continue nx, ny = x + i * offset_x, y + i * offset_y current_role = board.get((nx, ny)) if current_role is None: continue if current_role == 2: opponent_count += 1 elif current_role == role: self_count += 1 elif current_role == 0: empty_count += 1 if self_count == 2: if opponent_count == 0: return shapes['TWO'], self_count, opponent_count, empty_count else: return shapes['NONE'], self_count, opponent_count, empty_count # Reset counts and prepare string for pattern matching empty_count, self_count, opponent_count = 0, 1, 0 result_string = '1' # Build result string for pattern matching for i in range(1, 6): nx = x + i * offset_x + 1 ny = y + i * offset_y + 1 currentRole = board[nx][ny] if currentRole == 2: result_string += '2' elif currentRole == 0: result_string += '0' else: result_string += '1' if currentRole == role else '2' if currentRole == 2 or currentRole == opponent: opponent_count += 1 break if currentRole == 0: empty_count += 1 if currentRole == role: self_count += 1 for i in range(1, 6): nx = x - i * offset_x + 1 ny = y - i * offset_y + 1 currentRole = board[nx][ny] if currentRole == 2: result_string = '2' + result_string elif currentRole == 0: result_string = '0' + result_string else: result_string = '1' if currentRole == role else '2' + result_string if currentRole == 2 or currentRole == opponent: opponent_count += 1 break if currentRole == 0: empty_count += 1 if currentRole == role: self_count += 1 # Check patterns and update performance for pattern_key, shape_key in [('five', 'FIVE'), ('four', 'FOUR'), ('block_four', 'BLOCK_FOUR'), ('three', 'THREE'), ('block_three', 'BLOCK_THREE'), ('two', 'TWO')]: if patterns[pattern_key].search(result_string): shape = shapes[shape_key] performance[pattern_key] += 1 performance['total'] += 1 break ## 尽量减少多余字符串生成 if self_count <= 1 or len(result_string) < 5: return shape, self_count, opponent_count, empty_count return shape, self_count, opponent_count, empty_count def count_shape(board, x, y, offset_x, offset_y, role): opponent = - role inner_empty_count = 0 # Number of empty positions inside the player's stones temp_empty_count = 0 self_count = 0 # Number of the player's stones in the shape total_length = 0 side_empty_count = 0 # Number of empty positions on the side of the shape no_empty_self_count = 0 one_empty_self_count = 0 # Right direction for i in range(1, 6): nx = x + i * offset_x + 1 ny = y + i * offset_y + 1 current_role = board[nx][ny] if current_role == 2 or current_role == opponent: break if current_role == role: self_count += 1 side_empty_count = 0 if temp_empty_count: inner_empty_count += temp_empty_count temp_empty_count = 0 if inner_empty_count == 0: no_empty_self_count += 1 one_empty_self_count += 1 elif inner_empty_count == 1: one_empty_self_count += 1 total_length += 1 if current_role == 0: temp_empty_count += 1 side_empty_count += 1 if side_empty_count >= 2: break if not inner_empty_count: one_empty_self_count = 0 return { 'self_count': self_count, 'total_length': total_length, 'no_empty_self_count': no_empty_self_count, 'one_empty_self_count': one_empty_self_count, 'inner_empty_count': inner_empty_count, 'side_empty_count': side_empty_count } # Fast shape detection function def get_shape_fast(board, x, y, offsetX, offsetY, role): if ( board[x + offsetX + 1][y + offsetY + 1] == 0 and board[x - offsetX + 1][y - offsetY + 1] == 0 and board[x + 2 * offsetX + 1][y + 2 * offsetY + 1] == 0 and board[x - 2 * offsetX + 1][y - 2 * offsetY + 1] == 0 ): return [shapes['NONE'], 1] selfCount = 1 totalLength = 1 shape = shapes['NONE'] leftEmpty = 0 rightEmpty = 0 noEmptySelfCount = 1 OneEmptySelfCount = 1 left = count_shape(board, x, y, -offsetX, -offsetY, role) right = count_shape(board, x, y, offsetX, offsetY, role) selfCount = left['self_count'] + right['self_count'] + 1 totalLength = left['total_length'] + right['total_length'] + 1 noEmptySelfCount = left['no_empty_self_count'] + right['no_empty_self_count'] + 1 OneEmptySelfCount = max( left['one_empty_self_count'] + right['no_empty_self_count'], left['no_empty_self_count'] + right['one_empty_self_count'], ) + 1 rightEmpty = right['side_empty_count'] leftEmpty = left['side_empty_count'] if totalLength < 5: return [shape, selfCount] if noEmptySelfCount >= 5: if rightEmpty > 0 and leftEmpty > 0: return [shapes['FIVE'], selfCount] else: return [shapes['BLOCK_FIVE'], selfCount] if noEmptySelfCount == 4: if ( (rightEmpty >= 1 or right['one_empty_self_count'] > right['no_empty_self_count']) and (leftEmpty >= 1 or left['one_empty_self_count'] > left['no_empty_self_count']) ): return [shapes['FOUR'], selfCount] elif not (rightEmpty == 0 and leftEmpty == 0): return [shapes['BLOCK_FOUR'], selfCount] if OneEmptySelfCount == 4: return [shapes['BLOCK_FOUR'], selfCount] if noEmptySelfCount == 3: if (rightEmpty >= 2 and leftEmpty >= 1) or (rightEmpty >= 1 and leftEmpty >= 2): return [shapes['THREE'], selfCount] else: return [shapes['BLOCK_THREE'], selfCount] if OneEmptySelfCount == 3: if rightEmpty >= 1 and leftEmpty >= 1: return [shapes['THREE'], selfCount] else: return [shapes['BLOCK_THREE'], selfCount] if (noEmptySelfCount == 2 or OneEmptySelfCount == 2) and totalLength > 5: shape = shapes['TWO'] return [shape, selfCount] # Helper functions to check for specific shapes def is_five(shape): # Checked return shape in [shapes['FIVE'], shapes['BLOCK_FIVE']] def is_four(shape): # Checked return shape in [shapes['FOUR'], shapes['BLOCK_FOUR']] # Function to get all shapes at a specific point def get_all_shapes_of_point(shape_cache, x, y, role = None): # Checked roles = [role] if role else [1, -1] result = [] for r in roles: for d in range(4): shape = shape_cache[r][d][x][y] if shape > 0: result.append(shape) return result if __name__ == "__main__": pass