MadLittleMods
4/2/2014 - 6:38 PM

StackExchange Unicoin Miner in Python

StackExchange Unicoin Miner in Python

# Unicoin Miner
# By: Eric Eastwood (ericeastwood.com)
#
# ** Press the `P` key or `Space` to stop the program **
#
# Description: 
# Looks for rocks and clicks them
# Unicoins were a April Fools Joke on all StackExchange sites
# Just wanted to try out some basic vision and input emulation
#
# See it in action: 
# http://youtu.be/B74NHzUbvYo
#
# Made and Tested with Python 2.7 on Windows 8
#
# Dependencies:
# pyhk: https://github.com/schurpf/pyhk
#	For global hotkey stopping
# Pillow: http://www.lfd.uci.edu/~gohlke/pythonlibs/#pillow
# 	Screen capture
# pywin: http://www.lfd.uci.edu/~gohlke/pythonlibs/#pywin32
# 	Left click emulation
#
# Note:
# Only captures and works on the primary monitor. This is a limitation of pillow.
# Does not work perfectly...

import os
import time
import pyhk
import win32api, win32con
import PIL.ImageGrab as ImageGrab
# from PIL import Image
import pythoncom
from multiprocessing.pool import ThreadPool
from random import randrange

# we use for saving the image if you want to
BASE_DIR = os.path.realpath(os.path.dirname(__file__))




def leftClick(x, y):
	# Emulate a left click as coordiate x, y
    win32api.SetCursorPos((x,y))
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,x,y,0,0)
    time.sleep(.002)
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,x,y,0,0)

def getPixelFromIndex(im, index):
	# Given a index from a sequence of pixels (ie. im.getdata()), 
	# give the x and y coordinate
	width, height = im.size

	x = index%width
	y = index/width

	return (x, y)


def clamp(n, minn, maxn):
	# just a simple utility function to clamp
    return max(min(maxn, n), minn)

def sampleArea(coord, color, numSamples, im):
	# Sample the area to the left and down randomly
	mylist = list(((clamp(x-randrange(10), 0, im.size[0]), clamp(x+randrange(10), 0, im.size[1])) for x in range(numSamples)))
	mylist.append(coord)

	found = False
	for listcoord in mylist:
		if im.getpixel(listcoord) == color:
			found = True

	return found



# This is variable that controls the main while loop
running = True

def stopLoop():
	# function that stops the main while loop
	# We use this for the global stop hotkeys
	global running

	print("stopping")
	running = False


# Create pyhk class instance
hot = pyhk.pyhk()
 
# Add in the globabl stop hotkeys
# Find more hotkeys: http://schurpf.com/python/python-hotkey-module/pyhk-end-user-documentation/#hotkey-list
hot.addHotkey(['P'], stopLoop, isThread=True)
hot.addHotkey(['Space'], stopLoop, isThread=True)


# Set up our thread pool we use to capture the screen
pool = ThreadPool(processes=1)

# Define some colors of the rocks to look for
effectColor = (187, 187, 187) # The sqaure sparkle chipping effect color
mainRockColor = (220, 220, 220) # The light gray color of the rock
secondaryRockColor = (154, 154, 154) # The dark gray color of the rock

# Initially capture the screen
im = ImageGrab.grab()


# Simple main loop
count = 0;
lastTimeSinceSearch = 0
needNewRock = True
returnImageFromAsync = True
while running:

	# Refesh the image every second
	if time.time() - lastTimeSinceSearch > 1:
		# fullscreen
		#im = ImageGrab.grab()

		# Spin off a thread pool that captures the screen
		async_result = pool.apply_async(ImageGrab.grab)

		# We are going to actually get the image at the end of the while loop
		returnImageFromAsync = True

		lastTimeSinceSearch = time.time()

	# Find a new rock if we need it
	if needNewRock:
		print("Finding rocks")
		
		# Uncomment to save the image we are looking at
		#im.save(BASE_DIR + '\\full_snap__' + str(int(time.time())) + '.png', 'PNG')

		#print(im)
		#print(im.getpixel((0, 0)))

		# Reset the coord
		rockCoord = (-1, -1)

		# there is aliasing on the rocks so we need to buffer the transition from light gray to dark gray
		bufferMax = 2
		bufferCount = 0;
		# Loop through the whole image
		for index, pixelColor in enumerate(list(im.getdata())):

			# If we find the main rock color(light gray), then reset the buffer count
			if(pixelColor == mainRockColor):
				bufferCount = 0

			# If we can find the secondary rock color(dark gray) within the buffer max 
			# This means we found a rock
			if(bufferCount <= bufferMax and pixelColor == secondaryRockColor):
				#print(im.getpixel(getPixelFromIndex(im, index)))
				rockCoord = getPixelFromIndex(im, index)
				print("found it: " + str(rockCoord))

				# Stop loopin through the image
				break;

			bufferCount += 1





	# Start clicking on the rock if there is a valid coord
	if(rockCoord[0] > 0 and rockCoord[1] > 0):
		# Offset the rock coord just to make it click more on the rock. Better for smaller rocks
		clickCoord = (rockCoord[0]-bufferMax, rockCoord[1])
		# Emulate click
		leftClick(*clickCoord)

		# If we destroyed a rock, we need to find another
		if not (sampleArea(rockCoord, mainRockColor, 10, im) or sampleArea(rockCoord, secondaryRockColor, 10, im) or sampleArea(rockCoord, effectColor, 10, im)):
			print("destroyed rock: " + str(im.getpixel(rockCoord)))
			needNewRock = True
		else:
			needNewRock = False
	else:
		needNewRock = True


	# If there is a image to update our variable with
	if returnImageFromAsync:
		# Load it in
		im = async_result.get()
		returnImageFromAsync = False


	# Uncomment the block to break after a certain number of iterations
	#count += 1
	#if count > 100000:
	#	print("broke after count: " + str(count))
	#	break


	# continue pumping hotkey events
	# This is for pyhk.
	pythoncom.PumpWaitingMessages()