-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsnake.py
134 lines (112 loc) · 5.01 KB
/
snake.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
# Valentin Macé
# Developed for fun
# Feel free to use this code as you wish as long as you quote me as author
"""
snake.py
~~~~~~~~~~
This module is for building the snake itself in the snake game
The snake:
- Is on the form of a list, each element for a body block (containing its coordinates)
- Has a head pointing on the first block, a direction and also a neural network (brain)
- Has vision given by the map (Map.scan method)
- Is in charge of moving its blocks, aging, growing by adding a block to the right place
and makes decision with neural net
- Gives its fitness based on self age and length
"""
from neural_network import *
class Snake:
"""Snake Class"""
def __init__(self, neural_net=None, xMaxSize = 20, yMaxSize = 20):
"""
:param neural_net: NeuralNet given to the snake in charge of decisions (AI)
"""
self.body = [[10, 10], [9, 10], [9, 11], [9, 12]] # the snake is in fact a list of coordinates
self.head = self.body[0][:] # first body block
self.old_tail = self.head[:] # useful to grow
self.direction = RIGHT
self.age = 0
self.starve = 500 # useful to avoid looping AI snakes
self.alive = True
self.neural_net = neural_net
self.vision = [] # holds the map.scan() and is used by the neural net
self.xMaxSize = xMaxSize
self.yMaxSize = yMaxSize
def update(self):
"""
Actualize the snake through time, making it older and more hungryat each game iteration,
sorry snek
"""
self.age += 1
self.starve -= 1
if self.starve < 1:
self.alive = False
self.move()
def grow(self):
"""
Makes snake grow one block longer
Called by map.update() when snake's head is in collision with food
"""
self.starve = 500 # useful to avoid looping AI snakes (they die younger -> bad fitness)
self.body.append(self.old_tail) # that's why I keep old_tail
def move(self):
"""
Makes the snake move, head moves in current direction and each blocks replace its predecessor
"""
self.old_tail = self.body[-1][:] # save old position of last block
self.head[0] += self.direction[0] # moves head
self.head[1] += self.direction[1]
self.head[0] = (self.head[0] + self.xMaxSize) % self.xMaxSize
self.head[1] = (self.head[1] + self.yMaxSize) % self.yMaxSize
if self.head in self.body[1:]: # if snakes hits himself
self.alive = False
self.body.insert(0, self.body.pop()) # each block is replace by predecessor
self.body[0] = self.head[:] # first block is head
def turn_right(self):
"""
Makes the snake direction to the right of the current direction
Current direction = [x,y], turn_right gives [-y,x]
Example:
If [0,1] (down) is current direction, [-1,0] (right) is new direction
"""
temp = self.direction[0]
self.direction[0] = -self.direction[1]
self.direction[1] = temp
def turn_left(self):
"""
Makes the snake direction to the right of the current direction
Current direction = [x,y], turn_right gives [y,-x]
"""
temp = self.direction[0]
self.direction[0] = self.direction[1]
self.direction[1] = -temp
def AI(self):
"""
Makes decision for the snake direction according to its current vision
Vision is given to the NeuralNetwork and most activated output neuron is considered as decision
"""
decision = np.argmax(self.neural_net.feed_forward(self.vision))
if decision == 1:
self.turn_right()
elif decision == 2:
self.turn_left()
def fitness(self):
"""
Measures how well the snake is doing as a function of its length and age
Note:
- You can be creative with the formula and find a better solution
- It has a big impact on the genetic algorithm
:return: integer representing how good the snake is performing
"""
return (len(self.body)**2) * self.age
def render(self, window):
"""
Renders the map (background, walls and food) on the window surface and calls render() of snake
Very very very unoptimized since render does not affect the genetic algorithm
:param window: surface window
"""
body = pygame.image.load(IMAGE_SNAKE).convert_alpha() # loading image
for block in self.body:
window.blit(body, (block[0]*SPRITE_SIZE, block[1]*SPRITE_SIZE)) # painting a beautiful snek
if self.neural_net: # calls for neural net rendering
self.neural_net.render(window, self.vision)