@jutge.org/toolkit 4.2.22 → 4.2.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/problems/games/the-walking-dead.pbm/README.md +12 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/Makefile +6 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/api.tex +246 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/defs.tex +2 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/main.tex +63 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/programming.tex +452 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/rules.tex +222 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/screenshot.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/tips.tex +81 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/twd.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc/viewer.tex +41 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/Makefile +6 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/api.tex +246 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/defs.tex +2 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/main.tex +63 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/programming.tex +176 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/rules.tex +194 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/screenshot.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/tips.tex +85 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/twd.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/viewer.tex +36 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.Linux64 +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.Linux64.Debug +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.MacOS +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.MacOS.ARM +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.MacOS.ARM.Debug +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.MacOS.Debug +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.Linux64 +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.Linux64.Debug +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.MacOS +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.MacOS.ARM +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.MacOS.ARM.Debug +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.MacOS.Debug +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/AIDemo.cc +89 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/AIDummy.cc +202 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/AINull.cc +37 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Action.cc +34 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Action.hh +107 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Board.cc +975 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Board.hh +287 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Defs.hh +2 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Game.cc +55 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Game.hh +23 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Info.cc +174 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Info.hh +129 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Main.cc +83 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Makefile +56 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Player.cc +66 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Player.hh +63 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/README.txt +9 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Random.cc +3 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Random.hh +88 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Registry.cc +28 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Registry.hh +42 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/SecGame.cc +368 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/SecGame.hh +90 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/SecMain.cc +99 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Settings.cc +67 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Settings.hh +175 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/State.cc +3 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/State.hh +188 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Structs.cc +3 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Structs.hh +377 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Utils.cc +3 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Utils.hh +77 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/default-fixed.cnf +120 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Runner/default.cnf +17 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/jquery-ui-1.8.18.custom.css +310 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/help.html +18 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_close.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_end.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_help.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_pause.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_play.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_refresh.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_start.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/logo.png +0 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/js/jquery-1.7.1.min.js +4 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/js/jquery-ui-1.8.18.custom.min.js +49 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/sample.out +52807 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/viewer.html +118 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/viewer.js +846 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/viewer.sh +28 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/handler.yml +6 -0
- package/assets/problems/games/the-walking-dead.pbm/ca/problem.ca.yml +3 -0
- package/assets/prompts/creators/create-solution.tpl.txt +15 -8
- package/assets/prompts/proglangs/cc.md +6 -2
- package/assets/prompts/proglangs/py.md +10 -6
- package/dist/index.js +370 -366
- package/docs/getting-started-guide.md +1 -1
- package/docs/install-linux.md +1 -1
- package/docs/install-macos.md +1 -1
- package/docs/install-windows.md +1 -1
- package/package.json +11 -11
- package/toolkit/ask.ts +0 -2
- package/toolkit/doctor.ts +1 -1
- package/toolkit/make.ts +2 -2
|
@@ -0,0 +1,975 @@
|
|
|
1
|
+
//////// STUDENTS DO NOT NEED TO READ BELOW THIS LINE ////////
|
|
2
|
+
|
|
3
|
+
#include "Board.hh"
|
|
4
|
+
#include "Action.hh"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Board::Board (istream& is, int seed) {
|
|
8
|
+
set_random_seed(seed);
|
|
9
|
+
*static_cast<Settings*>(this) = Settings::read_settings(is);
|
|
10
|
+
|
|
11
|
+
player2alive_units = vector<set<int>>(num_players());
|
|
12
|
+
player2dead_units = vector<set<int>>(num_players());
|
|
13
|
+
zombies_ = set<int>();
|
|
14
|
+
|
|
15
|
+
names = vector<string>(num_players());
|
|
16
|
+
scr = vector<int> (num_players(), 0);
|
|
17
|
+
scr_accumulated = vector<int> (num_players(), 0);
|
|
18
|
+
nb_cells = vector<int> (num_players(), 0); // Is computed in read grid
|
|
19
|
+
overall_strength = vector<int> (num_players(), clan_ini_strength());
|
|
20
|
+
|
|
21
|
+
stats = vector<double> (num_players(), 0);
|
|
22
|
+
|
|
23
|
+
rnd = 0;
|
|
24
|
+
|
|
25
|
+
fresh_id = 0;
|
|
26
|
+
read_generator_and_grid(is);
|
|
27
|
+
|
|
28
|
+
for (auto& p : units) fresh_id = max(fresh_id,p.first);
|
|
29
|
+
++fresh_id;
|
|
30
|
+
|
|
31
|
+
_my_assert(ok(), "Invariants are not satisfied.");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void Board::check_is_good_initial_fixed_board () const {
|
|
35
|
+
vector<int> num_units(num_players(),0);
|
|
36
|
+
int num_zombies = 0;
|
|
37
|
+
int num_food = 0;
|
|
38
|
+
|
|
39
|
+
_my_assert(int(grid.size()) == board_rows(), "Fixed board has wrong number of rows.");
|
|
40
|
+
_my_assert(int(grid[0].size()) == board_cols(), "Fixed board has wrong number of cols.");
|
|
41
|
+
|
|
42
|
+
for (int i = 0; i < board_rows(); ++i)
|
|
43
|
+
for (int j = 0; j < board_cols(); ++j) {
|
|
44
|
+
Cell c = grid[i][j];
|
|
45
|
+
if (c.food) ++num_food;
|
|
46
|
+
if (c.id != -1) {
|
|
47
|
+
int id = c.id;
|
|
48
|
+
_my_assert(units.count(id) != 0, "Unit places in grid does noe appear in units");
|
|
49
|
+
const Unit& u = units.find(id)->second;
|
|
50
|
+
_my_assert(u.type != Dead, "Initial unit already dead");
|
|
51
|
+
if (u.type == Alive) {
|
|
52
|
+
_my_assert(player_ok(u.player), "Player not ok in check_is_good_initial_fixed_board");
|
|
53
|
+
_my_assert(c.owner == u.player, "Unit placed in a cell but player does not own it");
|
|
54
|
+
_my_assert(u.pos == Pos(i,j), "Live unit does not have right position");
|
|
55
|
+
_my_assert(player2alive_units[u.player].count(id) != 0, "Live unit not in player2alive_units");
|
|
56
|
+
++num_units[u.player];
|
|
57
|
+
}
|
|
58
|
+
else { // We know it is a zombie
|
|
59
|
+
++num_zombies;
|
|
60
|
+
_my_assert(u.player == -1, "Zombies should have player -1");
|
|
61
|
+
_my_assert(u.pos == Pos(i,j), "Zombie does not have right position");
|
|
62
|
+
_my_assert(zombies_.count(id) != 0, "Live unit not in zombies_");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_my_assert(num_food == num_ini_food(),"Fixed board has wrong number of initial food.");
|
|
68
|
+
_my_assert(num_zombies == num_ini_zombies(),"Fixed board has wrong number of initial zombies.");
|
|
69
|
+
for (int p = 0; p < num_players(); ++p)
|
|
70
|
+
_my_assert(int(player2alive_units[p].size()) == num_units[p], "Fixed board has wrong number of initial live units.");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void Board::print_settings (ostream& os) const {
|
|
74
|
+
|
|
75
|
+
os << version() << endl;
|
|
76
|
+
os << endl;
|
|
77
|
+
os << "NUM_PLAYERS" << "\t\t\t" << num_players() << endl;
|
|
78
|
+
os << "NUM_ROUNDS" << "\t\t\t" << num_rounds() << endl;
|
|
79
|
+
os << "BOARD_ROWS" << "\t\t\t" << board_rows() << endl;
|
|
80
|
+
os << "BOARD_COLS" << "\t\t\t" << board_cols() << endl;
|
|
81
|
+
os << "NUM_INI_UNITS_PER_CLAN" << "\t\t" << num_ini_units_per_clan() << endl;
|
|
82
|
+
os << "NUM_INI_ZOMBIES" << "\t\t\t" << num_ini_zombies() << endl;
|
|
83
|
+
os << "NUM_INI_FOOD" << "\t\t\t" << num_ini_food() << endl;
|
|
84
|
+
os << "CLAN_INI_STRENGTH" << "\t\t" << clan_ini_strength() << endl;
|
|
85
|
+
os << "POINTS_FOR_KILLING_PERSON" << "\t" << points_for_killing_person() << endl;
|
|
86
|
+
os << "POINTS_FOR_KILLING_ZOMBIE" << "\t" << points_for_killing_zombie() << endl;
|
|
87
|
+
os << "POINTS_PER_OWNED_CELL" << "\t\t" << points_per_owned_cell() << endl;
|
|
88
|
+
os << "FOOD_STRENGTH" << "\t\t\t" << food_strength() << endl;
|
|
89
|
+
os << "ROUNDS_BEFORE_BECOMING_ZOMBIE" << "\t" << rounds_before_becoming_zombie() << endl;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
void Board::print_names (ostream& os) const {
|
|
94
|
+
os << "names ";
|
|
95
|
+
for (int pl = 0; pl < num_players(); ++pl) os << ' ' << name(pl);
|
|
96
|
+
os << endl;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
void Board::print_state (ostream& os) {
|
|
101
|
+
|
|
102
|
+
// Should start with the same format of Info::read_grid.
|
|
103
|
+
// Then other data describing the state.
|
|
104
|
+
|
|
105
|
+
os << endl << endl;
|
|
106
|
+
|
|
107
|
+
os << " ";
|
|
108
|
+
for (int j = 0; j < board_cols(); ++j)
|
|
109
|
+
os << j / 10;
|
|
110
|
+
os << endl;
|
|
111
|
+
|
|
112
|
+
os << " ";
|
|
113
|
+
for (int j = 0; j < board_cols(); ++j)
|
|
114
|
+
os << j % 10;
|
|
115
|
+
os << endl;
|
|
116
|
+
|
|
117
|
+
for (int i = 0; i < board_rows(); ++i) {
|
|
118
|
+
os << i / 10 << i % 10 << " ";
|
|
119
|
+
for (int j = 0; j < board_cols(); ++j) {
|
|
120
|
+
const Cell& c = grid[i][j];
|
|
121
|
+
if (c.type == Waste) os << 'W';
|
|
122
|
+
else if (c.owner == 0) os << '0';
|
|
123
|
+
else if (c.owner == 1) os << '1';
|
|
124
|
+
else if (c.owner == 2) os << '2';
|
|
125
|
+
else if (c.owner == 3) os << '3';
|
|
126
|
+
else os << '.';
|
|
127
|
+
}
|
|
128
|
+
os << endl;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
os << endl << "units" << endl;
|
|
132
|
+
os << units.size() << endl;
|
|
133
|
+
os << "type\tid\tplayer\trow\tcolumn\trounds" << endl;
|
|
134
|
+
for (const auto& ci : units) {
|
|
135
|
+
os << UnitType2char(ci.second.type) << "\t";
|
|
136
|
+
os << ci.second.id << "\t";
|
|
137
|
+
os << ci.second.player << "\t";
|
|
138
|
+
os << ci.second.pos.i << "\t";
|
|
139
|
+
os << ci.second.pos.j << "\t";
|
|
140
|
+
os << ci.second.rounds_for_zombie << endl;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
os << endl << "food" << endl;
|
|
144
|
+
// Collect them
|
|
145
|
+
vector<Pos> food;
|
|
146
|
+
for (int i = 0; i < board_rows(); ++i)
|
|
147
|
+
for (int j = 0; j < board_cols(); ++j)
|
|
148
|
+
if (grid[i][j].food) food.push_back(Pos(i,j));
|
|
149
|
+
os << food.size() << endl;
|
|
150
|
+
os << "row\tcolumn" << endl;
|
|
151
|
+
for (const auto& p : food) {
|
|
152
|
+
os << p.i << "\t";
|
|
153
|
+
os << p.j << endl;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
os << endl;
|
|
157
|
+
|
|
158
|
+
os << "round " << rnd << endl;
|
|
159
|
+
os << endl;
|
|
160
|
+
|
|
161
|
+
os << "score";
|
|
162
|
+
for (auto s : scr) os << "\t" << s;
|
|
163
|
+
os << endl;
|
|
164
|
+
os << endl;
|
|
165
|
+
|
|
166
|
+
os << "scr_acc";
|
|
167
|
+
for (auto s : scr_accumulated) os << "\t" << s;
|
|
168
|
+
os << endl;
|
|
169
|
+
os << endl;
|
|
170
|
+
|
|
171
|
+
os << "strength";
|
|
172
|
+
for (auto s : overall_strength) os << "\t" << s;
|
|
173
|
+
os << endl;
|
|
174
|
+
os << endl;
|
|
175
|
+
|
|
176
|
+
os << "status";
|
|
177
|
+
for (auto s : stats) os << "\t" << s;
|
|
178
|
+
os << endl;
|
|
179
|
+
os << endl;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
void Board::print_results () const {
|
|
184
|
+
int max_score = 0;
|
|
185
|
+
vector<int> v;
|
|
186
|
+
for (int pl = 0; pl < num_players(); ++pl) {
|
|
187
|
+
|
|
188
|
+
cerr << "info: player " << name(pl)
|
|
189
|
+
<< " got score " << score(pl) << endl;
|
|
190
|
+
|
|
191
|
+
if (score(pl) == max_score) v.push_back(pl);
|
|
192
|
+
else if (score(pl) > max_score) {
|
|
193
|
+
max_score = score(pl);
|
|
194
|
+
v = vector<int>(1, pl);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
cerr << "info: player(s)";
|
|
199
|
+
for (int pl : v) cerr << " " << name(pl);
|
|
200
|
+
cerr << " got top score" << endl;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Returns whether c1 wins
|
|
204
|
+
bool Board::first_unit_wins_attack (const Unit& u1, const Unit& u2) {
|
|
205
|
+
int S = 30; // 30% vegades attack is a surprise and u1 wins
|
|
206
|
+
if (random(0,100) < S) return true;
|
|
207
|
+
|
|
208
|
+
// Otherwise, it depend on the strength
|
|
209
|
+
int u1_strength = strength(u1.player);
|
|
210
|
+
int u2_strength = strength(u2.player);
|
|
211
|
+
|
|
212
|
+
if (u1_strength + u2_strength == 0) return random(0,1) == 0; // If both no strength --> 50-50
|
|
213
|
+
int M = 1000;
|
|
214
|
+
int num = random(0,M);
|
|
215
|
+
double threshold = double(u1_strength)/(u1_strength + u2_strength)*M;
|
|
216
|
+
return num < threshold;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
void Board::perform_attack (Unit& orig_u, Unit& dest_u, vector<vector<int>>& alive_to_dead){
|
|
220
|
+
bool first_wins = first_unit_wins_attack(orig_u,dest_u);
|
|
221
|
+
Unit& winner = (first_wins ? orig_u : dest_u);
|
|
222
|
+
Unit& loser = (first_wins ? dest_u : orig_u );
|
|
223
|
+
|
|
224
|
+
alive_to_dead[loser.player].push_back(loser.id);
|
|
225
|
+
loser.type = Dead;
|
|
226
|
+
loser.rounds_for_zombie = rounds_before_becoming_zombie();
|
|
227
|
+
scr_accumulated[winner.player] += points_for_killing_person();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
bool Board::execute (const Command& m,
|
|
231
|
+
vector<int>& food_to_regenerate,
|
|
232
|
+
vector<vector<int>>& zombie_to_unit,
|
|
233
|
+
vector<vector<int>>& alive_to_dead
|
|
234
|
+
) {
|
|
235
|
+
int id = m.id;
|
|
236
|
+
Dir dir = Dir(m.dir);
|
|
237
|
+
CommandType c_type = CommandType(m.c_type);
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
if (not command_type_ok(c_type)) {
|
|
241
|
+
cerr << "warning: invalid command type in command: " << c_type << endl;
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (not dir_ok(dir)) {
|
|
246
|
+
cerr << "warning: invalid dir in command: " << dir << endl;
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
Unit& un = units[id];
|
|
251
|
+
UnitType type = un.type;
|
|
252
|
+
int pl = un.player;
|
|
253
|
+
Pos op = un.pos;
|
|
254
|
+
Cell& oc = grid[op.i][op.j];
|
|
255
|
+
|
|
256
|
+
if (type == Alive and (dir == DR or dir == RU or dir == UL or dir == LD)) return false; // Alive not diagonal
|
|
257
|
+
|
|
258
|
+
if (type == Dead) return false; // Unit is dead (maybe has been dead in this round)
|
|
259
|
+
|
|
260
|
+
Pos np = op + dir;
|
|
261
|
+
if (not pos_ok(np)) {
|
|
262
|
+
cerr << "warning: cannot move to position " << np << " out of the board." << endl;
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
Cell& nc = grid[np.i][np.j];
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
if (type == Zombie) {
|
|
270
|
+
if (nc.type == Waste) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
else if (nc.food) { // Cell with food
|
|
274
|
+
++food_to_regenerate[num_players()]; // last position is for zombies
|
|
275
|
+
nc.food = false;
|
|
276
|
+
nc.owner = -1;
|
|
277
|
+
nc.id = id;
|
|
278
|
+
un.pos = np;
|
|
279
|
+
oc.id = -1;
|
|
280
|
+
}
|
|
281
|
+
else if (nc.id == -1){ // Cell with no unit
|
|
282
|
+
nc.owner = -1;
|
|
283
|
+
nc.id = id;
|
|
284
|
+
un.pos = np;
|
|
285
|
+
oc.id = -1;
|
|
286
|
+
}
|
|
287
|
+
else { // Cell with unit
|
|
288
|
+
Unit& au = units[nc.id]; // attacked unit
|
|
289
|
+
if (au.type == Zombie or au.type == Dead){ // Zombie attacks zombie
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
else { // Attack and bite a live unit
|
|
293
|
+
if (au.rounds_for_zombie == -1) {
|
|
294
|
+
au.rounds_for_zombie = rounds_before_becoming_zombie() + 1;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (type == Alive) {
|
|
300
|
+
if (nc.type == Waste) {
|
|
301
|
+
cerr << "warning: cannot move to position " << np << " with waste." << endl;
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
else if (nc.food) { // Cell with food
|
|
305
|
+
++food_to_regenerate[pl];
|
|
306
|
+
nc.food = false;
|
|
307
|
+
nc.owner = pl;
|
|
308
|
+
nc.id = id;
|
|
309
|
+
un.pos = np;
|
|
310
|
+
oc.id = -1;
|
|
311
|
+
}
|
|
312
|
+
else if (nc.id == -1){ // Cell with no unit
|
|
313
|
+
nc.owner = pl;
|
|
314
|
+
nc.id = id;
|
|
315
|
+
un.pos = np;
|
|
316
|
+
oc.id = -1;
|
|
317
|
+
}
|
|
318
|
+
else { // Cell with unit
|
|
319
|
+
Unit& au = units[nc.id]; // attacked unit
|
|
320
|
+
if (au.type == Zombie) { // Kill a zombie
|
|
321
|
+
zombie_to_unit[pl].push_back(nc.id); // zombie will be regenerated as unit of this clan
|
|
322
|
+
nc.id = -1; // nobody is in the cell previouly occupied by the zombie
|
|
323
|
+
au.pos = {-1,-1}; // no position (pending to be regenerated)
|
|
324
|
+
scr_accumulated[pl] += points_for_killing_zombie();
|
|
325
|
+
}
|
|
326
|
+
else if (au.type == Dead) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
else if (au.player == pl) { // attacks among same clan not allowed
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
else { // attacks among different clans
|
|
333
|
+
perform_attack(un,au,alive_to_dead);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
bool Board::is_good_pos_to_regen ( const Pos& p) const {
|
|
343
|
+
|
|
344
|
+
if (not grid[p.i][p.j].is_empty()) return false;
|
|
345
|
+
|
|
346
|
+
for (int i = p.i - 2; i <= p.i + 2; ++i) { // Check nobody is nearby
|
|
347
|
+
for (int j = p.j - 2; j <= p.j + 2; ++j) {
|
|
348
|
+
if (pos_ok(Pos(i,j)) and grid[i][j].id != -1) return false;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
Pos Board::get_random_pos_where_regenerate ( ) {
|
|
355
|
+
vector<Pos> res;
|
|
356
|
+
for (int i = 0; i < board_rows(); ++i){
|
|
357
|
+
for (int j = 0; j < board_cols(); ++j) {
|
|
358
|
+
if (is_good_pos_to_regen(Pos(i,j))) res.push_back(Pos(i,j));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (res.size() != 0) return res[random(0,res.size()-1)];
|
|
363
|
+
else return get_empty_pos();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
// pair<bool,Pos> Board::get_random_pos_where_regenerate ( ) {
|
|
368
|
+
// vector<Pos> res;
|
|
369
|
+
// for (int i = 0; i < board_rows(); ++i){
|
|
370
|
+
// for (int j = 0; j < board_cols(); ++j) {
|
|
371
|
+
// if (is_good_pos_to_regen(Pos(i,j))) res.push_back(Pos(i,j));
|
|
372
|
+
// }
|
|
373
|
+
// }
|
|
374
|
+
|
|
375
|
+
// if (res.size() != 0) return {true,res[random(0,res.size()-1)]};
|
|
376
|
+
// else return {false,Pos()};
|
|
377
|
+
// }
|
|
378
|
+
|
|
379
|
+
void Board::next (const vector<Action>& act, ostream& os) {
|
|
380
|
+
|
|
381
|
+
_my_assert(ok(), "Invariants are not satisfied.");
|
|
382
|
+
|
|
383
|
+
int npl = num_players();
|
|
384
|
+
_my_assert(int(act.size()) == npl, "Size should be number of players.");
|
|
385
|
+
|
|
386
|
+
// Elements to be regenerated
|
|
387
|
+
vector<int> food_to_regenerate(num_players()+1,0); // we know how much food each clan has collected, in order to change overall_strength at the end of the round. Last position is for food eaten by zombies
|
|
388
|
+
vector<vector<int>> zombie_to_unit(num_players());
|
|
389
|
+
vector<vector<int>> alive_to_dead(num_players());
|
|
390
|
+
|
|
391
|
+
// Chooses (at most) one command per unit.
|
|
392
|
+
set<int> seen;
|
|
393
|
+
vector<vector<Command>> v(npl);
|
|
394
|
+
for (int pl = 0; pl < npl; ++pl)
|
|
395
|
+
for (const Command& m : act[pl].v) {
|
|
396
|
+
int id = m.id;
|
|
397
|
+
int c_type = m.c_type;
|
|
398
|
+
int dir = m.dir;
|
|
399
|
+
|
|
400
|
+
auto it = units.find(id);
|
|
401
|
+
|
|
402
|
+
if (it == units.end()) cerr << "warning: invalid id : " << id << endl;
|
|
403
|
+
else if (it->second.player != pl)
|
|
404
|
+
cerr << "warning: unit " << id << " of player " << it->second.player
|
|
405
|
+
<< " not owned by " << pl << endl;
|
|
406
|
+
else {
|
|
407
|
+
// Here an assert as repetitions should have already been filtered out.
|
|
408
|
+
_my_assert(not seen.count(id), "More than one command for the same unit.");
|
|
409
|
+
seen.insert(id);
|
|
410
|
+
|
|
411
|
+
v[pl].push_back(Command(id, c_type, dir));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
// Makes all players' commands using a random order,
|
|
417
|
+
// but respecting the relative order of the units of the same player.
|
|
418
|
+
// Permutations are not equally likely to avoid favoring leading clans.
|
|
419
|
+
int num = 0; // Counts number of pending commands
|
|
420
|
+
for (int pl = 0; pl < npl; ++pl) num += v[pl].size();
|
|
421
|
+
|
|
422
|
+
set<int> killed;
|
|
423
|
+
vector<Command> commands_done;
|
|
424
|
+
vector<int> index(npl, 0);
|
|
425
|
+
while (num--) {
|
|
426
|
+
int q = 0; // Counts number of players with some action pending
|
|
427
|
+
for (int pl = 0; pl < npl; ++pl) q += index[pl] < (int)v[pl].size();
|
|
428
|
+
_my_assert(q > 0, "q > 0 in next.");
|
|
429
|
+
int ran = random(1,q); // Decide whether 1st, 2nd, 3rd,, player with something pending is chosen
|
|
430
|
+
int pl = -1;
|
|
431
|
+
int acum = 0;
|
|
432
|
+
while (acum < ran) {
|
|
433
|
+
++pl;
|
|
434
|
+
acum += index[pl] < (int)v[pl].size(); // If index > ..., then player has nothing pending
|
|
435
|
+
// and does not count
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const Command& m = v[pl][index[pl]++];
|
|
439
|
+
if (execute(m, food_to_regenerate, zombie_to_unit, alive_to_dead))
|
|
440
|
+
commands_done.push_back(m);
|
|
441
|
+
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
move_zombies(food_to_regenerate, zombie_to_unit, alive_to_dead, commands_done);
|
|
445
|
+
// Es mouen els zombies (de moment, que facin moviments random, ja els farem intel·ligents)
|
|
446
|
+
// Mirar com es fa a Moria (pel que sembla, s'execute un move i es posar a la llista de commands_done)
|
|
447
|
+
|
|
448
|
+
os << "commands" << endl;
|
|
449
|
+
Action::print(commands_done, os);
|
|
450
|
+
|
|
451
|
+
// Es decrementen les rondes per a convertir-se en zombie
|
|
452
|
+
// Es converteix en zombie a qui li toca
|
|
453
|
+
// S'actualitzen zombies_ i player2alive_units i player2dead_units
|
|
454
|
+
// Es regeneren les units (zombies morts que passen a ser unitats)
|
|
455
|
+
// Es regenera tot el menjar que ha desaparegut
|
|
456
|
+
// Comptem number cells de cada un
|
|
457
|
+
// Actualitzem score, que serà el scr_accumulated + les cel·les de cadascu
|
|
458
|
+
|
|
459
|
+
decrement_rounds_for_becoming_zombie();
|
|
460
|
+
|
|
461
|
+
execute_conversion_to_zombie();
|
|
462
|
+
|
|
463
|
+
execute_conversion_zombie_to_alive(zombie_to_unit);
|
|
464
|
+
|
|
465
|
+
execute_conversion_alive_to_dead(alive_to_dead);
|
|
466
|
+
|
|
467
|
+
execute_random_conversion_live_unit( );
|
|
468
|
+
|
|
469
|
+
regenerate_food_and_update_strength(food_to_regenerate); // also substracts the food consumed per round
|
|
470
|
+
|
|
471
|
+
update_nb_cells();
|
|
472
|
+
|
|
473
|
+
update_score();
|
|
474
|
+
|
|
475
|
+
++rnd;
|
|
476
|
+
|
|
477
|
+
_my_assert(ok(), "Invariants are not satisfied.");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
void Board::execute_conversion_to_zombie ( ) {
|
|
481
|
+
for (auto& p : units) {
|
|
482
|
+
Unit& u = p.second;
|
|
483
|
+
if (u.rounds_for_zombie == 0) {
|
|
484
|
+
_my_assert(u.type != Zombie, "Cannot convert zombie to zombie");
|
|
485
|
+
if (u.type == Alive) { // Alive to zombie
|
|
486
|
+
_my_assert(player2alive_units[u.player].count(u.id), "Alive unit not found in player2alive_units");
|
|
487
|
+
player2alive_units[u.player].erase(u.id);
|
|
488
|
+
zombies_.insert(u.id);
|
|
489
|
+
u.type = Zombie;
|
|
490
|
+
u.player = -1;
|
|
491
|
+
u.rounds_for_zombie = -1;
|
|
492
|
+
grid[u.pos.i][u.pos.j].owner = -1;
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
_my_assert(u.type == Dead, "If not zombie or alive, should be dead");
|
|
496
|
+
_my_assert(player2dead_units[u.player].count(u.id), "Dead unit not found in player2dead_units");
|
|
497
|
+
player2dead_units[u.player].erase(u.id);
|
|
498
|
+
zombies_.insert(u.id);
|
|
499
|
+
u.type = Zombie;
|
|
500
|
+
u.player = -1;
|
|
501
|
+
u.rounds_for_zombie = -1;
|
|
502
|
+
grid[u.pos.i][u.pos.j].owner = -1;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
void Board::execute_conversion_zombie_to_alive (vector<vector<int>>& zombie_to_unit) {
|
|
509
|
+
for (int p = 0; p < num_players(); ++p) {
|
|
510
|
+
for (int id : zombie_to_unit[p]) {
|
|
511
|
+
_my_assert(units.count(id) != 0, "Unit which should be converted zombie->unit not found in units");
|
|
512
|
+
_my_assert(zombies_.count(id) != 0, "Unit which should be converted zombie->unit not found in zombies_");
|
|
513
|
+
_my_assert(units[id].type == Zombie, "Unit in zombie_to_unit is not a zombie");
|
|
514
|
+
Pos new_pos = get_random_pos_where_regenerate( );
|
|
515
|
+
Unit& u = units[id];
|
|
516
|
+
u.type = Alive;
|
|
517
|
+
u.player = p;
|
|
518
|
+
u.pos = new_pos;
|
|
519
|
+
u.rounds_for_zombie = -1;
|
|
520
|
+
zombies_.erase(id);
|
|
521
|
+
player2alive_units[p].insert(id);
|
|
522
|
+
|
|
523
|
+
Cell& c = grid[new_pos.i][new_pos.j];
|
|
524
|
+
c.owner = p;
|
|
525
|
+
c.id = id;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
void Board::execute_conversion_alive_to_dead (vector<vector<int>>& alive_to_dead) {
|
|
531
|
+
for (int p = 0; p < num_players(); ++p) {
|
|
532
|
+
for (int id : alive_to_dead[p]) {
|
|
533
|
+
_my_assert(units.count(id) != 0, "Unit which should be converted alive->dead not found in units");
|
|
534
|
+
_my_assert(player2alive_units[p].count(id) != 0, "Unit which should be converted alive->dead not found in player2alive_units");
|
|
535
|
+
_my_assert(units[id].type == Dead, "Unit in alive_to_dead should have already been markes as dead");
|
|
536
|
+
Unit& u = units[id];
|
|
537
|
+
u.type = Dead;
|
|
538
|
+
u.player = p;
|
|
539
|
+
u.rounds_for_zombie = rounds_before_becoming_zombie();
|
|
540
|
+
player2alive_units[p].erase(id);
|
|
541
|
+
player2dead_units[p].insert(id);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
pair<int,int> Board::selectLargestSmallestClan ( ) {
|
|
547
|
+
vector<pair<int,int>> alive_clan(num_players());
|
|
548
|
+
for (uint p = 0; p < alive_clan.size(); ++p) alive_clan[p] = {player2alive_units[p].size(),p};
|
|
549
|
+
sort(alive_clan.begin(),alive_clan.end(),greater<pair<int,int>>()); // sort descending
|
|
550
|
+
|
|
551
|
+
if (alive_clan[0].first == alive_clan.back().first) { // All clans same number of alive units
|
|
552
|
+
int clan_wins = random(0,3);
|
|
553
|
+
int clan_loses = random(0,3);
|
|
554
|
+
while (clan_loses == clan_wins) clan_loses = random(0,3);
|
|
555
|
+
return {clan_wins, clan_loses};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
vector<int> largest_clans = {alive_clan[0].second};
|
|
559
|
+
int i = 1;
|
|
560
|
+
while (i < int(alive_clan.size()) and alive_clan[i].first == alive_clan[0].first) {
|
|
561
|
+
largest_clans.push_back(alive_clan[i].second);
|
|
562
|
+
++i;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
int clan_that_loses_unit = largest_clans[random(0,largest_clans.size()-1)];
|
|
567
|
+
|
|
568
|
+
vector<int> smallest_clans = {alive_clan.back().second};
|
|
569
|
+
i = int(alive_clan.size()) - 2;
|
|
570
|
+
while (i >= 0 and alive_clan[i].first == alive_clan.back().first) {
|
|
571
|
+
smallest_clans.push_back(alive_clan[i].second);
|
|
572
|
+
--i;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
int clan_that_wins_unit = smallest_clans[random(0,smallest_clans.size()-1)];
|
|
576
|
+
return {clan_that_wins_unit, clan_that_loses_unit};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
void Board::execute_random_conversion_live_unit ( ) {
|
|
580
|
+
if (random(0,4) != 0) return; // 20% probability of conversion
|
|
581
|
+
|
|
582
|
+
pair<int,int> tmp = selectLargestSmallestClan();
|
|
583
|
+
int wins = tmp.first;
|
|
584
|
+
int loses = tmp.second;
|
|
585
|
+
|
|
586
|
+
_my_assert(wins != loses, "A clan cannot win and lose a unit");
|
|
587
|
+
|
|
588
|
+
if (player2alive_units[loses].size() == 0) return; // No alive unit (very strange)
|
|
589
|
+
|
|
590
|
+
// Choose randomly a unit from clan "loses"
|
|
591
|
+
int k = random(0,player2alive_units[loses].size()-1);
|
|
592
|
+
auto it = player2alive_units[loses].begin();
|
|
593
|
+
advance(it,k);
|
|
594
|
+
int u_t = *it; // unit transferred
|
|
595
|
+
|
|
596
|
+
player2alive_units[loses].erase(it);
|
|
597
|
+
player2alive_units[wins].insert(u_t);
|
|
598
|
+
Unit& u = units[u_t];
|
|
599
|
+
_my_assert(u.type == Alive, "Transferred unit should be alive");
|
|
600
|
+
u.player = wins;
|
|
601
|
+
grid[u.pos.i][u.pos.j].owner = wins;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
void Board::decrement_rounds_for_becoming_zombie ( ) {
|
|
605
|
+
for (auto& p : units) {
|
|
606
|
+
Unit& u = p.second;
|
|
607
|
+
if (u.rounds_for_zombie > 0)
|
|
608
|
+
--u.rounds_for_zombie;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
void Board::generate_food_item ( ){
|
|
613
|
+
Pos p = get_random_pos_where_regenerate( );
|
|
614
|
+
Cell& c = grid[p.i][p.j];
|
|
615
|
+
c.food = true;
|
|
616
|
+
_my_assert(c.id == -1 and c.type == Street, "Generated food in already full cell");
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
void Board::regenerate_food_and_update_strength (vector<int>& food_to_regenerate) {
|
|
620
|
+
for (int p = 0; p < num_players(); ++p) {
|
|
621
|
+
overall_strength[p] += food_strength()*food_to_regenerate[p];
|
|
622
|
+
overall_strength[p] -= player2alive_units[p].size(); // One unit of food is eaten by each alive unit
|
|
623
|
+
overall_strength[p] = max(0,overall_strength[p]); // Strength >= 0
|
|
624
|
+
for (int k = 0; k < food_to_regenerate[p];++k)
|
|
625
|
+
generate_food_item();
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Food items eaten by zombies
|
|
629
|
+
for (int k = 0; k < food_to_regenerate.back();++k)
|
|
630
|
+
generate_food_item();
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
void Board::update_nb_cells ( ) {
|
|
634
|
+
// Set all to zero
|
|
635
|
+
for (auto& x : nb_cells) x = 0;
|
|
636
|
+
|
|
637
|
+
// Add the ones in the grid
|
|
638
|
+
for (int i = 0; i < board_rows(); ++i)
|
|
639
|
+
for (int j = 0; j < board_cols(); ++j)
|
|
640
|
+
if (grid[i][j].owner != -1)
|
|
641
|
+
++nb_cells[grid[i][j].owner];
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
void Board::update_score ( ) {
|
|
645
|
+
for (int p = 0; p < num_players(); ++p)
|
|
646
|
+
scr[p] = scr_accumulated[p] + nb_cells[p]*1;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
bool Board::cell_has_dead_unit (int i, int j){
|
|
650
|
+
if (grid[i][j].id == -1) return false;
|
|
651
|
+
Unit& u = units[grid[i][j].id];
|
|
652
|
+
return (u.type == Dead);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
bool Board::cell_has_zombie (int i, int j){
|
|
656
|
+
if (grid[i][j].id == -1) return false;
|
|
657
|
+
Unit& u = units[grid[i][j].id];
|
|
658
|
+
return (u.type == Zombie);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
void Board::move_zombies (vector<int>& food_to_regenerate, vector<vector<int>>& zombie_to_unit, vector<vector<int>>& alive_to_dead, vector<Command>& commands_done) {
|
|
662
|
+
// First compute distances
|
|
663
|
+
int r = board_rows();
|
|
664
|
+
int c = board_cols();
|
|
665
|
+
|
|
666
|
+
int inf = 1e9;
|
|
667
|
+
vector<vector<int>> T(r,vector<int>(c,inf));
|
|
668
|
+
queue<Pos> Q;
|
|
669
|
+
for (int i = 0; i < r; ++i)
|
|
670
|
+
for (int j = 0; j < c; ++j) {
|
|
671
|
+
const Cell& c = grid[i][j];
|
|
672
|
+
if (c.id != -1 and units[c.id].type == Alive) {
|
|
673
|
+
Q.push(Pos(i,j));
|
|
674
|
+
T[i][j] = 0;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
while (not Q.empty()) {
|
|
679
|
+
Pos p = Q.front(); Q.pop();
|
|
680
|
+
for (auto d : {Down, DR, Right, RU, Up, UL, Left, LD}) {
|
|
681
|
+
Pos np = p + d;
|
|
682
|
+
if (pos_ok(np) and
|
|
683
|
+
not cell_has_dead_unit(np.i,np.j) and
|
|
684
|
+
not cell_has_zombie(np.i, np.j) and
|
|
685
|
+
grid[np.i][np.j].type != Waste and
|
|
686
|
+
T[np.i][np.j] == inf
|
|
687
|
+
) {
|
|
688
|
+
T[np.i][np.j] = T[p.i][p.j] + 1;
|
|
689
|
+
Q.push(np);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
for (int z_id : zombies_) {
|
|
695
|
+
if (units[z_id].pos != Pos(-1,-1)) { // Not dead
|
|
696
|
+
const Unit& u = units[z_id];
|
|
697
|
+
Pos p1 = u.pos;
|
|
698
|
+
vector<int> C;
|
|
699
|
+
int minim = 1e8;
|
|
700
|
+
for (int d = 0; d < 8; ++d) {
|
|
701
|
+
Pos p2 = p1 + Dir(d);
|
|
702
|
+
if (pos_ok(p2)) {
|
|
703
|
+
int dist = T[p2.i][p2.j];
|
|
704
|
+
if (dist < minim) {
|
|
705
|
+
minim = dist;
|
|
706
|
+
C = {d};
|
|
707
|
+
}
|
|
708
|
+
else if (dist == minim) C.push_back(d);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (not C.empty()){
|
|
713
|
+
Dir dir = Dir(C[random(0, C.size() - 1)]);
|
|
714
|
+
Command com(z_id,Move,dir);
|
|
715
|
+
if (execute(com, food_to_regenerate, zombie_to_unit, alive_to_dead)){
|
|
716
|
+
commands_done.push_back(com);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
vector<Dir> Board::dir_permutation ( ) {
|
|
724
|
+
vector<Dir> dirs = {Up, Down, Left, Right};
|
|
725
|
+
vector<int> p = random_permutation(4);
|
|
726
|
+
vector<Dir> new_dirs(4);
|
|
727
|
+
for (int i = 0; i < 4; ++i) new_dirs[i] = dirs[p[i]];
|
|
728
|
+
return new_dirs;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
//************************************************************
|
|
732
|
+
// BOARD GENERATION
|
|
733
|
+
//************************************************************
|
|
734
|
+
|
|
735
|
+
int Board::generate_waste (int s_id, int length) {
|
|
736
|
+
int filled = 0;
|
|
737
|
+
vector<Dir> dirs = {Up, Down, Left, Right};
|
|
738
|
+
Dir last_dir = dirs[random(0,dirs.size()-1)];
|
|
739
|
+
Pos p = get_ok_pos_for_initial_street();
|
|
740
|
+
street_plan[p.i][p.j] = s_id;
|
|
741
|
+
++filled;
|
|
742
|
+
while (length > 0) {
|
|
743
|
+
random_shuffle(dirs.begin(),dirs.end()); // Careful: this does not behave the same in MAC
|
|
744
|
+
//dirs = dir_permutation();
|
|
745
|
+
Dir new_possible_dir = Up; // Explore the possibility of turning
|
|
746
|
+
bool dir_found = false;
|
|
747
|
+
for (auto& d:dirs) {
|
|
748
|
+
if (pos_ok_for_street(s_id,p+d)){
|
|
749
|
+
new_possible_dir = d;
|
|
750
|
+
dir_found = true;
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (random(1,8) != 1 and pos_ok_for_street(s_id,p+last_dir)){ // Continue same direction
|
|
756
|
+
p += last_dir;
|
|
757
|
+
street_plan[p.i][p.j] = s_id;
|
|
758
|
+
--length;
|
|
759
|
+
++filled;
|
|
760
|
+
}
|
|
761
|
+
else if (dir_found){ // Turn
|
|
762
|
+
last_dir = new_possible_dir;
|
|
763
|
+
_my_assert(cell(p+new_possible_dir).is_empty(), "Cell no empty");
|
|
764
|
+
p += new_possible_dir;
|
|
765
|
+
_my_assert(pos_ok_for_street(s_id,p), "Pos not ok for street.");
|
|
766
|
+
street_plan[p.i][p.j] = s_id;
|
|
767
|
+
--length;
|
|
768
|
+
++filled;
|
|
769
|
+
}
|
|
770
|
+
else // Stop
|
|
771
|
+
return filled;
|
|
772
|
+
}
|
|
773
|
+
return filled;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
void Board::generate_all_waste (int num_waste_cells, int num_streets){
|
|
777
|
+
street_plan = vector<vector<int>>(board_rows(),vector<int>(board_cols(),0));
|
|
778
|
+
|
|
779
|
+
int num_streets_pending = num_streets;
|
|
780
|
+
while (num_streets_pending > 0) {
|
|
781
|
+
int length;
|
|
782
|
+
if (num_streets_pending != 1) length = num_waste_cells/num_streets_pending;
|
|
783
|
+
else length = num_waste_cells;
|
|
784
|
+
num_waste_cells -= generate_waste(num_streets_pending,length);
|
|
785
|
+
--num_streets_pending;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
for (int i = 0; i < board_rows(); ++i) {
|
|
789
|
+
for (int j = 0; j < board_cols(); ++j){
|
|
790
|
+
if (street_plan[i][j] != 0) {
|
|
791
|
+
grid[i][j].type = Waste;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
Pos Board::get_empty_pos ( ){
|
|
799
|
+
while (true) {
|
|
800
|
+
int i = random(0,board_rows()-1);
|
|
801
|
+
int j = random(0,board_cols()-1);
|
|
802
|
+
if (cell(i,j).is_empty()) return Pos(i,j);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
Pos Board::get_ok_pos_for_street(int s_id) {
|
|
807
|
+
while (true) {
|
|
808
|
+
int i = random(1,board_rows()-2);
|
|
809
|
+
int j = random(1,board_cols()-2);
|
|
810
|
+
if (pos_ok_for_street(s_id, Pos(i,j))) return Pos(i,j);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
Pos Board::get_ok_pos_for_initial_street() {
|
|
815
|
+
while (true) {
|
|
816
|
+
int i = random(1,board_rows()-2);
|
|
817
|
+
int j = random(1,board_cols()-2);
|
|
818
|
+
if (pos_ok_for_initial_street(Pos(i,j))) return Pos(i,j);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
bool Board::pos_ok_for_street(int s_id, const Pos& p){
|
|
823
|
+
int i = p.i, j = p.j;
|
|
824
|
+
if (not pos_ok(p)) return false;
|
|
825
|
+
if (street_plan[i][j] != 0) return false;
|
|
826
|
+
if (i == 0) return false;
|
|
827
|
+
if (i == board_rows() -1) return false;
|
|
828
|
+
if (j == 0) return false;
|
|
829
|
+
if (j == board_cols() -1) return false;
|
|
830
|
+
|
|
831
|
+
int num_occupied = 0;
|
|
832
|
+
vector<Dir> dirs = {Up, Down, Left, Right};
|
|
833
|
+
for (auto& d: dirs) {
|
|
834
|
+
Pos newPos = p + d; // Will exists because p is not on a border
|
|
835
|
+
int ni = newPos.i, nj = newPos.j;
|
|
836
|
+
if (street_plan[ni][nj] != 0 and street_plan[ni][nj] != s_id) return false;
|
|
837
|
+
else if (street_plan[ni][nj] == s_id) ++num_occupied;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
vector<pair<int,int>> diags = {{1,1},{1,-1},{-1,1},{-1,-1}};
|
|
841
|
+
for (auto& d : diags) {
|
|
842
|
+
Pos newPos = Pos(p.i + d.first, p.j + d.second);
|
|
843
|
+
int ni = newPos.i, nj = newPos.j;
|
|
844
|
+
if (street_plan[ni][nj] != 0 and street_plan[ni][nj] != s_id) return false;
|
|
845
|
+
else if (street_plan[ni][nj] == s_id) ++num_occupied;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
return num_occupied <= 2;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
bool Board::pos_ok_for_initial_street(const Pos& p){
|
|
852
|
+
int i = p.i, j = p.j;
|
|
853
|
+
if (not pos_ok(p)) return false;
|
|
854
|
+
if (street_plan[i][j] != 0) return false;
|
|
855
|
+
if (i == 0) return false;
|
|
856
|
+
if (i == board_rows() -1) return false;
|
|
857
|
+
if (j == 0) return false;
|
|
858
|
+
if (j == board_cols() -1) return false;
|
|
859
|
+
|
|
860
|
+
int num_occupied = 0;
|
|
861
|
+
vector<Dir> dirs = {Up, Down, Left, Right};
|
|
862
|
+
for (auto& d: dirs) {
|
|
863
|
+
Pos newPos = p + d; // Will exists because p is not on a border
|
|
864
|
+
int n_i = newPos.i, n_j = newPos.j;
|
|
865
|
+
if (street_plan[n_i][n_j] != 0) return false;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
vector<pair<int,int>> diags = {{1,1},{1,-1},{-1,1},{-1,-1}};
|
|
869
|
+
for (auto& d : diags) {
|
|
870
|
+
Pos newPos = Pos(p.i + d.first, p.j + d.second);
|
|
871
|
+
int n_i = newPos.i, n_j = newPos.j;
|
|
872
|
+
if (street_plan[n_i][n_j] != 0) return false;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
return true;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
void Board::explore_from(vector<vector<int>>& G, int i, int j, int n) {
|
|
879
|
+
G[i][j] = n;
|
|
880
|
+
for (auto& d:{Up,Down,Left,Right}) {
|
|
881
|
+
Pos np = Pos(i,j) + d;
|
|
882
|
+
if (pos_ok(np) and G[np.i][np.j] == -1) explore_from(G,np.i,np.j,n);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
int Board::num_connected_components( ){
|
|
887
|
+
vector<vector<int>> G(board_rows(),vector<int>(board_cols(),-1));
|
|
888
|
+
|
|
889
|
+
for (int i = 0; i < board_rows(); ++i)
|
|
890
|
+
for (int j = 0; j < board_cols(); ++j)
|
|
891
|
+
if (grid[i][j].type == Waste) G[i][j] = -2;
|
|
892
|
+
|
|
893
|
+
int n = 0;
|
|
894
|
+
for (int i = 0; i < board_rows(); ++i) {
|
|
895
|
+
for (int j = 0; j < board_cols(); ++j){
|
|
896
|
+
if (G[i][j] == -1) {
|
|
897
|
+
explore_from(G,i,j,n);
|
|
898
|
+
++n;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
return n;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
void Board::create_new_unit (Pos& p, int player) {
|
|
907
|
+
int id = fresh_id;
|
|
908
|
+
++fresh_id;
|
|
909
|
+
_my_assert(not units.count(id), "Identifier is not fresh.");
|
|
910
|
+
|
|
911
|
+
units[id] = Unit(Alive, id, player, p, -1);
|
|
912
|
+
player2alive_units[player].insert(id);
|
|
913
|
+
|
|
914
|
+
_my_assert(grid[p.i][p.j].is_empty(), "Cell is already full.");
|
|
915
|
+
|
|
916
|
+
grid[p.i][p.j].id = id;
|
|
917
|
+
grid[p.i][p.j].owner = player;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
void Board::create_new_zombie (Pos& p) {
|
|
921
|
+
int id = fresh_id;
|
|
922
|
+
++fresh_id;
|
|
923
|
+
_my_assert(not units.count(id), "Identifier is not fresh.");
|
|
924
|
+
_my_assert(grid[p.i][p.j].is_empty(), "Cell is already full.");
|
|
925
|
+
|
|
926
|
+
units[id] = Unit(Zombie, id, -1, p, -1);
|
|
927
|
+
zombies_.insert(id);
|
|
928
|
+
|
|
929
|
+
grid[p.i][p.j].id = id;
|
|
930
|
+
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
void Board::generate_random_board ( ){
|
|
934
|
+
int rows = board_rows();
|
|
935
|
+
int cols = board_cols();
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
// Generate buildings (leaving space for citizens)
|
|
939
|
+
static const int num_waste_cells = 0.5*rows*cols; // goal of 50% waste
|
|
940
|
+
static const int num_streets = 8;
|
|
941
|
+
|
|
942
|
+
do {
|
|
943
|
+
// Create grid
|
|
944
|
+
grid = vector<vector<Cell>>(rows, vector<Cell>(cols));
|
|
945
|
+
generate_all_waste(num_waste_cells, num_streets);
|
|
946
|
+
} while (num_connected_components() != 1);
|
|
947
|
+
|
|
948
|
+
for (int i = 0; i < board_rows(); ++i)
|
|
949
|
+
for (int j = 0; j < board_cols(); ++j)
|
|
950
|
+
if (street_plan[i][j] != 0)
|
|
951
|
+
grid[i][j].type = Waste;
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
// Generate units
|
|
955
|
+
for (int pl = 0; pl < num_players(); ++pl) {
|
|
956
|
+
for (int i = 0; i < num_ini_units_per_clan(); ++i) {
|
|
957
|
+
Pos p = get_random_pos_where_regenerate( );
|
|
958
|
+
create_new_unit(p,pl);
|
|
959
|
+
++nb_cells[pl];
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Generate zombies
|
|
964
|
+
for (int i = 0; i < num_ini_zombies(); ++i) {
|
|
965
|
+
Pos p = get_random_pos_where_regenerate( );
|
|
966
|
+
create_new_zombie(p);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// Generate food
|
|
970
|
+
for (int i = 0; i < num_ini_food(); ++i) {
|
|
971
|
+
Pos p = get_random_pos_where_regenerate( );
|
|
972
|
+
grid[p.i][p.j].food = true;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|