本篇教程由作者设定使用 CC BY-NC 协议。

写字台可以创建一个迷你小游戏,通过完成这个小游戏,你可以获得蜂窝和特定杂交种类的笔记。

那么是否有一种方式可以自动进行这个游戏呢?答案是肯定的,我们可以使用 Python 中的 PyAutoGUI 来做到这点。

import tkinter as tk
from tkinter import filedialog
from threading import Thread
from time import sleep
import queue
import pyautogui
import json

def threaded_function(log_queue):
    '''主要的逻辑代码'''
    loopCount = 1
    delay=0.1
    start = tuple(int(i) for i in bee_entry.get().split(",") if i) # if i 可以移除元组中的空元素
    points = []
    for point in points_text.get("1.0", tk.END).split("\n"):
        temp = tuple(int(i) for i in point.split(",") if i)
        if temp:
            points.append(temp)                                    # 但是 if i 移除不了空元祖 

    def my_click(point: tuple):
        '''
        传入点的元祖,移动并点击。\n
        pyautogui 的 click 方法加上坐标会造成前一步的物品拿在手上,无法使用。
        '''
        pyautogui.moveTo(point[0], point[1])
        sleep(delay)
        pyautogui.click()
        sleep(delay)

    def whether_lose(point: tuple) -> bool:
        '''传入点的元组并判定是否为“×”,传出 True 即为失败。'''
        pyautogui.moveTo(1, 1) # 鼠标复位以防止遮挡到“×”
        sleep(delay)
        pix = pyautogui.pixel(point[0], point[1])
        if pix == (255, 73, 64):
            return(True)
        else:
            return(False)
        
    while True:
        my_click(start)
        my_click(start)
        # 重复两次以重新放蜜蜂
        loseBool = False
        for i in range(len(points)):
            my_click(points[i])
            if i % 2 == 1:
                lose = whether_lose(points[i])
                if lose:
                    loseBool = True
                    break
        if loseBool:
            log_queue.put(f"第 {loopCount} 次操作 失败。\n")
        else:
            log_queue.put(f"第 {loopCount} 次操作 成功。\n")
        loopCount+=1

def save():
    saveList = [bee_entry.get(), points_text.get("1.0", tk.END)]
    file = filedialog.asksaveasfilename(
        defaultextension=".json",
        filetypes=[("Text files", "*.json"), ("All files", "*.*")],
        title="Save As"
        )
    with open(file, "w", encoding='utf-8') as f:
        json.dump(saveList, f, ensure_ascii=False, indent=4)

def load():
    file_path = filedialog.askopenfilename(
        defaultextension=".json",
        filetypes=[("Text files", "*.json"), ("All files", "*.*")],
        title="Open File"
    )

    if file_path:
        # 使用open函数打开文件并指定编码方式为UTF-8
        with open(file_path, "r", encoding="utf-8") as file:
            loadList = json.load(file)
        bee_entry.delete(0, tk.END)
        bee_entry.insert(tk.END, loadList[0])
        points_text.delete(1.0, tk.END)
        points_text.insert(tk.END, loadList[1])

def update_log(log_queue):
    try:
        while True:  # 尝试读取队列中的所有消息
            log_message = log_queue.get_nowait()  # 非阻塞读取队列
            log_text.insert(tk.END, log_message)  # 更新文本框
            log_text.see(tk.END)  # 滚动到文本框的底部
    except queue.Empty:
        pass  # 如果队列为空,什么也不做
    root.after(300, lambda: update_log(log_queue))  # 每 300ms 调用一次自身以检查新消息

def start_thread(log_queue):
    log_text.delete(1.0, tk.END)
    t = Thread(target=threaded_function, args=(log_queue,))
    t.daemon = True  # 设置为守护线程,确保它会在主程序退出时结束
    t.start()
    update_log(log_queue)  # 开始更新日志的循环

root = tk.Tk()
root.title("林业写字台自动化")
root.geometry("600x400")
root.attributes('-topmost', 1) # 窗口置顶
root.resizable(width=False, height=False)

def font(size):
    return(('微软雅黑', size))

log_text = tk.Text(root, font=font(10))
log_text.place(anchor="center", x=450, y=200, width=300, height=400)
log_text.insert(tk.END, "这个脚本用于林业写字台的自动化。请以管理员方式运行。\n右边的这个窗口为日志。\n"+
                "左边上面填入蜜蜂放置点的坐标(例:700,445)\n左边下面填要点击的点的坐标。"+
                "\n注意:坐标只需要给出 x 逗号 y 的形式。要点击的点的坐标在错误时为 FF3333(255, 73, 64) 的红色。\n"+
                "左边下面用于读取以前保存的坐标,右边则是加载以前的坐标。没有做停止的代码,要停止的话可以把鼠标往右上角移动,触及屏幕边界时 PyAutoGUI 会自动以报错形式停止。\n"+
                "启动时把蜜蜂先放进写字台,然后填完所有必要的信息再启动即可。")

entry_frame = tk.Frame(root)
entry_frame.place(width=300, height=50)
bee_lable = tk.Label(entry_frame, text="蜜蜂放置点", font=font(13))
bee_lable.grid(row=0, column=0)
bee_entry = tk.Entry(entry_frame)
bee_entry.grid(row=0, column=1)

points_text = tk.Text(root)
points_text.place(anchor="center", x=150, y=225, width=300, height=350)

load_button = tk.Button(root, text="读取", command=load)
load_button.place(anchor="center", x=50, y=350, width=100, height=100)
log_queue = queue.Queue()  # 创建一个队列用于线程间通信
start_button = tk.Button(root, text="开始", command=lambda: start_thread(log_queue))
start_button.place(anchor="center", x=150, y=350, width=100, height=100)
save_button = tk.Button(root, text="保存", command=save)
save_button.place(anchor="center", x=250, y=350, width=100, height=100)

root.mainloop()

这是一个 GUI 程序,请确保安装 python 时安装了 Tkinter,并使用 pip install pyautogui 安装 PyAutoGUI,然后以管理员身份运行脚本(非管理员启动则可能无法在以管理员权限启动的游戏中点击)即可。

基于林业写字台和 Python 的前期自动获取蜂窝方案-第1张图片效果如上,写字台的产物可以使用管道输出。


给学过组合数且乐于思考的人留下的问题:假设共需翻牌2n次,n组蜜蜂中无重复的,请问一次翻牌成功的概率是多少?