@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.
Files changed (111) hide show
  1. package/assets/problems/games/the-walking-dead.pbm/README.md +12 -0
  2. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/Makefile +6 -0
  3. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/api.tex +246 -0
  4. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/defs.tex +2 -0
  5. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/main.tex +63 -0
  6. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/programming.tex +452 -0
  7. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/rules.tex +222 -0
  8. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/screenshot.png +0 -0
  9. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/tips.tex +81 -0
  10. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/twd.png +0 -0
  11. package/assets/problems/games/the-walking-dead.pbm/ca/Doc/viewer.tex +41 -0
  12. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/Makefile +6 -0
  13. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/api.tex +246 -0
  14. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/defs.tex +2 -0
  15. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/main.tex +63 -0
  16. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/programming.tex +176 -0
  17. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/rules.tex +194 -0
  18. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/screenshot.png +0 -0
  19. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/tips.tex +85 -0
  20. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/twd.png +0 -0
  21. package/assets/problems/games/the-walking-dead.pbm/ca/Doc-eng/viewer.tex +36 -0
  22. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.Linux64 +0 -0
  23. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.Linux64.Debug +0 -0
  24. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.MacOS +0 -0
  25. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.MacOS.ARM +0 -0
  26. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.MacOS.ARM.Debug +0 -0
  27. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/AIDummy.o.MacOS.Debug +0 -0
  28. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.Linux64 +0 -0
  29. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.Linux64.Debug +0 -0
  30. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.MacOS +0 -0
  31. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.MacOS.ARM +0 -0
  32. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.MacOS.ARM.Debug +0 -0
  33. package/assets/problems/games/the-walking-dead.pbm/ca/Obj/Board.o.MacOS.Debug +0 -0
  34. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/AIDemo.cc +89 -0
  35. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/AIDummy.cc +202 -0
  36. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/AINull.cc +37 -0
  37. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Action.cc +34 -0
  38. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Action.hh +107 -0
  39. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Board.cc +975 -0
  40. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Board.hh +287 -0
  41. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Defs.hh +2 -0
  42. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Game.cc +55 -0
  43. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Game.hh +23 -0
  44. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Info.cc +174 -0
  45. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Info.hh +129 -0
  46. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Main.cc +83 -0
  47. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Makefile +56 -0
  48. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Player.cc +66 -0
  49. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Player.hh +63 -0
  50. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/README.txt +9 -0
  51. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Random.cc +3 -0
  52. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Random.hh +88 -0
  53. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Registry.cc +28 -0
  54. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Registry.hh +42 -0
  55. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/SecGame.cc +368 -0
  56. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/SecGame.hh +90 -0
  57. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/SecMain.cc +99 -0
  58. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Settings.cc +67 -0
  59. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Settings.hh +175 -0
  60. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/State.cc +3 -0
  61. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/State.hh +188 -0
  62. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Structs.cc +3 -0
  63. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Structs.hh +377 -0
  64. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Utils.cc +3 -0
  65. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/Utils.hh +77 -0
  66. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/default-fixed.cnf +120 -0
  67. package/assets/problems/games/the-walking-dead.pbm/ca/Runner/default.cnf +17 -0
  68. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  69. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  70. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  71. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  72. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  73. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  74. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  75. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  76. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  77. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  78. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  79. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
  80. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  81. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  82. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/css/ui-lightness/jquery-ui-1.8.18.custom.css +310 -0
  83. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/help.html +18 -0
  84. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_close.png +0 -0
  85. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_end.png +0 -0
  86. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_help.png +0 -0
  87. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_pause.png +0 -0
  88. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_play.png +0 -0
  89. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_refresh.png +0 -0
  90. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/but_start.png +0 -0
  91. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/img/logo.png +0 -0
  92. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/js/jquery-1.7.1.min.js +4 -0
  93. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/js/jquery-ui-1.8.18.custom.min.js +49 -0
  94. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/sample.out +52807 -0
  95. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/viewer.html +118 -0
  96. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/viewer.js +846 -0
  97. package/assets/problems/games/the-walking-dead.pbm/ca/Viewer/viewer.sh +28 -0
  98. package/assets/problems/games/the-walking-dead.pbm/ca/handler.yml +6 -0
  99. package/assets/problems/games/the-walking-dead.pbm/ca/problem.ca.yml +3 -0
  100. package/assets/prompts/creators/create-solution.tpl.txt +15 -8
  101. package/assets/prompts/proglangs/cc.md +6 -2
  102. package/assets/prompts/proglangs/py.md +10 -6
  103. package/dist/index.js +370 -366
  104. package/docs/getting-started-guide.md +1 -1
  105. package/docs/install-linux.md +1 -1
  106. package/docs/install-macos.md +1 -1
  107. package/docs/install-windows.md +1 -1
  108. package/package.json +11 -11
  109. package/toolkit/ask.ts +0 -2
  110. package/toolkit/doctor.ts +1 -1
  111. 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
+