forked from radjavi/considition20
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logic.py
197 lines (169 loc) · 6.63 KB
/
logic.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
from collections import defaultdict
from constants import *
def nr_ticks_left(state):
return state.max_turns - state.turn - 1
def residence_heuristic_score(state, residence, nr_ticks):
"""Logic for estimating a new residence's contribution to the score at the end of the game
Args:
state (GameState) - The current game state
residence (BlueprintResidenceBuilding) - The new building blueprint
nr_ticks (int) - Number of ticks the building will contribute to the score
Returns:
int - The chosen building's contribution to the final score
"""
happiness = residence_heuristic_happiness(state, residence, nr_ticks)
co2 = residence_heuristic_co2(state, residence, nr_ticks)
return 15 * residence.max_pop + 0.1 * happiness - co2
def residence_heuristic_happiness(state, residence, nr_ticks):
return (
(
residence.max_happiness
+ (
residence.max_happiness * 0.1
if not any(
residence.building_name == y.building_name for y in state.residences
)
else 0
)
)
* residence.max_pop
* nr_ticks
)
def residence_heuristic_co2(state, residence, nr_ticks):
avg_map_temp = (state.max_temp + state.min_temp) / 2
return (
residence.co2_cost
+ residence.max_pop * CO2_PER_POP * nr_ticks
+ 0.15
* (
(
(OPT_TEMP - avg_map_temp) * residence.emissivity
- DEGREES_PER_POP * residence.max_pop
)
/ DEGREES_PER_EXCESS_MWH
+ residence.base_energy_need
)
* nr_ticks
)
def calculate_energy_need(state, residence, blueprint):
"""Logic for determinating the energy need for a building
Args:
state (GameState) - The current game state
residence (Residence) - The residence
blueprint (BlueprintResidenceBuilding) - The building blueprint
Returns:
int - The energy needed
"""
base_energy_need = (
blueprint.base_energy_need + 1.8
if "Charger" in residence.effects
else blueprint.base_energy_need
)
emissivity = (
blueprint.emissivity * 0.6
if "Insulation" in residence.effects
else blueprint.emissivity
)
energy_wanted = (
OPT_TEMP
- residence.temperature
- DEGREES_PER_POP * residence.current_pop
+ (residence.temperature - state.current_temp) * emissivity
) / DEGREES_PER_EXCESS_MWH + base_energy_need
return max(energy_wanted, base_energy_need + 1e-2)
def best_residence_location(state):
"""Logic for determinating the best residence location based on the current game state
Args:
state (GameState) - The current game state
Returns:
(int, int) - x and y coordinates for the best residence location
"""
scores = defaultdict(int)
available = available_map_slots(state)
for x1, y1 in available:
scores[(x1, y1)] = 0
for x2 in range(len(state.map)):
for y2 in range(len(state.map)):
d = manhattan_distance(x1, y1, x2, y2)
if d > 0:
if d <= 3 and state.map[x2][y2] == POS_EMPTY:
scores[(x1, y1)] += 1 / d
if state.map[x2][y2] == POS_RESIDENCE:
scores[(x1, y1)] += 10 / d
if d <= 3 and state.map[x2][y2] == POS_MALL:
scores[(x1, y1)] += 100 / d
if d <= 2 and state.map[x2][y2] == POS_PARK:
scores[(x1, y1)] += 100 / d
if d <= 2 and state.map[x2][y2] == POS_WINDTURBINE:
scores[(x1, y1)] += 100 / d
if not scores:
return (-1, -1)
return max(scores, key=lambda x: scores[x]) # Key with max value
def best_utility_location(state, building_name):
"""Logic for determinating the best utility location based on the current game state
Args:
state (GameState) - The current game state
building_name (str) - The building name
Returns:
(int, int) - x and y coordinates for the best residence location
"""
scores = defaultdict(int)
available = available_map_slots(state)
for x1, y1 in available:
scores[(x1, y1)] = 0
for x2 in range(len(state.map)):
for y2 in range(len(state.map)):
d = manhattan_distance(x1, y1, x2, y2)
if d > 0:
if d <= 3 and state.map[x2][y2] == POS_EMPTY:
scores[(x1, y1)] += 1 / d
if (
d <= 3
and state.map[x2][y2] == POS_RESIDENCE
and building_name == "Mall"
):
scores[(x1, y1)] += 100 / d
if (
d <= 2
and state.map[x2][y2] == POS_RESIDENCE
and (building_name == "Park" or building_name == "WindTurbine")
):
scores[(x1, y1)] += 100 / d
if ( # Don't place in range of identical utility
d <= 3 * 2
and state.map[x2][y2] == POS_MALL
and building_name == "Mall"
):
scores[(x1, y1)] = -1e5
if ( # Don't place in range of identical utility
d <= 2 * 2
and state.map[x2][y2] == POS_PARK
and building_name == "Park"
):
scores[(x1, y1)] = -1e5
if ( # Don't place in range of identical utility
d <= 2 * 2
and state.map[x2][y2] == POS_WINDTURBINE
and building_name == "WindTurbine"
):
scores[(x1, y1)] = -1e5
if not scores:
return (-1, -1)
return max(scores, key=lambda x: scores[x]) # Key with max value
def available_map_slots(state):
"""Goes through the map and finds available slots
Args:
state (GameState) - The current game state
Returns:
[(int,int)] - A list of tuples containing ints indicating the available locations
"""
# Go through the map and find available slots
return [
(i, j)
for i in range(len(state.map))
for j in range(len(state.map))
if state.map[i][j] == 0
]
def manhattan_distance(x1, y1, x2, y2):
"""Calculates the manhattan distance"""
return abs(x1 - x2) + abs(y1 - y2)