This repository was archived by the owner on Aug 1, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmain.py
420 lines (340 loc) · 12.6 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# ROUBOT
# Author: Zach Moore - Zach@NerdVenture.net
from PIL import ImageGrab, ImageEnhance, ImageFilter, ImageOps
import PIL
import win32api, win32con, win32gui
import pytesseract
import keyboard
from easysettings import EasySettings
import datetime, time, json, os
# ------------- Globals ------------- #
# load the config
config = EasySettings('config.conf')
# true if we have a window handle
windowset = False
# the windows api hwnd for the game window
activeWin = 0
# true if the bot is ready to start
initdone = False
reds = [1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36]
colorLog = [] # stores previously drawn colors
lastdiff = 0 # the financial gain or loss from the last round
balance = 0
betAmount = 1
startTime = datetime.datetime.now()
maxbet = 1 # for keeping track of the maximum bet that has been placed for the session
startBalance = 0
streakThresh = 2 # the threshold for what is considered a streak -- ex: 2 reds in a row
skipRound = False # used after a loss to skip one round (Try to hedge against long losing streaks?)
# these are configured to hold the coords of the important places on the game board that
#the bot will use to read data and play the game
spinBtnCoords = []
clearBtnCoords = []
redCoords = []
blackCoords = []
numblock = ()
balanceBlock = ()
# ------------- Windows Api ------------- #
def leftClick():
''' Simulate a mouse click '''
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0)
time.sleep(.1)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0)
def mousePos(coord):
''' move the mouse '''
x,y = win32gui.ClientToScreen(activeWin, (coord[0], coord[1]))
win32api.SetCursorPos((x, y))
def get_coords(e):
''' get the client coords '''
x,y = win32api.GetCursorPos()
x,y = win32gui.ScreenToClient(activeWin, (x, y))
print (x,y)
return x,y
# ------------- Functions to read data from the screen with PIL and Tesseract-OCR ------------- #
def sreenGrab(box):
''' grab a screenshot of the game - box is the area of the screen to capture '''
global activeWin
x1, y1 = win32gui.ClientToScreen(activeWin, (box[0], box[1]))
x2, y2 = win32gui.ClientToScreen(activeWin, (box[2], box[3]))
img = ImageGrab.grab((x1, y1, x2, y2))
# These image manipulations are required to help tesseract read the data properly
img = img.convert('L') # greyscale
img = img.resize((img.size[0]*3, img.size[1]*3), 1)
img = ImageEnhance.Contrast(img).enhance(5.0)
img = ImageOps.equalize(img)
img = ImageOps.invert(img)
return img
def getNumber(e):
''' capture the last drawn number and convert it into a python int
some numbers require all 3 steps to get a readable number '''
try:
img = sreenGrab(numblock)
img = img.filter(ImageFilter.MaxFilter(5))
# img.save(os.getcwd() + '\\full_snap__' + str(int(time.time())) + '.png', 'PNG') # for debugging
numStr = pytesseract.image_to_string(img, lang='eng', config='--psm 10 --oem 3 digits')
print(numStr)
return int(numStr)
except:
try:
img = sreenGrab(numblock)
img = img.filter(ImageFilter.MaxFilter(3))
# img.save(os.getcwd() + '\\full_snap__' + str(int(time.time())) + '.png', 'PNG') # for debugging
numStr = pytesseract.image_to_string(img, lang='eng', config='--psm 10 --oem 3 digits')
print(numStr)
return int(numStr)
except:
try:
img = sreenGrab(numblock)
img = img.filter(ImageFilter.MinFilter(1))
# img.save(os.getcwd() + '\\full_snap__' + str(int(time.time())) + '.png', 'PNG') # for debugging
numStr = pytesseract.image_to_string(img, lang='eng', config='--psm 10 --oem 3 digits')
print(numStr)
return int(numStr)
except:
return 0
def getBalance(e):
''' captures the balance and converts it into a Python float '''
img = sreenGrab(balanceBlock)
try:
img = img.filter(ImageFilter.MaxFilter(5))
# img.save(os.getcwd() + '\\full_snap__' + str(int(time.time())) + '.png', 'PNG') # for debugging
numStr = pytesseract.image_to_string(img, lang='eng', config='--psm 10 --oem 3 digits')
return float(numStr)
except:
# img.save(os.getcwd() + '\\full_snap__' + str(int(time.time())) + '.png', 'PNG') # for debugging
numStr = pytesseract.image_to_string(img, lang='eng', config='--psm 10 --oem 3 digits')
# handle edge case where multiple 1's read for instance 1,001.00 --> 1.001.00
numparts = list(numStr) # each character in a list
if len(numparts) > 6: # large enough number to matter
del numparts[-7] # remove comma that is a dot
numStr = ''.join(numparts) # recreate string
return float(numStr)
# ------------- Functions used by the bot to place bets, hit buttons, and play the game ------------- #
def getLastDrawnColor(e):
''' returns the color of the last drawn number '''
num = getNumber(e)
if num == 0:
return 'green'
elif num in reds:
return 'red'
else:
return 'black'
def getOppCol(col):
''' returns the opposite of the last drawn number '''
if col == 'red':
return 'black'
return 'red'
def getStreak(e):
''' determine whether or not a streak is occuring or has just previously '''
global streakThresh
if len(colorLog) < streakThresh: # not enough data to determine if a streak has occured
return False, False
last5 = colorLog[-streakThresh:]
prev5 = colorLog[-(streakThresh + 1):-1]
# returns (is on a streak), (just had a streak)
return (len(set(last5)) <= 1), (len(set(prev5)) <= 1)
def spin(e):
''' hit the spin button '''
mousePos(spinBtnCoords)
leftClick()
def clear(e):
''' hit the clear button '''
mousePos(clearBtnCoords)
leftClick()
def betRed(e):
''' bets red '''
mousePos(redCoords)
leftClick()
def betBlack(e):
''' bets black '''
mousePos(blackCoords)
leftClick()
def placeBets(col):
''' places (betAmount) of bets on the given color '''
if col == 'both':
for i in range(0, betAmount):
betRed(0)
betBlack(0)
elif col == 'red':
for i in range(0, betAmount):
betRed(0)
else:
for i in range(0, betAmount):
betBlack(0)
# ------------- Functions for initializing ------------- #
def setWindow():
''' allow the user to select the game window '''
global windowset
global activeWin
print('click the game window and press enter')
keyboard.wait('Enter')
hwnd = win32gui.GetForegroundWindow()
activeWin = hwnd
win32gui.MoveWindow(hwnd, 0, 0, 1920, 1080, True)
windowset = True
# exit()
def stopGame(e):
''' end the game '''
quit()
def init():
''' make sure the game window is set,
gives the user a chance to configure the game,
bring the game window forward and sets the inital balance var '''
global startBalance
global initdone
global spinBtnCoords
global clearBtnCoords
global redCoords
global blackCoords
global numblock
global balanceBlock
global config
if initdone:
return
if not windowset:
setWindow()
# load in an existing config
if config.has_option('spinBtnCoords'):
spinBtnCoords = config.get('spinBtnCoords')
clearBtnCoords = config.get('clearBtnCoords')
redCoords = config.get('redCoords')
blackCoords = config.get('blackCoords')
numblock = config.get('numblock')
balanceBlock = config.get('balanceBlock')
doConfig = input('Would you like to configure? [y, n]')
if doConfig == 'y':
configure(0)
# brings the window forward - TODO should be it's own function
win32gui.BringWindowToTop(activeWin)
win32gui.ShowWindow(activeWin, 3) # force maximize
win32gui.SetForegroundWindow(activeWin)
win32gui.SetActiveWindow(activeWin)
startBalance = getBalance(0)
initdone = True
def configure(e):
''' walks the user through capturing the positions and bounding boxes of each
of the relavent areas on the game screen - then saves them in a config file '''
global spinBtnCoords
global clearBtnCoords
global redCoords
global blackCoords
global numblock
global balanceBlock
global config
print('move mouse over left top corner of balance area and press enter')
balancebox = []
keyboard.wait('enter')
x,y = get_coords(0)
balancebox.append(x)
balancebox.append(y)
print('move mouse over bottom right corner of balance area and press enter')
keyboard.wait('enter')
x,y = get_coords(0)
balancebox.append(x)
balancebox.append(y)
print('move mouse over left top corner of number area and press enter')
numbox = []
keyboard.wait('enter')
x,y = get_coords(0)
numbox.append(x)
numbox.append(y)
print('move mouse over bottom right corner of number area and press enter')
keyboard.wait('enter')
x,y = get_coords(0)
numbox.append(x)
numbox.append(y)
print('move mouse over the spin button and press enter')
keyboard.wait('enter')
spinarea = get_coords(e)
print('move mouse over the clear button and press enter')
keyboard.wait('enter')
cleararea = get_coords(e)
print('move mouse over the red area and press enter')
keyboard.wait('enter')
redarea = get_coords(e)
print('move mouse over the black area and press enter')
keyboard.wait('enter')
blackarea = get_coords(e)
spinBtnCoords = spinarea
clearBtnCoords = cleararea
redCoords = redarea
blackCoords = blackarea
numblock = tuple(numbox)
balanceBlock = tuple(balancebox)
# save the new config
config.set('spinBtnCoords', spinBtnCoords)
config.set('clearBtnCoords', clearBtnCoords)
config.set('redCoords', redCoords)
config.set('blackCoords', blackCoords)
config.set('numblock', numblock)
config.set('balanceBlock', balanceBlock)
config.save()
# ------------- Functions for playing the game ------------- #
# TODO: it'd be nice to abstract the actual strategy in some way so that
# multiple strategies could be play in one session or based on certain
# conditions
def playGame(e):
''' called each loop to analyze the numbers and balance - then make bets and spin '''
global windowset
global lastdiff
global balance
global strategyIndex
global strategy
global betAmount
global maxbet
global startTime
global skipRound
print('Started: ' + str(startTime))
profit = getBalance(0) - startBalance
print('Session Profit: $' + str(profit))
print('max bet amount: ' + str(maxbet))
clear(0)
time.sleep(2) # TODO: need a way to know when it's time to clear rather than depending on timing
# the net gain or loss from the last round
lastdiff = getBalance(0) - balance
balance = getBalance(0)
print('Balance: $' + str(balance))
# log num color
lastcol = getLastDrawnColor(0)
if not (lastcol == 'green'):
colorLog.append(lastcol)
# if we took a loss last round, double the bet amount:
if lastdiff < 0:
if not skipRound:
betAmount *= 2
skipRound = not skipRound
else:
if not skipRound:
betAmount = 1
skipRound = False
# check if betamount is greater than max bet and set
if betAmount > maxbet:
maxbet = betAmount # keep track of the maximum bet for the session
streak, recentStreak = getStreak(0)
# place bets
# if streak - chase the streak - bet same color as last drawn
if skipRound:
placeBets('both')
time.sleep(1)
elif streak:
print('STREAK')
placeBets(lastcol)
time.sleep(1)
else: # not on a streak
placeBets(getOppCol(lastcol)) # bet opposite
time.sleep(1)
# spin
spin(0)
print('Round complete \n\n')
# ------------- Keyboard hooks for convinence and debugging ------------- #
keyboard.on_press_key('a', get_coords)
keyboard.on_press_key('q', playGame)
keyboard.on_press_key('w', stopGame)
keyboard.on_press_key('c', configure)
def main():
init()
while 1:
playGame(0) # comment out to use keyboard hooks for debugging
time.sleep(13) # sleep to give the game time to be ready to accept a hit on the clear button
if __name__ == '__main__':
main()