news 2026/11/6 1:38:40

Bejeweled 3 经典模式 —— 穷举暴力试错版 v4(真零延迟)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bejeweled 3 经典模式 —— 穷举暴力试错版 v4(真零延迟)
# -*- coding: utf-8 -*- """ ==================================================================== Bejeweled 3 经典模式 —— 穷举暴力试错版 v4(真·零延迟) -------------------------------------------------------------------- Bejeweled 3 / 宝石迷阵3(CHS 中文版) 改动: ✓ pyautogui.PAUSE = 0 ✓ 只截两格(不截整屏) ✓ 优先用 mss 截屏(快 5~10 倍) ✓ 预览降频到 200ms,可关闭 ✓ 日志批量写 ✓ 每圈保证 112 对全部尝试 ==================================================================== """ import os import json import time import threading import numpy as np import cv2 import pyautogui from PIL import Image, ImageTk import tkinter as tk from tkinter import ttk # ★ 零延迟关键 pyautogui.PAUSE = 0 pyautogui.FAILSAFE = False # ★ 优先用 mss 截屏 try: import mss _SCT = mss.mss() HAS_MSS = True except ImportError: HAS_MSS = False try: import keyboard HAS_KEYBOARD = True except ImportError: HAS_KEYBOARD = False print("[警告] 未安装 keyboard 库,F1/F2 热键不可用。请运行: pip install keyboard") # ===================== 常量 ===================== BOARD_W = 8 BOARD_H = 8 DEFAULT_GEM_SIZE = 101 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) CONFIG_PATH = os.path.join(BASE_DIR, "bej3_bf_config.json") DEFAULT_CONFIG = { "window_left": 0, "window_top": 0, "capture_width": 900, "capture_height": 984, "board_w": BOARD_W, "board_h": BOARD_H, "gem_size": DEFAULT_GEM_SIZE, "x_margin": -1, "y_margin": -1, "hash_threshold": 6, "bad_move_cooldown": 3.0, "show_preview": True, } # ===================== 配置 ===================== def load_config(): if os.path.exists(CONFIG_PATH): try: with open(CONFIG_PATH, "r", encoding="utf-8") as f: c = json.load(f) c["board_w"] = BOARD_W c["board_h"] = BOARD_H return {**DEFAULT_CONFIG, **c} except Exception: pass return dict(DEFAULT_CONFIG) def save_config(cfg): with open(CONFIG_PATH, "w", encoding="utf-8") as f: json.dump(cfg, f, indent=2, ensure_ascii=False) def get_margins(cfg): gs = cfg["gem_size"] xm = cfg["x_margin"] if cfg["x_margin"] >= 0 else int((cfg["capture_width"] - gs * cfg["board_w"]) / 2) ym = cfg["y_margin"] if cfg["y_margin"] >= 0 else int((cfg["capture_height"] - gs * cfg["board_h"]) / 2) return xm, ym def grab_window(cfg): """整屏截图(预览用)""" region = (int(cfg["window_left"]), int(cfg["window_top"]), cfg["capture_width"], cfg["capture_height"]) if HAS_MSS: mon = {"left": region[0], "top": region[1], "width": region[2], "height": region[3]} img = np.array(_SCT.grab(mon)) return cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) return cv2.cvtColor(np.array(pyautogui.screenshot(region=region)), cv2.COLOR_RGB2BGR) def grab_cell(cfg, x, y): """★ 只截一格,速度极快""" xm, ym = get_margins(cfg) gs = cfg["gem_size"] px = int(cfg["window_left"]) + xm + x * gs py = int(cfg["window_top"]) + ym + y * gs if HAS_MSS: mon = {"left": px, "top": py, "width": gs, "height": gs} img = np.array(_SCT.grab(mon)) return cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) return cv2.cvtColor(np.array(pyautogui.screenshot(region=(px, py, gs, gs))), cv2.COLOR_RGB2BGR) def cell_center(cfg, x, y): xm, ym = get_margins(cfg) gs = cfg["gem_size"] return (int(cfg["window_left"]) + xm + x * gs + gs // 2, int(cfg["window_top"]) + ym + y * gs + gs // 2) def cell_phash_from_crop(crop): """从小图直接算 64bit 哈希""" if crop is None or crop.size == 0: return None small = cv2.resize(crop, (16, 16), interpolation=cv2.INTER_AREA) gray = cv2.cvtColor(small, cv2.COLOR_BGR2GRAY) flat = gray.flatten() return np.packbits(flat > flat.mean()) def cell_diff(h1, h2): return 999 if (h1 is None or h2 is None) else int((h1 != h2).sum()) def all_adjacent_pairs(bw, bh): pairs = [] for y in range(bh): for x in range(bw - 1): pairs.append((x, y, x + 1, y)) for x in range(bw): for y in range(bh - 1): pairs.append((x, y, x, y + 1)) return pairs # ===================== tkinter GUI ===================== class AutoGUI: def __init__(self, root, cfg): self.root = root self.cfg = cfg self.running = False self.thread = None self.photo = None self.last_try = None self.last_good = False self._log_buf = [] self._log_lock = threading.Lock() root.title("Bejeweled 3 暴力试错版 v4(真零延迟) [F1=开始 F2=停止]") root.resizable(False, False) left = ttk.Frame(root, padding=8) left.grid(row=0, column=0, sticky="n") ttk.Label(left, text="实时画面 + 棋盘网格(绿=合法 红=被弹回 白=正在试)", font=("Microsoft YaHei", 10)).grid(row=0, column=0, pady=(0, 4)) self.canvas = tk.Canvas(left, width=cfg["capture_width"], height=cfg["capture_height"], bg="black", highlightthickness=1, highlightbackground="#888") self.canvas.grid(row=1, column=0) right = ttk.Frame(root, padding=8) right.grid(row=0, column=1, sticky="n") row = 0 ttk.Label(right, text="【窗口位置】", font=("Microsoft YaHei", 10, "bold") ).grid(row=row, column=0, columnspan=2, sticky="w") row += 1 self.var_x = tk.IntVar(value=cfg["window_left"]) self.var_y = tk.IntVar(value=cfg["window_top"]) row = self._add_slider(right, row, "窗口 X 偏移", self.var_x, -2000, 2000, lambda v: cfg.update(window_left=int(float(v))), fmt="%d") row = self._add_slider(right, row, "窗口 Y 偏移", self.var_y, -2000, 2000, lambda v: cfg.update(window_top=int(float(v))), fmt="%d") ttk.Label(right, text="【网格 / 边距】", font=("Microsoft YaHei", 10, "bold") ).grid(row=row, column=0, columnspan=2, sticky="w", pady=(8, 0)) row += 1 ttk.Label(right, text=f"棋盘: {cfg['board_w']} 列 × {cfg['board_h']} 行 (固定 8×8)" ).grid(row=row, column=0, columnspan=2, sticky="w") row += 1 self.var_gem = tk.IntVar(value=cfg["gem_size"]) row = self._add_slider(right, row, "每格像素", self.var_gem, 16, 200, lambda v: cfg.update(gem_size=int(float(v))), fmt="%d") self.var_xm = tk.IntVar(value=cfg["x_margin"]) row = self._add_slider(right, row, "水平边距(-1=自动)", self.var_xm, -1, 500, lambda v: cfg.update(x_margin=int(float(v))), fmt="%d") self.var_ym = tk.IntVar(value=cfg["y_margin"]) row = self._add_slider(right, row, "垂直边距(-1=自动)", self.var_ym, -1, 500, lambda v: cfg.update(y_margin=int(float(v))), fmt="%d") ttk.Label(right, text="【判定】", font=("Microsoft YaHei", 10, "bold") ).grid(row=row, column=0, columnspan=2, sticky="w", pady=(8, 0)) row += 1 self.var_ht = tk.IntVar(value=cfg["hash_threshold"]) row = self._add_slider(right, row, "哈希阈值(大=更松)", self.var_ht, 2, 40, lambda v: cfg.update(hash_threshold=int(float(v))), fmt="%d") self.var_show = tk.BooleanVar(value=cfg.get("show_preview", True)) ttk.Checkbutton(right, text="显示实时画面+网格", variable=self.var_show, command=lambda: cfg.update(show_preview=self.var_show.get()) ).grid(row=row, column=0, columnspan=2, sticky="w") row += 1 btns = ttk.Frame(right) btns.grid(row=row, column=0, columnspan=2, pady=(10, 4), sticky="ew") row += 1 ttk.Button(btns, text="鼠标校准", command=self.calibrate).grid(row=0, column=0, padx=2, sticky="ew") ttk.Button(btns, text="保存配置", command=self.save).grid(row=0, column=1, padx=2, sticky="ew") ttk.Button(btns, text="重置 0,0", command=self.reset_pos).grid(row=1, column=0, padx=2, sticky="ew") ttk.Button(btns, text="退出", command=self.quit).grid(row=1, column=1, padx=2, sticky="ew") btns.columnconfigure(0, weight=1) btns.columnconfigure(1, weight=1) self.btn_start = ttk.Button(right, text="▶ 开始暴力试错 (F1)", command=self.toggle_run) self.btn_start.grid(row=row, column=0, columnspan=2, pady=(8, 4), sticky="ew") row += 1 self.lbl_status = ttk.Label(right, text="就绪 | F1=开始 F2=停止", foreground="#080", font=("Microsoft YaHei", 10)) self.lbl_status.grid(row=row, column=0, columnspan=2, sticky="w") row += 1 self.txt_log = tk.Text(right, width=44, height=13, font=("Consolas", 9)) self.txt_log.grid(row=row, column=0, columnspan=2, sticky="ew") self.txt_log.configure(state="disabled") self._setup_hotkeys() self.refresh_preview() self._flush_log_periodic() def _setup_hotkeys(self): if not HAS_KEYBOARD: self.log("[提示] 未安装 keyboard,热键不可用") return try: keyboard.add_hotkey("f1", lambda: self.root.after(0, self._start_run), suppress=False) keyboard.add_hotkey("f2", lambda: self.root.after(0, self._stop_run), suppress=False) self.log("[热键] F1=开始 F2=停止") except Exception as e: self.log(f"[热键注册失败] {e}") def _start_run(self): if self.running: return self.running = True self.btn_start.config(text="■ 停止 (F2)") self.lbl_status.config(text="暴力运行中(真零延迟)", foreground="#080") self.log("[开始] 穷举暴力试错已启动") self.thread = threading.Thread(target=self.worker, daemon=True) self.thread.start() def _stop_run(self): if not self.running: return self.running = False self.btn_start.config(text="▶ 开始暴力试错 (F1)") self.lbl_status.config(text="已停止", foreground="#c00") self.log("[停止] 已停止") def _add_slider(self, parent, row, label, var, from_, to_, cmd, fmt="%d"): ttk.Label(parent, text=label).grid(row=row, column=0, columnspan=2, sticky="w") row += 1 s = ttk.Scale(parent, from_=from_, to=to_, orient="horizontal", length=280) s.set(var.get()) lbl = ttk.Label(parent, text=fmt % var.get(), width=8) s.grid(row=row, column=0, sticky="ew") lbl.grid(row=row, column=1, sticky="w") s._fmt = fmt s._label = lbl s._var = var s._cmd = cmd s.configure(command=lambda v, scale=s: self._on_slider(v, scale)) row += 1 return row def _on_slider(self, v, scale): fmt = scale._fmt var = scale._var if fmt == "%d": var.set(int(float(v))) else: var.set(round(float(v), 2)) scale._label.config(text=fmt % var.get()) if scale._cmd: scale._cmd(v) def refresh_preview(self): try: if self.cfg.get("show_preview", True): frame = grab_window(self.cfg) vis = frame.copy() xm, ym = get_margins(self.cfg) gs = self.cfg["gem_size"] bw, bh = self.cfg["board_w"], self.cfg["board_h"] for x in range(bw + 1): px = xm + x * gs cv2.line(vis, (px, ym), (px, ym + bh * gs), (255, 255, 255), 1) for y in range(bh + 1): py = ym + y * gs cv2.line(vis, (xm, py), (xm + bw * gs, py), (255, 255, 255), 1) if self.last_try is not None: x1, y1, x2, y2 = self.last_try color = (0, 230, 0) if self.last_good else (0, 0, 230) for (cx, cy) in ((x1, y1), (x2, y2)): px, py = xm + cx * gs, ym + cy * gs cv2.rectangle(vis, (px + 1, py + 1), (px + gs - 2, py + gs - 2), color, 3) self.photo = ImageTk.PhotoImage( Image.fromarray(cv2.cvtColor(vis, cv2.COLOR_BGR2RGB))) self.canvas.delete("all") self.canvas.create_image(0, 0, anchor="nw", image=self.photo) except Exception as e: self.log(f"[预览错误] {e}") self.root.after(200, self.refresh_preview) # ★ 60ms → 200ms def calibrate(self): self.log("1 秒内把鼠标移到游戏棋盘【左上角第一格的中心】...") self.root.update() pos = pyautogui.position() gs = self.cfg["gem_size"] xm, ym = get_margins(self.cfg) self.cfg["window_left"] = pos.x - xm - gs // 2 self.cfg["window_top"] = pos.y - ym - gs // 2 self.var_x.set(self.cfg["window_left"]) self.var_y.set(self.cfg["window_top"]) self.log(f"校准完成:窗口左上角 = ({self.cfg['window_left']}, {self.cfg['window_top']})") def reset_pos(self): self.var_x.set(0) self.var_y.set(0) self.cfg["window_left"] = 0 self.cfg["window_top"] = 0 def save(self): save_config(self.cfg) self.log("配置已保存") def toggle_run(self): if self.running: self._stop_run() else: self._start_run() # ================= 暴力主循环(真零延迟) ================= def worker(self): pairs = all_adjacent_pairs(self.cfg["board_w"], self.cfg["board_h"]) n = len(pairs) bad_moves = {} idx = 0 total_tried = 0 total_good = 0 total_bad = 0 lap = 0 # ★ 预计算所有格子中心坐标,省去循环内重复计算 centers = {} for (x1, y1, x2, y2) in pairs: centers[(x1, y1)] = cell_center(self.cfg, x1, y1) centers[(x2, y2)] = cell_center(self.cfg, x2, y2) while self.running: try: now = time.time() # 黑名单老化 if len(bad_moves) > n * 0.6: old = now - self.cfg["bad_move_cooldown"] * 0.6 bad_moves = {k: v for k, v in bad_moves.items() if v > old} # ★ 每圈结束:不清空黑名单,只老化,保证下一圈全试 if idx >= n: idx = 0 lap += 1 self.log(f"[第 {lap} 圈] 尝试 {total_tried} | " f"合法 {total_good} | 被弹回 {total_bad}") # 不再 sleep,纯忙循环 x1, y1, x2, y2 = pairs[idx] key = (x1, y1, x2, y2) idx += 1 # if key in bad_moves: # continue # 1) 只截两格(关键提速) c1 = grab_cell(self.cfg, x1, y1) c2 = grab_cell(self.cfg, x2, y2) h1a = cell_phash_from_crop(c1) h1b = cell_phash_from_crop(c2) # 2) 交换(零延迟) p1 = centers[(x1, y1)] p2 = centers[(x2, y2)] pyautogui.moveTo(p1[0], p1[1], duration=0) pyautogui.mouseDown() pyautogui.moveTo(p2[0], p2[1], duration=0) pyautogui.mouseUp() # 3) 立刻再截两格判定 c1b = grab_cell(self.cfg, x1, y1) c2b = grab_cell(self.cfg, x2, y2) h2a = cell_phash_from_crop(c1b) h2b = cell_phash_from_crop(c2b) diff = cell_diff(h1a, h2a) + cell_diff(h1b, h2b) total_tried += 1 good = diff >= self.cfg["hash_threshold"] self.last_try = (x1, y1, x2, y2) self.last_good = good if good: total_good += 1 self.log(f" ✓ ({x1},{y1})<->({x2},{y2}) 累计 {total_good}") else: total_bad += 1 # bad_moves[key] = now + self.cfg["bad_move_cooldown"] if total_tried % 50 == 0: self.log(f"[统计] 尝试 {total_tried} | 合法 {total_good} | " f"弹回 {total_bad} | 有效率 {total_good/total_tried*100:.0f}%") except Exception as e: self.log(f"[异常] {e}") # ★ 日志批量写,避免 Tk 锁竞争 def log(self, msg): with self._log_lock: self._log_buf.append(msg) def _flush_log_periodic(self): try: with self._log_lock: if self._log_buf: lines = self._log_buf[:] self._log_buf.clear() self.txt_log.configure(state="normal") for m in lines: self.txt_log.insert("end", m + "\n") self.txt_log.see("end") self.txt_log.configure(state="disabled") except Exception: pass self.root.after(200, self._flush_log_periodic) def quit(self): self.running = False if HAS_KEYBOARD: try: keyboard.remove_all_hotkeys() except Exception: pass self.root.after(200, self.root.destroy) # ===================== main ===================== def main(): print("=" * 64) print("Bejeweled 3 经典模式 —— 穷举暴力试错版 v4(真零延迟)") print(" mss 截屏:", "已启用" if HAS_MSS else "未安装(pip install mss 可再快 5~10 倍)") print(" 热键:F1 = 开始 F2 = 停止") print("=" * 64) cfg = load_config() root = tk.Tk() AutoGUI(root, cfg) root.mainloop() if __name__ == "__main__": main()

python-宝石迷阵3(CHS 中文版)-Bejeweled 3 经典模式游戏自动化脚本源代码.zip
https://download.csdn.net/download/qq_32257509/93805601

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/6 1:38:31

如何用 iloader 免费给 iPhone 侧载 App:10 分钟新手完整教程

如何用 iloader 免费给 iPhone 侧载 App&#xff1a;10 分钟新手完整教程 【免费下载链接】iloader User friendly sideloader 项目地址: https://gitcode.com/GitHub_Trending/iloa/iloader iloader 是一款免费的 iOS 侧载工具&#xff1a;不用越狱&#xff0c;就能把 …

作者头像 李华
网站建设 2026/11/6 1:34:31

HTML 嵌入交互式地图免 API Key 方案实测:iframe vs JS API 选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华