

Show HN: My latest pet project, pydroid - meese_
http://github.com/msanders/pydroid#readme

======
hugs
I love it, except for the project name. I was thinking it was going to be
another "Run Python on Android" project.

~~~
puffythefish
Yeah, I wasn't sure about the name. Any suggestions on an alternative?

~~~
mcantelon
AutoPy? A quick Google doesn't reveal other projects with this name.

~~~
meese_
Done.

Rather messy since github doesn't do redirects, but I suppose it's worth it.

By the way, to uninstall the apparently poorly-named pydroid:

    
    
      cd ${PYTHON_DIR}/site-packages/
      rm -r pydroid pydroid-0.42-*.egg-info
    

where ${PYTHON_DIR} is "/Library/Python/2.6" on OS X, "C:\Python26\" on
Windows, etc...

Then just run the new installer shown on the script page (or install from
source), now at:

<http://github.com/msanders/autopy>

~~~
ArcticCelt
autopy is really perfect. It's reminiscent of autopilot and much more
descriptive of what your tool does.

~~~
pizza
autopylot?

~~~
mcantelon
Taken, unfortunately (that was my first idea before AutoPy).

------
ZitchDog
I'm loving this library. I'm having a ton of fun messing around with it.

Here's a script to play basketball at <http://www.onlinegames.com/basketball>

I'm having trouble getting calculate_shot_pos right given the ball pos and
basketball pos. My trig is pretty rusty, so if anyone wants to help me out
(even with some strategy) I would love to hear it! I've learned the library,
so getting it to actually play the game well is just a bonus.

Anyway, here goes:

    
    
        from pydroid import mouse, bitmap
        from math import floor, sqrt
        import time
    
        rim_color = 15474
        ball_color = 10440243
        basket_pos = (143, 365)
    
        def is_ball_on_screen(screen):
            return len(screen.find_every_color(ball_color)) > 0 and find_ball_pos(screen)[0] > basket_pos[0]
    
        def is_basket_on_screen(screen):
            return screen.get_color(basket_pos[0], basket_pos[1]) == rim_color
    
        def average(xs):
            return int(sum(xs, 0.0) / len(xs))
    
        def find_ball_pos(screen): 
            coords = screen.find_every_color(ball_color)
            xs = [coord[0] for coord in coords]
            ys = [coord[1] for coord in coords]
            return (average(xs), average(ys))
    
        def calculate_shot_pos(basket, ball):
            x = basket[0] + (ball[0] - basket[0]) / 3.0
            y = basket[1] - (ball[1] - basket[1])
            return (int(x), int(y))
    
        while(True):
            screen = bitmap.capture_screen()
            if is_basket_on_screen(screen) and is_ball_on_screen(screen):
                ball_pos = find_ball_pos(screen)
                shot_pos = calculate_shot_pos(basket_pos, ball_pos)
                mouse.move(shot_pos[0], shot_pos[1])
                time.sleep(.5)
                mouse.click()
            time.sleep(1)

------
m0th87
Script to play Winterbells (<http://www.ferryhalim.com/orisinal/g3/bells.htm>)
based on pydroid:

    
    
        import pydroid
        
        INIT_COLOR = 473428
        BELL_COLORS = set([12702690, 8950940, 9016477, 9017249, 9018536, 12505567,
                           12505568, 11715538, 10862803, 12965603, 12439776, 12834019,
                           14411502, 8953000, 5401724])
        
        def find_init_color(screen, xr, yr):
            for x in xr:
                for y in yr:
                    if screen.get_color(x, y) == INIT_COLOR:
                        return (x, y)
        
        def init():
            while True:
                screen = pydroid.bitmap.capture_screen()
                first = find_init_color(screen, xrange(0, screen.width), xrange(0, screen.height))
                last = find_init_color(screen, xrange(screen.width - 1, -1, -1), xrange(screen.height - 1, -1, -1))
                
                if first is not None and last is not None:
                    return first, last
        
        def check(first, last):
            screen = pydroid.bitmap.capture_screen()
            
            for y in xrange(last[1] - 1, first[1], -1):
                for x in xrange(first[0] + 1, last[0]):
                    if screen.get_color(x, y) in BELL_COLORS:
                        return pydroid.mouse.move(x, y)
        
        def main():
            print 'Searching for Winterbells...'
            first, last = init()
            
            print 'Found at %s, %s. Running.' % (first, last)
            while True:
                check(first, last)
        
        if __name__ == '__main__':
            main()
    

It's far from perfect, but it's a good start. To run it, execute the script,
then open the game in a resolution large enough such that the whole game is
visible.

All it does is find pixels with colors unique to the bells and moves the mouse
there. I made the same script in C# a while ago. This script is faster (even
though it's Python!) and far more concise. It was astonishingly easy to make
too thanks to pydroid. The only time-consuming bit was finding the bell
colors. Kudos for the nice library!

~~~
meese_
Very cool!

~~~
m0th87
Significantly optimized:

    
    
        import pydroid, operator
        
        INIT_COLOR = 473428
        BELL_COLORS = set([12702690, 8950940, 9016477, 9017249, 9018536, 12505567,
                           12505568, 11715538, 10862803, 12965603, 12439776, 12834019,
                           14411502, 8953000, 5401724])
        CANVAS_WIDTH = 751
        CANVAS_HEIGHT = 501
        
        CHECK_WIDTH = CANVAS_WIDTH / 2 - 50
        
        def find_init_color(screen, xr, yr):
            for x in xr:
                for y in yr:
                    if screen.get_color(x, y) == INIT_COLOR:
                        return (x, y)
        
        def init():
            while True:
                screen = pydroid.bitmap.capture_screen()
                first = find_init_color(screen, xrange(0, screen.width), xrange(0, screen.height))
                last = find_init_color(screen, xrange(screen.width - 1, -1, -1), xrange(screen.height - 1, -1, -1))
                
                if first is not None and last is not None and last[0] - first[0] >= CANVAS_WIDTH and last[1] - first[1] >= CANVAS_HEIGHT:
                    return first, last
                
        def triangle(base, width, height, top_left, bottom_right):
            for i in xrange(0, height):
                y = base[1] - i
                diff = i * width / height
                
                if y > bottom_right[1]:
                    continue
                if y < top_left[1]:
                    break
                
                for x in xrange(base[0] - width + diff, base[0] + width - diff):
                    if x > top_left[0] and x < bottom_right[0]:
                        yield (x, y)
        
        def check(first, last, prev):
            screen = pydroid.bitmap.capture_screen()
            
            if prev is not None:
                for y in xrange(prev[1] + 20, prev[1] - 20, -1):
                    for x in xrange(prev[0] - 20, prev[0] + 20):
                        if screen.get_color(x, y) in BELL_COLORS:
                            pydroid.mouse.move(x, y)
                            return (x, y)
                                
                for point in triangle(prev, CHECK_WIDTH, 150, first, last):
                    if screen.get_color(point[0], point[1]) in BELL_COLORS:
                        pydroid.mouse.move(point[0], point[1])
                        return point
                    
            for y in xrange(last[1] - 100, first[1], -1):
                for x in xrange(first[0] + 1, last[0]):
                    if screen.get_color(x, y) in BELL_COLORS:
                        pydroid.mouse.move(x, y)
                        return (x, y)
        
        def main():
            print 'Searching for Winterbells...'
            first, last = init()
            prev = None
            
            print 'Found at %s, %s. Running.' % (first, last)
            while True:
                prev = check(first, last, prev)
        
        if __name__ == '__main__':
            main()
    

It still only scores in the thousands because one of the bell colors sometimes
maps to the rabbit, but it's at the point where it scores better than a
newbie. Instead of looking from the bottom up for bell pixels, it tries to
look in the following regions in order:

1) The area near where it last looked.

2) A triangular area above where it last looked, which is a heuristic guess of
where the rabbit would be able to jump to.

If those fail, then it looks from the bottom up for a bell. Okay that was fun,
but it made me realize I really need a day job.

~~~
meese_
Cool :)

Just a minor nitpick: you may want to try the library method bmp.find_color()
instead of your find_init_color()
([http://msanders.github.com/autopy/documentation/api-
referenc...](http://msanders.github.com/autopy/documentation/api-
reference/bitmap.html#bmp.find_color)), it should be significantly faster.

Also, instead of iterating through the width & height manually, autopy offers
this shortcut:

    
    
      for x, y in bitmap:
          do_something(bitmap, x, y)

------
meese_
"The prize is the pleasure of finding the thing out, the kick in the
discovery, the observation that other people use it. Those are the real
things." — Richard Feynman

:)

------
csytan
This sounds like it would be very useful for writing a poker bot.

~~~
BrandonM
I was thinking along these lines, but not a bot. It would be nice as a helper
program while playing to put the mouse over the bet buttons, to set standard
cbet amounts and raise sizes, etc.

------
ehsanul
This reminds me of how I started "programming", making bots for online text-
based mmorpgs with AutoIt V3 (<http://www.autoitscript.com/>), automating IE.
That was a great introduction to programming for me, very simple language with
useful functions and great help files. Since I switched to using Linux, I've
been trying to find a Linux equivalent. Of course, a general programming
language with an http library could be used to do what I was initially doing
with AutoIt, but it's kind of fun to see windows being manipulated and the
mouse moving, keyboard text being sent out, like a ghost user.

AutoPy gives me an excuse to learn python too. Totally sweet.

------
pizza
It looks great. I have a slight issue, though; whenever I try to import
pydroid, it goes

    
    
        Traceback (most recent call last):
      File "<pyshell#0>", line 1, in <module>
        import pydroid
      File "C:\Python26\lib\site-packages\pydroid\__init__.py", line 8, in <module>
        import pydroid.bitmap
        ImportError: DLL load failed: The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log for more detail.

~~~
meese_
What OS?

~~~
pizza
Windows Vista 64bit

~~~
pizza
For some reason, if I use the binaries to install it, it works fine as long as
I remove

    
    
        import autopy.bitmap
    

from __init__.py

~~~
meese_
Yes, that removes the libpng and zlib dependencies. Those should be statically
compiled in on Windows though.

Unfortunately, I don't have a Vista installation to test this on, which makes
it a bit difficult for me to debug.

------
wensing
I've been trying to figure out a way to automatically do screen captures of
the Stormpulse map at various intervals. Being able to use Python ... perfect.
Thank you.

------
sili
Sounds very interesting. I'm going to try writing some simple tests for my
application.

On a side-note, the name sounds a little inapropriate in Russian.

~~~
meese_
> On a side-note, the name sounds a little inapropriate in Russian.

What does it mean?

~~~
d0mine
It sounds like a derivative form of a word fag (very offensive, foul
language).

------
gridspy
To use this to script applications there could be a few useful wrappers -

For example: \- Enumerate open windows (with names from title of each) \- Get
xy coords, height + width \- Enumerate controls that windows knows about and
their locations

------
bob647
The bitmap matching really sucks though. It won't match images that have been
taken straight from larger images.

------
ciupicri
How about some distutils/setuptools based packaging? Or a RPM spec file?

------
Oranj
You are my favorite person today.

------
BearOfNH
Doesn't seem to work on Python 2.5.x under WinXP. I get the DLL load failure
message "This application has failed to start because python26.dll was not
found."

The installer was happy finding Python 2.5.

Oh well, I should probably upgrade to 2.6 anyway ...

------
dustingetz
um, <http://www.autohotkey.com/> ?

it doesn't matter if your scripts are cross platform when the apps they script
aren't.

~~~
pyre
Not really. It means that you can learn the technology once and apply it to
multiple projects with different target platforms.

