@jutge.org/toolkit 4.2.23 → 4.2.25

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