python小白请教一个类似数独解密游戏的自动破解程序

Yaoafu 2025-04-12 15:18:49

最近尝试用python编写一个小工具,但是小白一个,只会基础的逻辑,不会复杂的专业代码。

尝试用AI编写了一个程序,但是总有各种问题,没办法达到理想的效果。

希望有大佬帮忙看下要怎么优化AI提示词,或者帮忙看下代码哪里有问题,如果能优化解题方式就更好了,感谢大佬!

另外还有个想请教一下的就是,怎么直接抓取网址中的谜题,或者识别图片自己加载谜题。

这个工具的目的是用于破解一个类似数独的系列谜题,谜题名字叫【星系】也有叫【对称之美】的。

谜题原地址:因版权审核不通过,不能放原始地址,网址开头有:【puzzle】

游戏规则说明:
星系 也被称为“螺旋星系”,是一个规则简单且富有挑战性的逻辑谜题。游戏规则 

很简单。 您必须按照以下方式将网格划分为不同区域(星系):
- 每个区域有且仅有 1 个白色圆圈。
- 圆圈是其中心对称点。 也就是说,区域如果围绕圆圈旋转 180°,您应该获得相同形状、位置和方向的区域。

===================================


下面是我给AI的提示词:

以这道题为例,设计一个pyhton解题器,并对代码进行标注
0,0,1,1,0
0,0,1,1,0
2,0,3,3,0
0,0,4,4,0
0,0,5,5,0


默认设定:

1、将题目设置为5乘5棋盘形式,题目数字可以自行输入,并且默认全为0,单击后全选已有数字,方便直接录入数字;

2、提供一个生成谜题按钮(将题目输出一个初始棋盘),和开始解题按钮;

3、题目中0是“空地”,非0数字是互不相关的“城”,解题中,用0修改成其他数字时,称为对应数字的“领地”

4、“城”的数字不得修改

 


解题数学逻辑:

1、设定坐标系,左上为(1,1),右下为(5,5);

2、把每个格子的信息进行记录,信息包括【序号】【坐标】【原始数字标记】【临时数字标记】【是否为“城”】【“城中心”】【是否有相同数字的180度对称格子】【可归属城数量】等

3、把所有“城”的中心点进行记录,录入“城中心”;中心点只能是0.5级别的小数坐标或者整数坐标;把每个“城”录入信息,【城序号】【城中心】【城原始坐标】

4、检验已知“城”占领格子,非0格子是否都能围绕相同数字的“城中心”找到已标注了相同数字的180度中心对称格子。如果存在“城”本身是非180度中心对称的,则停止做题,提示对应数字的城,非180度中心对称。如果都符合180度中心对称,则继续做题

5、城只有1格,2格,4格三种情况

6、从数字最小的4格城开始,尽可能扩展领地。

7、拓展方式,先检测N号城上下左右相邻的所有空地,逐个成对拓展领地;

8、拓展领地时,所有格子(x,y)必须以相同数字的“城中心”(a,b)找到另一个相同数字的格子(2a-x,2b-y)

9、拓展完一次后,需要再次检测目前N号城及N号领地上下左右相邻的所有空地,逐个成对拓展领地;直至无法再拓展;

10、4格城都拓展完毕后,然后再拓展2格城,再拓展1格城11、最后检查是否还有剩余空格

在之前的结果基础上,用递归回溯算法,对剩余空格二次尝试;测试方式如下:

1、先确认所有剩余空格坐标;

2、计算每个空格可对称的城的数量(此时的可对称判定条件为:对称位置不为城且不超范围即可)

3、按可对称数量从小到大进行尝试;

4、测试时,在空格填入测试值时,测试空格的对称位置A改为测试值,对称位置A原本的对称位置B,改为0,变更为空地。

5、重新按1到4方式不断测试,直到没有空格。

提供算法步骤日志,每拓展完一个城,就输出一个解题步骤棋盘,不限制长度,我不介意网页很长,我需要确认每个步骤的算法是正确的
二次尝试时,强制每次修改都输出一次日志,要求需要能体现是哪个逻辑出错,以便检查。


递归回溯推导。

这是AI提供的代码

import tkinter as tk
from tkinter import messagebox, ttk
import copy
import time
from threading import Thread

class Cell:
    def __init__(self, row, col, value=0):
        self.row = row
        self.col = col
        self.original_value = value
        self.temp_value = value
        self.is_city = (value != 0)  # 只在初始化时设置,后续不修改
        self.center = None
        self.symmetry_pos = None
        
    def reset(self):
        self.temp_value = self.original_value
        self.is_city = (self.original_value != 0)  # 重置时也保持原始状态
        self.center = None
        self.symmetry_pos = None


class City:
    def __init__(self, value):
        self.value = value
        self.cells = []
        self.territory = []
        self.center = None
        
    def add_cell(self, cell):
        self.cells.append(cell)
        
    def calculate_center(self):
        if len(self.cells) == 1:
            self.center = (self.cells[0].row, self.cells[0].col)
        elif len(self.cells) == 2:
            row_avg = (self.cells[0].row + self.cells[1].row) / 2
            col_avg = (self.cells[0].col + self.cells[1].col) / 2
            self.center = (row_avg, col_avg)
        elif len(self.cells) == 4:
            rows = [cell.row for cell in self.cells]
            cols = [cell.col for cell in self.cells]
            row_avg = (min(rows) + max(rows)) / 2
            col_avg = (min(cols) + max(cols)) / 2
            self.center = (row_avg, col_avg)
            
        for cell in self.cells:
            cell.center = self.center
            
    def validate_symmetry(self):
        for cell in self.cells:
            if cell.symmetry_pos is None:
                if len(self.cells) == 1:
                    cell.symmetry_pos = (cell.row, cell.col)
                else:
                    x, y = cell.row, cell.col
                    a, b = self.center
                    sym_x = round(2 * a - x, 1)
                    sym_y = round(2 * b - y, 1)
                    cell.symmetry_pos = (sym_x, sym_y)
                    
                    found = False
                    for other in self.cells:
                        if other.row == sym_x and other.col == sym_y:
                            found = True
                            break
                    if not found:
                        return False
        return True

class PuzzleSolver:
    def __init__(self, root):
        self.root = root
        self.root.title("对称城市领地解题器")
        
        # 默认设置
        self.grid_size = 5
        self.cells = []
        self.cities = []
        self.solving = False
        self.stop_requested = False
        
        # 创建UI
        self.create_widgets()
        self.create_grid()
        
    def create_widgets(self):
        # 顶部控制面板
        control_frame = tk.Frame(self.root)
        control_frame.pack(pady=10)
        
        # 网格尺寸选择
        size_label = tk.Label(control_frame, text="网格尺寸:")
        size_label.grid(row=0, column=0, padx=5)
        
        self.size_var = tk.StringVar(value="5")
        size_options = ["5", "7", "10", "15", "20", "25", "30"]
        size_menu = ttk.Combobox(control_frame, textvariable=self.size_var, values=size_options, width=5)
        size_menu.grid(row=0, column=1, padx=5)
        size_menu.bind("<<ComboboxSelected>>", self.change_grid_size)
        
        # 按钮
        generate_btn = tk.Button(control_frame, text="生成谜题", command=self.generate_puzzle)
        generate_btn.grid(row=0, column=2, padx=5)
        
        solve_btn = tk.Button(control_frame, text="开始解题", command=self.start_solving)
        solve_btn.grid(row=0, column=3, padx=5)
        
        stop_btn = tk.Button(control_frame, text="强制停止", command=self.stop_solving)
        stop_btn.grid(row=0, column=4, padx=5)
        
        clear_btn = tk.Button(control_frame, text="清空网格", command=self.clear_grid)
        clear_btn.grid(row=0, column=5, padx=5)
        
        # 网格框架
        self.grid_frame = tk.Frame(self.root)
        self.grid_frame.pack(pady=10)
        
        # 日志区域
        log_frame = tk.Frame(self.root)
        log_frame.pack(pady=10, fill=tk.BOTH, expand=True)
        
        log_label = tk.Label(log_frame, text="解题日志:")
        log_label.pack(anchor=tk.W)
        
        self.log_text = tk.Text(log_frame, height=15, width=60, state=tk.DISABLED)
        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        scrollbar = tk.Scrollbar(log_frame, command=self.log_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.log_text.config(yscrollcommand=scrollbar.set)
        
    def create_grid(self):
        # 清除现有网格
        for widget in self.grid_frame.winfo_children():
            widget.destroy()
            
        self.cells = []
        self.cities = []
        
        # 创建新的网格
        for row in range(self.grid_size):
            row_cells = []
            for col in range(self.grid_size):
                cell_value = tk.StringVar(value="0")
                entry = tk.Entry(self.grid_frame, textvariable=cell_value, width=3, 
                                justify=tk.CENTER, font=('Arial', 12))
                entry.grid(row=row, column=col, padx=2, pady=2)
                entry.bind("<FocusIn>", lambda e, entry=entry: entry.select_range(0, tk.END))
                
                cell = Cell(row+1, col+1)
                cell_value.trace("w", lambda *args, r=row+1, c=col+1, var=cell_value: 
                                self.update_cell_value(r, c, var))
                row_cells.append(cell)
                
            self.cells.append(row_cells)
            
    def change_grid_size(self, event=None):
        try:
            new_size = int(self.size_var.get())
            if new_size != self.grid_size:
                self.grid_size = new_size
                self.create_grid()
                self.log("网格尺寸已更改为 {}x{}".format(new_size, new_size))
        except ValueError:
            pass
            
    def update_cell_value(self, row, col, var):
        try:
            value = var.get()
            # 只允许输入单个数字
            if len(value) > 1:
                var.set(value[0])
                value = value[0]
            value = int(value)
            cell = self.cells[row-1][col-1]
            cell.original_value = value
            cell.temp_value = value
            cell.is_city = (value != 0)
        except ValueError:
            var.set("0")
            
    def clear_grid(self):
        # 清空所有单元格数据
        for row in self.cells:
            for cell in row:
                cell.reset()
        
        # 重置录入区显示
        for row in range(self.grid_size):
            for col in range(self.grid_size):
                entry = self.grid_frame.grid_slaves(row=row, column=col)[0]
                entry.config(state='normal')
                entry.delete(0, tk.END)
                entry.insert(0, "0")
                entry.config(state='normal')
                
        self.update_grid_display()
        self.log("网格已清空")
        
    def generate_puzzle(self):
        # 重置网格状态
        # self.clear_grid()
        
        # 确保所有输入框可编辑
        for row in range(self.grid_size):
            for col in range(self.grid_size):
                entry = self.grid_frame.grid_slaves(row=row, column=col)[0]
                entry.config(state='normal')
                
        # 识别并验证城市
        if not self.identify_cities():
            messagebox.showerror("错误", "无效的城市配置,请检查输入")
            return False
            
        # 更新显示并记录日志
        self.update_grid_display()
        self.log("已生成初始谜题")
        self.log_current_grid()
        return True


    def log_current_grid(self):
        """记录当前棋盘状态到日志"""
        self.log("当前棋谱:")
        grid_str = ""
        for row in self.cells:
            row_str = " ".join(str(cell.temp_value) for cell in row)
            grid_str += row_str + "\n"
        self.log(grid_str)
        
    def identify_cities(self):
        self.cities = []
        visited = [[False for _ in range(self.grid_size)] for _ in range(self.grid_size)]
        
        # 首先收集所有非零单元格
        non_zero_cells = []
        for row in range(self.grid_size):
            for col in range(self.grid_size):
                cell = self.cells[row][col]
                if cell.original_value != 0:
                    non_zero_cells.append((row, col, cell.original_value))
        
        # 按值分组处理
        value_groups = {}
        for row, col, value in non_zero_cells:
            if value not in value_groups:
                value_groups[value] = []
            value_groups[value].append((row, col))
            
        # 验证每个城市组
        for value, positions in value_groups.items():
            city = City(value)
            
            # 连接相邻单元格
            for row, col in positions:
                if not visited[row][col]:
                    self.find_connected_cells(row, col, value, visited, city)
            
            # 验证城市结构
            if len(city.cells) not in [1, 2, 4]:
                self.log(f"错误: 城市 {value} 有 {len(city.cells)} 个格子,但只允许 1, 2 或 4 格")
                return False
                
            # 计算中心点并验证对称性
            city.calculate_center()
            if not city.validate_symmetry():
                self.log(f"错误: 城市 {value} 对称性验证失败")
                return False
                
            # 设置城市属性
            for cell in city.cells:
                cell.is_city = True
                
            self.cities.append(city)
            
        # 按优先级排序
        self.cities.sort(key=lambda x: (-len(x.cells), x.value))
        return True

    def find_connected_cells(self, row, col, value, visited, city):
        if (row < 0 or row >= self.grid_size or col < 0 or col >= self.grid_size or 
            visited[row][col] or self.cells[row][col].original_value != value):
            return
            
        visited[row][col] = True
        cell = self.cells[row][col]
        city.add_cell(cell)
        
        # 检查相邻的4个方向
        self.find_connected_cells(row-1, col, value, visited, city)
        self.find_connected_cells(row+1, col, value, visited, city)
        self.find_connected_cells(row, col-1, value, visited, city)
        self.find_connected_cells(row, col+1, value, visited, city)


    def update_grid_display(self):
        for row in range(self.grid_size):
            for col in range(self.grid_size):
                cell = self.cells[row][col]
                entry = self.grid_frame.grid_slaves(row=row, column=col)[0]
                entry.config(state=tk.NORMAL)
                entry.delete(0, tk.END)
                entry.insert(0, str(cell.temp_value))
                
                # 设置颜色 - 严格区分城市和领地
                if cell.is_city:
                    entry.config(bg='blue')  # 城市标记为蓝色
                elif cell.temp_value != 0:
                    entry.config(bg='gold')  # 领地标记为黄色
                else:
                    entry.config(bg='white')  # 空地标记为白色
                    
                entry.config(state='readonly' if self.solving else 'normal')

    def log(self, message):
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
        self.root.update()
        
    def start_solving(self):
        if self.solving:
            return
            
        if not self.identify_cities():
            return
            
        self.solving = True
        self.stop_requested = False
        self.update_grid_display()
        
        # 在新线程中运行解题算法
        Thread(target=self.solve_puzzle).start()
        
    def stop_solving(self):
        if self.solving:
            self.stop_requested = True
            self.log("请求停止解题...")
            
    def solve_puzzle(self):
        self.log("=== 开始解题 ===")
        start_time = time.time()
        self.log_current_grid()
        
        # 第一阶段:领地扩展
        self.log("\n=== 开始领地扩展阶段 ===")
        self.expand_territories()
        
        if self.stop_requested:
            self.log("解题已停止")
            self.solving = False
            self.update_grid_display()
            return
            
        if self.is_solved():
            self.log("\n=== 领地扩展阶段已解决全部空格 ===")
            self.log("解题完成! 用时: {:.2f}秒".format(time.time() - start_time))
            self.log_current_grid()
            self.solving = False
            self.update_grid_display()
            return
            
        # 第二阶段:递归回溯
        self.log("\n=== 开始递归回溯阶段 ===")
        solution_found = self.backtrack_solve()
        
        if solution_found:
            self.log("\n=== 递归回溯阶段找到解决方案 ===")
            self.log("解题完成! 用时: {:.2f}秒".format(time.time() - start_time))
        else:
            self.log("\n=== 递归回溯阶段未找到解决方案 ===")
            self.log("无法找到完整解决方案")
            
        self.log_current_grid()
        self.solving = False
        self.update_grid_display()
    def get_empty_cells(self):
        """获取所有空格并按可能城市数排序"""
        empty_cells = []
        for row in self.cells:
            for cell in row:
                if cell.temp_value == 0:
                    possible = len(self.get_possible_cities(cell))
                    empty_cells.append((cell, possible))
        
        # 按可能城市数升序排序
        empty_cells.sort(key=lambda x: x[1])
        return [cell for cell, _ in empty_cells]

    def backtrack_solve(self):
        # 先记录当前棋盘状态
        self.log("\n=== 当前棋盘状态确认 ===")
        empty_count = 0
        for row in range(self.grid_size):
            for col in range(self.grid_size):
                cell = self.cells[row][col]
                status = "城市" if cell.is_city else "领地" if cell.temp_value != 0 else "空地"
                if status == "空地":
                    empty_count += 1
                self.log(f"位置({row+1},{col+1}): {status} (值={cell.temp_value})")
        
        self.log(f"\n=== 开始递归回溯,共有 {empty_count} 个空格需要处理 ===")
        
        empty_cells = self.get_empty_cells()
        if not empty_cells:
            self.log("所有空格已填满,找到解决方案!")
            return True
            
        # 获取当前空格并排序
        cell = min(empty_cells, key=lambda c: len(self.get_possible_cities(c)))
        possible_cities = self.get_possible_cities(cell)
        
        self.log(f"\n正在处理空格 ({cell.row},{cell.col})")
        self.log(f"可选城市数: {len(possible_cities)}")
        for i, city in enumerate(possible_cities, 1):
            self.log(f"{i}. 城市{city.value} (中心:{city.center})")
            
        if not possible_cities:
            self.log(f"错误: 空格({cell.row},{cell.col})没有可分配的城市")
            return False
            
        for city in possible_cities:
            self.log(f"\n尝试分配: 空格({cell.row},{cell.col}) -> 城市{city.value}")
            
            # 创建快照以便回溯
            snapshot = self.create_snapshot()
            
            if self.try_fill_cell(cell, city):
                self.log(f"成功分配 空格({cell.row},{cell.col}) 给城市{city.value}")
                self.log_current_grid()
                
                # 递归解决剩余空格
                if self.backtrack_solve():
                    return True
                    
                # 回溯
                self.log(f"回溯: 撤销空格({cell.row},{cell.col})的分配")
                self.restore_snapshot(snapshot)
            else:
                self.log(f"分配失败: 空格({cell.row},{cell.col})给城市{city.value}")
                
        self.log(f"\n空格({cell.row},{cell.col})的所有可能性已尝试完毕,未找到可行解")
        return False


    def expand_territories(self):
        changed = True
        while changed and not self.stop_requested:
            changed = False
            for city in self.cities:
                expanded = self.expand_city_territory(city)
                if expanded:
                    changed = True
                    self.log("城市 {} 扩展了领地".format(city.value))
                    self.update_grid_display()
                    time.sleep(0.5)
                    
    def expand_city_territory(self, city):
        expanded = False
        a, b = city.center
        
        # 从现有城市和领地出发
        frontier = city.cells + city.territory
        
        for cell in frontier:
            for dr, dc in [(-1,0), (1,0), (0,-1), (0,1)]:
                row = cell.row - 1 + dr
                col = cell.col - 1 + dc
                
                if (0 <= row < self.grid_size and 0 <= col < self.grid_size):
                    target_cell = self.cells[row][col]
                    # 双重验证:必须是空地且不是城市
                    if target_cell.temp_value == 0 and not target_cell.is_city:
                        # 计算对称位置
                        x, y = row+1, col+1
                        sym_x = round(2 * a - x, 1)
                        sym_y = round(2 * b - y, 1)
                        
                        # 检查对称位置是否有效
                        sym_row = int(sym_x - 1) if sym_x.is_integer() else -1
                        sym_col = int(sym_y - 1) if sym_y.is_integer() else -1
                        
                        if (0 <= sym_row < self.grid_size and 0 <= sym_col < self.grid_size):
                            sym_cell = self.cells[sym_row][sym_col]
                            # 对称点也必须是空地且不是城市
                            if sym_cell.temp_value == 0 and not sym_cell.is_city:
                                # 标记为领地(不修改is_city属性)
                                target_cell.temp_value = city.value
                                sym_cell.temp_value = city.value
                                
                                # 添加到领地列表
                                city.territory.append(target_cell)
                                city.territory.append(sym_cell)
                                
                                # 详细日志 - 明确说明是领地
                                self.log(f"城市{city.value}扩展领地: "
                                       f"位置({row+1},{col+1})和对称位置({sym_row+1},{sym_col+1}) "
                                       f"被标记为领地(中心:{a},{b})")
                                
                                expanded = True
                        
        return expanded


    def is_solved(self):
        for row in self.cells:
            for cell in row:
                if cell.temp_value == 0:
                    return False
        return True
        
    def get_possible_cities(self, cell):
        possible = []
        a, b = cell.row, cell.col
        
        for city in self.cities:
            # 计算对称位置
            center_x, center_y = city.center
            sym_x = round(2 * center_x - a, 1)
            sym_y = round(2 * center_y - b, 1)
            
            # 检查对称位置是否有效
            sym_row = int(sym_x - 1) if sym_x.is_integer() else -1
            sym_col = int(sym_y - 1) if sym_y.is_integer() else -1
            
            if (0 <= sym_row < self.grid_size and 0 <= sym_col < self.grid_size):
                sym_cell = self.cells[sym_row][sym_col]
                # 允许对称位置是其他城市的领地(非城市本身)
                if not sym_cell.is_city:
                    possible.append(city)
                    # 明确说明对称点的状态
                    status = "城市" if sym_cell.is_city else "领地" if sym_cell.temp_value != 0 else "空地"
                    self.log(f"城市{city.value}可能: 中心({center_x},{center_y}) "
                           f"对称点({sym_row+1},{sym_col+1})当前状态={status}(值={sym_cell.temp_value})")
                else:
                    self.log(f"城市{city.value}排除: 对称点({sym_row+1},{sym_col+1})是城市(值={sym_cell.temp_value})")
            else:
                self.log(f"城市{city.value}排除: 对称点({sym_x},{sym_y})超出网格")
                    
        return possible


    def try_fill_cell(self, cell, city):
        a, b = cell.row, cell.col
        center_x, center_y = city.center
        
        # 计算对称位置
        sym_x = round(2 * center_x - a, 1)
        sym_y = round(2 * center_y - b, 1)
        
        sym_row = int(sym_x - 1) if sym_x.is_integer() else -1
        sym_col = int(sym_y - 1) if sym_y.is_integer() else -1
        
        if not (0 <= sym_row < self.grid_size and 0 <= sym_col < self.grid_size):
            self.log(f"对称位置({sym_x},{sym_y})无效,无法分配")
            return False
            
        sym_cell = self.cells[sym_row][sym_col]
        
        # 处理对称位置A的原有领地
        if sym_cell.temp_value != 0 and not sym_cell.is_city:
            old_city = self.find_cell_owner(sym_cell)
            if old_city:
                self.log(f"需要清除城市{old_city.value}在({sym_cell.row},{sym_cell.col})的领地")
                # ... 原有清除领地逻辑 ...
        
        # ... 剩余原有逻辑 ...

        a, b = cell.row, cell.col
        center_x, center_y = city.center
        
        # 计算对称位置
        sym_x = round(2 * center_x - a, 1)
        sym_y = round(2 * center_y - b, 1)
        
        sym_row = int(sym_x - 1) if sym_x.is_integer() else -1
        sym_col = int(sym_y - 1) if sym_y.is_integer() else -1
        
        if not (0 <= sym_row < self.grid_size and 0 <= sym_col < self.grid_size):
            return False
            
        sym_cell = self.cells[sym_row][sym_col]
        
        # 处理对称位置A的原有领地
        if sym_cell.temp_value != 0 and not sym_cell.is_city:
            old_city = self.find_cell_owner(sym_cell)
            if old_city:
                # 找到对称位置A的对称位置B(相对于旧城市中心)
                old_center_x, old_center_y = old_city.center
                b_x = round(2 * old_center_x - sym_cell.row, 1)
                b_y = round(2 * old_center_y - sym_cell.col, 1)
                
                # 将位置B重置为0
                if b_x.is_integer() and b_y.is_integer():
                    b_row = int(b_x) - 1
                    b_col = int(b_y) - 1
                    if 0 <= b_row < self.grid_size and 0 <= b_col < self.grid_size:
                        b_cell = self.cells[b_row][b_col]
                        if b_cell.temp_value != 0 and not b_cell.is_city:
                            b_cell.temp_value = 0
                            if b_cell in old_city.territory:
                                old_city.territory.remove(b_cell)
                
                # 清除对称位置A的领地
                sym_cell.temp_value = 0
                if sym_cell in old_city.territory:
                    old_cell = self.cells[sym_row][sym_col]
                    old_cell.temp_value = 0
                    old_city.territory.remove(sym_cell)
        
        # 标记新领地
        cell.temp_value = city.value
        sym_cell.temp_value = city.value
        city.territory.append(cell)
        city.territory.append(sym_cell)
        
        return True
    def find_cell_owner(self, cell):
        for city in self.cities:
            if cell in city.territory:
                return city
        return None
        
    def clear_territory_pair(self, city, cell):
        a, b
    def clear_territory_pair(self, city, cell):
        # 找到对称位置
        a, b = city.center
        x, y = cell.row, cell.col
        sym_x = round(2 * a - x, 1)
        sym_y = round(2 * b - y, 1)
        
        # 清除当前格和对称格的领地标记
        cell.temp_value = 0
        if sym_x.is_integer() and sym_y.is_integer():
            sym_row = int(sym_x) - 1
            sym_col = int(sym_y) - 1
            if 0 <= sym_row < self.grid_size and 0 <= sym_col < self.grid_size:
                self.cells[sym_row][sym_col].temp_value = 0
        
        # 从领地列表中移除
        if cell in city.territory:
            city.territory.remove(cell)
        if sym_x.is_integer() and sym_y.is_integer():
            sym_row = int(sym_x) - 1
            sym_col = int(sym_y) - 1
            if 0 <= sym_row < self.grid_size and 0 <= sym_col < self.grid_size:
                sym_cell = self.cells[sym_row][sym_col]
                if sym_cell in city.territory:
                    city.territory.remove(sym_cell)

    def create_snapshot(self):
        # 深度克隆当前状态
        snapshot = {
            'cells': [[copy.deepcopy(cell) for cell in row] for row in self.cells],
            'cities': copy.deepcopy(self.cities)
        }
        
        # 重建引用关系
        for city in snapshot['cities']:
            # 重建城市格子引用
            new_cells = []
            for cell in city.cells:
                new_cells.append(snapshot['cells'][cell.row-1][cell.col-1])
            city.cells = new_cells
            
            # 重建领地格子引用
            new_territory = []
            for cell in city.territory:
                new_territory.append(snapshot['cells'][cell.row-1][cell.col-1])
            city.territory = new_territory
            
        return snapshot
        
    def restore_snapshot(self, snapshot):
        # 恢复网格状态
        for row in range(self.grid_size):
            for col in range(self.grid_size):
                self.cells[row][col].__dict__ = snapshot['cells'][row][col].__dict__
                
        # 恢复城市列表
        self.cities = []
        for city in snapshot['cities']:
            new_city = City(city.value)
            new_city.cells = city.cells
            new_city.territory = city.territory
            new_city.center = city.center
            self.cities.append(new_city)
            
        # 重新排序城市
        self.cities.sort(key=lambda x: (-len(x.cells), x.value))

    def validate_puzzle(self):
        # 检查所有城市是否合法
        for city in self.cities:
            if len(city.cells) not in [1, 2, 4]:
                return False, f"城市 {city.value} 有 {len(city.cells)} 个格子,但只允许 1, 2 或 4 格"
                
            if not city.validate_symmetry():
                return False, f"城市 {city.value} 对称性验证失败"
                
        # 检查是否有重叠的城市
        city_positions = set()
        for city in self.cities:
            for cell in city.cells:
                pos = (cell.row, cell.col)
                if pos in city_positions:
                    return False, f"位置 ({cell.row}, {cell.col}) 被多个城市占用"
                city_positions.add(pos)
                
        return True, "谜题验证通过"

    def show_validation_result(self):
        valid, message = self.validate_puzzle()
        if valid:
            self.log(message)
            messagebox.showinfo("验证结果", message)
        else:
            self.log("验证失败: " + message)
            messagebox.showerror("验证失败", message)

if __name__ == "__main__":
    root = tk.Tk()
    app = PuzzleSolver(root)
    root.mainloop()  


======================


我按到5乘5的棋盘说明一下(15*15的基本半小时以内都能手动解答出来,实际想让程序破解20*20,25*25,30*30的谜题):

以这道题为例,解释一下目前的算法步骤
坐标系我定的是左上为(1,1),右下(5,5)

0,0,1,1,0
0,0,1,1,0
2,0,3,3,0
0,0,4,4,0
0,0,5,5,0

城1:四格城,原始位置:(3,1)(4,1)(3,2)(4,2)
城2:单格城,原始位置:(1,3)
城3:双格城,原始位置:(3,3)(4,3)
城4:双格城,原始位置:(3,4)(4,4)
城5:双格城,原始位置:(3,5)(4,5)

城1:中心点:(3.5,1.5)
城2:中心点:(1,3)
城3:中心点:(3.5,3)
城4:中心点:(3.5,4)
城5:中心点:(3.5,5)

阶段1:领地拓展
拓展顺序,先按四格城,双格城,单格城;再按数字排序

先拓展1号城:
1号城接壤空地:(2,1)(5,1)(2,2)(5,2)
尝试(2,1),
(2,1)以城1中心点(3.5,1.5)的对称点是(5,2),且格子属性为【空地】,将(2,1)和(5,2)改写为1,算做1的领地。


以此类推,得出
0,1,1,1,1
0,1,1,1,1
2,0,3,3,0
0,0,4,4,0
0,0,5,5,0


再次计算1号城接壤空地:(1,1)(1,2)(2,3)(5,3)
尝试(1,1),

(1,1)以城1中心点(3.5,1.5)的对称点是(6,2),6超出范围,不成立,这个格子不能拓展。

都尝试下来,1号城无可拓展领地

得出,1号城无可拓展领地
0,1,1,1,1
0,1,1,1,1
2,0,3,3,0
0,0,4,4,0
0,0,5,5,0


再下来尝试双格城,依次查3号城,4号城,5号城

得出,3号城可拓展领地

0,1,1,1,1
0,1,1,1,1
2,3,3,3,3
0,0,4,4,0
0,0,5,5,0

得出,4号城可拓展领地

0,1,1,1,1
0,1,1,1,1
2,3,3,3,3
0,4,4,4,4
0,0,5,5,0

得出,5号城可拓展领地

0,1,1,1,1
0,1,1,1,1
2,3,3,3,3
0,4,4,4,4
0,5,5,5,5


再下来尝试单格城,2号城

遍历拓展后

得出,2号城可拓展领地

2,1,1,1,1
2,1,1,1,1
2,3,3,3,3
2,4,4,4,4
2,5,5,5,5

题目得解
2,1,1,1,1
2,1,1,1,1
2,3,3,3,3
2,4,4,4,4
2,5,5,5,5



上面这种题目,AI写的程序目前基本都能秒答

=======================================================

接下来这题用来测试对AI来说比较复杂点情况
0,0,0,0,0
0,0,1,1,0
3,3,0,0,2
0,0,4,4,0
0,0,4,4,0


城1:双格城,原始位置:(3,2)(4,2)
城2:单格城,原始位置:(5,3)
城3:双格城,原始位置:(1,3)(2,3)
城4:四格城,原始位置:(3,4)(4,4)(3,5)(4,5)

城1:中心点:(3.5,2)
城2:中心点:(5,3)
城3:中心点:(1.5,3)
城4:中心点:(3.5,4.5)



同理,先拓展4号城(四格城)
0,0,0,0,0
0,0,1,1,0
3,3,0,0,2
0,4,4,4,4
0,4,4,4,4

拓展1号城(双格城)
0,0,1,1,0
0,1,1,1,1
3,3,1,1,2
0,4,4,4,4
0,4,4,4,4

拓展3号城(双格城)【无可拓展】
0,0,1,1,0
0,1,1,1,1
3,3,1,1,2
0,4,4,4,4
0,4,4,4,4

拓展2号城(单格城)【无可拓展】
0,0,1,1,0
0,1,1,1,1
3,3,1,1,2
0,4,4,4,4
0,4,4,4,4

接下来是跟上一题不一样的地方,需要进行二次校对
需要遍历剩余空格,直接找到可对称的点位,强制改写
我给AI提示的是用递归遍历法去尝试。
但是AI老是把可修改得【领地】标记成不可修改得【城市】属性

 

我希望的步骤如下:

先检查当前剩余空格:(1,1)(2,1)(5,1)(1,2)(1,4)(1,5)
先查(1,1)的可能对称点有几个【对称点计算方式,点(x,y),中心点(a,b),对称点为(2a-x,2b-y)】

城1:中心点:(3.5,2)   【(1,1)对称点(6,2),6超出范围,不可用】
城2:中心点:(5,3)      【(1,1)对称点(9,5),9超出范围,不可用】
城3:中心点:(1.5,3)   【(1,1)对称点(2,5),范围内,且非原始的城,当前为领地,可用】
城4:中心点:(3.5,4.5)【(1,1)对称点(6,8),6超出范围,不可用】


因此(1,1)只有一个对称的点位


同时计算剩余几个剩余空格的可对称点位
(1,1)【1个】
(2,1)【1个】
(5,1)【1个】
(1,2)【1个】
(1,4)【1个】
(1,5)【1个】


按可对称点位最少的,坐标靠前的依次尝试

尝试(1,1)空格的对称位(2,5),当前为4号城领地,将(2,5)清空为空格,同时将(2,5)的原对称位(5,4)也清空为空格


得出【尝试(1,1)为3】
3,0,1,1,0
0,1,1,1,1
3,3,1,1,2
0,4,4,4,0
0,3,4,4,4

接下来沿着(5,4)继续递归尝试
(5,4)可对称的点位(2,5)(5,2)【(2,5)为递归的上级尝试点,先尝试其他点位】


得出【尝试(5,4)可对称的点位(5,2)为2,同时将旧对位(2,2)清空】
3,0,1,1,0
0,0,1,1,2
3,3,1,1,2
0,4,4,4,2
0,3,4,4,4

接下来沿着(2,2)继续递归尝试
(2,2)可对称的点位(1,4)(2,5)【(2,5)为递归的上级尝试点,先尝试其他点位】


得出【尝试(2,2)可对称的点位(1,4)为3,同时因对位(1,4)为空值,此链路推导停止,切换下一空格】
3,0,1,1,0
0,3,1,1,2
3,3,1,1,2
3,4,4,4,2
0,3,4,4,4

接下来检查当前剩余空格:(2,1)(5,1)(1,2)(1,5)【注:(1,1)(1,4)在上一轮已完成标记】


先查可能对称点数量

(2,1)【1个】
(5,1)【1个】
(1,2)【1个】
(1,5)【1个】


同理,推导下一步
3,3,1,1,0
0,3,1,1,2
3,3,1,1,2
3,4,4,4,2
3,3,4,4,4


同理,推导下一步
3,3,1,1,0
3,3,1,1,2
3,3,1,1,2
3,3,4,4,2
3,3,4,4,0

同理,推导下一步
3,3,1,1,2
3,3,1,1,2
3,3,1,1,2
3,3,4,4,2
3,3,4,4,2


至此谜题得解
3,3,1,1,2
3,3,1,1,2
3,3,1,1,2
3,3,4,4,2
3,3,4,4,2

这题因为棋盘比较小,空格都是唯一解,当棋盘扩大到20*20,30*30,容易出现多个可对称点的剩余空格,按我目前的策略,可能会很慢。大佬们有没有更优化的解题方式。

...全文
55 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

6,203

社区成员

发帖
与我相关
我的任务
社区描述
人生苦短,我用python
社区管理员
  • Python 学习者
  • 嗨学编程
  • 松鼠爱吃饼干
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

欢迎来到Python学习者们的社区,

 

本社区分享你需要的文章、问题解答、技术互助、学习资源、面试系列等等

 

欢迎你的加入,祝你学有所成~

试试用AI创作助手写篇文章吧