-
Notifications
You must be signed in to change notification settings - Fork 0
/
battlefield.py
192 lines (158 loc) · 7.14 KB
/
battlefield.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
"""
The battlefield in the game, consisting a 2D list of grids
"""
import random
import math
from grid import Grid
from Framework.interface import IDisplayable
from barricade import Barricade
from barricade import HardBarricade
from robot import Robot
class Battlefield(IDisplayable):
def __init__(self, rows: int, columns: int) -> None:
"""
Create a new empty 2D list consisting of empty Grid
:param rows: the num of rows in the battlefield
:param columns: the num of colomns in the battlefield
"""
# create new empty grid
self.field = [[Grid((j, i)) for j in range(0, columns)] for i in range(0, rows)]
def initialize_field(self, barricade_coverage: float, hard_barricade_coverage: float,
barricade_HP_range: tuple, barricade_armor_range: tuple) -> None:
"""
Install barricades and hard barricades at random locations on the field
Preconditions:
- 0 <= barricade_coverage < 1
- 0 <= hard_barricade_coverage < barricade_coverage
- len(barricade_HP_range) == 2 and barricade_HP_range[1] >= barricade_HP_range[0]
- len(barricade_armor_range) == 2 and barricade_armor_range[1] >= barricade_armor_range[0]
:param barricade_coverage: the proportion of field covered by barricade
:param hard_barricade_coverage: the proportion of field covered by hard_barricade,
hard barricades are covered on barricade
:param barricade_HP_range: the range of HP for hard barricade
:param barricade_armor_range: the range of armor for hard barricade
:return: None
"""
for row in self.field:
for grid in row:
random_temp = random.random()
set_barricade = random_temp <= barricade_coverage
set_hard_barricade = random_temp <= hard_barricade_coverage
if not grid.get_occupant() and set_barricade: # cover an empty grid
if set_hard_barricade:
# set up hard barricade with random HP and armor in range
HP_range = random.randint(barricade_HP_range[0], barricade_HP_range[1])
armor_range = random.randint(barricade_armor_range[0], barricade_armor_range[1])
grid.change_occupant(HardBarricade(HP_range, armor_range, grid))
else:
# set up barricade
grid.change_occupant(Barricade(grid))
def initialize_player_location(self, player: Robot, max_trial=20):
"""
Spawn the player at random empty locations on the field.
If an empty location is not found in max_trail times, spawn at any location not occupied by another player
:param player: the player to place
:param max_trial: the maximum times to select empty locations before overriding an existing barricade is allowed
:return: None
"""
fixed = False # whether a valid location is selected
trail = 0
while not fixed:
trail += 1
row = random.randint(0, len(self.field) - 1)
col = random.randint(0, len(self.field[0]) - 1)
grid = self.field[row][col]
if grid.get_occupant() is None: # find empty grid
grid.change_occupant(player)
player.set_pos(grid)
fixed = True
elif not isinstance(grid.get_occupant(), Robot) and trail > max_trial: # not override another player
grid.change_occupant(player)
player.set_pos(grid)
fixed = True
def get_grid(self, x: int, y: int):
"""
Return the grid at a given row and col, return none if the
indexes are out of bound
:param x: the x-coordinate of grid
:param y: the y-ccordinate of grid
:return: field[y][x]
"""
if y < 0 or y >= len(self.field) or x < 0 or x >= len(self.field[0]):
print("grid index out of bound")
return None
return self.field[y][x]
def is_blocked(self, x: int, y: int) -> bool:
"""
Return whether field[row][col] is occupied by a player
or a hard barricade
Return True if (x, y) is out of bound
:param x: the x-coordinate of grid
:param y: the y-ccordinate of grid
:return: whether the grid is blocked
"""
# check out-of-bound cases
if self.get_grid(x, y) is None:
return True
return self.get_grid(x, y).display() == '#' or self.get_grid(x, y).display() == 'R'
def is_occupied(self, x: int, y: int) -> bool:
"""
Return whether field[row][col] is occupied by any object
Return True if (x. y) is out of bound
:param x: the x-coordinate of grid
:param y: the y-ccordinate of grid
:return: whether the grid is occupied
"""
if self.get_grid(x, y) is None:
return True
return not self.get_grid(x, y).display() == '_'
def generate_heat(self, x: int, y: int, intensity: int) -> None:
"""
Generate heat signal from a given coordinate (x, y)
Preconditions:
- 0 <= x < len(self.field[0])
- 0 <= y < len(self.field)
:param x: the x-coordinate of the heat source
:param y: the y-coordinate of the heat source
:param intensity: the intensity of the heat
:return: None
"""
for field_row in self.field:
for grid in field_row:
px, py = grid.get_pos()
grid.change_heat(max(intensity - int(math.sqrt((x - px) ** 2 + (y - py) ** 2)), 0))
def generate_sound(self, x: int, y: int, intensity: int) -> None:
"""
Generate sound signal from a given coordinate (x, y)
Preconditions:
- 0 <= x < len(self.field[0])
- 0 <= y < len(self.field)
:param x: the x-coordinate of the sound source
:param y: the y-coordinate of the sound source
:param intensity: the intensity of the sound
:return: None
"""
for field_row in self.field:
for grid in field_row:
px, py = grid.get_pos()
grid.change_sound(max(intensity - int(math.sqrt((x - px) ** 2 + (y - py) ** 2)), 0))
def reduce_sound_and_heat(self, sound_reduction: int, heat_reduction: int) -> None:
"""
Reduce the sound and heat intensity in the entire field
Preconditions:
- sound_reduction >= 0
- heat_reduction >= 0
:param sound_reduction: the amount of sound reduced
:param heat_reduction: the amount of heat reduced
:return: None
"""
for field_row in self.field:
for grid in field_row:
grid.change_sound(-sound_reduction)
grid.change_heat(-heat_reduction)
def display(self) -> list[list[str]]:
"""
Override display() in IDisplayable
:return: a string representation of the battlefield
"""
return [[grid.display() for grid in row] for row in self.field]