Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making a cross-platform product #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Inspired by [https://github.com/ivanseidel/IAMDinosaur](https://github.com/ivans
* Run the AI: `python ai.py`
* After 5/8 generations, the dino should be a ninja!

## FAQ
* Try this first is you have problem installing pyHook: https://stackoverflow.com/questions/48327695/pyhook-for-python-3-6
* You maybe should install this if you still cannot install pyHook: https://nchc.dl.sourceforge.net/project/pywin32/pywin32/Build%20221/pywin32-221.win-amd64-py3.6.exe

## TODO

- [ ] Find game automatically in the screen at the beginning
Expand Down
11 changes: 11 additions & 0 deletions dino-ml-win/ai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from generation import Generation

def main():
generation = Generation()
while True:
generation.execute()
generation.keep_best_genomes()
generation.mutations()

if __name__ == '__main__':
main()
70 changes: 70 additions & 0 deletions dino-ml-win/generation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from scanner import Scanner
from network import Network
from time import sleep
import numpy as np
import pykeyboard
import random
import copy

class Generation:
def __init__(self):
self.__genomes = [Network() for i in range(12)]
self.__best_genomes = []

def execute(self):
k = pykeyboard.PyKeyboard()
scanner = Scanner()
scanner.find_game()
for genome in self.__genomes:
scanner.reset()
k.press_keys([k.control_l_key, 'r'])
k.release_key(k.control_l_key)
sleep(1)
k.press_key(k.space)
while True:
try:
obs = scanner.find_next_obstacle()
inputs = [obs['distance'] / 1000, obs['length'], obs['speed'] / 10]
outputs = genome.forward(np.array(inputs, dtype=float))
if outputs[0] > 0.55:
k.press_key(k.space)
except:
break
genome.fitness = scanner.get_fitness()

def keep_best_genomes(self):
self.__genomes.sort(key=lambda x: x.fitness, reverse=True)
self.__genomes = self.__genomes[:4]
self.__best_genomes = self.__genomes[:]

def mutations(self):
while len(self.__genomes) < 10:
genome1 = random.choice(self.__best_genomes)
genome2 = random.choice(self.__best_genomes)
self.__genomes.append(self.mutate(self.cross_over(genome1, genome2)))
while len(self.__genomes) < 12:
genome = random.choice(self.__best_genomes)
self.__genomes.append(self.mutate(genome))

def cross_over(self, genome1, genome2):
new_genome = copy.deepcopy(genome1)
other_genome = copy.deepcopy(genome2)
cut_location = int(len(new_genome.W1) * random.uniform(0, 1))
for i in range(cut_location):
new_genome.W1[i], other_genome.W1[i] = other_genome.W1[i], new_genome.W1[i]
cut_location = int(len(new_genome.W2) * random.uniform(0, 1))
for i in range(cut_location):
new_genome.W2[i], other_genome.W2[i] = other_genome.W2[i], new_genome.W2[i]
return new_genome

def __mutate_weights(self, weights):
if random.uniform(0, 1) < 0.2:
return weights * (random.uniform(0, 1) - 0.5) * 3 + (random.uniform(0, 1) - 0.5)
else:
return 0

def mutate(self, genome):
new_genome = copy.deepcopy(genome)
new_genome.W1 += self.__mutate_weights(new_genome.W1)
new_genome.W2 += self.__mutate_weights(new_genome.W2)
return new_genome
20 changes: 20 additions & 0 deletions dino-ml-win/network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import numpy as np

class Network:
def __init__(self):
self.input_size = 3
self.hidden_size = 4
self.output_size = 1
self.W1 = np.random.randn(self.input_size, self.hidden_size)
self.W2 = np.random.randn(self.hidden_size, self.output_size)
self.fitness = 0

def forward(self, inputs):
self.z2 = np.dot(inputs, self.W1)
self.a2 = np.tanh(self.z2)
self.z3 = np.dot(self.a2, self.W2)
yHat = np.tanh(self.z3)
return yHat

def sigmoid(self, z):
return 1 / (1 + np.exp(-z))
95 changes: 95 additions & 0 deletions dino-ml-win/scanner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from PIL import Image
from PIL import ImageGrab
from datetime import datetime
import os

dino_color = (83, 83, 83)

def screenshot(x, y, w, h):
# os.system("screencapture -R{},{},{},{} tmp.png".format(x, y, w, h))
img = ImageGrab.grab()
img.save("tmp.png")
save = Image.open("tmp.png")
return save

def is_dino_color(pixel):
return pixel == dino_color

def obstacle(distance, length, speed, time):
return { 'distance': distance, 'length': length, 'speed': speed, 'time': time }

class Scanner:
def __init__(self):
self.dino_start = (0, 0)
self.dino_end = (0, 0)
self.last_obstacle = {}
self.__current_fitness = 0
self.__change_fitness = False

def find_game(self):
image = screenshot(0, 0, 1500, 1500)
size = image.size
pixels = []
for y in range(0, size[1], 10):
for x in range(0, size[0], 10):
color = image.getpixel((x, y))
if is_dino_color(color):
pixels.append((x, y))

if not pixels:
raise Exception("Game not found!")
print("Game found!")
self.__find_dino(pixels)

def __find_dino(self, pixels):
start = pixels[0]
end = pixels[1]
for pixel in pixels:
if pixel[0] < start[0] and pixel[1] > start[1]:
start = pixel
if pixel[0] > end[0] and pixel[1] > end[1]:
end = pixel
self.dino_start = start
self.dino_end = end

def find_next_obstacle(self):
image = screenshot(210, 100, 500, 155)
dist = self.__next_obstacle_dist(image)
if dist < 50 and not self.__change_fitness:
self.__current_fitness += 1
self.__change_fitness = True
elif dist > 50:
self.__change_fitness = False
time = datetime.now()
delta_dist = 0
speed = 0
if self.last_obstacle:
delta_dist = self.last_obstacle['distance'] - dist
speed = (delta_dist / ((time - self.last_obstacle['time']).microseconds)) * 10000
self.last_obstacle = obstacle(dist, 1, speed, time)
return self.last_obstacle

def __next_obstacle_dist(self, image):
s = 0
for y in range(0, 250, 5):
for x in range(0, 1000, 5):
color = image.getpixel((x, y))
if is_dino_color(color):
s += 1
if s > 150:
raise Exception('Game over!')

for x in range(0, 1000, 5):
for y in range(0, 310, 5):
color = image.getpixel((x, y))
if is_dino_color(color):
return x
return 1000000

def reset(self):
self.last_obstacle = {}
self.__current_fitness = 0
self.__change_fitness = False

def get_fitness(self):
return self.__current_fitness
Binary file added pyHook-1.5.1-cp36-cp36m-win32.whl
Binary file not shown.
Binary file added pyHook-1.5.1-cp36-cp36m-win_amd64.whl
Binary file not shown.