├── images ├── network.png ├── preprocess.png └── flappy_bird_demp.gif ├── assets ├── audio │ ├── die.ogg │ ├── die.wav │ ├── hit.ogg │ ├── hit.wav │ ├── wing.ogg │ ├── wing.wav │ ├── point.ogg │ ├── point.wav │ ├── swoosh.ogg │ └── swoosh.wav └── sprites │ ├── 0.png │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ ├── base.png │ ├── pipe-green.png │ ├── redbird-midflap.png │ ├── redbird-upflap.png │ ├── background-black.png │ └── redbird-downflap.png ├── game ├── flappy_bird_utils.py └── wrapped_flappy_bird.py └── main.py /images/network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/images/network.png -------------------------------------------------------------------------------- /assets/audio/die.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/die.ogg -------------------------------------------------------------------------------- /assets/audio/die.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/die.wav -------------------------------------------------------------------------------- /assets/audio/hit.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/hit.ogg -------------------------------------------------------------------------------- /assets/audio/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/hit.wav -------------------------------------------------------------------------------- /assets/audio/wing.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/wing.ogg -------------------------------------------------------------------------------- /assets/audio/wing.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/wing.wav -------------------------------------------------------------------------------- /assets/sprites/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/0.png -------------------------------------------------------------------------------- /assets/sprites/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/1.png -------------------------------------------------------------------------------- /assets/sprites/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/2.png -------------------------------------------------------------------------------- /assets/sprites/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/3.png -------------------------------------------------------------------------------- /assets/sprites/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/4.png -------------------------------------------------------------------------------- /assets/sprites/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/5.png -------------------------------------------------------------------------------- /assets/sprites/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/6.png -------------------------------------------------------------------------------- /assets/sprites/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/7.png -------------------------------------------------------------------------------- /assets/sprites/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/8.png -------------------------------------------------------------------------------- /assets/sprites/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/9.png -------------------------------------------------------------------------------- /images/preprocess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/images/preprocess.png -------------------------------------------------------------------------------- /assets/audio/point.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/point.ogg -------------------------------------------------------------------------------- /assets/audio/point.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/point.wav -------------------------------------------------------------------------------- /assets/audio/swoosh.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/swoosh.ogg -------------------------------------------------------------------------------- /assets/audio/swoosh.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/audio/swoosh.wav -------------------------------------------------------------------------------- /assets/sprites/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/base.png -------------------------------------------------------------------------------- /assets/sprites/pipe-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/pipe-green.png -------------------------------------------------------------------------------- /images/flappy_bird_demp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/images/flappy_bird_demp.gif -------------------------------------------------------------------------------- /assets/sprites/redbird-midflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/redbird-midflap.png -------------------------------------------------------------------------------- /assets/sprites/redbird-upflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/redbird-upflap.png -------------------------------------------------------------------------------- /assets/sprites/background-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/background-black.png -------------------------------------------------------------------------------- /assets/sprites/redbird-downflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SukerZ/Playing-Flappy-Bird-by-DQN-on-PyTorch/HEAD/assets/sprites/redbird-downflap.png -------------------------------------------------------------------------------- /game/flappy_bird_utils.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import sys 3 | def load(): 4 | # path of player with different states 5 | PLAYER_PATH = ( 6 | 'assets/sprites/redbird-upflap.png', 7 | 'assets/sprites/redbird-midflap.png', 8 | 'assets/sprites/redbird-downflap.png' 9 | ) 10 | 11 | # path of background 12 | BACKGROUND_PATH = 'assets/sprites/background-black.png' 13 | 14 | # path of pipe 15 | PIPE_PATH = 'assets/sprites/pipe-green.png' 16 | 17 | IMAGES, SOUNDS, HITMASKS = {}, {}, {} 18 | 19 | # numbers sprites for score display 20 | IMAGES['numbers'] = ( 21 | pygame.image.load('assets/sprites/0.png').convert_alpha(), 22 | pygame.image.load('assets/sprites/1.png').convert_alpha(), 23 | pygame.image.load('assets/sprites/2.png').convert_alpha(), 24 | pygame.image.load('assets/sprites/3.png').convert_alpha(), 25 | pygame.image.load('assets/sprites/4.png').convert_alpha(), 26 | pygame.image.load('assets/sprites/5.png').convert_alpha(), 27 | pygame.image.load('assets/sprites/6.png').convert_alpha(), 28 | pygame.image.load('assets/sprites/7.png').convert_alpha(), 29 | pygame.image.load('assets/sprites/8.png').convert_alpha(), 30 | pygame.image.load('assets/sprites/9.png').convert_alpha() 31 | ) 32 | 33 | # base (ground) sprite 34 | IMAGES['base'] = pygame.image.load('assets/sprites/base.png').convert_alpha() 35 | 36 | # sounds 37 | if 'win' in sys.platform: 38 | soundExt = '.wav' 39 | else: 40 | soundExt = '.ogg' 41 | 42 | SOUNDS['die'] = pygame.mixer.Sound('assets/audio/die' + soundExt) 43 | SOUNDS['hit'] = pygame.mixer.Sound('assets/audio/hit' + soundExt) 44 | SOUNDS['point'] = pygame.mixer.Sound('assets/audio/point' + soundExt) 45 | SOUNDS['swoosh'] = pygame.mixer.Sound('assets/audio/swoosh' + soundExt) 46 | SOUNDS['wing'] = pygame.mixer.Sound('assets/audio/wing' + soundExt) 47 | 48 | # select random background sprites 49 | IMAGES['background'] = pygame.image.load(BACKGROUND_PATH).convert() 50 | 51 | # select random player sprites 52 | IMAGES['player'] = ( 53 | pygame.image.load(PLAYER_PATH[0]).convert_alpha(), 54 | pygame.image.load(PLAYER_PATH[1]).convert_alpha(), 55 | pygame.image.load(PLAYER_PATH[2]).convert_alpha(), 56 | ) 57 | 58 | # select random pipe sprites 59 | IMAGES['pipe'] = ( 60 | pygame.transform.rotate( 61 | pygame.image.load(PIPE_PATH).convert_alpha(), 180), 62 | pygame.image.load(PIPE_PATH).convert_alpha(), 63 | ) 64 | 65 | # hismask for pipes 66 | HITMASKS['pipe'] = ( 67 | getHitmask(IMAGES['pipe'][0]), 68 | getHitmask(IMAGES['pipe'][1]), 69 | ) 70 | 71 | # hitmask for player 72 | HITMASKS['player'] = ( 73 | getHitmask(IMAGES['player'][0]), 74 | getHitmask(IMAGES['player'][1]), 75 | getHitmask(IMAGES['player'][2]), 76 | ) 77 | 78 | return IMAGES, SOUNDS, HITMASKS 79 | 80 | def getHitmask(image): 81 | """returns a hitmask using an image's alpha.""" 82 | mask = [] 83 | for x in range(image.get_width()): 84 | mask.append([]) 85 | for y in range(image.get_height()): 86 | mask[x].append(bool(image.get_at((x,y))[3])) 87 | return mask 88 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import pdb 2 | import cv2 3 | import sys 4 | import os 5 | sys.path.append("game/") 6 | import wrapped_flappy_bird as game 7 | import random 8 | import numpy as np 9 | from collections import deque 10 | import torch 11 | from torch.autograd import Variable 12 | import torch.nn as nn 13 | 14 | GAME = 'bird' # the name of the game being played for log files 15 | ACTIONS = 2 # number of valid actions 16 | GAMMA = 0.99 # decay rate of past observations 17 | OBSERVE = 1000. # timesteps to observe before training 18 | EXPLORE = 2000000. # frames over which to anneal epsilon 19 | FINAL_EPSILON = 0.0001 # final value of epsilon 20 | INITIAL_EPSILON = 0.0001 # starting value of epsilon 21 | REPLAY_MEMORY = 50000 # number of previous transitions to remember 22 | BATCH_SIZE = 32 # size of minibatch 23 | FRAME_PER_ACTION = 1 24 | UPDATE_TIME = 100 25 | width = 80 26 | height = 80 27 | 28 | def preprocess(observation): 29 | observation = cv2.cvtColor(cv2.resize(observation, (80, 80)), cv2.COLOR_BGR2GRAY) 30 | ret, observation = cv2.threshold(observation,1,255,cv2.THRESH_BINARY) 31 | return np.reshape(observation, (1,80,80)) 32 | 33 | class DeepNetWork(nn.Module): 34 | def __init__(self,): 35 | super(DeepNetWork,self).__init__() 36 | self.conv1 = nn.Sequential( 37 | nn.Conv2d(in_channels=4, out_channels=32, kernel_size=8, stride=4, padding=2), 38 | nn.ReLU(inplace=True), 39 | nn.MaxPool2d(kernel_size=2) 40 | ) 41 | self.conv2 = nn.Sequential( 42 | nn.Conv2d(in_channels=32, out_channels=64, kernel_size=4, stride=2, padding=1), 43 | nn.ReLU(inplace=True) 44 | ) 45 | self.conv3 = nn.Sequential( 46 | nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1), 47 | nn.ReLU(inplace=True) 48 | ) 49 | self.fc1 = nn.Sequential( 50 | nn.Linear(1600,256), 51 | nn.ReLU() 52 | ) 53 | self.out = nn.Linear(256,2) 54 | 55 | def forward(self, x): 56 | x = self.conv1(x); x = self.conv2(x); 57 | x = self.conv3(x); x = x.view(x.size(0),-1) 58 | x = self.fc1(x); return self.out(x) 59 | 60 | class BrainDQNMain(object): 61 | def save(self): 62 | print("save model param") 63 | torch.save(self.Q_net.state_dict(), 'params3.pth') 64 | 65 | def load(self): 66 | if os.path.exists("params3.pth"): 67 | print("load model param") 68 | self.Q_net.load_state_dict(torch.load('params3.pth')) 69 | self.Q_netT.load_state_dict(torch.load('params3.pth')) 70 | 71 | def __init__(self,actions): 72 | self.replayMemory = deque() # init some parameters 73 | self.timeStep = 0 74 | self.epsilon = INITIAL_EPSILON 75 | self.actions = actions 76 | self.Q_net=DeepNetWork() 77 | self.Q_netT=DeepNetWork(); 78 | self.load() 79 | self.loss_func=nn.MSELoss() 80 | LR=1e-6 81 | self.optimizer = torch.optim.Adam(self.Q_net.parameters(), lr=LR) 82 | 83 | def train(self): # Step 1: obtain random minibatch from replay memory 84 | minibatch = random.sample(self.replayMemory, BATCH_SIZE) 85 | state_batch = [data[0] for data in minibatch] 86 | action_batch = [data[1] for data in minibatch] 87 | reward_batch = [data[2] for data in minibatch] 88 | nextState_batch = [data[3] for data in minibatch] # Step 2: calculate y 89 | y_batch = np.zeros([BATCH_SIZE,1]) 90 | nextState_batch=np.array(nextState_batch) #print("train next state shape") 91 | #print(nextState_batch.shape) 92 | nextState_batch=torch.Tensor(nextState_batch) 93 | action_batch=np.array(action_batch) 94 | index=action_batch.argmax(axis=1) 95 | print("action "+str(index)) 96 | index=np.reshape(index,[BATCH_SIZE,1]) 97 | action_batch_tensor=torch.LongTensor(index) 98 | QValue_batch = self.Q_netT(nextState_batch) 99 | QValue_batch=QValue_batch.detach().numpy() 100 | 101 | for i in range(0, BATCH_SIZE): 102 | terminal = minibatch[i][4] 103 | if terminal: 104 | y_batch[i][0]=reward_batch[i] 105 | else: 106 | # 这里的QValue_batch[i]为数组,大小为所有动作集合大小,QValue_batch[i],代表 107 | # 做所有动作的Q值数组,y计算为如果游戏停止,y=rewaerd[i],如果没停止,则y=reward[i]+gamma*np.max(Qvalue[i]) 108 | # 代表当前y值为当前reward+未来预期最大值*gamma(gamma:经验系数) 109 | y_batch[i][0]=reward_batch[i] + GAMMA * np.max(QValue_batch[i]) 110 | 111 | y_batch=np.array(y_batch) 112 | y_batch=np.reshape(y_batch,[BATCH_SIZE,1]) 113 | state_batch_tensor=Variable(torch.Tensor(state_batch)) 114 | y_batch_tensor=Variable(torch.Tensor(y_batch)) 115 | y_predict=self.Q_net(state_batch_tensor).gather(1,action_batch_tensor) 116 | loss=self.loss_func(y_predict,y_batch_tensor) 117 | print("loss is "+str(loss)) 118 | self.optimizer.zero_grad() 119 | loss.backward() 120 | self.optimizer.step() 121 | 122 | if self.timeStep % UPDATE_TIME == 0: 123 | self.Q_netT.load_state_dict(self.Q_net.state_dict()) 124 | self.save() 125 | 126 | def setPerception(self,nextObservation,action,reward,terminal): #print(nextObservation.shape) 127 | newState = np.append(self.currentState[1:,:,:],nextObservation,axis = 0) # newState = np.append(nextObservation,self.currentState[:,:,1:],axis = 2) 128 | self.replayMemory.append((self.currentState,action,reward,newState,terminal)) 129 | if len(self.replayMemory) > REPLAY_MEMORY: 130 | self.replayMemory.popleft() 131 | if self.timeStep > OBSERVE: # Train the network 132 | self.train() 133 | 134 | # print info 135 | state = "" 136 | if self.timeStep <= OBSERVE: 137 | state = "observe" 138 | elif self.timeStep > OBSERVE and self.timeStep <= OBSERVE + EXPLORE: 139 | state = "explore" 140 | else: 141 | state = "train" 142 | print ("TIMESTEP", self.timeStep, "/ STATE", state, "/ EPSILON", self.epsilon) 143 | self.currentState = newState 144 | self.timeStep += 1 145 | 146 | def getAction(self): 147 | currentState = torch.Tensor([self.currentState]) 148 | QValue = self.Q_net(currentState)[0] 149 | action = np.zeros(self.actions) 150 | if self.timeStep % FRAME_PER_ACTION == 0: 151 | if random.random() <= self.epsilon: 152 | action_index = random.randrange(self.actions) 153 | print("choose random action " + str(action_index)) 154 | action[action_index] = 1 155 | else: 156 | action_index = np.argmax(QValue.detach().numpy()) 157 | print("choose qnet value action " + str(action_index)) 158 | action[action_index] = 1 159 | else: 160 | action[0] = 1 # do nothing 161 | 162 | # change episilon 163 | if self.epsilon > FINAL_EPSILON and self.timeStep > OBSERVE: 164 | self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / EXPLORE 165 | return action 166 | 167 | def setInitState(self, observation): 168 | self.currentState = np.stack((observation, observation, observation, observation),axis=0) 169 | print(self.currentState.shape) 170 | 171 | if __name__ == '__main__': 172 | # Step 1: init BrainDQN 173 | actions = 2 174 | brain = BrainDQNMain(actions) # Step 2: init Flappy Bird Game 175 | flappyBird = game.GameState() # Step 3: play game 176 | # Step 3.1: obtain init state 177 | action0 = np.array([1,0]) # do nothing 178 | observation0, reward0, terminal = flappyBird.frame_step(action0) 179 | observation0 = cv2.cvtColor(cv2.resize(observation0, (80, 80)), cv2.COLOR_BGR2GRAY) 180 | ret, observation0 = cv2.threshold(observation0,1,255,cv2.THRESH_BINARY) 181 | brain.setInitState(observation0) 182 | print(brain.currentState.shape) # Step 3.2: run the game 183 | 184 | while 1!= 0: 185 | action = brain.getAction() 186 | nextObservation,reward,terminal = flappyBird.frame_step(action) 187 | nextObservation = preprocess(nextObservation) 188 | #print(nextObservation.shape) 189 | brain.setPerception(nextObservation,action,reward,terminal) 190 | -------------------------------------------------------------------------------- /game/wrapped_flappy_bird.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import sys 3 | import random 4 | import pygame 5 | import flappy_bird_utils 6 | import pygame.surfarray as surfarray 7 | from pygame.locals import * 8 | from itertools import cycle 9 | 10 | FPS = 30 11 | SCREENWIDTH = 288 12 | SCREENHEIGHT = 512 13 | 14 | pygame.init() 15 | FPSCLOCK = pygame.time.Clock() 16 | SCREEN = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT)) 17 | pygame.display.set_caption('Flappy Bird') 18 | 19 | IMAGES, SOUNDS, HITMASKS = flappy_bird_utils.load() 20 | PIPEGAPSIZE = 100 # gap between upper and lower part of pipe 21 | BASEY = SCREENHEIGHT * 0.79 22 | 23 | PLAYER_WIDTH = IMAGES['player'][0].get_width() 24 | PLAYER_HEIGHT = IMAGES['player'][0].get_height() 25 | PIPE_WIDTH = IMAGES['pipe'][0].get_width() 26 | PIPE_HEIGHT = IMAGES['pipe'][0].get_height() 27 | BACKGROUND_WIDTH = IMAGES['background'].get_width() 28 | 29 | PLAYER_INDEX_GEN = cycle([0, 1, 2, 1]) 30 | 31 | 32 | class GameState: 33 | def __init__(self): 34 | self.score = self.playerIndex = self.loopIter = 0 35 | self.playerx = int(SCREENWIDTH * 0.2) 36 | self.playery = int((SCREENHEIGHT - PLAYER_HEIGHT) / 2) 37 | self.basex = 0 38 | self.baseShift = IMAGES['base'].get_width() - BACKGROUND_WIDTH 39 | 40 | newPipe1 = getRandomPipe() 41 | newPipe2 = getRandomPipe() 42 | self.upperPipes = [ 43 | {'x': SCREENWIDTH, 'y': newPipe1[0]['y']}, 44 | {'x': SCREENWIDTH + (SCREENWIDTH / 2), 'y': newPipe2[0]['y']}, 45 | ] 46 | self.lowerPipes = [ 47 | {'x': SCREENWIDTH, 'y': newPipe1[1]['y']}, 48 | {'x': SCREENWIDTH + (SCREENWIDTH / 2), 'y': newPipe2[1]['y']}, 49 | ] 50 | 51 | # player velocity, max velocity, downward accleration, accleration on flap 52 | self.pipeVelX = -4 53 | self.playerVelY = 0 # player's velocity along Y, default same as playerFlapped 54 | self.playerMaxVelY = 10 # max vel along Y, max descend speed 55 | self.playerMinVelY = -8 # min vel along Y, max ascend speed 56 | self.playerAccY = 1 # players downward accleration 57 | self.playerFlapAcc = -9 # players speed on flapping 58 | self.playerFlapped = False # True when player flaps 59 | 60 | def frame_step(self, input_actions): 61 | pygame.event.pump() 62 | 63 | reward = 0.1 64 | terminal = False 65 | 66 | if sum(input_actions) != 1: 67 | raise ValueError('Multiple input actions!') 68 | 69 | # input_actions[0] == 1: do nothing 70 | # input_actions[1] == 1: flap the bird 71 | if input_actions[1] == 1: 72 | if self.playery > -2 * PLAYER_HEIGHT: 73 | self.playerVelY = self.playerFlapAcc 74 | self.playerFlapped = True 75 | #SOUNDS['wing'].play() 76 | 77 | # check for score 78 | playerMidPos = self.playerx + PLAYER_WIDTH / 2 79 | for pipe in self.upperPipes: 80 | pipeMidPos = pipe['x'] + PIPE_WIDTH / 2 81 | if pipeMidPos <= playerMidPos < pipeMidPos + 4: 82 | self.score += 1 83 | #SOUNDS['point'].play() 84 | reward = 1 85 | 86 | # playerIndex basex change 87 | if (self.loopIter + 1) % 3 == 0: 88 | self.playerIndex = next(PLAYER_INDEX_GEN) 89 | self.loopIter = (self.loopIter + 1) % 30 90 | self.basex = -((-self.basex + 100) % self.baseShift) 91 | 92 | # player's movement 93 | if self.playerVelY < self.playerMaxVelY and not self.playerFlapped: 94 | self.playerVelY += self.playerAccY 95 | if self.playerFlapped: 96 | self.playerFlapped = False 97 | self.playery += min(self.playerVelY, BASEY - self.playery - PLAYER_HEIGHT) 98 | if self.playery < 0: 99 | self.playery = 0 100 | 101 | # move pipes to left 102 | for uPipe, lPipe in zip(self.upperPipes, self.lowerPipes): 103 | uPipe['x'] += self.pipeVelX 104 | lPipe['x'] += self.pipeVelX 105 | 106 | # add new pipe when first pipe is about to touch left of screen 107 | if 0 < self.upperPipes[0]['x'] < 5: 108 | newPipe = getRandomPipe() 109 | self.upperPipes.append(newPipe[0]) 110 | self.lowerPipes.append(newPipe[1]) 111 | 112 | # remove first pipe if its out of the screen 113 | if self.upperPipes[0]['x'] < -PIPE_WIDTH: 114 | self.upperPipes.pop(0) 115 | self.lowerPipes.pop(0) 116 | 117 | # check if crash here 118 | isCrash= checkCrash({'x': self.playerx, 'y': self.playery, 119 | 'index': self.playerIndex}, 120 | self.upperPipes, self.lowerPipes) 121 | if isCrash: 122 | #SOUNDS['hit'].play() 123 | #SOUNDS['die'].play() 124 | terminal = True 125 | self.__init__() 126 | reward = -1 127 | 128 | # draw sprites 129 | SCREEN.blit(IMAGES['background'], (0,0)) 130 | 131 | for uPipe, lPipe in zip(self.upperPipes, self.lowerPipes): 132 | SCREEN.blit(IMAGES['pipe'][0], (uPipe['x'], uPipe['y'])) 133 | SCREEN.blit(IMAGES['pipe'][1], (lPipe['x'], lPipe['y'])) 134 | 135 | SCREEN.blit(IMAGES['base'], (self.basex, BASEY)) 136 | # print score so player overlaps the score 137 | # showScore(self.score) 138 | SCREEN.blit(IMAGES['player'][self.playerIndex], 139 | (self.playerx, self.playery)) 140 | 141 | image_data = pygame.surfarray.array3d(pygame.display.get_surface()) 142 | pygame.display.update() 143 | FPSCLOCK.tick(FPS) 144 | #print self.upperPipes[0]['y'] + PIPE_HEIGHT - int(BASEY * 0.2) 145 | return image_data, reward, terminal 146 | 147 | def getRandomPipe(): 148 | """returns a randomly generated pipe""" 149 | # y of gap between upper and lower pipe 150 | gapYs = [20, 30, 40, 50, 60, 70, 80, 90] 151 | index = random.randint(0, len(gapYs)-1) 152 | gapY = gapYs[index] 153 | 154 | gapY += int(BASEY * 0.2) 155 | pipeX = SCREENWIDTH + 10 156 | 157 | return [ 158 | {'x': pipeX, 'y': gapY - PIPE_HEIGHT}, # upper pipe 159 | {'x': pipeX, 'y': gapY + PIPEGAPSIZE}, # lower pipe 160 | ] 161 | 162 | 163 | def showScore(score): 164 | """displays score in center of screen""" 165 | scoreDigits = [int(x) for x in list(str(score))] 166 | totalWidth = 0 # total width of all numbers to be printed 167 | 168 | for digit in scoreDigits: 169 | totalWidth += IMAGES['numbers'][digit].get_width() 170 | 171 | Xoffset = (SCREENWIDTH - totalWidth) / 2 172 | 173 | for digit in scoreDigits: 174 | SCREEN.blit(IMAGES['numbers'][digit], (Xoffset, SCREENHEIGHT * 0.1)) 175 | Xoffset += IMAGES['numbers'][digit].get_width() 176 | 177 | 178 | def checkCrash(player, upperPipes, lowerPipes): 179 | """returns True if player collders with base or pipes.""" 180 | pi = player['index'] 181 | player['w'] = IMAGES['player'][0].get_width() 182 | player['h'] = IMAGES['player'][0].get_height() 183 | 184 | # if player crashes into ground 185 | if player['y'] + player['h'] >= BASEY - 1: 186 | return True 187 | else: 188 | 189 | playerRect = pygame.Rect(player['x'], player['y'], 190 | player['w'], player['h']) 191 | 192 | for uPipe, lPipe in zip(upperPipes, lowerPipes): 193 | # upper and lower pipe rects 194 | uPipeRect = pygame.Rect(uPipe['x'], uPipe['y'], PIPE_WIDTH, PIPE_HEIGHT) 195 | lPipeRect = pygame.Rect(lPipe['x'], lPipe['y'], PIPE_WIDTH, PIPE_HEIGHT) 196 | 197 | # player and upper/lower pipe hitmasks 198 | pHitMask = HITMASKS['player'][pi] 199 | uHitmask = HITMASKS['pipe'][0] 200 | lHitmask = HITMASKS['pipe'][1] 201 | 202 | # if bird collided with upipe or lpipe 203 | uCollide = pixelCollision(playerRect, uPipeRect, pHitMask, uHitmask) 204 | lCollide = pixelCollision(playerRect, lPipeRect, pHitMask, lHitmask) 205 | 206 | if uCollide or lCollide: 207 | return True 208 | 209 | return False 210 | 211 | def pixelCollision(rect1, rect2, hitmask1, hitmask2): 212 | """Checks if two objects collide and not just their rects""" 213 | rect = rect1.clip(rect2) 214 | 215 | if rect.width == 0 or rect.height == 0: 216 | return False 217 | 218 | x1, y1 = rect.x - rect1.x, rect.y - rect1.y 219 | x2, y2 = rect.x - rect2.x, rect.y - rect2.y 220 | 221 | for x in range(rect.width): 222 | for y in range(rect.height): 223 | if hitmask1[x1+x][y1+y] and hitmask2[x2+x][y2+y]: 224 | return True 225 | return False 226 | --------------------------------------------------------------------------------