三国杀钓鱼自动化

时间:2024-10-25 08:38:45

三国杀钓鱼脚本

前言

本来是想做必杀的,但是后来测试了大约400钓发现纯靠连点没有漏掉的鱼,所以必杀功能就舍弃了。
我pyinstaller打包后运行.exe居然黑屏了???可能是多进程报错处理没写好,反正还是用vscode运行python吧。这样最起码可以保证不死机。

环境配置

python 3.12.7
Visual Studio Code 任何版本
雷电模拟器9
Win11 23H2

雷电模拟器安装

通过官网https://www.ldmnq.com下载。这个没啥好说的,没有网络问题,直接下一步下一步就行了。
说一下问题我运行vivo端的游戏商店或者三国杀时,发现不能使用。最后查阅信息有一个解决方法是将模拟器的手机型号设置为除了vivo以外的任意一款手机型号。

python 安装

通过官网https://www.python.org/downloads/release/python-3127找到下面Windows installer (64-bit)下载。
链接为https://www.python.org/ftp/python/3.12.7/python-3.12.7-amd64.exe,可以点击此处直接下载
之后,安装python,安装步骤这里不是重点。推荐一位bilibili的up主的视频,根据他的安装即可,后面会讲。

Visual Studio Code 安装

通过官网https://code.visualstudio.com找到Download for Windows下载即可。
同样安装步骤,后面会讲。

如何安装?

https://www.bilibili.com/video/BV1w7411N7Ti这个up主的BV1w7411N7Ti视频可以很好的讲解。记得Visual Studio Code 需要加插件,别忘了看文档安装一下。

脚本

脚本代码-python

这是一段python代码,复制到文本文件更改后缀名为py,即可运行。也可以继续观看B站up的视频稍微学习一点点怎么运行代码。

from PIL import ImageGrab
import multiprocessing
import pygetwindow
import pyautogui
import keyboard
import numpy
import json
import time

class CustomError(Exception):
    def __init__(self,message):
        self.message=message
class Fishing():
    standWidth=1962
    standHeight=1115
    def __init__(self,window):
        self.window= window 
        self.left=window.left
        self.top=window.top
        self.jsonDir="./position.json"
        self.config=None
        self.GameInit()
    def WindowJudge(self,window):
        return (window.width,window.height)==(Fishing.standWidth,Fishing.standHeight)
    def GameInit(self):
        if not self.WindowJudge(self.window):
            raise CustomError("window size is not match!")
        else:
            self.config=dict(json.loads(self.__read(self.jsonDir)))
    def Click(self,position):
        pyautogui.click(self.left+position[0],self.top+position[1])
    def DragTo(self,position,speed):
        pyautogui.dragTo(self.left+position[0],self.top+position[1],speed,button="left")
    def __read(self,file):
        f=open(file,'r',encoding="utf-8")
        res=f.read()
        f.close()
        return res
def StateFishing(CodeRun):
    while CodeRun.value:#不可更改!
        if keyboard.is_pressed('esc'):
            CodeRun.value=False
            exit(0)

def FishingRun(codeRun,runTime,slow,stink,again,queue,queueNumber):
    window = pygetwindow.getWindowsWithTitle('雷电模拟器')[0]
    fishingGame=Fishing(window)
    queueNumber.value=0
    queue.put((window.left,window.top))
    queue.put(fishingGame.config['slowColor'])
    queue.put(fishingGame.config['WatchRange'])
    time.sleep(0.1)
    queueNumber.value=1
    queue.put((window.left,window.top))
    queue.put(fishingGame.config['stinkColor'])
    queue.put(fishingGame.config['stinkOffset'])
    queue.put(fishingGame.config['StinkyRange'])
    time.sleep(0.1)
    queueNumber.value=2
    queue.put((window.left,window.top))
    queue.put(fishingGame.config['againColor'])
    queue.put(fishingGame.config['AgainRange'])
    time.sleep(0.1)
    queueNumber.value=3
    queue.put((window.left,window.top))
    queue.put(fishingGame.config['energyColor'])
    queue.put(fishingGame.config['EnergyRange'])
    time.sleep(0.1)
    while runTime>0 and codeRun.value:
        runTime-=1
        print("剩余%d次垂钓"%runTime)
        time.sleep(0.5)
        fishingGame.Click(fishingGame.config['beginButton'])
        time.sleep(0.5)
        fishingGame.DragTo(fishingGame.config['dragToPosition'],0.3)
        time.sleep(5.3)
        startTime=time.time()
        while not stink.value and codeRun:
            if time.time()-startTime>2:
                break
        fishingGame.Click(fishingGame.config['liftingPosition'])
        stink.value=False
        while codeRun.value:
            if slow.value :
                fishingGame.Click(fishingGame.config['fishingButton'])
            if again.value:
                fishingGame.Click(fishingGame.config['againButton'])
                break
    codeRun.value=False
    exit(0)
def WatchSpeed(codeRun,slow,queue,queueNumber):
    while queueNumber.value!=0:
        pass
    window=queue.get()
    slowColor=queue.get()
    watchRange=queue.get()
    left,top=watchRange["position1"]
    right,down=watchRange["position2"]
    while codeRun.value:
        screenshot =numpy.array( ImageGrab.grab(bbox=(window[0]+left, window[1]+top, window[0]+right, window[1]+down)))
        screenshotShape=screenshot.shape
        cnt=0
        for i in range(screenshotShape[0]):
            for j in range(screenshotShape[1]):
                temp=screenshot[i][j]
                if ((int(temp[0])-slowColor[0])**2+(int(temp[1])-slowColor[1])**2+(int(temp[2])-slowColor[2])**2)**0.5>5:
                    cnt+=1
        slow.value= cnt/(screenshotShape[0]*screenshotShape[1])<0.6
    exit(0)
def WatchStinky(codeRun,stink,queue,queueNumber):
    while queueNumber.value!=1:
        pass
    window=queue.get()
    stinkColor=queue.get()
    stinkOffset=queue.get()
    watchRange=queue.get()
    left,top=watchRange["position1"]
    right,down=watchRange["position2"]
    top+=stinkOffset
    down+=stinkOffset
    while codeRun.value:
        screenshot =numpy.array( ImageGrab.grab(bbox=(window[0]+left, window[1]+top, window[0]+right, window[1]+down)))
        screenshotShape=screenshot.shape
        cnt=0
        for i in range(screenshotShape[0]):
            for j in range(screenshotShape[1]):
                temp=screenshot[i][j]
                if ((int(temp[0])-stinkColor[0])**2+(int(temp[1])-stinkColor[1])**2+(int(temp[2])-stinkColor[2])**2)**0.5<50:
                    cnt+=1
        stink.value= cnt/(screenshotShape[0]*screenshotShape[1])>0.1
    exit(0)

def WatchAgain(codeRun,again,queue,queueNumber):
    while queueNumber.value!=2:
        pass
    window=queue.get()
    againColor=queue.get()
    watchRange=queue.get()
    left,top=watchRange["position1"]
    right,down=watchRange["position2"]
    while codeRun.value:
        screenshot =numpy.array( ImageGrab.grab(bbox=(window[0]+left, window[1]+top, window[0]+right, window[1]+down)))
        screenshotShape=screenshot.shape
        cnt=0
        for i in range(screenshotShape[0]):
            for j in range(screenshotShape[1]):
                temp=screenshot[i][j]
                if ((int(temp[0])-againColor[0])**2+(int(temp[1])-againColor[1])**2+(int(temp[2])-againColor[2])**2)**0.5<50:
                    cnt+=1
        again.value= cnt/(screenshotShape[0]*screenshotShape[1])>0.8
    exit(0)
def WatchEnergy(codeRun,energy,queue,queueNumber):
    while queueNumber.value!=3:
        pass
    window=queue.get()
    energyColor=queue.get()
    watchRange=queue.get()
    left,top=watchRange["position1"]
    right,down=watchRange["position2"]
    while codeRun.value:
        screenshot =numpy.array( ImageGrab.grab(bbox=(window[0]+left, window[1]+top, window[0]+right, window[1]+down)))
        screenshotShape=screenshot.shape
        cnt=0
        for i in range(screenshotShape[0]):
            for j in range(screenshotShape[1]):
                temp=screenshot[i][j]
                if ((int(temp[0])-energyColor[0])**2+(int(temp[1])-energyColor[1])**2+(int(temp[2])-energyColor[2])**2)**0.5<50:
                    cnt+=1
        energy.value= cnt/(screenshotShape[0]*screenshotShape[1])>0.8
if __name__ == '__main__':
    runTime=999# 运行次数
    codeRun = multiprocessing.Value('b', True)
    queueNumber = multiprocessing.Value('i', -1)
    slow = multiprocessing.Value('b', True)
    stink= multiprocessing.Value('b', True)
    again= multiprocessing.Value('b', False)
    queue=multiprocessing.Queue(20)
    processList=[]
    processList.append(multiprocessing.Process(target=StateFishing, args=(codeRun,)))
    processList.append(multiprocessing.Process(target=FishingRun, args=(codeRun,runTime,slow,stink,again,queue,queueNumber)))
    processList.append(multiprocessing.Process(target=WatchSpeed,args=(codeRun,slow,queue,queueNumber)))
    processList.append(multiprocessing.Process(target=WatchStinky,args=(codeRun,stink,queue,queueNumber)))
    processList.append(multiprocessing.Process(target=WatchAgain,args=(codeRun,again,queue,queueNumber)))
    for each in processList:
        each.start()
    for each in processList:
        each.join()

脚本代码-json

此处需要创建在你的上述python脚本文件的同一目录下,命名为position.json,注意扩展名为json。此处由于电脑性能不同需要手动调节一下stinkOffset,负号代表检测区间向上方偏移像素。由于本脚本设计原理是通过监控并对进程通信实现的,所以由于电脑性能问题需要平移一定距离以留充足反应。如果刺鱼点太靠上,数值应当由-32适当调大,如果太靠下应当由-32适当调小。防呆小tips:-40比-32小。

{
    "stinkOffset":-32,
    "beginButton":[1625,880],
    "dragToPosition":[1625,630],
    "liftingPosition":[1625,880],
    "fishingButton":[1625,880],
    "againButton":[1420,950],
    "WatchRange":{
        "position1":[1195,135],
        "position2":[1305,140]
    },
    "AgainRange":{
        "position1":[1250,920],
        "position2":[1290,960]
    },
    "StinkyRange":{
        "position1":[626,530],
        "position2":[629,600]
    },
    "EnergyRange":
    {
        "position1":[1366,189],
        "position2":[1367,209]
    },
    "stinkColor":[239,199,107],
    "slowColor":[99,77,66],
    "againColor":[255, 199, 99],
    "energyColor":[255,170,90]
}

安装库

复制下面的命令,在命令框中输入即可自动安装清华镜像下的依赖库。如果不会安装,还是去看一下上文B站up的视频,会有讲解pip指令的教程,这里也只是需要知道在哪里输入就可以了。或者去搜一下别人的文档,只要能确定在那里输入就可以。

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple keyboard pillow pyautogui pygetwindow numpy

说明

  1. 脚本是针对雷电模拟器做的适配,因此最好不要换模拟器,而且甚至版本都不要变。
  2. 窗口分辨率设置为平板版 > 1920x1080(dpi 280)。代码中做了窗口检测,而且需要做到像素匹配。这里代码中像素匹配是1962x1115,设置分辨率后应该就是这个尺寸,不要缩放,如果缩放了,改成其他分辨率后再改回来。
  3. 再runTime中输入此次运行次数,如果不写就会按照我的代码默认参数999次去运行下去了。且换鱼饵是要重开的
  4. 脚本是模拟鼠标操作,所以不要让任何可能影响点击的操作置于模拟器前,运行脚本时模拟器必须处于显示的状态,而不是最小化。
  5. 可以按下esc退出,如果前10秒左右按