├── .gitignore ├── .goreleaser.yml ├── README.md ├── adb.go ├── adb.png ├── app.go ├── cmd └── main.go ├── game.png └── location.go /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - main: cmd/main.go 3 | binary: jump 4 | goos: 5 | - windows 6 | - darwin 7 | - linux 8 | goarch: 9 | - amd64 10 | # Archive customization 11 | archive: 12 | format: zip 13 | replacements: 14 | amd64: 64-bit 15 | darwin: macOS 16 | linux: linux 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 全自动玩微信跳一跳 2 | Golang实现的自动玩微信跳一跳 3 | 4 | 5 | 6 | #### 准备条件 7 | - 需要安装adb驱动, 这里有一篇国外作者的[教程](https://www.xda-developers.com/install-adb-windows-macos-linux/) 8 | - 手机连接电脑后,进入设置-开发者选项-打开usb调试 9 | 10 | 准备就绪后,在终端输入`adb devices`, 如果可以看到对应设备,表示adb驱动已经安装配置完毕 11 | 12 | 13 | #### 如何安装使用 14 | - 安装方法一: 15 | 16 | 一键下载安装,无需搭建环境,傻瓜化使用方法 17 | 请在[release](https://github.com/sundy-li/wechat_autojump_game/releases) 页面下载对应操作系统的二进制压缩包,解压后,执行jump文件即可 18 | 19 | ``` 20 | $ ./jump 21 | ``` 22 | 23 | - 安装方法二: 24 | 25 | 手动安装,开发调试 26 | ``` 27 | $ go get -u github.com/sundy-li/wechat_autojump_game 28 | $ cd $GOPATH/src/github.com/sundy-li/wechat_autojump_game/cmd 29 | $ go run main.go 30 | ``` 31 | 32 | 33 | #### 参数 34 | - `./jump -s 1.392`, 参数`s`表示距离速度值,不同分辨率可以适当调节 35 | - `./jump -m 2000`, 参数 `m` 表示休眠时间毫秒值 36 | 37 | #### 原理 38 | - 利用adb shell截图游戏屏幕 39 | - 读取截屏图片,获取当前位置,下一跳位置,计算跳动距离和触屏时间 40 | - 利用adb shell发送input swipe事件来跳跃 41 | 42 | #### 展示 43 | 实验结果:![game](game.png) 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /adb.go: -------------------------------------------------------------------------------- 1 | package wechat_autojump_game 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "os/exec" 9 | "strings" 10 | ) 11 | 12 | var ( 13 | SCREEN_WIDTH = 1080 14 | SCREEN_HEIGHT = 1920 15 | //这个系数好难调,好像不同机型不一样... 16 | Speed float64 = 1.392 17 | ) 18 | 19 | func jump(distance float64) { 20 | pressTime := distance * Speed 21 | x, y := randomPosition() 22 | runAdb("shell", fmt.Sprintf("input swipe %d %d %d %d %d", x, y, x, y, int(pressTime))) 23 | } 24 | 25 | func saveScreenShot(filename string) { 26 | filePath := "/sdcard/" + filename 27 | runAdb("shell", "screencap -p "+filePath) 28 | runAdb("pull", filePath, ".") 29 | } 30 | 31 | func runAdb(args ...string) { 32 | var b bytes.Buffer 33 | cmd := exec.Command("adb", args...) 34 | cmd.Stdout = &b 35 | cmd.Stderr = &b 36 | log.Printf("adb %s", strings.Join(args, " ")) 37 | err := cmd.Run() 38 | if cmd.Process != nil { 39 | cmd.Process.Kill() 40 | } 41 | if err != nil { 42 | log.Fatalf("adb %s: %v", strings.Join(args, " "), err.Error()) 43 | } 44 | } 45 | 46 | //x : 350 - 450 47 | //y : 850 - 950 48 | func randomPosition() (x int, y int) { 49 | x = 350 + rand.Intn(100) 50 | y = 850 + rand.Intn(100) 51 | return 52 | } 53 | -------------------------------------------------------------------------------- /adb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundy-li/wechat_autojump_game/0f6e69e5af6509fcbc8899803da69ca224f0c4b0/adb.png -------------------------------------------------------------------------------- /app.go: -------------------------------------------------------------------------------- 1 | package wechat_autojump_game 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "time" 9 | 10 | "image/png" 11 | ) 12 | 13 | var ( 14 | filename = "wechat_jump.png" 15 | 16 | SleepMills time.Duration = 1000 17 | ) 18 | 19 | func Run() { 20 | // filename = "tmp_jump3.png" 21 | // currentX, currentY, nextX, nextY := getLocation(filename) 22 | // println(currentX, currentY, nextX, nextY) 23 | // return 24 | for true { 25 | time.Sleep(SleepMills * time.Millisecond) 26 | saveScreenShot(filename) 27 | currentX, currentY, nextX, nextY := getLocation(filename) 28 | if currentX == 0 || currentY == 0 { 29 | continue 30 | } 31 | distance := getDistance(currentX, currentY, nextX, nextY) 32 | jump(distance) 33 | } 34 | } 35 | 36 | func getLocation(name string) (x, y, nextX, nextY int) { 37 | f1, err := os.Open(name) 38 | if err != nil { 39 | panic(err) 40 | } 41 | defer f1.Close() 42 | data, _ := ioutil.ReadAll(f1) 43 | img, _ := png.Decode(bytes.NewReader(data)) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | location := NewLocation(img) 48 | x, y, nextX, nextY = location.judgeByRGBRange() 49 | return 50 | } 51 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "time" 7 | 8 | g "github.com/sundy-li/wechat_autojump_game" 9 | ) 10 | 11 | var ( 12 | mills int64 13 | speed float64 14 | ) 15 | 16 | func init() { 17 | flag.Int64Var(&mills, "m", 0, "millseconds sleep after each jump") 18 | flag.Float64Var(&speed, "s", 0, fmt.Sprintf("speed value, default to , %.4f", g.Speed)) 19 | flag.Parse() 20 | } 21 | func main() { 22 | if mills != 0 { 23 | g.SleepMills = time.Duration(mills) 24 | } 25 | if speed != 0 { 26 | g.Speed = speed 27 | } 28 | g.Run() 29 | } 30 | -------------------------------------------------------------------------------- /game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sundy-li/wechat_autojump_game/0f6e69e5af6509fcbc8899803da69ca224f0c4b0/game.png -------------------------------------------------------------------------------- /location.go: -------------------------------------------------------------------------------- 1 | package wechat_autojump_game 2 | 3 | import ( 4 | "image" 5 | "math" 6 | ) 7 | 8 | type location struct { 9 | img image.Image 10 | } 11 | 12 | func NewLocation(img image.Image) *location { 13 | return &location{img: img} 14 | } 15 | 16 | var ( 17 | //相邻平台斜率 18 | grade float64 = 0.575179 19 | ) 20 | 21 | //根据阈值找到中心点 22 | func (l *location) judgeByRGBRange() (currentX, currentY, nextX, nextY int) { 23 | bounds := l.img.Bounds() 24 | maxY := 0 25 | minX := 100000 26 | // r: 38-65 , G: 38-53 , B:70-91 27 | for y := bounds.Max.Y - 320; y >= 320; y-- { 28 | for x := bounds.Min.X; x < bounds.Max.X; x++ { 29 | r, g, b, _ := l.img.At(x, y).RGBA() 30 | if rgbInterval(r, g, b, 38, 65, 38, 53, 70, 91) { 31 | maxY = max(maxY, y) 32 | minX = min(minX, x) 33 | } 34 | } 35 | } 36 | currentY = maxY - 22 37 | if currentY == 0 { 38 | return 39 | } 40 | currentX = minX + 35 41 | 42 | for y := 320; y < currentY; y++ { 43 | r, g, b, _ := l.img.At(0, y).RGBA() 44 | r >>= 8 45 | g >>= 8 46 | b >>= 8 47 | if nextX != 0 { 48 | break 49 | } 50 | for x := bounds.Min.X; x < bounds.Max.X; x++ { 51 | //需要排除在棋子X范围内的像素点 52 | if x >= currentX-40 && x <= currentX+40 { 53 | continue 54 | } 55 | r1, g1, b1, _ := l.img.At(x, y).RGBA() 56 | r1 >>= 8 57 | g1 >>= 8 58 | b1 >>= 8 59 | if colorDiff(int(r), int(g), int(b), int(r1), int(g1), int(b1)) >= 30 { 60 | nextX = x 61 | break 62 | } 63 | } 64 | } 65 | //get next Y 66 | // r, g, b, _ := l.img.At(nextX, nextMinY).RGBA() 67 | // for y := nextMinY; y < currentY; y-- { 68 | // r1, g1, b1, _ := l.img.At(nextX, y).RGBA() 69 | // r1 >>= 8 70 | // g1 >>= 8 71 | // b1 >>= 8 72 | // if colorDiff(int(r), int(g), int(b), int(r1), int(g1), int(b1)) >= 10 { 73 | // break 74 | // } 75 | // } 76 | nextY = currentY - int(math.Abs(float64(nextX-currentX)*grade)) 77 | return 78 | } 79 | 80 | func getDistance(currentX, currentY, nextX, nextY int) float64 { 81 | return math.Sqrt(math.Pow(float64(currentX-nextX), 2) + math.Pow(float64(currentY-nextY), 2)) 82 | } 83 | 84 | func colorDiff(r, g, b, r1, g1, b1 int) float64 { 85 | return math.Sqrt(math.Pow(float64(r1-r), 2) + math.Pow(float64(g1-g), 2) + math.Pow(float64(b1-b), 2)) 86 | } 87 | 88 | func rgbInterval(r, g, b uint32, rmin, rmax, gmin, gmax, bmin, bmax int) bool { 89 | return rmin <= int(r>>8) && int(r>>8) <= rmax && gmin <= int(g>>8) && int(g>>8) <= gmax && bmin <= int(b>>8) && int(b>>8) <= bmax 90 | } 91 | 92 | func min(a, b int) int { 93 | if a < b { 94 | return a 95 | } 96 | return b 97 | } 98 | 99 | func max(a, b int) int { 100 | if a > b { 101 | return a 102 | } 103 | return b 104 | } 105 | --------------------------------------------------------------------------------