-
Notifications
You must be signed in to change notification settings - Fork 0
/
SnakeBot.py
130 lines (100 loc) · 3.64 KB
/
SnakeBot.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
from Structures.ExplorationNode import ExplorationNode
from Structures.UniqueStack import UniqueStack
from Utils import *
import math
import copy
class SnakeBot:
def decide_dfs(self, snake, food):
stack = UniqueStack(ExplorationNode(snake))
while stack.has_not_discarted_items():
node = stack.get_last_not_discarded_item()
if is_game_over(node.snake):
node.discard()
elif will_snake_eat_the_food(node.snake, food):
node.explore()
path = []
while node.parent:
path.append(node.snake.direction)
node = node.parent
if not path:
path.append(self.decide_by_side(snake, food))
return path
else:
node.explore()
discard_count = 0
# if random.randrange(0, 10) > 6:
# random.shuffle(DIRECTIONS)
for direction in DIRECTIONS:
new_snake = copy.deepcopy(node.snake)
new_snake.direction = direction
new_snake.move()
new_node = ExplorationNode(new_snake, node)
stack.push(new_node)
if new_node.is_discarded():
discard_count += 1
if discard_count == 4:
node.discard()
return [snake.direction]
def decide_bfs(self, snake, food):
queue = []
return snake.direction
def decide_with_distance(self, snake, food):
paths = []
for direction in DIRECTIONS:
new_snake = copy.deepcopy(snake)
new_snake.direction = direction
new_snake.move()
if not is_game_over(new_snake):
paths.append([new_snake.direction, _manhattan_distance(new_snake.head(), food)])
# already dead
if not paths:
return snake.direction
paths = sorted(paths, key=lambda x: x[1])
return paths[0][0]
def decide_by_side(self, snake, food):
head_x, head_y = snake.head()
food_x, food_y = food
snake_up = copy.deepcopy(snake)
snake_up.direction = UP
snake_up.move()
snake_down = copy.deepcopy(snake)
snake_down.direction = DOWN
snake_down.move()
snake_left = copy.deepcopy(snake)
snake_left.direction = LEFT
snake_left.move()
snake_right = copy.deepcopy(snake)
snake_right.direction = RIGHT
snake_right.move()
# optimal movement
if head_x < food_x and not is_game_over(snake_right):
return RIGHT
if head_x > food_x and not is_game_over(snake_left):
return LEFT
if head_y > food_y and not is_game_over(snake_up):
return UP
if head_y < food_y and not is_game_over(snake_down):
return DOWN
# survival movement
if not is_game_over(snake_right):
return RIGHT
if not is_game_over(snake_left):
return LEFT
if not is_game_over(snake_up):
return UP
if not is_game_over(snake_down):
return DOWN
# no choice
return snake.direction
def _manhattan_distance(head, food):
head_x, head_y = head
food_x, food_y = food
return abs(head_x - food_x) + abs(head_y - food_y)
def _euclidean_distance(head, food):
x1, y1 = head
x2, y2 = food
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
def _chebyshev_distance(head, food):
x1, y1 = head
x2, y2 = food
return max(abs(x1 - x2), abs(y1 - y2))