-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgame_utils.py
201 lines (161 loc) · 6.2 KB
/
game_utils.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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
from collections import namedtuple
from enum import Enum
import time
import torch
import numpy as np
from game_settings import BLOCK_SIZE, SCREEN_W, SCREEN_H
Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state', 'done'))
Point = namedtuple('Point', 'x, y')
DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
class Direction(Enum):
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
UP_LEFT = (-1, -1)
UP_RIGHT = (1, -1)
DOWN_LEFT = (-1, 1)
DOWN_RIGHT = (1, 1)
# rgb colors
WHITE = (255, 255, 255)
RED = (200,0,0)
BLUE1 = (0, 0, 255)
BLUE2 = (0, 100, 255)
BLACK = (0,0,0)
CLOCK_WISE = [Direction.RIGHT, Direction.DOWN, Direction.LEFT, Direction.UP]
def calculate_distance(snake_head, food_head):
# Manhattan
dx = abs(food_head.x - snake_head.x)
dy = abs(food_head.y - snake_head.y)
return (dx + dy) // BLOCK_SIZE
def calculate_angle(snake, food_position):
snake_head = snake.head
# Convert enum to direction vector
snake_direction_vector = np.array(snake.direction.value)
# Vector from snake to food
vector_to_food = np.array([food_position[0] - snake_head[0], food_position[1] - snake_head[1]])
# Normalize the vector to food to have a magnitude of 1 for accurate angle calculation
norm = np.linalg.norm(vector_to_food)
if norm == 0:
# Depending on your game logic, you might want to set vector_to_food_normalized to a specific value when the snake is at the food position
vector_to_food_normalized = vector_to_food # Or handle as appropriate for your application
else:
vector_to_food_normalized = vector_to_food / norm
# Calculate the dot product and angle
dot_product = np.dot(vector_to_food_normalized, snake_direction_vector)
dot_product = np.clip(dot_product, -1.0, 1.0) # Ensure it's within the valid range for arccos
angle = np.arccos(dot_product)
# Convert angle from radians to degrees
angle_degrees = np.degrees(angle)
return angle_degrees
def normalize_distance(distance, max_distance):
return distance / max_distance
def sort_obstacles(snake_head, obstacles):
# Calculate distance from snake head to each obstacle and store in a list of tuples
distances = [(obstacle, calculate_distance(snake_head, obstacle)) for obstacle in obstacles]
# Sort the list of tuples based on distance
sorted_obstacles = sorted(distances, key=lambda x: x[1])
# Extract sorted obstacles without distances
sorted_obstacles_without_distances = [obstacle[0] for obstacle in sorted_obstacles]
return sorted_obstacles
def ray_trace_to_obstacle(head, direction, obstacles):
"""
Trace a ray from the snake's head in the current direction until it hits an obstacle or boundary.
:param head: The starting point of the ray (snake's head position).
:param direction: The current direction of the snake.
:param obstacles: A list of obstacle positions.
:return: The distance to the closest obstacle in the current direction.
"""
BLOCK_SIZE = 20 # Assuming a defined block size for movements.
dx, dy = 0, 0
# Determine the direction vector
if direction == Direction.RIGHT:
dx = BLOCK_SIZE
elif direction == Direction.LEFT:
dx = -BLOCK_SIZE
elif direction == Direction.UP:
dy = -BLOCK_SIZE
elif direction == Direction.DOWN:
dy = BLOCK_SIZE
distance = 0
current_position = Point(head.x, head.y)
# Move the ray step by step in the specified direction
while True:
current_position = Point(current_position.x + dx, current_position.y + dy)
distance += BLOCK_SIZE
# Check for boundary collision
if current_position.x < 0 or current_position.x >= SCREEN_W or current_position.y < 0 or current_position.y >= SCREEN_H:
break
# Check for obstacle collision
if current_position in obstacles:
break
return distance
def check_dangers(game):
dangers = []
for direction in Direction:
dx, dy = direction.value
new_x = game.snake.head.x + (dx * BLOCK_SIZE)
new_y = game.snake.head.y + (dy * BLOCK_SIZE)
nearest_point = Point(new_x, new_y)
if nearest_point == game.food.head:
dangers.append(False)
continue
# Check if the new position is within the grid boundaries
if game.is_collision(nearest_point):
dangers.append(True)
else:
dangers.append(False)
return dangers
def rotate_grid(grid, direction):
"""
Rotate the grid so that it aligns with the snake's current direction.
Parameters:
- grid: The vision grid as a numpy array.
- direction: The current direction of the snake.
Returns:
- Rotated grid as a numpy array.
"""
if direction == Direction.UP:
# No rotation needed.
return grid
elif direction == Direction.LEFT:
# Rotate -90 degrees.
return np.rot90(grid, -1)
elif direction == Direction.RIGHT:
# Rotate 90 degrees
return np.rot90(grid, 1)
elif direction == Direction.DOWN:
# Rotate 180 degrees.
return np.rot90(grid, 2)
class Timer:
def __init__(self):
self.start_time = None
self.elapsed_time = 0
self.is_running = False
def start(self):
if not self.is_running:
self.start_time = time.time() - self.elapsed_time
self.is_running = True
else:
print("Timer is already running.")
def stop(self):
if self.is_running:
self.elapsed_time = time.time() - self.start_time
self.is_running = False
else:
print("Timer is not running.")
def continue_timer(self):
if not self.is_running:
self.start_time = time.time() - self.elapsed_time
self.is_running = True
# else:
# print("Timer is already running.")
def get_elapsed_time(self):
if self.is_running:
return time.time() - self.start_time
else:
return self.elapsed_time
def reset(self):
self.start_time = None
self.elapsed_time = 0
self.is_running = False