├── .gitignore ├── AITools ├── MemoryHooker.py └── __init__.py ├── README.md ├── environment_render.py ├── ga.py ├── planner.py ├── record_input.py └── test_inputs.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | *.pyc -------------------------------------------------------------------------------- /AITools/MemoryHooker.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | from ctypes.wintypes import * 3 | import struct 4 | import win32com.client 5 | 6 | class MemoryHooker: 7 | def __init__(self, pid): 8 | self.OpenProcess = windll.kernel32.OpenProcess 9 | self.ReadProcessMemory = windll.kernel32.ReadProcessMemory 10 | self.CloseHandle = windll.kernel32.CloseHandle 11 | self.shell = win32com.client.Dispatch("WScript.Shell") 12 | 13 | self.buffer = c_char_p(b'.'*4) 14 | PROCESS_ALL_ACCESS = 0x1F0FFF 15 | self.processHandle = self.OpenProcess(PROCESS_ALL_ACCESS, False, pid) 16 | 17 | def readInt32(self, address): 18 | bufferSize = 4 19 | bytesRead = c_ulong(0) 20 | 21 | if self.ReadProcessMemory(self.processHandle, address, self.buffer, bufferSize, byref(bytesRead)): 22 | v = self.buffer.value 23 | while len(v) < 4: 24 | v = v + b'\0' 25 | return struct.unpack('=13)&(map<=31)) 52 | |((map>=33)&(map<=37)) 53 | |((map>=39)&(map<=41)) 54 | |((map>=64)&(map<=66))] = 1 55 | 56 | map[(map==38) 57 | |((map>=42)&(map<=57)) 58 | |(map==67)] = 0 59 | 60 | map[(map==5) 61 | |(map==6) 62 | |((map>=9)&(map<=12)) 63 | |(map==38) 64 | |((map>=58)&(map<=63))] = -1 65 | 66 | # Coin 67 | map[(map==68)] = -2 68 | 69 | # Portal 70 | map[(map==32)] = -3 71 | 72 | # Super Jump 73 | map[(map>=69)&(map<=78)] = -4 74 | 75 | # Crop it till the first portal 76 | #map = map[:,0:500] 77 | 78 | # Pad it out to give each cell 26x26 pixels 79 | map = np.repeat(map, 26, axis=0) 80 | map_original = np.repeat(map, 26, axis=1) 81 | 82 | ## Get one in every 9 pixels (that's our horizontal move speed) 83 | map = map_original[:,offset_x:-1:speed_x] 84 | 85 | def isSolid(map, x,y,dx,dy, mode=1): 86 | x_map = x*speed_x + dx 87 | y_map = y + dy 88 | 89 | if y_map < 0: 90 | return True 91 | if mode == 2 and y_map > 26*10: 92 | return True 93 | elif map_original[int(y_map), x_map] == 1: 94 | return True 95 | return False 96 | 97 | def isJump(i): 98 | return i==1 99 | 100 | def evaluateInput(input, map): 101 | y = 0 102 | sy = 0 103 | jump = 0 104 | y_prev = 0 105 | dead = 0 106 | coins = 0 107 | jump_count = 0 108 | mode = 1 109 | 110 | map_img = np.array((map - map.min()) / (map.max() - map.min()) * 255, np.uint8) 111 | map_img = cv2.cvtColor(map_img, cv2.COLOR_GRAY2BGR) 112 | 113 | """ 114 | map_img = np.flip(map_img, axis=0) 115 | map_out = np.zeros(shape=map_img.shape, dtype=np.uint8) 116 | for i in range((map_img.shape[1]-13)//9): 117 | map_out[:,i*2] = map_img[:,i*9+13] 118 | 119 | cv2.imshow("test",map_out) 120 | cv2.waitKey() 121 | """ 122 | 123 | ## Draw inputs 124 | border = 30 125 | map_img = cv2.copyMakeBorder(map_img, top=border, bottom=0, left=0, right=0, borderType=cv2.BORDER_CONSTANT, 126 | value=[0, 0, 0]) 127 | for i in range(len(input)): 128 | if isJump(input[i]): 129 | map_img[0:border, i] = [255, 255, 0] 130 | 131 | ## Simulate inputs 132 | dt_since_mode_change = 0 133 | for i in range(1, len(input)): 134 | dt_since_mode_change += 1 135 | tile = map[int(y), i - 1] 136 | y_prev = int(y) 137 | 138 | override = False 139 | for e in user_enable: 140 | if i >= e[0] and i <= e[1]: 141 | override = True 142 | break 143 | if override: 144 | continue 145 | 146 | if mode == 1: 147 | ## Cube mode 148 | if tile == -3 and dt_since_mode_change > 10: 149 | ## Hit the portal 150 | mode = 2 151 | dt_since_mode_change = 0 152 | 153 | if tile == -4: 154 | ## Super jump 155 | sy = 20 156 | jump = 2000 157 | 158 | ## TODO: Air Jump 159 | ## TODO: Bounce jump 160 | 161 | if isSolid(map, i,y,13,0) or isSolid(map, i,y,13,20): 162 | if y%26 > 14: 163 | ny = (np.floor(y/26)+1)*26 164 | if isSolid(map, i, ny, 13, 0) or isSolid(map, i, ny, 13, 20): 165 | dead = 1 166 | break 167 | else: 168 | y = ny - sy + 3 169 | else: 170 | dead = 1 171 | break 172 | 173 | if jump <= 2: 174 | if input[i]: 175 | jump = 501 176 | sy = 23 177 | 178 | if jump < 2000: 179 | if sy < 4: 180 | sy -= 2.25 181 | else: 182 | sy -= 4 183 | 184 | else: 185 | if jump < 2009: 186 | if sy < 39: 187 | sy += 3 188 | else: 189 | sy = 18 190 | jump = 1000 191 | 192 | 193 | y += sy 194 | if sy > 0: 195 | if isSolid(map, i,y,-12,20) or isSolid(map, i,y,12,20): 196 | dead = 1 197 | break 198 | 199 | jump += 1 200 | else: 201 | fixed_height = False 202 | while isSolid(map, i, y, -12, 0) or isSolid(map, i, y, 12, 0): 203 | y = (np.floor(y/26)+1)*26 204 | fixed_height = True 205 | 206 | if fixed_height: 207 | sy = 0 208 | jump = 0 209 | else: 210 | jump += 1 211 | 212 | elif mode == 2: 213 | ## Fly 214 | if map[int(y), i] == -2 and dt_since_mode_change > 10: 215 | ## Hit the portal 216 | mode = 1 217 | dt_since_mode_change = 0 218 | """ 219 | if isSolid(map,i,y,13,1, mode=2) or isSolid(map,i,y,-13,24, mode=2): 220 | dead = 1 221 | break 222 | 223 | if input[i]==1: 224 | sy += 1.2 225 | if sy > 9: 226 | sy = 9 227 | else: 228 | sy -= 1 229 | if sy < -9: 230 | sy = -9 231 | 232 | y += sy 233 | 234 | if isSolid(map,i,y,-12,13, mode=2) or isSolid(map,i,y,12,13, mode=2): 235 | dead = 1 236 | break 237 | if sy>0: 238 | fixed_height = False 239 | while isSolid(map,i, y, -12, 25, mode=2) or isSolid(map, i, y, 12, 25, mode=2): 240 | y = (np.floor((y+25)/26)-1)*26 241 | fixed_height = True 242 | 243 | if y < 0: 244 | dead = 1 245 | break 246 | 247 | if dead: 248 | break 249 | 250 | if fixed_height: 251 | sy *= 0.5 252 | else: 253 | fixed_height = False 254 | while isSolid(map, i, y, -12, 0, mode=2) or isSolid(map, i, y, 12, 0, mode=2): 255 | y = (np.floor(y/26) + 1) * 26 256 | fixed_height = True 257 | 258 | if y > 800: 259 | dead = 1 260 | break 261 | if dead: 262 | break 263 | if fixed_height: 264 | sy *= 0.5 265 | """ 266 | 267 | #if (int(y) >= height * 26) or (map[int(y), i] == -1): 268 | # dead = 1 269 | #elif (i == len(input) - 1) or (map[int(y), i + 1] == 1): 270 | # dead = 1 271 | if tile == -1: 272 | dead = 1 273 | 274 | if i > 0: 275 | map_img = cv2.line(map_img, (i - 1, y_prev + border), (i, int(y) + border), [0, 0, 255], thickness=2) 276 | 277 | if dead: 278 | break 279 | 280 | score = (i*speed_x)//26 + 25*coins# - jump_count 281 | 282 | #max_map_img = np.flip(map_img, axis=0) 283 | #cv2.imshow("Trajectory", max_map_img) 284 | #cv2.waitKey(1) 285 | 286 | return i, score, map_img 287 | 288 | 289 | def playGame(input): 290 | hook = MemoryHooker.MemoryHooker(pid) 291 | keyboard = MemoryHooker.Keyboard() 292 | 293 | ## Unpause 294 | keyboard.pressKey(0x19) #P 295 | time.sleep(2/fps) 296 | keyboard.releaseKey(0x19) 297 | 298 | while 1: 299 | value = hook.readInt32(address) 300 | x = (value - 6) // 8 301 | i = ((x - offset_x) // speed_x)+2 ## Small adjustment because the map is shifted for some reason in-game 302 | if (i >= len(input)): 303 | ## Dead 304 | keyboard.releaseKey(0x11) # Release W 305 | break 306 | else: 307 | #print(value, x, i, input[i]) 308 | jump = input[i] 309 | for e in user_enable: 310 | if i>=e[0] and i<=e[1]: 311 | jump = user_inputs[i] 312 | break 313 | 314 | if jump: 315 | keyboard.pressKey(0x11) # Press W 316 | else: 317 | keyboard.releaseKey(0x11) # Release W 318 | ##hook.sendKeys("w") 319 | 320 | time.sleep(1 / fps) 321 | 322 | count = 0 323 | while 1: 324 | value = hook.readInt32(address) 325 | x = (value - 6) // 8 326 | i = (x - offset_x) // speed_x 327 | if (i < len(input)): 328 | ## Respawned 329 | count += 1 330 | 331 | if count > 4: 332 | break 333 | 334 | time.sleep(1 / fps) 335 | 336 | ## Pause 337 | keyboard.pressKey(0x19) # P 338 | time.sleep(2 / fps) 339 | keyboard.releaseKey(0x19) 340 | 341 | hook.close() 342 | 343 | 344 | import random 345 | from deap import base, creator, tools 346 | 347 | n_genes = map.shape[1] 348 | n_individuals = 50 349 | n_parents = 3 #int(n_individuals*0.05) 350 | frames_lookback_mutate = 50 #How many frames to look back for mutation 351 | frames_lookforward_mutate = 0 #How many frames to look back for mutation 352 | p_jump = 0.05 #Initial probability of each gene signalling jump 353 | p_mutate = 0.8 # Probability of mutating at all after evaluation 354 | p_gene_mutate = 0.1 #Probability of a single gene flipping 355 | p_cross = 0.0 #Probability two creatures are crossed 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | def mutate(individual, indpb): 367 | """Flip the value of the attributes of the input individual and return the 368 | mutant. The *individual* is expected to be a :term:`sequence` and the values of the 369 | attributes shall stay valid after the ``not`` operator is called on them. 370 | The *indpb* argument is the probability of each attribute to be 371 | flipped. This mutation is usually applied on boolean individuals. 372 | :param individual: Individual to be mutated. 373 | :param indpb: Independent probability for each attribute to be flipped. 374 | :returns: A tuple of one individual. 375 | This function uses the :func:`~random.random` function from the python base 376 | :mod:`random` module. 377 | """ 378 | ## Only mutate the last "lookback" number of genes and the next "frames_lookforward_mutate" genes 379 | max_idx = int(individual.fitness.values[1]+frames_lookforward_mutate) 380 | min_idx = max(0, max_idx-frames_lookback_mutate) 381 | 382 | for i in range(min_idx, max_idx): 383 | if random.random() < indpb: 384 | individual[i] = type(individual[i])(not individual[i]) 385 | 386 | return individual, 387 | 388 | def randGene(): 389 | if random.random() <= p_jump: 390 | return 1 391 | return 0 392 | 393 | 394 | creator.create("FitnessMax", base.Fitness, weights=(1,1)) 395 | creator.create("Individual", list, fitness=creator.FitnessMax) 396 | 397 | toolbox = base.Toolbox() 398 | #toolbox.register("attr_bool", random.randint, 0, 1) 399 | toolbox.register("attr_bool", randGene) 400 | toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, n_genes) 401 | toolbox.register("population", tools.initRepeat, list, toolbox.individual) 402 | 403 | toolbox.register("evaluate", evaluateInput) 404 | toolbox.register("mate", tools.cxTwoPoint) 405 | #toolbox.register("mutate", tools.mutFlipBit, indpb=p_gene_mutate) 406 | toolbox.register("mutate", mutate, indpb=p_gene_mutate) 407 | toolbox.register("select", tools.selTournament, tournsize=n_parents) 408 | 409 | 410 | def main(cp_file): 411 | random.seed(8) 412 | 413 | if os.path.isfile(cp_file): 414 | with open(cp_file, "rb") as f: 415 | cp = pickle.load(f) 416 | 417 | pop = cp["population"] 418 | g = cp["generation"] 419 | 420 | diff = map.shape[1]-len(pop[0]) 421 | if diff>0: 422 | diff = list(np.zeros((diff))) 423 | for i in pop: 424 | i.extend(diff) 425 | 426 | else: 427 | pop = toolbox.population(n=n_individuals) 428 | g = 0 429 | 430 | print("Start of evolution") 431 | 432 | # Evaluate the entire population 433 | for ind in pop: 434 | xpos, score, map_img = toolbox.evaluate(ind, map) 435 | ind.fitness.values = (score, xpos) 436 | 437 | print(" Evaluated %i individuals" % len(pop)) 438 | 439 | # Extracting all the fitnesses of 440 | fits = [ind.fitness.values[0] for ind in pop] 441 | 442 | # Variable keeping track of the number of generations 443 | prev_max_score = 0 444 | prev_max_dist = 0 445 | 446 | # Begin the evolution 447 | while g < 1000: #and max(fits) < 960 448 | # A new generation 449 | g = g + 1 450 | print("-- Generation %i --" % g) 451 | 452 | # Select the next generation individuals 453 | offspring_temp = toolbox.select(pop, len(pop)) 454 | # Clone the selected individuals 455 | offspring = [] 456 | for o in offspring_temp: 457 | offspring.append(toolbox.clone(o)) 458 | 459 | # Apply crossover and mutation on the offspring 460 | for child1, child2 in zip(offspring[::2], offspring[1::2]): 461 | if random.random() < p_cross: 462 | toolbox.mate(child1, child2) 463 | 464 | # fitness values of the children 465 | # must be recalculated later 466 | #del child1.fitness.values 467 | #del child2.fitness.values 468 | 469 | for mutant in offspring: 470 | if random.random() < p_mutate: 471 | toolbox.mutate(mutant) 472 | del mutant.fitness.values 473 | 474 | # Evaluate the individuals with an invalid fitness 475 | max_score = -np.inf 476 | max_map_img = None 477 | max_ind = None 478 | 479 | invalid_ind = [ind for ind in offspring if not ind.fitness.valid] 480 | for ind in invalid_ind: 481 | xpos, score, map_img = toolbox.evaluate(ind, map) 482 | ind.fitness.values = (score, xpos) 483 | 484 | if score > max_score: 485 | max_score = score 486 | max_dist = ((xpos*9)+13)//26 487 | max_map_img = map_img 488 | max_ind = ind 489 | 490 | print(" Evaluated %i individuals" % len(invalid_ind)) 491 | 492 | # The population is entirely replaced by the offspring 493 | pop[:] = offspring 494 | 495 | # Gather all the fitnesses in one list and print the stats 496 | fits = [ind.fitness.values[0] for ind in pop] 497 | 498 | length = len(pop) 499 | mean = sum(fits) / length 500 | sum2 = sum(x * x for x in fits) 501 | std = abs(sum2 / length - mean ** 2) ** 0.5 502 | 503 | print(" Min %s" % min(fits)) 504 | print(" Max %s" % max(fits)) 505 | print(" Avg %s" % mean) 506 | print(" Std %s" % std) 507 | 508 | 509 | ## Create a checkpoint 510 | if g%cp_freq == 0: 511 | cp = dict(population=pop, generation=g) 512 | with open(cp_file, "wb") as f: 513 | pickle.dump(cp, f) 514 | 515 | 516 | ## Let's see our progress 517 | print(max_ind) 518 | 519 | max_map_img = np.flip(max_map_img, axis=0) 520 | max_map_img = max_map_img.astype(np.uint8) 521 | max_map_img = cv2.putText(max_map_img, "Generation %4d" % g, (20, 40), cv2.FONT_HERSHEY_DUPLEX, 1.0, (0, 0, 0), 522 | lineType=cv2.LINE_AA, bottomLeftOrigin=False) 523 | max_map_img = cv2.putText(max_map_img, "Fitness: %4d" % max_score, (20, 70), cv2.FONT_HERSHEY_DUPLEX, 1.0, (0, 0, 0), 524 | lineType=cv2.LINE_AA, bottomLeftOrigin=False) 525 | max_map_img = cv2.putText(max_map_img, "Distance: %4d" % max_dist, (20, 100), cv2.FONT_HERSHEY_DUPLEX, 1.0, 526 | (0, 0, 0), 527 | lineType=cv2.LINE_AA, bottomLeftOrigin=False) 528 | cv2.imshow("Best trajectory", max_map_img) 529 | cv2.moveWindow("Best trajectory", 0, 0) 530 | cv2.waitKey(1) 531 | 532 | #if max_score >= prev_max_score + video_points: 533 | if max_dist >= prev_max_dist + video_dist: 534 | prev_max_score = max_score 535 | prev_max_dist = max_dist 536 | playGame(max_ind) 537 | 538 | if max_ind.fitness.values[1] >= map.shape[1]: 539 | break 540 | 541 | print("-- End of (successful) evolution --") 542 | 543 | best_ind = tools.selBest(pop, 1)[0] 544 | print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values)) 545 | 546 | 547 | if __name__ == "__main__": 548 | main(cp_file) -------------------------------------------------------------------------------- /record_input.py: -------------------------------------------------------------------------------- 1 | from AITools import MemoryHooker 2 | import time 3 | import numpy as np 4 | import keyboard 5 | 6 | pid = 0x47E8 7 | address = 0x09003FB8 8 | fps = 60 9 | 10 | inputs = np.zeros((1240), dtype=np.int8) 11 | 12 | def main(): 13 | hook = MemoryHooker.MemoryHooker(pid) 14 | #keyboard = MemoryHooker.Keyboard() 15 | while 1: 16 | value = hook.readInt32(address) 17 | x = (value-6)//8 18 | i = (x-13)//9 19 | 20 | if keyboard.is_pressed('w'): 21 | inputs[i] = 1 22 | 23 | if i>1240: 24 | break 25 | 26 | time.sleep(1/fps) 27 | 28 | hook.close() 29 | 30 | if __name__ == "__main__": 31 | try: 32 | main() 33 | except KeyboardInterrupt: 34 | pass -------------------------------------------------------------------------------- /test_inputs.py: -------------------------------------------------------------------------------- 1 | from AITools import MemoryHooker 2 | import time 3 | 4 | pid = 0x2940 5 | address = 0x1A32F178 6 | fps = 60 7 | 8 | ##This completes the first portion 9 | #inputs = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 10 | 11 | inputs = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 12 | 13 | 14 | def main(): 15 | hook = MemoryHooker.MemoryHooker(pid) 16 | keyboard = MemoryHooker.Keyboard() 17 | while 1: 18 | value = hook.readInt32(address) 19 | x = (value-6)//8 20 | i = (x-13)//9 21 | if (i > len(inputs)): 22 | ## Dead 23 | keyboard.releaseKey(0x11) # Release W 24 | else: 25 | print(value, x, i, inputs[i]) 26 | if inputs[i]: 27 | keyboard.pressKey(0x11) # Press W 28 | else: 29 | keyboard.releaseKey(0x11) # Release W 30 | ##hook.sendKeys("w") 31 | 32 | time.sleep(1/fps) 33 | 34 | hook.close() 35 | 36 | if __name__ == "__main__": 37 | try: 38 | main() 39 | except KeyboardInterrupt: 40 | pass --------------------------------------------------------------------------------