-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
executable file
·166 lines (140 loc) · 7 KB
/
main.py
File metadata and controls
executable file
·166 lines (140 loc) · 7 KB
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
#!/usr/bin/python3
import argparse
import configparser
import logging
import pathlib
import sys
from field import Field
from sheep import Sheep
from wolf import Wolf
WELCOME_STRING = r"""
____ _ _ _ _
/ ___| | |__ ___ ___ _ __ _ __ ___ _ _ | | __ _ | |_ (_) ___ _ __
\___ \ | '_ \ / _ \ / _ \| '_ \ | '_ ` _ \ | | | || | / _` || __|| | / _ \ | '_ \
___) || | | || __/| __/| |_) || | | | | || |_| || || (_| || |_ | || (_) || | | |
|____/ |_| |_| \___| \___|| .__/ |_| |_| |_| \__,_||_| \__,_| \__||_| \___/ |_| |_|
|_|
By Tomasz Kowalczyk & Jakub Kalinowski
"""
def prepare_argument_parser() -> argparse.ArgumentParser:
argument_parser = argparse.ArgumentParser(
prog="Sheepmulation",
description="Simulate wolf hunting a herd of sheep",
epilog="By Tomasz Kowalczyk & Jakub Kalinowski",
add_help=True)
argument_parser.add_argument("-c", "--config",
help="path to config file",
action="store",
dest="config_file",
default=None,
type=str)
argument_parser.add_argument("-l", "--log",
help="logging level",
action="store",
dest="log_level",
default=None,
type=str)
argument_parser.add_argument("-r", "--rounds",
help="number of rounds to simulate",
action="store",
dest="rounds",
default=50,
type=int)
argument_parser.add_argument("-s", "--sheep",
help="number of sheep at the start",
action="store",
dest="sheep",
default=15,
type=int)
argument_parser.add_argument("-w", "--wait",
help="wait for input after each round",
action="store_true",
dest="wait",
default=False)
return argument_parser
def load_sheep_config_file_and_apply(config_from_file: configparser.ConfigParser) -> None:
if "InitPosLimit" in config_from_file["Sheep"]:
try:
max_init_coord_from_file = float(config_from_file["Sheep"]["InitPosLimit"])
except ValueError as e:
raise ValueError(f"config file: invalid InitPosLimit value in config file") from e
if max_init_coord_from_file <= 0.0:
raise ValueError(
f"config file: invalid max initial coord of sheep ({max_init_coord_from_file}) in config file")
Sheep.max_init_coord = max_init_coord_from_file
logging.debug(f"Loaded max init sheep coord = {max_init_coord_from_file} from config file")
if "MoveDist" in config_from_file["Sheep"]:
try:
movement_dist_from_file = float(config_from_file["Sheep"]["MoveDist"])
except ValueError as e:
raise ValueError(f"config file: invalid Sheep MoveDist value in config file") from e
if movement_dist_from_file <= 0.0:
raise ValueError(
f"config file: invalid movement distance of sheep ({movement_dist_from_file}) in config file")
Sheep.movement_distance = movement_dist_from_file
logging.debug(f"Loaded sheep movement distance = {movement_dist_from_file} from config file")
def load_wolf_config_file_and_apply(config_from_file: configparser.ConfigParser) -> None:
if "MoveDist" in config_from_file["Wolf"]:
try:
movement_dist_from_file = float(config_from_file["Wolf"]["MoveDist"])
except ValueError as e:
raise ValueError(f"config file: invalid Wolf MoveDist value in config file") from e
if movement_dist_from_file <= 0.0:
raise ValueError(
f"config file: invalid movement distance of wolf ({movement_dist_from_file}) in config file")
Wolf.movement_distance = movement_dist_from_file
logging.debug(f"Loaded wolf movement distance = {movement_dist_from_file} from config file")
def load_config_file_and_apply(filename: str) -> None:
try:
config_from_file = configparser.ConfigParser()
try:
config_from_file.read(filename)
except OSError as e:
raise ValueError(f"argument -c/--config: could not read from config file (OSError)") from e
if "Sheep" in config_from_file:
load_sheep_config_file_and_apply(config_from_file)
if "Wolf" in config_from_file:
load_wolf_config_file_and_apply(config_from_file)
except configparser.Error as e:
raise ValueError(f"argument -c/--config: invalid config file format") from e
def validate_program_arguments(arguments: argparse.Namespace) -> argparse.Namespace:
# Additional argument validation
log_level = arguments.log_level
if log_level is not None and log_level not in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
raise ValueError(f"argument -l/--log: invalid value: {log_level}!")
rounds = arguments.rounds
if rounds <= 0:
raise ValueError(f"argument -r/--rounds: invalid value: {rounds}!")
sheep = arguments.sheep
if sheep <= 0:
raise ValueError(f"argument -s/--sheep: invalid value: {sheep}!")
# Load config file
config_file_name = arguments.config_file
if config_file_name is not None and not pathlib.Path(config_file_name).is_file():
raise ValueError(f"argument -c/--config: selected config file ({config_file_name}) does not exist")
return arguments
def main():
argument_parser = prepare_argument_parser()
# Custom exception handler to display program usage with exception and hide call stack
def exception_handler(exception_type, exception, traceback):
argument_parser.print_usage()
print(f"{exception_type.__name__}: {exception}")
sys.excepthook = exception_handler
# Get and validate program arguments
args = validate_program_arguments(argument_parser.parse_args())
# Set logging level
if args.log_level is not None:
logging.basicConfig(filename="chase.log", filemode="w", level=args.log_level,
format='[%(asctime)s] [%(levelname)s] %(message)s',
datefmt='%Y/%m/%d %H:%M:%S')
else:
logging.disable()
# Load and apply config from file
if args.config_file is not None:
load_config_file_and_apply(args.config_file)
print("\033[0;32m" + WELCOME_STRING + "\033[0m")
field = Field(args.sheep, args.rounds, args.wait)
# Start simulation
field.run_simulation()
if __name__ == '__main__':
main()