-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.py
188 lines (143 loc) · 6.04 KB
/
game.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
import pygame
from pygame.locals import *
import sys
import time
import random
import numpy as np
#COLORS :: RGB VALUES
# fruit color
fruitColor = (255, 147, 59) #pink
# primary snake color
snakeColor1 = (70, 74, 224)
#secondary snake color
snakeColor2 = (143, 145, 247)
# snake head color
snakeHeadColor = (220,220,220) #light gray
# background color
backgroundColor = (0,0,0) #black
#enviromenment class
class game_ai:
def __init__(self, display_width=640, display_height=480):
# Initailize pygame
# clock and framerate
self.FPS = 40 #standard FPS
self.fpsClock = pygame.time.Clock()
self.display_width = display_width
self.display_height = display_height
self.board = pygame.display.set_mode((self.display_width, self.display_height))
self.__init_game()
def __init_game(self):
self.y_change = 0
self.x_change = 20
# intial snake position, starts with 4 segments including head
self.snakePosition = [random.randint(4,5)*20, random.randint(4,5)*20] # Snake head
self.snakeSegments = [[self.snakePosition[0], self.snakePosition[1]],
[self.snakePosition[0]-20, self.snakePosition[1]],
[self.snakePosition[0]-40, self.snakePosition[1]]]
self.fruitPosition = [random.randint(0, (self.display_width-20)//20)*20, random.randint(0, (self.display_height-20)//20)*20]
self.fruitSpawned = 1
#initially snake is moving straight
self.action = [1, 0, 0] # [straight, right, left]
self.score = 0
self.episode = 0
#called when we want to emulate a move
def frameStep(self, action):
self.action = action
reward, done, score = self.play(self.board, self.action)
return reward, done, score
#called when game is done to reinitialize for next training game
def reset(self):
score1 = self.score
self.__init_game()
return score1
# emulate single move
def play(self, board, action):
#update episode count every time we call frameStep
self.episode += 1
#ESC to quit
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.event.post(pygame.event.Event(QUIT))
#perform move
self.move(action)
reward = 0
# assume fruit was eaten, add a snake segment
self.snakeSegments.insert(0, list(self.snakePosition))
#checks if fruit was actually eaten
if self.snakePosition[0] == self.fruitPosition[0] and self.snakePosition[1] == self.fruitPosition[1]:
self.fruitSpawned = 0 #fruit was eaten, set bool to FALSE
reward = 10 #increase reward for eating fruit :)
else:
self.snakeSegments.pop() #fruit wasn't eaten so we want to remove the segment we added
#fruit was eaten so a new fruit needs to be created
if self.fruitSpawned == 0:
#randomly choose location for new fruit
x = random.randrange(0, (self.display_width-20)//20)
y = random.randrange(0, (self.display_height-20)//20)
self.fruitPosition = [int(x*20), int(y*20)]
self.fruitSpawned = 1
self.score += 1
#REFRESH FRAME
#draw board
board.fill(backgroundColor)
#draw snake body
for position in self.snakeSegments[1:]:
pygame.draw.rect(self.board, snakeColor2, Rect(position[0], position[1], 20, 20))
pygame.draw.rect(self.board, snakeColor1, Rect(position[0]+4, position[1]+4, 12, 12))
#draw snake head
pygame.draw.rect(self.board, snakeHeadColor, Rect(self.snakePosition[0], self.snakePosition[1], 20, 20))
#draw fruit
pygame.draw.rect(self.board, fruitColor, Rect(self.fruitPosition[0], self.fruitPosition[1], 20, 20))
#Update the full display Surface to the screen now that new objects have been drawn
pygame.display.flip()
#assume game is not done
done = False
#criteria for game being done
#agent timed out
if self.episode > 100*len(self.snakeSegments):
done = True
reward = -10
return reward, done, self.score
#snake hit wall
if self.snakePosition[0]>self.display_width-20 or self.snakePosition[0]<0:
done = True
reward = -10
return reward, done, self.score
#snake hit wall
if self.snakePosition[1]>self.display_height-20 or self.snakePosition[1]<0:
done = True
reward = -10
return reward, done, self.score
#snake hit its own body
for snakeBody in self.snakeSegments[1:]:
if self.snakePosition[0] == snakeBody[0] and self.snakePosition[1] == snakeBody[1]:
done = True
reward = -10
return reward, done, self.score
self.fpsClock.tick(self.FPS)
return reward, done, self.score
def move(self, action):
#[straight, right, left]
move_array = [self.x_change, self.y_change]
# no change
if np.array_equal(action, [1, 0, 0]):
move_array = self.x_change, self.y_change
# horizontal right
elif np.array_equal(action, [0, 1, 0]) and self.y_change == 0:
move_array = [0, self.x_change]
# vertical right
elif np.array_equal(action, [0, 1, 0]) and self.x_change == 0:
move_array = [-self.y_change, 0]
# horizontal left
elif np.array_equal(action, [0, 0, 1]) and self.y_change == 0:
move_array = [0, -self.x_change]
# vertical left
elif np.array_equal(action, [0, 0, 1]) and self.x_change == 0:
move_array = [self.y_change, 0]
self.x_change, self.y_change = move_array
self.snakePosition[0] += self.x_change
self.snakePosition[1] += self.y_change