In [12]:
# played on http://www.trex-game.skipser.com/
import os
import numpy as np
import mss
import cv2
import time
from pynput.keyboard import Key, Controller
import pynput.mouse as pmouse
from collections import deque

In [13]:
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import optimizers

In [14]:
def search_screen(template):
    with mss.mss() as sct:
        img = np.array(sct.grab(sct.monitors[0]))
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
        w, h = res.shape[::-1]
        idx = np.argmax(res)
        return int(idx%w), int(idx//w)

In [15]:
class Environment:
    def __init__(self, motion=2, delay=25):
        self.action = 0
        self.last_action = 0
        self.actions = ['', Key.up, Key.down]
        self.delay = delay
        self.motion = motion
        self.keyboard = Controller()
        self.mouse = pmouse.Controller()
        self.frames = deque()
        # find browser
        self.gameover_img = cv2.imread('gameover.png', 0)
        gw, gh = self.gameover_img.shape[::-1]
        cord = search_screen(self.gameover_img)
        screen_x = cord[0] - (600 - gw)//2
        screen_y = cord[1] + gh - 2
        self.gameover = {'top': cord[1], 'left': cord[0], 'width': gw, 'height': gh}
        self.monitor = {'top': screen_y, 'left': screen_x, 'width': 600, 'height': 80}
        self.size = (self.monitor['height'] // 10, self.monitor['width'] // 4)
        print('screen', self.monitor)
        print('output', self.size)
        
    def __del__(self):
        self.keyboard.release(Key.up)
        self.keyboard.release(Key.down)
    
    def reset(self):
        self.mouse.position = (self.gameover['left'], self.gameover['top'])
        self.mouse.press(pmouse.Button.left)
        cv2.waitKey(self.delay)
        self.mouse.release(pmouse.Button.left)
        with mss.mss() as sct:
            self.frames.clear()
            for idx in range(self.motion):
                cv2.waitKey(self.delay)
                self.frames.append(self.get_frame(sct))
        return self.get_state()
        
    def get_state(self):
        state = np.zeros((self.motion, self.size[0], self.size[1]))
        ary = []
        for frame in self.frames:
            ary.append(frame)
        return np.dstack(tuple(ary))
        
    def get_frame(self, sct):
        img = np.array(sct.grab(self.monitor))
        img = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
        th, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
        img = cv2.resize(img, (0,0), fx=0.25, fy=0.1, interpolation = cv2.INTER_LINEAR)
        return np.array(img, dtype=float) / 255
    
    def is_done(self, sct):
        gameover = cv2.cvtColor(np.array(sct.grab(self.gameover)), cv2.COLOR_BGR2GRAY)
        return np.amax(cv2.matchTemplate(gameover, self.gameover_img, cv2.TM_CCOEFF_NORMED)) > .95
        
    def step(self, verbose=False):
        frame = 0
        done = False
        with mss.mss() as sct:
            while not done and frame < 1999:
                done = self.is_done(sct)
                if not done:
                    if self.action != self.last_action:
                        if self.last_action != 0:
                            self.keyboard.release(self.actions[self.last_action])
                        if self.action != 0:
                            self.keyboard.press(self.actions[self.action])
                        self.last_action = self.action
                    if cv2.waitKey(self.delay) & 0xFF == ord('q'):
                         return
                else:
                    if self.last_action != 0:
                        self.keyboard.release(self.actions[self.last_action])
                reward = 1 if done else 0
                self.frames.pop()
                self.frames.appendleft(self.get_frame(sct))
                frame += 1
                yield self.get_state(), reward
    
    def show(self):
        img = np.array(self.frames[0]*255, dtype=np.uint8)
        img = cv2.resize(img, (0,0), fx=2, fy=5, interpolation = cv2.INTER_NEAREST)
        cv2.imshow('dino', img)

In [16]:
class Agent:
    def __init__(self, env):
        self.env = env
        self.action_size = len(env.actions)
        self.height = env.size[0]
        self.width = env.size[1]
        self.channels = env.motion
        self.shape = [1, self.height, self.width, self.channels]
        self.model = self.get_model(.001)
        self.exploration = .1
        self.decay = .995
        self.memory = deque()
        
    def get_model(self, learning_rate):
        model = Sequential()
        model.add(Conv2D(64, kernel_size=(3,3), activation='relu', input_shape=self.shape[1:]))
        model.add(Dropout(0.2))
        model.add(Flatten())
        model.add(Dense(512, activation='relu'))
        model.add(Dropout(0.2))
        model.add(Dense(32, activation='relu'))
        model.add(Dense(3, activation='linear'))
        #model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
        model.compile(loss='mse', optimizer=optimizers.Adam(lr=learning_rate))
        return model
        
    def get_move(self, data, explore=True):
        if explore and np.random.rand() < self.exploration:
            return np.random.randint(self.action_size)
        self.shape[0] = 1
        return np.argmin(self.model.predict(data.reshape(self.shape))[0])
    
    def update(self, gamma):
        self.shape[0] = len(self.memory)
        train_x = np.zeros(self.shape)
        for idx, (state, action, reward) in enumerate(self.memory):
            train_x[idx,:] = state.reshape(self.shape[1:])
        value = 0
        train_y = self.model.predict(train_x)
        for idx, (state, action, reward) in enumerate(self.memory):
            value = reward + value*gamma
            train_y[idx,action] = value
            #train_y[idx,action] += .3*(value - train_y[idx,action])
        self.model.fit(train_x, train_y, batch_size=4, epochs=1, verbose=0)
        self.memory.clear()
        self.exploration = max(self.exploration*self.decay, .01)
        
    def save(self, state, action, reward):
        self.memory.appendleft((state, action, reward))
    
    def save_to_disk(self, filename='trex_model.h5'):
        self.model.save(filename)
        
    def load_from_disk(self, filename='trex_model.h5'):
        if os.path.isfile(filename):
            self.model = keras.models.load_model(filename)

In [17]:
cv2.destroyAllWindows()
env = Environment(2, delay=20)
aj = Agent(env)
aj.load_from_disk()
cv2.imshow('dino', np.zeros((50,50), dtype=np.uint8))
cv2.waitKey(25)

best, episode = 0, 1
running_avg = 0
while True:
    explore = False if episode % 3 == 0 else True
    state = env.reset()
    distance = 0
    for next_state, reward in env.step():
        env.show()
        aj.save(state, env.action, reward)
        state = next_state
        env.action = aj.get_move(state, explore and distance > running_avg)
        distance += 1
    cv2.waitKey(25)
    running_avg += .3*(distance - running_avg)
    if not explore:
        if distance > best + 10:
            aj.save_to_disk('trex_{0}_{1}.h5'.format(episode, distance))
        best = max(best, distance)
    print('\rEpisode: {0}({1}), distance = {2}(best = {3}, running={4}), explore = {5:.2f}% '.format(\
        episode, episode % 3, distance, best, int(running_avg), aj.exploration*100), end='')
    aj.update(.9)
    if distance < 30:
        break
    episode += 1
cv2.destroyAllWindows()
aj.save_to_disk()


screen {'top': 334, 'left': 2056, 'width': 600, 'height': 80}
output (8, 150)
---------------------------------------------------------------------------
InternalError                             Traceback (most recent call last)
c:\python36\lib\site-packages\tensorflow\python\client\session.py in _do_call(self, fn, *args)
   1326     try:
-> 1327       return fn(*args)
   1328     except errors.OpError as e:

c:\python36\lib\site-packages\tensorflow\python\client\session.py in _run_fn(session, feed_dict, fetch_list, target_list, options, run_metadata)
   1305                                    feed_dict, fetch_list, target_list,
-> 1306                                    status, run_metadata)
   1307 

c:\python36\lib\contextlib.py in __exit__(self, type, value, traceback)
     87             try:
---> 88                 next(self.gen)
     89             except StopIteration:

c:\python36\lib\site-packages\tensorflow\python\framework\errors_impl.py in raise_exception_on_not_ok_status()
    465           compat.as_text(pywrap_tensorflow.TF_Message(status)),
--> 466           pywrap_tensorflow.TF_GetCode(status))
    467   finally:

InternalError: Dst tensor is not initialized.
	 [[Node: _arg_Placeholder_69_0_11/_843 = _Recv[client_terminated=false, recv_device="/job:localhost/replica:0/task:0/gpu:0", send_device="/job:localhost/replica:0/task:0/cpu:0", send_device_incarnation=1, tensor_name="edge_77__arg_Placeholder_69_0_11", tensor_type=DT_FLOAT, _device="/job:localhost/replica:0/task:0/gpu:0"]()]]
	 [[Node: Assign_69/_885 = _Recv[_start_time=0, client_terminated=false, recv_device="/job:localhost/replica:0/task:0/cpu:0", send_device="/job:localhost/replica:0/task:0/gpu:0", send_device_incarnation=1, tensor_name="edge_110_Assign_69", tensor_type=DT_FLOAT, _device="/job:localhost/replica:0/task:0/cpu:0"]()]]

During handling of the above exception, another exception occurred:

InternalError                             Traceback (most recent call last)
<ipython-input-17-4be94e9e0f2c> in <module>()
      2 env = Environment(2, delay=20)
      3 aj = Agent(env)
----> 4 aj.load_from_disk()
      5 cv2.imshow('dino', np.zeros((50,50), dtype=np.uint8))
      6 cv2.waitKey(25)

<ipython-input-16-7fecbf83f54f> in load_from_disk(self, filename)
     54     def load_from_disk(self, filename='trex_model.h5'):
     55         if os.path.isfile(filename):
---> 56             self.model = keras.models.load_model(filename)

c:\python36\lib\site-packages\keras\models.py in load_model(filepath, custom_objects, compile)
    283                                        optimizer_weight_names]
    284             try:
--> 285                 model.optimizer.set_weights(optimizer_weight_values)
    286             except ValueError:
    287                 warnings.warn('Error in loading the saved optimizer '

c:\python36\lib\site-packages\keras\optimizers.py in set_weights(self, weights)
    105                                  'provided weight shape ' + str(w.shape))
    106             weight_value_tuples.append((p, w))
--> 107         K.batch_set_value(weight_value_tuples)
    108 
    109     def get_weights(self):

c:\python36\lib\site-packages\keras\backend\tensorflow_backend.py in batch_set_value(tuples)
   2191             assign_ops.append(assign_op)
   2192             feed_dict[assign_placeholder] = value
-> 2193         get_session().run(assign_ops, feed_dict=feed_dict)
   2194 
   2195 

c:\python36\lib\site-packages\tensorflow\python\client\session.py in run(self, fetches, feed_dict, options, run_metadata)
    893     try:
    894       result = self._run(None, fetches, feed_dict, options_ptr,
--> 895                          run_metadata_ptr)
    896       if run_metadata:
    897         proto_data = tf_session.TF_GetBuffer(run_metadata_ptr)

c:\python36\lib\site-packages\tensorflow\python\client\session.py in _run(self, handle, fetches, feed_dict, options, run_metadata)
   1122     if final_fetches or final_targets or (handle and feed_dict_tensor):
   1123       results = self._do_run(handle, final_targets, final_fetches,
-> 1124                              feed_dict_tensor, options, run_metadata)
   1125     else:
   1126       results = []

c:\python36\lib\site-packages\tensorflow\python\client\session.py in _do_run(self, handle, target_list, fetch_list, feed_dict, options, run_metadata)
   1319     if handle is None:
   1320       return self._do_call(_run_fn, self._session, feeds, fetches, targets,
-> 1321                            options, run_metadata)
   1322     else:
   1323       return self._do_call(_prun_fn, self._session, handle, feeds, fetches)

c:\python36\lib\site-packages\tensorflow\python\client\session.py in _do_call(self, fn, *args)
   1338         except KeyError:
   1339           pass
-> 1340       raise type(e)(node_def, op, message)
   1341 
   1342   def _extend_graph(self):

InternalError: Dst tensor is not initialized.
	 [[Node: _arg_Placeholder_69_0_11/_843 = _Recv[client_terminated=false, recv_device="/job:localhost/replica:0/task:0/gpu:0", send_device="/job:localhost/replica:0/task:0/cpu:0", send_device_incarnation=1, tensor_name="edge_77__arg_Placeholder_69_0_11", tensor_type=DT_FLOAT, _device="/job:localhost/replica:0/task:0/gpu:0"]()]]
	 [[Node: Assign_69/_885 = _Recv[_start_time=0, client_terminated=false, recv_device="/job:localhost/replica:0/task:0/cpu:0", send_device="/job:localhost/replica:0/task:0/gpu:0", send_device_incarnation=1, tensor_name="edge_110_Assign_69", tensor_type=DT_FLOAT, _device="/job:localhost/replica:0/task:0/cpu:0"]()]]

In [ ]: