Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: My latest pet project, pydroid (github.com/msanders)
109 points by meese_ on Jan 10, 2010 | hide | past | favorite | 50 comments



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


Also from my understanding the term "droid" is trademarked and pretty aggressively protected.

(ergo even all the Motorola Droid advertising actually has disclaimers. They must have a deal with Lucas)


I assume Motorola is doing that just to be safe. "Droid" is a dictionary word.


droid is a shortening of android. How can that be trademarked?


Trademarks only apply in the domain the term is used in. So we can trademark common words already used elsewhere as long as they're not already associated with something in that domain. If I made harddrives called Banana, I could trademark Banana in that domain, but I would have no claim over it in, say, the food industry. Nor could I get a trademark on "Harddrive" in the harddrive industry.

The obvious example is Windows.


That's what I mean. If Lucas has trademarked "droid", that can't possibly apply for a piece of software. Motorola might have a problem, though.


Motorola did in fact pay Lucas to use the word. I don't think that necessarily means Lucas had a valid case against them, though; I think companies sometimes seek more clearance than they need in order to mitigate risk.

http://www.cnn.com/2010/TECH/01/07/cnet.google.nexus.one.bla...


Yeah, me too .. I thought it was related to Android. Just a little "branding" issue there ;)


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


Python GUI scripting -> PythonGoose -> PyGoose -> Goose?


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


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


Perhaps you could augment the github project description to include some keywords like: keyboard, mouse, control, click, UI.


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


autopylot?


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


The link from the title of this post should be be updated.


Yes, it should. HN won't let me do that any longer though.


Awesome! I'll definitely be playing with it soon.


Hmm, autopy looks a bit to much like autopsy to me...

http://en.wikipedia.org/wiki/Autopsy


This sounds like a good idea — not sure how to change it without botching this page, though.


No offense, but did Ruby get all the dev's who can think of a great name? ;)


"Rails" is a great name for a web framework? "Hpricot" is a great name for an XML parser?


To be fair, Hpricot was created by _why. I'm sure 400 years down the road another madman will laugh, completely understanding what _why was going for :D


Ghost


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)


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!


Very cool!


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.


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...), 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)


"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

:)


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


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.


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.


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.


What OS?


Windows Vista 64bit


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


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.


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.


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.


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

What does it mean?


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


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


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


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


You are my favorite person today.


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 ...


um, http://www.autohotkey.com/ ?

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


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




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: