forked from tanjeffreyz/auto-maple
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbot.py
191 lines (161 loc) · 6.83 KB
/
bot.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
"""An interpreter that reads and executes user-created routines."""
import config
import detection
import threading
import time
import cv2
import mss
import mss.windows
import utils
import inspect
import components
import numpy as np
from os.path import splitext, basename
from routine import Routine
from components import Point
from vkeys import press, click
# The rune's buff icon
RUNE_BUFF_TEMPLATE = cv2.imread('assets/rune_buff_template.jpg', 0)
class Bot:
"""A class that interprets and executes user-defined routines."""
def __init__(self):
"""Loads a user-defined routine on start up and initializes this Bot's main thread."""
config.bot = self
self.rune_active = False
self.rune_pos = (0, 0)
self.rune_closest_pos = (0, 0) # Location of the Point closest to rune
self.buff = components.Buff()
self.command_book = {}
for c in (components.Wait, components.Walk, components.Fall,
components.Move, components.Adjust, components.Buff):
self.command_book[c.__name__.lower()] = c
config.routine = Routine()
self.ready = False
self.thread = threading.Thread(target=self._main)
self.thread.daemon = True
def start(self):
"""
Starts this Bot object's thread.
:return: None
"""
print('\n[~] Started main bot loop.')
self.thread.start()
def _main(self):
"""
The main body of Bot that executes the user's routine.
:return: None
"""
print('\n[~] Initializing detection algorithm...\n')
model = detection.load_model()
print('\n[~] Initialized detection algorithm.')
mss.windows.CAPTUREBLT = 0
with mss.mss() as sct:
self.ready = True
config.listener.enabled = True
while True:
if config.enabled and len(config.routine) > 0:
self.buff.main()
# Highlight the current Point
config.gui.view.routine.select(config.routine.index)
config.gui.view.details.display_info(config.routine.index)
# Execute next Point in the routine
element = config.routine[config.routine.index]
if self.rune_active and isinstance(element, Point) \
and element.location == self.rune_closest_pos:
self._solve_rune(model, sct)
element.execute()
config.routine.step()
else:
time.sleep(0.01)
@utils.run_if_enabled
def _solve_rune(self, model, sct):
"""
Moves to the position of the rune and solves the arrow-key puzzle.
:param model: The TensorFlow model to classify with.
:param sct: The mss instance object with which to take screenshots.
:return: None
"""
move = self.command_book['move']
move(*self.rune_pos).execute()
adjust = self.command_book['adjust']
adjust(*self.rune_pos).execute()
time.sleep(0.2)
press('y', 1, down_time=0.2) # Press 'y' to interact with rune in-game
print('\nSolving rune:')
inferences = []
for _ in range(15):
frame = np.array(sct.grab(config.MONITOR))
solution = detection.merge_detection(model, frame)
if solution:
print(', '.join(solution))
if solution in inferences:
print('Solution found, entering result.')
for arrow in solution:
press(arrow, 1, down_time=0.1)
time.sleep(1)
for _ in range(3):
time.sleep(0.3)
frame = np.array(sct.grab(config.MONITOR))
rune_buff = utils.multi_match(frame[:frame.shape[0]//8, :],
RUNE_BUFF_TEMPLATE,
threshold=0.9)
if rune_buff:
rune_buff_pos = min(rune_buff, key=lambda p: p[0])
click(rune_buff_pos, button='right')
break
elif len(solution) == 4:
inferences.append(solution)
self.rune_active = False
def load_commands(self, file):
"""Prompts the user to select a command module to import. Updates config's command book."""
utils.print_separator()
print(f"[~] Loading command book '{basename(file)}':")
ext = splitext(file)[1]
if ext != '.py':
print(f" ! '{ext}' is not a supported file extension.")
return False
new_step = components.step
new_cb = {}
for c in (components.Wait, components.Walk, components.Fall):
new_cb[c.__name__.lower()] = c
# Import the desired command book file
module_name = splitext(basename(file))[0]
module = __import__(f'command_books.{module_name}', fromlist=[''])
# Check if the 'step' function has been implemented
step_found = False
for name, func in inspect.getmembers(module, inspect.isfunction):
if name.lower() == 'step':
step_found = True
new_step = func
# Populate the new command book
for name, command in inspect.getmembers(module, inspect.isclass):
new_cb[name.lower()] = command
# Check if required commands have been implemented and overridden
required_found = True
for command in [components.Buff]:
name = command.__name__.lower()
if name not in new_cb:
required_found = False
new_cb[name] = command
print(f" ! Error: Must implement required command '{name}'.")
# Look for overridden movement commands
movement_found = True
for command in (components.Move, components.Adjust):
name = command.__name__.lower()
if name not in new_cb:
movement_found = False
new_cb[name] = command
if not step_found and not movement_found:
print(f" ! Error: Must either implement both the 'move' and 'adjust' commands, "
f"or the function 'step'.")
if required_found and (step_found or movement_found):
self.command_book = new_cb
self.buff = new_cb['buff']()
components.step = new_step
config.gui.view.status.set_cb(basename(file))
config.routine.clear()
print(f"[~] Successfully loaded command book '{module_name}'.")
return True
else:
print(f"[!] Command book '{module_name}' was not loaded.")
return False