-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
174 lines (143 loc) · 5.65 KB
/
main.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
"""
Main call module for my GDMC submission, includes a command line parser and the outline of the algorithm
"""
import argparse
import logging
import multiprocessing as mp
from os import sep
import time
import sys
import warnings
from typing import Dict, Union, Callable, List
warnings.filterwarnings("ignore")
# Managing dependencies and Python path
this_path = str(__file__) # path to this file
this_path = sep.join(this_path.split(sep)[:-1]) # path to the directory where
if not this_path: this_path = "."
sys.path.insert(0, this_path + sep + 'src') # path to our code
logging.basicConfig()
from building_seeding import district
from settlement import Settlement
from terrain import TerrainMaps, ObstacleMap
def main(districts=None, seeding=None, parcels=None, generation=None, visualize=False, undo=False) -> None:
print("Hello Settlers!")
# get & parse building zone
terrain: TerrainMaps = TerrainMaps.request()
ObstacleMap.from_terrain(terrain) # initialize obstacle map from the terrain
settlement = Settlement(terrain)
if districts:
districts(settlement, visualize=visualize)
else: return
if seeding:
# define buildings list and seed them
seeding(settlement, visualize)
else: return
settlement.clean_road_network()
if parcels:
# define parcels around seeds
parcels(settlement, visualize=visualize)
else: return
if generation:
# build buildings on parcels
settlement.terraform()
generation(settlement)
else: return
# Optional erasing of the generated settlement
if undo:
do_undo = input("Undo ? [y]/n").lower()
if do_undo in {"", "y"}:
terrain.undo()
def get_parser() -> argparse.ArgumentParser:
"""
Parser for my gdmc submission
:return: argument parser
"""
parser = argparse.ArgumentParser()
parser.add_argument("--steps", "-s", nargs='+', type=str, default=["D1", "S0", "G", "P"])
parser.add_argument("--visualize", "-v", action="store_true", help="Export visualization maps during run")
parser.add_argument("--time", "-T", type=int, default=600, help="Time limit in seconds for the whole run, negative value for no limit")
run_modes = parser.add_mutually_exclusive_group()
run_modes.add_argument("--undo", "-u", action="store_true", help="Undo generation after user input")
run_modes.add_argument("--perf", "-p", action="store_true", help="Run code with profiler")
return parser
def get_generation_options(step_list: List[str]) -> Dict[str, Union[Callable, None]]:
"""
For all the algorithm steps, selects the algorithm to use from the args of the command line
:param step_list: command line "steps" arg
:return: mapping step -> function
"""
# Mapping between options in the command line to parts of the generation algorithm
_step_to_option = {
"D": "districts",
"S": "seeding",
"P": "parcels",
"G": "generation"
}
# Options for the steps arguments
_steps_dictionary = {
"D": {
"0": (district.build_default_district, "Generate a single district all over the build area"),
"1": (Settlement.build_districts, "Apply Kmeans algorithm to select multiple villages (2021 submission)")
},
"S": {
"0": (Settlement.build_skeleton, "Iteratively seed positions for parcels of specific type")
},
"P": {
"": (Settlement.define_parcels, "Iteratively expand parcels one direction after another")
},
"G": {
"": (Settlement.generate, "Generate parcels and road network")
}
}
# Functions to be used during generation, will be passed to the main function
_gen_options = {
"districts": None,
"seeding": None,
"parcels": None,
"generation": None
}
assert step_list
for step in step_list:
step, step_variation = step[0], step[1:]
assert step in _step_to_option
gen_step = _step_to_option[step]
print(f"Selecting {gen_step} algorithm...")
func, desc = _steps_dictionary[step][step_variation]
_gen_options[gen_step] = func
print(f"\tWill use '{func.__name__}': {desc}")
try:
unspec_step = next(step for step in _gen_options if _gen_options[step] is None)
print(f"The algorithm will stop right before the {unspec_step} step: no option specified")
except StopIteration:
print("All steps are correctly configured, settlers are ready to move in !")
finally:
return _gen_options
if __name__ == '__main__':
t0 = time.time_ns()
# Parse the command line
args = get_parser().parse_args()
# Inspect the steps argument
gen_options = get_generation_options(args.steps)
gen_options["visualize"] = args.visualize
gen_options["undo"] = args.undo
if args.perf:
print("Running profiler mode...")
# code profiler
from pstats import Stats, SortKey
import cProfile
stats: Stats = cProfile.run(f"main(**gen_options)", sort=SortKey.CUMULATIVE)
elif args.time > 0 and not args.undo:
# run code with time limit
print(f"Running default mode... Will time-out after {args.time} seconds")
p = mp.Process(target=main, name="gdmc_run", kwargs=gen_options)
p.start()
p.join(args.time)
if p.is_alive():
print(f"Reached time limit of {args.time}s, timing-out generation !")
p.terminate()
p.join()
else:
print("Running endless mode...")
main(**gen_options)
t1 = time.time_ns()
print(f"Generation successfully completed in {(t1 - t0)/10**9:0.3f} seconds !")