朋友圈曬跳一跳成績(jī)好久了,今天無意中看到以前一個(gè)同事小妞曬用代碼刷分的視頻,百度了一下果然看到了代碼(代碼在最后),幾經(jīng)波折,終于成功運(yùn)行,刷了一點(diǎn)分?jǐn)?shù)。
首先大概說一下步驟:
1.百度下載刷分代碼
2.安裝adb
3.找個(gè)手機(jī)使用USB調(diào)試模式連接電腦
4.啟動(dòng)跳一跳微信小程序
5.在eclipse中運(yùn)行代碼(此處要不斷調(diào)試根據(jù)手機(jī)屏幕大小修改參數(shù))
結(jié)果就是你的手機(jī)屏幕會(huì)自動(dòng)按壓然后讓棋子跳。
再說下問題:
一、安裝adb問題集:
下載adb工具地址
在此處的設(shè)備管理器中,如果沒有安裝在其他設(shè)備這里,adb就是一個(gè)感嘆號(hào),安裝完畢后如圖所示,會(huì)出現(xiàn)android device 這一行
安裝的話在adb一欄里右擊選擇屬性,彈出如下界面,點(diǎn)擊更新驅(qū)動(dòng)程序,選擇瀏覽計(jì)算機(jī)選擇程序(也就是第二個(gè)選項(xiàng)),此時(shí)會(huì)彈出一個(gè)瀏覽計(jì)算機(jī)上的驅(qū)動(dòng)程序選項(xiàng),選擇安裝包所在地,然后一切放行,就能安裝。
問題來了:
安裝完后,你在cmd命令窗口下能使用adb,但是在eclipse中運(yùn)行代碼完全沒有效果(程序不報(bào)錯(cuò),手機(jī)里也沒有截圖),然后eclipse控制臺(tái)就顯示圖片不存在,
此時(shí)需要把你安裝好的一個(gè)exe程序,兩個(gè)動(dòng)態(tài)鏈接庫dll拷貝到如下兩個(gè)目錄下:(找不到就在c盤全局搜一下)
adb.exe
AdbWinApi.dll
AdbWinUsbApi.dll
C:\Windows\System32
C:\Windows\SysWOW64
此時(shí)一定要放在SysWOW64下,我是win7 64位,所以有這個(gè)目錄(網(wǎng)上其他人說win32就不需要放這個(gè)了,我沒試過),
如果沒有放SysWOW64目錄,此時(shí)eclipse運(yùn)行依舊沒有效果:但如果你在cmd中運(yùn)行adb shell screencap -p /sdcard/tencent/customerpic/current.png這條命令,發(fā)現(xiàn)手機(jī)里面會(huì)有current.png圖片,這說明eclipse沒有找到對(duì)應(yīng)的adb工具。
我找出這個(gè)問題是通過cd到System32和安裝目錄(C:\Program Files (x86)\Thunder Network\Thunder\Program)下,我在Program中運(yùn)行上述命令成功,在System32中運(yùn)行報(bào)錯(cuò):
---------------------------adb.exe - 系統(tǒng)錯(cuò)誤---------------------------
無法啟動(dòng)此程序,因?yàn)橛?jì)算機(jī)中丟失 AdbWinApi.DLL。嘗試重新安裝該程序以解決此問題。
---------------------------
確定
---------------------------
OK,這說明System32中的adb找不到AdbWinApi.Dll動(dòng)態(tài)鏈接文件,但明明就有,機(jī)緣巧合之下,老夫看到了SysWOW64這個(gè)目錄,然后百度這個(gè)目錄是什么意思,什么作用,OK,將拷貝到System32目錄下的三個(gè)文件再次拷貝一份到此SysWOW64目錄下,搞定。
問題二:device offline
在cmd命令窗口運(yùn)行adb shell結(jié)果報(bào)錯(cuò)device offline,我以為是我adb安裝有問題,百度了一大堆,試過adb kill server,adb remount等命令都沒用,后來換了發(fā)現(xiàn)代碼里面是/sdcard/,一看這應(yīng)該是外置SD卡吧,是不是路徑不對(duì),(我用的是vivo x9,這手機(jī)沒有外置SD卡選項(xiàng)),換了舊一點(diǎn)的手機(jī)(vivo y27)后運(yùn)行adb shell可以了,不過/sdcard不是外置SD卡路徑,而是手機(jī)U盤路徑。
那么就說明不應(yīng)該是代碼路徑問題,又是百度了一番,被告知是adb工具太老,adb version得到版本 1.0.26,好吧,我也懶得去找新版adb,用老的vivo y27調(diào)試了下能刷就行。
列舉一下我學(xué)到的:
1.知道有adb這東西,也知道使用adb shell可以得到手機(jī)的bash會(huì)話,可以截圖,使用adb pull可以從手機(jī)里面得到文件,更多命令的話官網(wǎng)有,我看多了也記不住。
2.知道原來代碼里面java使用Runtime.getRuntime().exec()可以在windows中調(diào)用系統(tǒng)命令:
1
2
3
4
5
6
|
process = Runtime.getRuntime().exec(command); System.out.println( "exec command start: " + command); process.waitFor(); process.getInputStream(); BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(process.getErrorStream())); String line = bufferedReader.readLine(); |
3.明白代碼中通過計(jì)算截圖的RGB顏色值等分析一張圖片int pixel = bufferedImage.getRGB(x, y);
全部代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
|
package com.lw.test; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; /** * 參考知乎 * * @link <a href="https://zhuanlan.zhihu.com/p/32452473" rel="external nofollow" target="_blank">https://zhuanlan.zhihu.com/p/32452473</a> * * 跳一跳輔助 * * @author LeeHo */ public class JumpJumpHelper { private static final String IMAGE_NAME = "current.png" ; private static final String STORE_DIR = "d:/jump_screencapture" ; //數(shù)量 private static final int imageLengthLength = 5 ; //存放圖片的大小 private static final long [] imageLength = new long [imageLengthLength]; private final RGBInfo rgbInfo = new RGBInfo(); private final String path = "/sdcard/tencent/customerpic/" ; private final String[] ADB_SCREEN_CAPTURE_CMDS = { "adb shell screencap -p " +path + IMAGE_NAME, "adb pull " +path+ "current.png " + STORE_DIR }; //截屏中游戲分?jǐn)?shù)顯示區(qū)域最下方的Y坐標(biāo),300是 1920x1080的值,根據(jù)實(shí)際情況修改 private final int gameScoreBottomY = 300 ; //按壓的時(shí)間系數(shù),可根據(jù)具體情況適當(dāng)調(diào)節(jié) private final double pressTimeCoefficient = 2.05 ; //按壓的起始點(diǎn)坐標(biāo),也是再來一局的起始點(diǎn)坐標(biāo) private final int swipeX = 280 ; private final int swipeY = 600 ; //二分之一的棋子底座高度 private final int halfBaseBoardHeight = 20 ; //棋子的寬度,從截屏中量取,自行調(diào)節(jié) private final int halmaBodyWidth = 74 ; //游戲截屏里的兩個(gè)跳板的中點(diǎn)坐標(biāo),主要用來計(jì)算角度,可依據(jù)實(shí)際的截屏計(jì)算,計(jì)算XY的比例 private final int boardX1 = 813 ; private final int boardY1 = 1122 ; private final int boardX2 = 310 ; private final int boardY2 = 813 ; /** * 獲取跳棋以及下一塊跳板的中心坐標(biāo) * * @return * @author LeeHo * @throws IOException * @update 2017年12月31日 下午12:18:22 */ private int [] getHalmaAndBoardXYValue(File currentImage) throws IOException { BufferedImage bufferedImage = ImageIO.read(currentImage); int width = bufferedImage.getWidth(); int height = bufferedImage.getHeight(); System.out.println( "寬度:" + width + ",高度:" + height); int halmaXSum = 0 ; int halmaXCount = 0 ; int halmaYMax = 0 ; int boardX = 0 ; int boardY = 0 ; //從截屏從上往下逐行遍歷像素點(diǎn),以棋子顏色作為位置識(shí)別的依據(jù),最終取出棋子顏色最低行所有像素點(diǎn)的平均值,即計(jì)算出棋子所在的坐標(biāo) for ( int y = gameScoreBottomY; y < height; y++) { for ( int x = 0 ; x < width; x++) { processRGBInfo(bufferedImage, x, y); int rValue = this .rgbInfo.getRValue(); int gValue = this .rgbInfo.getGValue(); int bValue = this .rgbInfo.getBValue(); //根據(jù)RGB的顏色來識(shí)別棋子的位置, if (rValue > 50 && rValue < 60 && gValue > 53 && gValue < 63 && bValue > 95 && bValue < 110 ) { halmaXSum += x; halmaXCount++; //棋子底行的Y坐標(biāo)值 halmaYMax = y > halmaYMax ? y : halmaYMax; } } } if (halmaXSum != 0 && halmaXCount != 0 ) { //棋子底行的X坐標(biāo)值 int halmaX = halmaXSum / halmaXCount; //上移棋子底盤高度的一半 int halmaY = halmaYMax - halfBaseBoardHeight; //從gameScoreBottomY開始 for ( int y = gameScoreBottomY; y < height; y++) { processRGBInfo(bufferedImage, 0 , y); int lastPixelR = this .rgbInfo.getRValue(); int lastPixelG = this .rgbInfo.getGValue(); int lastPixelB = this .rgbInfo.getBValue(); //只要計(jì)算出來的boardX的值大于0,就表示下個(gè)跳板的中心坐標(biāo)X值取到了。 if (boardX > 0 ) { break ; } int boardXSum = 0 ; int boardXCount = 0 ; for ( int x = 0 ; x < width; x++) { processRGBInfo(bufferedImage, x, y); int pixelR = this .rgbInfo.getRValue(); int pixelG = this .rgbInfo.getGValue(); int pixelB = this .rgbInfo.getBValue(); //處理棋子頭部比下一個(gè)跳板還高的情況 if (Math.abs(x - halmaX) < halmaBodyWidth) { continue ; } //從上往下逐行掃描至下一個(gè)跳板的頂點(diǎn)位置,下個(gè)跳板可能為圓形,也可能為方框,取多個(gè)點(diǎn),求平均值 if ((Math.abs(pixelR - lastPixelR) + Math.abs(pixelG - lastPixelG) + Math.abs(pixelB - lastPixelB)) > 10 ) { boardXSum += x; boardXCount++; } } if (boardXSum > 0 ) { boardX = boardXSum / boardXCount; } } //按實(shí)際的角度來算,找到接近下一個(gè) board 中心的坐標(biāo) boardY = ( int ) (halmaY - Math.abs(boardX - halmaX) * Math.abs(boardY1 - boardY2) / Math.abs(boardX1 - boardX2)); if (boardX > 0 && boardY > 0 ) { int [] result = new int [ 4 ]; //棋子的X坐標(biāo) result[ 0 ] = halmaX; //棋子的Y坐標(biāo) result[ 1 ] = halmaY; //下一塊跳板的X坐標(biāo) result[ 2 ] = boardX; //下一塊跳板的Y坐標(biāo) result[ 3 ] = boardY; return result; } } return null ; } /** * 執(zhí)行命令 * * @param command * @author LeeHo * @update 2017年12月31日 下午12:13:39 */ private void executeCommand(String command) { Process process = null ; try { process = Runtime.getRuntime().exec(command); System.out.println( "exec command start: " + command); process.waitFor(); process.getInputStream(); BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(process.getErrorStream())); String line = bufferedReader.readLine(); if (line != null ) { System.out.println(line); } bufferedReader = new BufferedReader( new InputStreamReader(process.getInputStream())); String line02 = bufferedReader.readLine(); if (line02 != null ) { System.out.println(line02); } System.out.println( "exec command end: " + command); } catch (Exception e) { e.printStackTrace(); } finally { if (process != null ) { process.destroy(); } } } /** * ADB獲取安卓截屏 * * @author LeeHo * @update 2017年12月31日 下午12:11:42 */ private void executeADBCaptureCommands() { for (String command : ADB_SCREEN_CAPTURE_CMDS) { executeCommand(command); } } /** * 跳一下 * * @param distance * @author LeeHo * @update 2017年12月31日 下午12:23:19 */ private void doJump( double distance) { System.out.println( "distance: " + distance); //計(jì)算按壓時(shí)間,最小200毫秒 int pressTime = ( int ) Math.max(distance * pressTimeCoefficient, 200 ); System.out.println( "pressTime: " + pressTime); //執(zhí)行按壓操作 String command = String.format( "adb shell input swipe %s %s %s %s %s" , swipeX, swipeY, swipeX, swipeY, pressTime); System.out.println(command); executeCommand(command); } /** * 再來一局 * * @author LeeHo * @update 2017年12月31日 下午12:47:06 */ private void replayGame() { String command = String.format( "adb shell input tap %s %s" , swipeX, swipeY); executeCommand(command); } /** * 計(jì)算跳躍的距離,也即兩個(gè)點(diǎn)之間的距離 * * @param halmaX * @param halmaY * @param boardX * @param boardY * @return * @author LeeHo * @update 2017年12月31日 下午12:27:30 */ private double computeJumpDistance( int halmaX, int halmaY, int boardX, int boardY) { return Math.sqrt(Math.pow(Math.abs(boardX - halmaX), 2 ) + Math.pow(Math.abs(boardY - halmaY), 2 )); } public static void main(String[] args) { JumpJumpHelper jumpjumpHelper = new JumpJumpHelper(); // String command = "adb shell screencap -p "+jumpjumpHelper.path + IMAGE_NAME; //// command = "adb devices"; // jumpjumpHelper.executeCommand(command); // // if(true){return ;} try { File storeDir = new File(STORE_DIR); if (!storeDir.exists()) { boolean flag = storeDir.mkdir(); if (!flag) { System.err.println( "創(chuàng)建圖片存儲(chǔ)目錄失敗" ); return ; } } //執(zhí)行次數(shù) int executeCount = 0 ; for (;;) { //執(zhí)行ADB命令,獲取安卓截屏 jumpjumpHelper.executeADBCaptureCommands(); File currentImage = new File(STORE_DIR, IMAGE_NAME); if (!currentImage.exists()) { System.out.println( "圖片不存在" ); continue ; } long length = currentImage.length(); imageLength[executeCount % imageLengthLength] = length; //查看是否需要重新開局 jumpjumpHelper.checkDoReplay(); executeCount++; System.out.println( "當(dāng)前第" + executeCount + "次執(zhí)行!" ); //獲取跳棋和底板的中心坐標(biāo) int [] result = jumpjumpHelper.getHalmaAndBoardXYValue(currentImage); if (result == null ) { System.out.println( "The result of method getHalmaAndBoardXYValue is null!" ); continue ; } int halmaX = result[ 0 ]; int halmaY = result[ 1 ]; int boardX = result[ 2 ]; int boardY = result[ 3 ]; System.out.println( "halmaX: " + halmaX + ", halmaY: " + halmaY + ", boardX: " + boardX + ", boardY: " + boardY); //計(jì)算跳躍的距離 double jumpDistance = jumpjumpHelper.computeJumpDistance(halmaX, halmaY, boardX, boardY); jumpjumpHelper.doJump(jumpDistance); //每次停留2.5秒 TimeUnit.MILLISECONDS.sleep( 2500 ); } } catch (Exception e) { e.printStackTrace(); } } /** * 檢查是否需要重新開局 * * @author LeeHo * @update 2017年12月31日 下午1:39:18 */ private void checkDoReplay() { if (imageLength[ 0 ] > 0 && imageLength[ 0 ] == imageLength[ 1 ] && imageLength[ 1 ] == imageLength[ 2 ] && imageLength[ 2 ] == imageLength[ 3 ] && imageLength[ 3 ] == imageLength[ 4 ]) { //此時(shí)表示已經(jīng)連續(xù)5次圖片大小一樣了,可知當(dāng)前屏幕處于再來一局 Arrays.fill(imageLength, 0 ); //模擬點(diǎn)擊再來一局按鈕重新開局 replayGame(); } } /** * 獲取指定坐標(biāo)的RGB值 * * @param bufferedImage * @param x * @param y * @author LeeHo * @update 2017年12月31日 下午12:12:43 */ private void processRGBInfo(BufferedImage bufferedImage, int x, int y) { this .rgbInfo.reset(); int pixel = bufferedImage.getRGB(x, y); //轉(zhuǎn)換為RGB數(shù)字 this .rgbInfo.setRValue((pixel & 0xff0000 ) >> 16 ); this .rgbInfo.setGValue((pixel & 0xff00 ) >> 8 ); this .rgbInfo.setBValue((pixel & 0xff )); } class RGBInfo { private int RValue; private int GValue; private int BValue; public int getRValue() { return RValue; } public void setRValue( int rValue) { RValue = rValue; } public int getGValue() { return GValue; } public void setGValue( int gValue) { GValue = gValue; } public int getBValue() { return BValue; } public void setBValue( int bValue) { BValue = bValue; } public void reset() { this .RValue = 0 ; this .GValue = 0 ; this .BValue = 0 ; } } } |
當(dāng)然,現(xiàn)在刷了一會(huì)就被清空成績(jī),但作為一個(gè)程序員知道還是好的。從一開始的post提交漏洞,讓電腦作為代理抓包修改數(shù)據(jù),現(xiàn)在代碼模擬點(diǎn)擊(雖然不會(huì)生效。)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://blog.csdn.net/lw18751836671/article/details/78981527