├── offsets.json ├── doc_images └── bot_img.png ├── readme.md ├── offset.py ├── .gitignore ├── Burrito-bot_OLD ├── README.md └── bison.py ├── desktop_magic.py └── bison.py /offsets.json: -------------------------------------------------------------------------------- 1 | [588, 117] -------------------------------------------------------------------------------- /doc_images/bot_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chriskiehl/Burrito-Bot/HEAD/doc_images/bot_img.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Burrito Bot 2 | 3 |

4 | 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /offset.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | def load_from_json(): 4 | with open('offsets.json', 'rb') as f: 5 | return json.load(f) 6 | 7 | def save_to_json(offsets): 8 | with open('offsets.json', 'wb') as f: 9 | f.write(json.dumps(offsets)) 10 | 11 | __offsets = load_from_json() 12 | 13 | x = __offsets[0] 14 | y = __offsets[1] 15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo -------------------------------------------------------------------------------- /Burrito-bot_OLD/README.md: -------------------------------------------------------------------------------- 1 | # Burrito Bison bot 2 | 3 | *Edit (05-19-13): This is years old. Keeping it for time capsule purposes..* 4 | 5 | Below are some of the thoughts and failed ideas that I went through while wrtting this thing. All the problems I faced 6 | are most likely really simple and will likely seem trival as I learn more, but I leave them here for those who, like me, 7 | are just starting out. 8 | 9 | Simple little script to 'play' Burrito Bison (found here: http://notdoppler.com/burritobison.php). The code is pretty 10 | weak, this was written just to learn python a little more, and these 'launch' games all seemed mindless enough that 11 | I could easily get a bot to control them.. (it ended up being trickier than I expected) 12 | 13 | The code is pretty crappy, and there are tons of things I'd do differently, and, in fact, if you look at the code you'll 14 | notice that calls start changing little by little and becoming more concise. Which was really the point of this, to 15 | get a little better at constructing a program. So the things I don't like about how it's written now, didn't occur to 16 | me as possible issues when I first started coding, but little by little, better ways of approaching a problem became 17 | clear. 18 | 19 | It's now completely workable, and will play the game from start to finish. There's only one menu that it doesn't catch, 20 | I noticed it when I was recording the final start-to-finish playthrough, but I was too lazy to fix it. 21 | 22 | The bot works by gathering in about the game by taking screen shots and then checking specific pixel locations. 23 | It's a very fragil way to do things, as any difference in browser size ruins the expected pixel values. 24 | 25 | I tried all kinds of things to get around this problem. Being that all important events are different colors from the 26 | main stage and gummies (like the cop for instance), I tried all kinds of funniness with averaging the colors of a 27 | snapshot, assuming that when a new color appeared on screen the average color disrtibution would change. I still think 28 | it's a neat idea for flexibly checking what's on screen, but in practice I couldn't get it to work due to speed issues 29 | (and probably poor code issues). Several variations of this idea were tried in an effort to reduce the number of 30 | calculations needed. A few attempts were made at grayscalling the image first and then getting the colors, others 31 | included taking a full snapshot of the playarea, thumbnailing it to something like 25x25, and then averaging the colors 32 | that way. 33 | 34 | Of all the things I tried (revalting to the getcolors idea) this proved the most reliable way 35 | of noticing when a cop was on screen. Reliable in my case being about (maybe)10% success rate. Even the current 36 | iteration is quite poor, altough, when the bison is moving at slower speeds I'd say it's around a 40% success rate. 37 | Which I was happy enough with to move on. There tons of things with the current implementation that could raise this 38 | number quite a bit (I think). Such as multiprocessing some of the search events so there's not so much time inbetween 39 | calls, turning off all of the other search features that aren't needed anymore (like dialog boxes) would free up a ton 40 | of processing room. They're the old version that takes a unique screen shot instead of 'sharing' one between all 41 | functions. 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /desktop_magic.py: -------------------------------------------------------------------------------- 1 | """ 2 | Robust functions for grabbing and saving screenshots on Windows. 3 | """ 4 | 5 | # TODO: support capture of individual displays (and at the same time with a "single screenshot") 6 | # Use GetDeviceCaps; see http://msdn.microsoft.com/en-us/library/dd144877%28v=vs.85%29.aspx 7 | 8 | import ctypes 9 | import win32gui 10 | import win32ui 11 | import win32con 12 | import win32api 13 | 14 | 15 | class BITMAPINFOHEADER(ctypes.Structure): 16 | _fields_ = [ 17 | ('biSize', ctypes.c_uint32), 18 | ('biWidth', ctypes.c_int), 19 | ('biHeight', ctypes.c_int), 20 | ('biPlanes', ctypes.c_short), 21 | ('biBitCount', ctypes.c_short), 22 | ('biCompression', ctypes.c_uint32), 23 | ('biSizeImage', ctypes.c_uint32), 24 | ('biXPelsPerMeter', ctypes.c_long), 25 | ('biYPelsPerMeter', ctypes.c_long), 26 | ('biClrUsed', ctypes.c_uint32), 27 | ('biClrImportant', ctypes.c_uint32) 28 | ] 29 | 30 | 31 | 32 | class BITMAPINFO(ctypes.Structure): 33 | _fields_ = [ 34 | ('bmiHeader', BITMAPINFOHEADER), 35 | ('bmiColors', ctypes.c_ulong * 3) 36 | ] 37 | 38 | class GrabFailed(Exception): 39 | """ 40 | Could not take a screenshot. 41 | """ 42 | 43 | class MonitorSelectionOutOfBounds(Exception): 44 | ''' 45 | Argument out of bounds 46 | ''' 47 | 48 | class BoundingBoxOutOfRange(Exception): 49 | ''' 50 | Coordinates are too large for the current resolution 51 | ''' 52 | 53 | 54 | class DIBFailed(Exception): 55 | pass 56 | 57 | 58 | 59 | def _deleteDCAndBitMap(dc, bitmap): 60 | dc.DeleteDC() 61 | win32gui.DeleteObject(bitmap.GetHandle()) 62 | 63 | def getMonitorCoordinates(targetMonitor): 64 | ''' 65 | Enumerates the available monitor. Return the 66 | Screen Dimensions of the selected monitor. 67 | ''' 68 | HMONITOR = 0 69 | HDCMONITOR = 1 70 | SCREENRECT = 2 71 | 72 | try: 73 | monitors = win32api.EnumDisplayMonitors(None, None) 74 | 75 | if targetMonitor > len(monitors)-1: 76 | raise MonitorSelectionOutOfBounds("Monitor argument exceeds attached number of devices.\n" 77 | "There are only %d display devices attached.\n" % len(monitors) + 78 | "Please select appropriate device ( 0=Primary, 1=Secondary, etc..)." ) 79 | 80 | left,top,right,bottom = monitors[targetMonitor][SCREENRECT] 81 | width = right - left 82 | height = bottom 83 | 84 | finally: 85 | # I can't figure out what to do with the handle to the Monitor 86 | # that gets returned from EnumDisplayMonitors (the first object in 87 | # the tuple). Trying to close it throws an error.. Does it not need 88 | # cleaned up at all? Most of the winApi is back magic to me... 89 | 90 | # These device context handles were the only things that I could Close() 91 | for monitor in monitors: 92 | monitor[HDCMONITOR].Close() 93 | 94 | return (left, top, width, height) 95 | 96 | def getSecondaryMonitorCoordinates(): 97 | ''' 98 | Enumerates the available monitors. Return the 99 | Screen Dimensions of the secondary monitor. 100 | ''' 101 | HMONITOR = 0 102 | HDCMONITOR = 1 103 | SCREENRECT = 2 104 | 105 | try: 106 | monitors = win32api.EnumDisplayMonitors(None, None) 107 | 108 | # if targetMonitor > len(monitors)-1: 109 | # raise MonitorSelectionOutOfBounds("Monitor argument exceeds attached number of devices.\n" 110 | # "There are only %d display devices attached.\n" % len(monitors) + 111 | # "Please select appropriate device ( 0=Primary, 1=Secondary, etc..)." ) 112 | 113 | left,top,right,bottom = monitors[-1][SCREENRECT] 114 | width = right - left 115 | height = bottom 116 | 117 | finally: 118 | # I can't figure out what to do with the handle to the Monitor 119 | # that gets returned from EnumDisplayMonitors (the first object in 120 | # the tuple). Trying to close it throws an error.. Does it not need 121 | # cleaned up at all? Most of the winApi is back magic to me... 122 | 123 | # These device context handles were the only things that I could Close() 124 | for monitor in monitors: 125 | monitor[HDCMONITOR].Close() 126 | 127 | return (left, top, width, height) 128 | 129 | 130 | def getDCAndBitMap(saveBmpFilename=None, bbox=None): 131 | """ 132 | Returns a (DC, PyCBitmap). On the returned PyCBitmap, you *must* call 133 | win32gui.DeleteObject(aPyCBitmap.GetHandle()). On the returned DC, 134 | you *must* call aDC.DeleteDC() 135 | """ 136 | hwnd = win32gui.GetDesktopWindow() 137 | if bbox: 138 | left, top, width, height = bbox 139 | if (left < win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) or 140 | top < win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) or 141 | width > win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) or 142 | height > win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)): 143 | raise Exception('Invalid bounding box. Range exceeds available screen area.') 144 | width = width - left 145 | height = height - top 146 | 147 | else: 148 | # Get complete virtual screen, including all monitors. 149 | left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) 150 | top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) 151 | width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) 152 | height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) 153 | ##print "L", left, "T", top, "dim:", width, "x", height 154 | 155 | # Retrieve the device context (DC) for the entire window. 156 | 157 | hwndDevice = win32gui.GetWindowDC(hwnd) 158 | ##print "device", hwndDevice 159 | assert isinstance(hwndDevice, (int, long)), hwndDevice 160 | 161 | mfcDC = win32ui.CreateDCFromHandle(hwndDevice) 162 | try: 163 | saveDC = mfcDC.CreateCompatibleDC() 164 | saveBitMap = win32ui.CreateBitmap() 165 | # Above line is assumed to never raise an exception. 166 | try: 167 | saveBitMap.CreateCompatibleBitmap(mfcDC, width, height) 168 | saveDC.SelectObject(saveBitMap) 169 | try: 170 | saveDC.BitBlt((0, 0), (width, height), mfcDC, (left, top), win32con.SRCCOPY) 171 | except win32ui.error, e: 172 | raise GrabFailed("Error during BitBlt. " 173 | "Possible reasons: locked workstation, no display, " 174 | "or an active UAC elevation screen. Error was: " + str(e)) 175 | if saveBmpFilename is not None: 176 | saveBitMap.SaveBitmapFile(saveDC, saveBmpFilename) 177 | except: 178 | _deleteDCAndBitMap(saveDC, saveBitMap) 179 | # Let's just hope the above line doesn't raise an exception 180 | # (or it will mask the previous exception) 181 | raise 182 | finally: 183 | mfcDC.DeleteDC() 184 | 185 | return saveDC, saveBitMap 186 | 187 | 188 | def getBGR32(dc, bitmap): 189 | """ 190 | Returns a (raw BGR str, (width, height)) for C{dc}, C{bitmap}. 191 | Guaranteed to be 32-bit. Note that the origin of the returned image is 192 | in the bottom-left corner, and the image has 32-bit line padding. 193 | """ 194 | bmpInfo = bitmap.GetInfo() 195 | width, height = bmpInfo['bmWidth'], bmpInfo['bmHeight'] 196 | 197 | bmi = BITMAPINFO() 198 | ctypes.memset(ctypes.byref(bmi), 0x00, ctypes.sizeof(bmi)) 199 | bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER) 200 | bmi.bmiHeader.biWidth = width 201 | bmi.bmiHeader.biHeight = height 202 | bmi.bmiHeader.biBitCount = 24 203 | bmi.bmiHeader.biPlanes = 1 204 | 205 | bufferLen = height * ((width * 3 + 3) & -4) 206 | pbBits = ctypes.create_string_buffer(bufferLen) 207 | 208 | ret = ctypes.windll.gdi32.GetDIBgetits( 209 | dc.GetHandleAttrib(), 210 | bitmap.GetHandle(), 211 | 0, 212 | height, 213 | ctypes.byref(pbBits), 214 | ctypes.pointer(bmi), 215 | win32con.DIB_RGB_COLORS) 216 | if ret == 0: 217 | raise DIBFailed("Return code 0 from GetDIBits") 218 | 219 | assert len(pbBits.raw) == bufferLen, len(pbBits.raw) 220 | 221 | return pbBits.raw, (width, height) 222 | 223 | 224 | def getScreenAsImage(bbox=None): 225 | """ 226 | Returns a PIL Image object (mode RGB) of the current screen (incl. 227 | all monitors). 228 | 229 | bbox = boundingBox. Used to snap a subarea of the screen. 230 | A tuple of (x, y, width, height). 231 | """ 232 | import Image 233 | dc, bitmap = getDCAndBitMap(bbox=bbox) 234 | try: 235 | bmpInfo = bitmap.GetInfo() 236 | # bmpInfo is something like { 237 | # 'bmType': 0, 'bmWidthBytes': 5120, 'bmHeight': 1024, 238 | # 'bmBitsPixel': 32, 'bmPlanes': 1, 'bmWidth': 1280} 239 | ##print bmpInfo 240 | size = (bmpInfo['bmWidth'], bmpInfo['bmHeight']) 241 | 242 | if bmpInfo['bmBitsPixel'] == 32: 243 | # Use GetBitmapBits and BGRX if the bpp == 32, because 244 | # it's ~15% faster than the method below. 245 | data = bitmap.GetBitmapBits(True) # asString=True 246 | return Image.frombuffer( 247 | 'RGB', size, data, 'raw', 'BGRX', 0, 1) 248 | else: 249 | # If bpp != 32, we cannot use GetBitmapBits, because it 250 | # does not return a 24/32-bit image when the screen is at 251 | # a lower color depth. 252 | try: 253 | data, size = getBGR32(dc, bitmap) 254 | except DIBFailed, e: 255 | raise GrabFailed("getBGR32 failed. Error was " + str(e)) 256 | # BGR, 32-bit line padding, origo in lower left corner 257 | return Image.frombuffer( 258 | 'RGB', size, data, 'raw', 'BGR', (size[0] * 3 + 3) & -4, -1) 259 | finally: 260 | _deleteDCAndBitMap(dc, bitmap) 261 | 262 | 263 | def saveScreenToBmp(bmpFilename, bbox=None): 264 | """ 265 | Save a screenshot (incl. all monitors) to a .bmp file. Does not require PIL. 266 | The .bmp file will have the same bit-depth as the screen; it is not 267 | guaranteed to be 32-bit. 268 | 269 | bbox = boundingBox. Used to snap a subarea of the screen. 270 | A tuple of (x, y, width, height). 271 | """ 272 | dc, bitmap = getDCAndBitMap(saveBmpFilename=bmpFilename, bbox=bbox) 273 | _deleteDCAndBitMap(dc, bitmap) 274 | 275 | 276 | def _demo(): 277 | saveNames = ['allMonitors', 'primaryMonitor', 'secondaryMonitor', 'boundingTestOne', 'boundingTestTwo'] 278 | params = [None, getMonitorCoordinates(0), getMonitorCoordinates(1), (0,0,100,50), (400,300, 200,200)] 279 | 280 | # for i in range(len(saveNames)): 281 | # saveScreenToBmp( saveNames[i] + '.bmp', params[i]) 282 | # im = getScreenAsImage(params[i]) 283 | # im.save(saveNames[i] + '.png', format='png' ) 284 | 285 | 286 | if __name__ == '__main__': 287 | im = getScreenAsImage((588, 117, 1307, 596)) 288 | im.save('testttttttt.png', 'png') 289 | -------------------------------------------------------------------------------- /Burrito-bot_OLD/bison.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | All of this is configured to run at 1280x1024 resolution, playing at 4 | http://notdoppler.com/burritobison.php 5 | 6 | Also on chrome. It'll mess up if browser is a different size as it uses 7 | very fragil and poorly designed getpixel() calls to check for things.. 8 | 9 | """ 10 | 11 | import ImageGrab, ImageOps 12 | import Image 13 | import sys, os 14 | import win32api, win32con 15 | import time 16 | from random import random 17 | from random import randrange 18 | from multiprocessing import Process 19 | 20 | ##GLOBALS 21 | shopping = True 22 | playing = True 23 | 24 | def mousePos(x=(0,0)): 25 | win32api.SetCursorPos(x) 26 | #Temporary position. Eventually this will receive arguments based on 27 | #game logic 28 | tmp = (156, 335) 29 | 30 | def isSpinning(): 31 | mousePos((475,620)) 32 | expectedVal = (255, 225, 13) 33 | box = (473,377,530,432) 34 | 35 | im = ImageGrab.grab() 36 | 37 | inVal = im.getpixel((500, 390)) 38 | ##print inVal 39 | ##print expectedVal 40 | im.save(os.getcwd() + '\\' + 'Spinning.png', "PNG") 41 | if inVal == expectedVal: 42 | print 'Spinning = True' 43 | return True 44 | else: 45 | print 'Not spinning' 46 | return False 47 | ##im.save(os.getcwd() + '\\' + 'text_002.png', "PNG") 48 | 49 | def launcher(): 50 | 51 | expectedVal= (250, 152, 135) 52 | im = ImageGrab.grab((455, 435, 501, 467)) 53 | inVal = im.getpixel((41,24)) 54 | ## print inVal 55 | ## im.save(os.getcwd() + '\\' + 'launcher.png', "PNG") 56 | ## im.putpixel((32,28), (0,0,0)) 57 | ## print inVal 58 | if inVal != expectedVal: 59 | leftClick() 60 | im.save(os.getcwd() + '\\' + 'launcher.png', "PNG") 61 | print 'Launch!' 62 | return 1 63 | else: 64 | print 'missed' 65 | launcher() 66 | 67 | def leftClick(): 68 | win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0) 69 | ## time.sleep(.1) 70 | win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0) 71 | print "MOUSE CLICK!!" 72 | time.sleep(.05) 73 | 74 | 75 | def runFinished(): 76 | expectedVal = (42,181,240) 77 | eVal2 = (17,31,75) 78 | box = (181,362,850,452) 79 | 80 | im = ImageGrab.grab(box) 81 | 82 | inVal = im.getpixel((591, 30)) 83 | inVal2 = im.getpixel((35, 49)) 84 | 85 | if inVal == expectedVal and inVal2 == eVal2: 86 | return True 87 | 88 | 89 | def fuzzCheck(im): 90 | coppa = (99,104,137) 91 | if im.getpixel((randrange(263,290),(randrange(196,223)))) == coppa: 92 | print 'A cop!' 93 | return True 94 | if im.getpixel((randrange(295,320),(randrange(196,223)))) == coppa: 95 | print 'A cop!' 96 | return True 97 | 98 | 99 | 100 | def bubbleCheck(im): 101 | bubble = (252,114,186) 102 | if im.getpixel((randrange(298,325),randrange(100,120))) == bubble: 103 | print 'A Bubble!' 104 | return True 105 | if im.getpixel((randrange(325,350),randrange(80,200))) == bubble: 106 | print 'A Bubble!' 107 | 108 | 109 | 110 | def specialChecker(im): 111 | special = (255,250,240) 112 | if im.getpixel((624,130)) == special: 113 | return(True) 114 | 115 | def spinCheck(im): 116 | launchGuy = (255,242,252) 117 | if im.getpixel((546,90)) == launchGuy: 118 | return True 119 | 120 | def multiClick(): 121 | for i in range(14): 122 | leftClick() 123 | 124 | def eventFinder(): 125 | 126 | box = (200,500,884,750) 127 | im = ImageGrab.grab(box) 128 | 129 | if specialChecker(im): 130 | print 'Special spotted!\n' 131 | p = Process(target= multiClick()) 132 | p.start() 133 | p.join() 134 | 135 | 136 | if bubbleCheck(im): 137 | print "A bubble has appeared!\n" 138 | leftClick() 139 | 140 | ## if specialChecker(im): 141 | ## print 'Special spotted!\n' 142 | ## for i in range(15): 143 | ## leftClick() 144 | 145 | if fuzzCheck(im): 146 | leftClick() 147 | 148 | if specialChecker(im): 149 | print 'Special spotted!\n' 150 | 151 | 152 | if spinCheck(im): 153 | if isSpinning(): 154 | launcher() 155 | 156 | print 'searching...' 157 | 158 | 159 | 160 | 161 | def canShop(): 162 | im = ImageGrab.grab() 163 | 164 | expectedVal = (242,227,110) 165 | inVal = im.getpixel((695,385)) 166 | if expectedVal == inVal: 167 | return True 168 | 169 | def exitShop(): 170 | mousePos((840,785)) 171 | leftClick() 172 | 173 | def checkSales(): 174 | im = ImageGrab.grab() 175 | 176 | 177 | if im.getpixel((275,440))[2] < 150: 178 | print 'Elastic for sale\n' 179 | mousePos((275,525)) 180 | leftClick() 181 | time.sleep(.05) 182 | return True 183 | 184 | elif im.getpixel((370,435))[2] < 150: 185 | print 'slip for sale\n' 186 | mousePos((370,435)) 187 | leftClick() 188 | time.sleep(.05) 189 | return True 190 | 191 | elif im.getpixel((465,439))[2] < 150: 192 | print 'Pickpocket for sale\n' 193 | mousePos((465,439)) 194 | leftClick() 195 | time.sleep(.05) 196 | return True 197 | 198 | elif im.getpixel((458,532))[2] < 150: 199 | print 'Resistance for sale\n' 200 | mousePos((458,532)) 201 | leftClick() 202 | time.sleep(.05) 203 | return True 204 | 205 | elif im.getpixel((365,530))[2] < 150: 206 | print 'rocket for sale\n' 207 | mousePos((365,530)) 208 | leftClick() 209 | time.sleep(.05) 210 | return True 211 | 212 | elif im.getpixel((270,530))[2] < 150: 213 | print 'Bouncies for sale\n' 214 | mousePos((270,530)) 215 | leftClick() 216 | time.sleep(.1) 217 | return True 218 | 219 | elif im.getpixel((272, 635))[2] < 150: 220 | print 'BubbleGum for sale\n' 221 | mousePos((272, 635)) 222 | leftClick() 223 | time.sleep(.1) 224 | return True 225 | 226 | elif im.getpixel((367,635))[2] < 150: 227 | print 'Glider for sale\n' 228 | mousePos((367,635)) 229 | leftClick() 230 | time.sleep(.1) 231 | return True 232 | 233 | elif im.getpixel((460,635))[2] < 130: 234 | print 'Rocket for sale\n' 235 | mousePos((460,635)) 236 | leftClick() 237 | time.sleep(.1) 238 | return True 239 | 240 | elif im.getpixel((270,725))[2] < 150: 241 | print 'Pogo for sale\n' 242 | mousePos((270,725)) 243 | leftClick() 244 | time.sleep(.1) 245 | return True 246 | 247 | elif im.getpixel((370,725))[2] < 150: 248 | print 'Pepper for sale\n' 249 | mousePos((370,725)) 250 | leftClick() 251 | time.sleep(.1) 252 | return True 253 | 254 | elif im.getpixel((463,725))[2] < 150: 255 | print 'general for sale\n' 256 | mousePos((463,725)) 257 | leftClick() 258 | time.sleep(.1) 259 | return True 260 | 261 | else: 262 | return False 263 | 264 | 265 | def buy(): 266 | mousePos((745,535)) 267 | leftClick() 268 | 269 | def retry(): 270 | mousePos((780,415)) 271 | leftClick() 272 | 273 | def clickInfo(): 274 | mousePos((195,424)) 275 | leftClick() 276 | 277 | def infoBoxLeft(): 278 | expectedVal = (46,195,251) 279 | eVal2 = (251,223,114) 280 | box = (160,339,460,652) 281 | 282 | im = ImageGrab.grab(box) 283 | 284 | inVal2 = im.getpixel((92,84)) 285 | inVal = im.getpixel((24,82)) 286 | 287 | if inVal == expectedVal and inVal2 == eVal2: 288 | print "an InfoBox is on screen" 289 | return True 290 | 291 | def infoBoxMid(): 292 | expectedVal = (28,184,250) 293 | eVal2 = (251,223,114) 294 | box = (160,339,460,652) 295 | 296 | im = ImageGrab.grab(box) 297 | 298 | inVal2 = im.getpixel((230,164)) 299 | inVal = im.getpixel((178,166)) 300 | 301 | if inVal == expectedVal and inVal2 == eVal2: 302 | print "mis-screen InfoBox showing" 303 | return True 304 | 305 | def infoExplosive(): 306 | box = (160,339,460,652) 307 | im = ImageGrab.grab() 308 | 309 | expectedVal = (33,175,235) 310 | inVal = im.getpixel((389,442)) 311 | 312 | eVal2 = (241,216,117) 313 | inVal2 = im.getpixel((445,440)) 314 | 315 | if inVal == expectedVal and inVal2 == eVal2: 316 | print "InfoExplosion Dialog On screen" 317 | return True 318 | 319 | def infoBubble(): 320 | im = ImageGrab.grab() 321 | 322 | inPix = (59,195,246) 323 | if im.getpixel((385,440)) == inPix: 324 | return True 325 | 326 | 327 | 328 | 329 | boxes = [1,1,1,1,1,1] 330 | def play(): 331 | shopping = False 332 | count =0 333 | 334 | while True: 335 | if shopping == False: 336 | if infoBoxLeft() == True: 337 | clickInfo() 338 | boxes[0]=0 339 | 340 | elif infoBoxMid() == True: 341 | mousePos((345,505)) 342 | leftClick() 343 | 344 | elif infoExplosive() == True: 345 | mousePos((395,440)) 346 | leftClick() 347 | if infoBubble(): 348 | mousePos((385,440)) 349 | leftClick() 350 | 351 | else: 352 | eventFinder() 353 | count +=1 354 | print count 355 | if count >15: 356 | leftClick() 357 | count=0 358 | eventFinder() 359 | 360 | if runFinished() == True: 361 | if canShop() == True: 362 | mousePos((695,385)) 363 | leftClick() 364 | shopping = True 365 | else: 366 | retry() 367 | time.sleep(2) 368 | 369 | 370 | 371 | else: 372 | 373 | while checkSales() == True: 374 | buy() 375 | exitShop() 376 | time.sleep(2) 377 | play() 378 | 379 | 380 | 381 | def main(): 382 | ## pass 383 | time.sleep(2) 384 | play() 385 | 386 | if __name__ == '__main__': 387 | main() 388 | 389 | 390 | 391 | 392 | -------------------------------------------------------------------------------- /bison.py: -------------------------------------------------------------------------------- 1 | """ 2 | A bot to automatically play Burrito Bison. 3 | 4 | This was my first big project that I tackled in Python. It has been 5 | one year since I wrote it, and it still gets forked every now and 6 | again. So, high time for a refactoring session! 7 | 8 | The original bison.py will remain. It's like a time capsule now. 9 | """ 10 | 11 | import os 12 | import csv 13 | import sys 14 | import time 15 | import Image 16 | import offset 17 | import win32api 18 | import win32con 19 | import ImageOps 20 | import ImageGrab 21 | from ctypes import * 22 | from random import random 23 | from random import randrange 24 | from multiprocessing import Process 25 | from desktop_magic import getMonitorCoordinates 26 | from desktop_magic import getScreenAsImage as grab 27 | 28 | def get_play_area(monitor): 29 | ''' 30 | Snaps an image of the chosen monitor (zero indexed). 31 | Loops through the RGB pixels looking for the value 32 | (244,222,176), which corresponds to the top left 33 | most pixel of the game. 34 | 35 | It returns the coordinates of the playarea. 36 | ''' 37 | 38 | TOP_LEFT_PIXELS = (204,204,204) 39 | GREY_BORDER = (204,204,204) 40 | SCREEN_WIDTH = 719 41 | SCREEN_HEIGH = 479 42 | 43 | monitor_coords = getMonitorCoordinates(0) #set to whatever monitor you have the game screen on 44 | im = grab(monitor_coords) 45 | imageWidth, imHeight = im.size 46 | imageArray = im.getdata() 47 | 48 | for index, pixel in enumerate(imageArray): 49 | if pixel == TOP_LEFT_PIXELS: 50 | # getdata returns a flat array, so the below figures out 51 | # the 2d coords based on the index position. 52 | top = (index / imageWidth) 53 | left = (index % imageWidth) 54 | if (im.getpixel((left + 1, top + 1)) == GREY_BORDER and 55 | im.getpixel((left + 2, top + 2)) == GREY_BORDER): 56 | top += 5 57 | left += 5 58 | 59 | return (left, top, left + SCREEN_WIDTH, top + SCREEN_HEIGH) 60 | 61 | raise Exception("Play area not in view." 62 | "Make sure the game is visible on screen!") 63 | 64 | 65 | def _getLocationOffsetAndPixel(): 66 | playArea = get_play_area() 67 | offset.x = playArea[0] 68 | offset.y = playArea[1] 69 | 70 | snapshot = ImageGrab.grab(playArea) 71 | 72 | pos = list(win32api.GetCursorPos()) 73 | pos[0] = pos[0] - offset.x 74 | pos[1] = pos[1] - offset.y 75 | pixelAtMousePos = getPixel(pos[0], pos[1]) 76 | 77 | print pos, pixelAtMousePos 78 | 79 | def _dumpDataToExcel(data): 80 | ''' 81 | dump frequency and bin information to a csv 82 | file for Histogram creation. 83 | ''' 84 | data.sort() 85 | bins = _createBins(data) 86 | with open('histogram.csv', 'wb') as csvfile: 87 | writer = csv.writer(csvfile, delimiter=',') 88 | writer.writerow(['Frequency', 'Bins']) 89 | for i in range(len(data)): 90 | if i < len(bins): 91 | writer.writerow([data[i], bins[i]]) 92 | else: 93 | writer.writerow([data[i]]) 94 | 95 | def _createBins(data, binNum=15): 96 | ''' 97 | Generates equally sized bins for 98 | histogram output 99 | ''' 100 | minVal = data[0] 101 | maxVal = data[-1] 102 | dataRange = maxVal - minVal 103 | binRange = dataRange/binNum 104 | bins = [] 105 | for i in range(binNum): 106 | bins.append(minVal) 107 | minVal += binRange 108 | return bins 109 | 110 | 111 | def getPixel(x,y): 112 | offset.x 113 | offset.y 114 | 115 | 116 | gdi= windll.gdi32 117 | RGBInt = gdi.GetPixel(windll.user32.GetDC(0), 118 | offset.x + x, offset.y + y) 119 | 120 | red = RGBInt & 255 121 | green = (RGBInt >> 8) & 255 122 | blue = (RGBInt >> 16) & 255 123 | return (red, green, blue) 124 | 125 | def check_pixel(location, color): 126 | pixel = getPixel(location[0], location[1]) 127 | print 'input color: ', color 128 | print 'check pixel color:', pixel 129 | if pixel in [color]: 130 | return True 131 | return False 132 | 133 | def is_spinner_screen(): 134 | STAR_LOCATION = (339, 47) 135 | GOLD_STAR_COLOR = (255, 225, 13) 136 | 137 | return check_pixel(STAR_LOCATION, GOLD_STAR_COLOR) 138 | 139 | def wait_for_needle(): 140 | NEEDLE_CENTER = (327, 121) 141 | BOARD_COLOR = (250,114,95) 142 | 143 | s = time.time() 144 | hit_count = 0 145 | while time.time() - s < 15: #If STILL not found after 15 sec. Prob on wrong screen 146 | if not check_pixel(NEEDLE_CENTER, BOARD_COLOR): 147 | hit_count += 1 148 | if hit_count > 30: 149 | return True 150 | 151 | 152 | def set_mouse_pos(pos=(0,0)): 153 | x,y = pos 154 | x = x + offset.x 155 | y = y + offset.y 156 | win32api.SetCursorPos((x,y)) 157 | 158 | def launcher(): 159 | 160 | expectedVal= (250, 152, 135) 161 | im = ImageGrab.grab((455, 435, 501, 467)) 162 | inVal = im.getpixel((41,24)) 163 | ## print inVal 164 | ## im.save(os.getcwd() + '\\' + 'launcher.png', "PNG") 165 | ## im.putpixel((32,28), (0,0,0)) 166 | ## print inVal 167 | if inVal != expectedVal: 168 | left_click() 169 | im.save(os.getcwd() + '\\' + 'launcher.png', "PNG") 170 | print 'Launch!' 171 | return 1 172 | else: 173 | print 'missed' 174 | launcher() 175 | 176 | 177 | 178 | 179 | def runFinished(): 180 | expectedVal = (42,181,240) 181 | eVal2 = (17,31,75) 182 | box = (181,362,850,452) 183 | 184 | im = ImageGrab.grab(box) 185 | 186 | inVal = im.getpixel((591, 30)) 187 | inVal2 = im.getpixel((35, 49)) 188 | 189 | if inVal == expectedVal and inVal2 == eVal2: 190 | return True 191 | 192 | 193 | def fuzzCheck(im): 194 | coppa = (99,104,137) 195 | if im.getpixel((randrange(263,290),(randrange(196,223)))) == coppa: 196 | print 'A cop!' 197 | return True 198 | if im.getpixel((randrange(295,320),(randrange(196,223)))) == coppa: 199 | print 'A cop!' 200 | return True 201 | 202 | 203 | 204 | def bubbleCheck(im): 205 | bubble = (252,114,186) 206 | if im.getpixel((randrange(298,325),randrange(100,120))) == bubble: 207 | print 'A Bubble!' 208 | return True 209 | if im.getpixel((randrange(325,350),randrange(80,200))) == bubble: 210 | print 'A Bubble!' 211 | 212 | 213 | 214 | def specialChecker(im): 215 | special = (255,250,240) 216 | if im.getpixel((624,130)) == special: 217 | return(True) 218 | 219 | def spinCheck(im): 220 | launchGuy = (255,242,252) 221 | if im.getpixel((546,90)) == launchGuy: 222 | return True 223 | 224 | def multiClick(): 225 | for i in range(14): 226 | left_click() 227 | 228 | def eventFinder(): 229 | 230 | box = (200,500,884,750) 231 | im = ImageGrab.grab(box) 232 | 233 | if specialChecker(im): 234 | print 'Special spotted!\n' 235 | p = Process(target= multiClick()) 236 | p.start() 237 | p.join() 238 | 239 | 240 | if bubbleCheck(im): 241 | print "A bubble has appeared!\n" 242 | left_click() 243 | 244 | ## if specialChecker(im): 245 | ## print 'Special spotted!\n' 246 | ## for i in range(15): 247 | ## left_click() 248 | 249 | if fuzzCheck(im): 250 | left_click() 251 | 252 | if specialChecker(im): 253 | print 'Special spotted!\n' 254 | 255 | 256 | if spinCheck(im): 257 | if isSpinning(): 258 | launcher() 259 | 260 | print 'searching...' 261 | 262 | 263 | 264 | 265 | def can_shop(): 266 | SHOP_BUTTON = (497, 55) 267 | BUTTON_COLOR = (39, 178, 237) 268 | 269 | if not check_pixel(SHOP_BUTTON, BUTTON_COLOR): 270 | return True 271 | return False 272 | 273 | def enter_shop(): 274 | SHOP_BUTTON = (497, 55) 275 | set_mouse_pos(SHOP_BUTTON) 276 | left_click() 277 | 278 | def exitShop(): 279 | set_mouse_pos((685, 446)) 280 | left_click() 281 | 282 | def check_sales_and_purcahse(): 283 | shop_items = { 284 | 'elastic_cables': [(116, 99), (236, 138, 207)], 285 | 'slippery_lotion': [(209, 104), (228, 132, 200)], 286 | 'pickpocket': [(304, 101), (234, 136, 205)], 287 | 'bounciness': [(112, 191), (235, 136, 206)], 288 | 'rocket_slam': [(207, 188), (234, 136, 205)], 289 | 'resistance': [(303, 191), (237, 138, 208)], 290 | 'bubble_gummies': [(115, 293), (232, 133, 203)], 291 | 'glider_gummies': [(206, 296), (235, 136, 206)], 292 | 'rocket_gummies': [(303, 295), (227, 131, 199)], 293 | 'pogostick': [(111, 381), (235, 136, 206)], 294 | 'pepper_gummies': [(210, 383), (234, 136, 205)], 295 | 'general_goods': [(306, 380), (233, 134, 204)] 296 | } 297 | 298 | while True: 299 | item_available = get_available_items(shop_items) 300 | if item_available is None: 301 | break 302 | purchase_item(item_available) 303 | print 'Done shopping' 304 | 305 | def get_available_items(shop_items): 306 | for k, v in shop_items.iteritems(): 307 | item_location = v[0] 308 | item_color = v[1] 309 | print 'checking:', k 310 | 311 | if not check_pixel(item_location, item_color): 312 | print 'purchasing item:', k 313 | return item_location 314 | return None 315 | 316 | def purchase_item(item_location): 317 | set_mouse_pos(item_location) 318 | left_click() 319 | 320 | BUY_BUTTON = (582, 193) 321 | set_mouse_pos(BUY_BUTTON) 322 | left_click() 323 | time.sleep(1) 324 | 325 | def on_retry_screen(): 326 | RETRY_LOC, RETRY_COLOR = [(637, 56), (35, 175, 243)] 327 | if check_pixel(RETRY_LOC, RETRY_COLOR): 328 | return True 329 | return False 330 | 331 | def infoBoxLeft(): 332 | expectedVal = (46,195,251) 333 | eVal2 = (251,223,114) 334 | box = (160,339,460,652) 335 | 336 | im = ImageGrab.grab(box) 337 | 338 | inVal2 = im.getpixel((92,84)) 339 | inVal = im.getpixel((24,82)) 340 | 341 | if inVal == expectedVal and inVal2 == eVal2: 342 | print "an InfoBox is on screen" 343 | return True 344 | 345 | def infoBoxMid(): 346 | expectedVal = (28,184,250) 347 | eVal2 = (251,223,114) 348 | box = (160,339,460,652) 349 | 350 | im = ImageGrab.grab(box) 351 | 352 | inVal2 = im.getpixel((230,164)) 353 | inVal = im.getpixel((178,166)) 354 | 355 | if inVal == expectedVal and inVal2 == eVal2: 356 | print "mis-screen InfoBox showing" 357 | return True 358 | 359 | def infoExplosive(): 360 | box = (160,339,460,652) 361 | im = ImageGrab.grab() 362 | 363 | expectedVal = (33,175,235) 364 | inVal = im.getpixel((389,442)) 365 | 366 | eVal2 = (241,216,117) 367 | inVal2 = im.getpixel((445,440)) 368 | 369 | if inVal == expectedVal and inVal2 == eVal2: 370 | print "InfoExplosion Dialog On screen" 371 | return True 372 | 373 | def infoBubble(): 374 | im = ImageGrab.grab() 375 | 376 | inPix = (59,195,246) 377 | if im.getpixel((385,440)) == inPix: 378 | return True 379 | 380 | 381 | 382 | 383 | boxes = [1,1,1,1,1,1] 384 | def play(): 385 | shopping = False 386 | count =0 387 | 388 | while True: 389 | if shopping == False: 390 | if infoBoxLeft() == True: 391 | clickInfo() 392 | boxes[0]=0 393 | 394 | elif infoBoxMid() == True: 395 | set_mouse_pos((345,505)) 396 | left_click() 397 | 398 | elif infoExplosive() == True: 399 | set_mouse_pos((395,440)) 400 | left_click() 401 | if infoBubble(): 402 | set_mouse_pos((385,440)) 403 | left_click() 404 | 405 | else: 406 | eventFinder() 407 | count +=1 408 | print count 409 | if count >15: 410 | left_click() 411 | count=0 412 | eventFinder() 413 | 414 | if runFinished() == True: 415 | if canShop() == True: 416 | set_mouse_pos((695,385)) 417 | left_click() 418 | shopping = True 419 | else: 420 | retry() 421 | time.sleep(2) 422 | 423 | 424 | 425 | else: 426 | 427 | while checkSales() == True: 428 | buy() 429 | exitShop() 430 | time.sleep(2) 431 | play() 432 | 433 | def left_click(): 434 | win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0) 435 | win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0) 436 | print "MOUSE CLICK!!" 437 | time.sleep(.05) 438 | 439 | def main(): 440 | play_area = get_play_area(0) 441 | # print play_area 442 | offset.save_to_json((play_area[0], play_area[1])) 443 | reload(offset) 444 | # print offset.x, offset.y 445 | 446 | im = grab(play_area) 447 | im.save('adfsadsf.png', 'png') 448 | # # print is_spinner_screen() 449 | # # set_mouse_pos((720, 270)) 450 | # # wait_for_needle() 451 | # # left_click() 452 | 453 | # if can_shop(): 454 | # enter_shop() 455 | 456 | # time.sleep(3) 457 | 458 | # for i in range(12): 459 | # time.sleep(3) 460 | pos = win32api.GetCursorPos() 461 | x = pos[0] - offset.x 462 | y = pos[1] - offset.y 463 | print '[(%d, %d), %s]' % (x,y, str(getPixel(x,y))) 464 | print 465 | 466 | print on_retry_screen() 467 | # print on_retry_screen() 468 | # check_sales_and_purcahse() 469 | # print getPixel(631, 54) 470 | 471 | bot_logo = ''' 472 | 473 | 474 | 475 | ____ _ _ 476 | | _ \ (_)| | 477 | | |_) | _ _ _ __ _ __ _ | |_ ___ 478 | | _ < | | | || '__|| '__|| || __| / _ \ 479 | | |_) || |_| || | | | | || |_ | (_) | 480 | |____/ \__,_||_| |_| |_| \__| \___/ 481 | ____ _ 482 | | _ \ | | 483 | | |_) | ___ | |_ 484 | | _ < / _ \ | __| 485 | | |_) || (_) || |_ 486 | |____/ \___/ \__| 487 | 488 | 489 | 490 | 1. Start Bot 491 | 2. Quit 492 | ''' 493 | 494 | if __name__ == '__main__': 495 | print bot_logo 496 | c = input('Select an option:') 497 | 498 | 499 | 500 | 501 | --------------------------------------------------------------------------------