📜 ⬆️ ⬇️

Python, under the pirate flag

image Yo-ho-ho, habrovchane!

While the IT community enthusiastically watches cryptocurrencies and their prey, I decided to remember what was lined up long before the crypt and everything connected with it became mainstream. Speech of course about gaming gold in MMO games.

Python 3.6 and the advice of fellow programmers helped me implement the idea. Although the article will be based on an example in a specific game, the goal is not to tell the hack story anymore, but to praise the python and show it to those who have not yet mastered what a non-programmer can do with it and why it is so cool .
')

Immediately worth some remarks:


Salt task


There is a game GuildWars2, it runs the Christmas event (mini game), which is very similar to the guitar hero with only a bell.

image

The meaning of the game is to timely press the buttons-notes 1-2-3-4 and 6-7-8-9, depending on which circle along which path comes to the center, where the character stands. If it is very interesting, then you can watch the video on YouTube by typing guild wars 2 choir bell.

For the full passage, albeit with inaccuracies, they give the maximum amount of valuable gifts that can be sold to the market at a price of about 5.5 silver per piece. I calculated that for the day of uninterrupted passage of this event, you can make ~ 3,300 gifts, and this is more than 180 pure gold, the price of which is 2 rubles per unit. on the black market. Penny in absolute terms, but very good compared to the same crypto mining, eh? Especially when you consider that for this we do not need an expensive video card or a paid account.

In general, my brooks of the ideological botovod combo and I decided to automate this action, if only from sports interest.

Step 0. Analyzing


For automation, we need only 2 things: recognize the pixels and press the buttons.
Here it is required to make a small digression and say that I am not being a programmer at all, I tried to build on c ++, #, PHP, delphi and even an assembler in masm32. The choice of python at this stage was almost random. I just thought, “Why not try to heap on python? Suddenly it will be more convenient? It was not a deliberate choice, then I did not even realize how cool the python was.

It was necessary to understand how to cling to the colors of the pixels and I began to look for a simple pipette that would show the color under the mouse pointer and its rgb value. From the game I recorded videos for analysis on the bandits, so Photoshop and image editors did not fit. Do not really cut the video into screenshots. I needed something simple, working in real time and showing the coordinates. Unfortunately, Google did not find a suitable recycle for me and I decided to concoct it myself. Here's what happened:

Pipette code hid under spoiler
from graphics import *#       ( ) import pyautogui #      (   ) import time #  . ( ) def main():#    win = GraphWin("pipetka", 200, 200, autoflush=True)#    200200     x, y = pyautogui.position()#  x, y   r, g, b = pyautogui.pixel(x, y)#   r, g, b  ColorDot = Circle(Point(100, 100), 25)#  ,   ColorDot.setFill(color_rgb(r, g, b))#        ColorDot.draw(win)#    win RGBtext = Entry(Point(win.getWidth()/2, 25), 10)#  RGB  RGBtext.draw(win)#    win RGBstring = Entry(Point(win.getWidth()/2, 45), 10)#    web  RGBstring.draw(win)#    win Coordstring = Entry(Point(win.getWidth() / 2, 185), 10)#    Coordstring.draw(win)#    win while True: #    time.sleep(0.1)#   0.1 ,       x, y = pyautogui.position()#  x, y   r, g, b = pyautogui.pixel(x, y)#   r, g, b  ColorDot.setFill(color_rgb(r, g, b))#  RGBtext.setText(pyautogui.pixel(x, y))# RGB RGBstring.setText(color_rgb(r, g, b))# web  Coordstring.setText(str(x)+" "+ str(y) )#  win.flush()#      #   . main()#  . 


image

Tulsa in action. I was digging with her and with regret found that my initial idea of ​​capturing pixels was already in the middle, a failure:

1) there is not enough time to determine the color and click

2) the colors of the circles are very heterogeneous.

3) minor deviations in the positioning of the camera strongly interfere

However, picking on a little bit more, I had the idea to capture not the pixels of the desired color but the brightness changes in the right places. experiments with a pipette showed that the circles are much higher on the R, G or B scale (depending on color) than the background of the playing field. In the end, I chose 8 points where the circles are in the largest size.

image

Step 1. Code recognition circle


Analyzer code
 import time#   (  ) import pyautogui#  (   ) import winsound#  ,  ,    (  ) import keyboard#     (   ) def analyzer(): etalon = [pyautogui.pixel(355, 288), pyautogui.pixel(460, 200), pyautogui.pixel(600, 130),\ pyautogui.pixel(735, 112), pyautogui.pixel(875, 109), pyautogui.pixel(1000, 145), \ pyautogui.pixel(1139, 203), pyautogui.pixel(1260, 290) ]#   trigger = [0,0,0,0,0,0,0,0]# ,   while True: change = [pyautogui.pixel(355, 288), pyautogui.pixel(460, 200), pyautogui.pixel(600, 130),\ pyautogui.pixel(735, 112), pyautogui.pixel(875, 109), pyautogui.pixel(1000, 145), \ pyautogui.pixel(1139, 203), pyautogui.pixel(1260, 290) ] #   change     for nomer in range(0,8): if change[nomer][0] > etalon[nomer][0]+50 or change[nomer][1] > etalon[nomer][1]+50 or change[nomer][2] > etalon[nomer][2]+50: if trigger[nomer] == 0: #     R,G  B    +50     trigger[nomer] = 1#   else: if trigger[nomer] == 1: #      trigger[nomer] = 0 #   print("push " +str(nomer) + time.strftime(' %X')) #   . keyboard.wait(combination="home")#     "Home" winsound.Beep(1000, 100) #    analyzer()#   


Parsing algorithm:

  1. Put the background color of the playing field into the etalon array, with which the colors of the array will be compared later.

     keyboard.wait(combination="home") 

    just needed to pick up the reference colors at the moment when the game is already deployed and the camera is centered
  2. Initialize the trigger trigger array. We need it, because the python works out many times while the circle runs to the center. Kruglyash big and the point at which we take readings is small. That is, in order that the same round we do not register twice or more.
  3. We start an infinite loop in which we constantly pick up the meaning of the colors in the change array and compare the colors of each of the collected points with the reference colors

     for nomer in range(0,8): if change[nomer][0] > etalon[nomer][0]+50 or change[nomer][1] > etalon[nomer][1]+50 or change[nomer][2] > etalon[nomer][2]+50: 
  4. If in the previous IF, we found out that the color of the point with the number nomer is brighter at least 50 units for any of the RGB values, then we check whether the trigger for this point is lit and if not, we light

      if trigger[nomer] == 0: trigger[nomer] = 1 
  5. If the color in the constantly updated change corresponds to the reference color, then there are two options. Either everything is quiet and the circle is not there yet, or the circle has just moved further from the point being checked, which is a signal for pressing the button. We are determined as a result of the state of the trigger. If the trigger was lit, then the roundabout left. Zero the trigger and conditionally press the button.

      if trigger[nomer] == 1: trigger[nomer] = 0 print("push " +str(nomer) + time.strftime(' %X')) 

Step 2. Run and test


It is difficult to track the correctness of work and debug in the game, so I recorded the game window on bandits again and tested it.

image


On the left - what gave the output of python, on the right - what I recorded, watching the video in slow motion. As you can see there are errors in the form of extra clicks 6 and 0 - these are damn snowflakes that can not be removed. The thing is that for the initial goal, the continuous collection of gifts, we do not need a 100% score, the game forgives the player quite a lot of mistakes. If this were not the case, then simply it would be necessary to introduce an additional check on the white color.

Step 3. Code the buttons through the streams.


There is actually nothing abstruse. We get the point number and after 2 seconds press the button. Why after 2 seconds? Because kruglyash from the moment when we spotted it reaches the middle in about 2 seconds. A little trick here is that you need to press the buttons independently of each other. We cannot pause the execution of the program by applying the classic sleep () . everything will get lost and in general kruglyashi fly fast enough. You can organize a queue or use threads, which I think is a more elegant solution (unless of course python and many threads do not slow down on your PC).

Add to code

 from threading import Timer 

and the delayed click handler itself

 def delaypress(keynum): if keynum < 4: keynum +=1 else: keynum +=2 t = Timer(2, keyboard.send, args=[str(keynum)]) t.start() 

If a 0-1-2-3 point number arrives at the input, press the point number + 1 after 2 seconds, or 4-5-6-7, then press the number + 2 after 2 seconds (since button 5 is not activated) in the mini game)

Final code view:
 import time#   (  ) import pyautogui#  (   ) import winsound#  ,  ,    (  ) import keyboard#     (   ) from threading import Timer#     (  ) def delaypress(keynum): if keynum < 4: keynum +=1 else: keynum +=2 t = Timer(2, keyboard.send, args=[str(keynum)]) t.start() def analyzer(): etalon = [pyautogui.pixel(355, 288), pyautogui.pixel(460, 200), pyautogui.pixel(600, 130),\ pyautogui.pixel(735, 112), pyautogui.pixel(875, 109), pyautogui.pixel(1000, 145), \ pyautogui.pixel(1139, 203), pyautogui.pixel(1260, 290) ]#   trigger = [0,0,0,0,0,0,0,0]# ,   while True: change = [pyautogui.pixel(355, 288), pyautogui.pixel(460, 200), pyautogui.pixel(600, 130),\ pyautogui.pixel(735, 112), pyautogui.pixel(875, 109), pyautogui.pixel(1000, 145), \ pyautogui.pixel(1139, 203), pyautogui.pixel(1260, 290) ] #   change     for nomer in range(0,8): if change[nomer][0] > etalon[nomer][0]+50 or change[nomer][1] > etalon[nomer][1]+50 or change[nomer][2] > etalon[nomer][2]+50: if trigger[nomer] == 0: #     R,G  B    +50     trigger[nomer] = 1#   else: if trigger[nomer] == 1: #      trigger[nomer] = 0 #   #print("push " +str(nomer) + time.strftime(' %X'))#     delaypress(nomer) #   . keyboard.wait(combination="home")#     "Home" winsound.Beep(1000, 100) #    analyzer()#   


Step 4. Row profit


image

Conclusion and links


And now, as promised, I praise the python (hopefully reasonably enough).


Pyautogui documentation
Graphics documentation
Keyboard module documentation

If there are interesting ideas for hacks on a python - write in a personal.

Source: https://habr.com/ru/post/346146/


All Articles