Fix poison duration
[homm5.git] / h5.cpp
1 #include <vector>
2 #include <algorithm>
3 #include <string_view>
4 #include <string>
5
6 enum class ability_category {
7         passive,
8         pre_attack,
9         pre_damage,
10         post_damage,
11         post_enemy_retaliation,
12         defensive,
13         pre_turn,
14         post_turn,
15         activated
16 };
17
18 struct creature_stack;
19 struct creature_ability {
20         ability_category cat;
21         virtual void add(creature_stack& stack) {}
22 };
23
24
25 struct creature {
26         std::string_view name;
27
28         int attack;
29         int defence;
30
31         unsigned max_hp;
32
33         struct {
34                 unsigned min;
35                 unsigned max;
36         } dmg;
37
38         unsigned speed;
39         unsigned initiative;
40
41         std::vector< creature_ability* > specials;
42 };
43
44 struct effect;
45 struct effect_base {
46         //virtual void add(creature_stack& stack) = 0;
47         virtual void remove(creature_stack& stack) = 0;
48         virtual void begin_turn(effect& eff, creature_stack& target) {}
49 };
50
51 // TODO: map[ effect_over_kind ] -> vector[ effect ]
52 struct effect {
53         int counter = 0;
54         int duration;
55         bool removed_on_damage = false;
56         effect_base* base;
57         double value = 0.0;
58 };
59
60 struct damage_context {
61         creature_stack& from;
62         creature_stack& to;
63         int attack;
64         int defence;
65 };
66
67 #include <cmath>
68 struct vec2d {
69         int x, y;
70         double length() const
71         {
72                 return std::sqrt(x*x+y*y);
73         }
74 };
75 struct point2d {
76         int x, y;
77 };
78 vec2d operator-(point2d a, point2d b)
79 {
80         return {a.x-b.x,a.y-b.y};
81 }
82 bool operator==(point2d a, point2d b)
83 {
84         return a.x == b.x && a.y == b.y;
85 }
86 bool operator!=(point2d a, point2d b)
87 {
88         return a.x != b.x || a.y != b.y;
89 }
90
91 bool operator<(point2d a, point2d b)
92 {
93         return a.x < b.x || (a.x == b.x && a.y < b.y);
94 }
95
96 struct creature_stack {
97         creature_stack(::creature const& creature, unsigned count, int team, point2d pos)
98                 : creature{creature}, count{count}, team{team}, pos{pos}
99         {
100                 hp     = int(creature.max_hp);
101                 max_hp = int(creature.max_hp);
102
103                 for (auto* ability : creature.specials)
104                         ability->add( *this );
105         }
106
107         creature_stack(::creature const& creature, unsigned count, point2d pos)
108                 : creature_stack{ creature, count, -1, pos }
109         {
110         }
111
112         std::string describe() const
113         {
114                 auto name = std::string(creature.name);
115                 name += '[';
116                 name += std::to_string(count);
117                 name += ']';
118                 return name;
119         }
120
121         void step_turn()
122         {
123                 if (dead()) return;
124                 for (auto& effect : effects) {
125                         effect.counter += 10;
126                         if (effect.counter > effect.duration)
127                                 effect.base->remove(*this);
128                 }
129                 auto it = std::remove_if(begin(effects),end(effects),[](auto e) { return e.counter > e.duration; } );
130                 effects.erase( it, end(effects) );
131                 if (disabled)
132                         return;
133                 initiative += creature.initiative;
134         }
135
136         void begin_turn()
137         {
138                 retaliations = max_retaliations;
139                 for ( auto func : pre_turn )
140                         func( *this );
141                 for ( auto e : effects )
142                         e.base->begin_turn( e, *this );
143         }
144
145         bool dead() const
146         {
147                 return count == 0;
148         }
149
150         unsigned apply_damage( int dmg )
151         {
152                 auto orig = count;
153                 unsigned kills = dmg / max_hp;
154                 dmg = dmg % max_hp;
155
156                 kills = std::min( kills, count );
157                 count -= kills;
158
159                 if (count > 0) {
160                         if (dmg >= hp) {
161                                 if (count > 1)
162                                         hp += max_hp;
163                                 count -= 1;
164                         }
165                         hp -= dmg;
166                 }
167
168                 if (count == 0)
169                         die();
170                 return orig - count;
171         }
172
173         void die()
174         {
175                 hp = 0;
176                 initiative = 0;
177         }
178
179         ::creature creature;
180         unsigned count;
181
182         int hp;
183         int max_hp;
184
185         point2d pos;
186
187         int team = -1;
188
189         unsigned initiative = 0;
190
191         unsigned max_retaliations = 1;
192         unsigned retaliations = max_retaliations;
193
194         bool disabled = false;
195         bool blocks_retaliation = false;
196
197         std::vector<effect> effects;
198         std::vector<void(*)(damage_context&)> pre_damage;
199         std::vector<void(*)(creature_stack&)> pre_turn;
200         std::vector<void(*)(creature_stack&, creature_stack&)> post_enemy_retaliation;
201         std::vector<void(*)(creature_stack&, creature_stack&)> post_damage;
202 };
203
204 void regenerate( creature_stack& stack );
205 struct ability_regenerate : creature_ability {
206         ability_regenerate()
207         {
208                 cat = ability_category::pre_turn;
209         }
210         void add(creature_stack& stack) override
211         {
212                 stack.pre_turn.push_back( regenerate );
213         }
214 } regeneration;
215
216 struct ability_no_retaliation : creature_ability {
217         ability_no_retaliation()
218         {
219                 cat = ability_category::passive;
220         }
221         void add(creature_stack& stack) override
222         {
223                 stack.blocks_retaliation = true;
224         }
225 } no_retaliation;
226
227 #include<iostream>
228 #include <random>
229 #include <aw/utility/random/mersenne_twister.h>
230 auto rng = aw::create_mersenne_twister_engine();
231
232 struct effect_poison : effect_base {
233         effect_poison()
234         {
235         }
236         void add(creature_stack& from,creature_stack& stack)
237         {
238                 for (auto e : stack.effects)
239                         if (e.base == this)
240                                 return;
241                 stack.effects.push_back( effect{0,(int)stack.creature.initiative*21,false,this,double(from.count)} );
242                 std::cout << from.describe() << " poisoned " << stack.describe() << '\n';
243         }
244         void remove(creature_stack& stack) override
245         {
246                 std::cout << "Poison wears off from " << stack.describe() << '\n';
247         }
248         void begin_turn(effect& eff, creature_stack& target) override
249         {
250                 int dmg = eff.value;
251                 std::cout << target.describe() << " suffers " << dmg << " damage from poison";
252                 auto kills = target.apply_damage(dmg);
253                 if (kills > 1)
254                         std::cout << " (" << kills << ' ' << target.creature.name << 's' << " perish)\n";
255                 else if (kills > 0)
256                         std::cout << " (" << kills << ' ' << target.creature.name << " perishes)\n";
257                 else
258                         std::cout << '\n';
259         }
260 } poison_effect;
261
262 struct effect_blind : effect_base {
263         effect_blind()
264         {
265         }
266         void add(creature_stack& stack)
267         {
268                 /*for (auto e : stack.effects)
269                         if (e.base == this)
270                                 return;*/
271                 stack.effects.push_back( effect{0,200,true,this} );
272                 stack.disabled = true;
273         }
274         void remove(creature_stack& stack) override
275         {
276                 stack.disabled = false;
277                 std::cout << "Blind wears off from " << stack.describe() << '\n';
278         }
279 } blind_effect;
280
281 void roll_blind( creature_stack& from, creature_stack& to)
282 {
283         if (to.dead())
284                 return;
285         double fhp = (from.count-1)*from.max_hp + from.hp;
286         double thp = (to.count-1)*to.max_hp + to.hp;
287         double chance = 0.25 + ((fhp > thp) ? (0.03*fhp/thp) : (-0.03*thp/fhp));
288         chance = std::max(0.05,chance);
289         chance = std::min(0.75,chance);
290         std::uniform_real_distribution<double> dist(0.0, 1.0);
291         auto roll = dist(rng);
292         //std::cout << "Blor " << int(roll*100) << '/' << int(chance*100) << '\n';
293         if (roll < chance) {
294                 std::cout << from.describe() << " blind " << to.describe() << '\n';
295                 blind_effect.add( to );
296         }
297 }
298
299 void add_poison( creature_stack& from, creature_stack& to )
300 {
301         poison_effect.add( from, to );
302 }
303
304 struct ability_blind : creature_ability {
305         ability_blind()
306         {
307                 cat = ability_category::post_damage;
308         }
309         void add(creature_stack& stack) override
310         {
311                 stack.post_damage.push_back( roll_blind );
312         }
313 } blind;
314
315 struct ability_poison : creature_ability {
316         ability_poison()
317         {
318                 cat = ability_category::post_damage;
319         }
320         void add(creature_stack& stack) override
321         {
322                 stack.post_damage.push_back( add_poison );
323         }
324 } poisoner;
325
326
327
328 unsigned calc_damage( damage_context& ctx )
329 {
330         auto atk = ctx.attack;
331         auto def = ctx.defence;
332         double coef = 1;
333         if (atk > def)
334                 coef = 1 + 0.05*(atk-def);
335         if (atk < def)
336                 coef = 1/(1 + 0.05*(def-atk));
337
338         coef *= ctx.from.count;
339         auto min = ctx.from.creature.dmg.min;
340         auto max = ctx.from.creature.dmg.max;
341         std::uniform_int_distribution<unsigned> dist( min*coef, max*coef );
342         return dist(rng);
343 }
344
345 void ignore_defence_80p( damage_context& ctx )
346 {
347         ctx.defence *= 0.2;
348 }
349
350 struct ability_ignore80p : creature_ability {
351         ability_ignore80p()
352         {
353                 cat = ability_category::pre_damage;
354         }
355         void add(creature_stack& stack) override
356         {
357                 stack.pre_damage.push_back( ignore_defence_80p );
358         }
359 } ignoredef80p;
360
361 void apply_damage( creature_stack& attacker, creature_stack& target )
362 {
363         if (attacker.dead()||target.dead())
364                 return;
365
366         damage_context ctx {
367                 attacker,
368                 target,
369                 attacker.creature.attack,
370                 target.creature.attack
371         };
372
373         for (auto func : attacker.pre_damage)
374                 func( ctx );
375
376         auto dmg = calc_damage( ctx );
377         std::cout << attacker.creature.name << " deals " << dmg << " damage to " << target.describe();
378
379         auto kills = target.apply_damage( dmg );
380
381         if (kills > 1)
382                 std::cout << " (" << kills << ' ' << target.creature.name << 's' << " perish)\n";
383         else if (kills > 0)
384                 std::cout << " (" << kills << ' ' << target.creature.name << " perishes)\n";
385         else
386                 std::cout << '\n';
387         if (target.count > 0) {
388                 auto iend = end(target.effects);
389                 auto it = std::stable_partition(begin(target.effects),iend,[](auto e) { return !e.removed_on_damage; } );
390                 for (auto t = it; t != iend; ++t)
391                         t->base->remove(target);
392                 target.effects.erase( it, end(target.effects) );
393
394                 for (auto func : attacker.post_damage)
395                         func(attacker, target);
396         }
397 }
398
399 struct ability_double_strike : creature_ability {
400         ability_double_strike()
401         {
402                 cat = ability_category::post_enemy_retaliation;
403         }
404         void add(creature_stack& stack) override
405         {
406                 stack.post_enemy_retaliation.push_back( apply_damage );
407         }
408 } double_attack;
409
410 bool can_retaliate(creature_stack& stack)
411 {
412         if (stack.dead())
413                 return false;
414         if (stack.retaliations == 0)
415                 return false;
416         if (stack.disabled)
417                 return false;
418         return true;
419 }
420 void retaliate(creature_stack& stack, creature_stack& attacker)
421 {
422         if (!can_retaliate(stack))
423                 return;
424         --stack.retaliations;
425         std::cout << stack.describe() << " retaliates\n";
426         apply_damage( stack, attacker );
427 }
428
429 void attack(creature_stack& attacker, creature_stack& target)
430 {
431         std::cout << attacker.describe() << " attacks " << target.describe() << '\n';
432         apply_damage( attacker, target );
433         if (!attacker.blocks_retaliation)
434                 retaliate(target, attacker);
435         if (!attacker.disabled)
436         for (auto func : attacker.post_enemy_retaliation)
437                 func( attacker, target );
438 }
439
440 void regenerate(creature_stack& stack)
441 {
442         if (stack.max_hp == stack.hp) return;
443         auto amount = std::min(stack.max_hp - stack.hp, 25);
444         std::cout << stack.describe() << " regenerates " << amount << " hit points\n";
445         stack.hp += amount;
446 }
447
448 #include <chrono>
449 #include <thread>
450
451 //#include <aw/types/containers/queue.h>
452 #include <deque>
453 #include <set>
454 #include <cmath>
455 // hilriously inefficient way to find all nearby targets
456 std::vector<creature_stack*> find_nearby_targets( creature_stack const& s, std::vector<creature_stack>& ss )
457 {
458         std::vector<creature_stack*> result;
459
460         constexpr char free = 0;
461         constexpr char occupied = -1;
462         constexpr char reachable_occupied = -2;
463         constexpr char reachable = 1;
464         char grid[11][11] = {};
465
466         for (auto& s : ss)
467                 if (!s.dead())
468                 grid[s.pos.x][s.pos.y] = -1;
469
470         struct node {
471                 double distance;
472                 point2d pos;
473         };
474
475         std::deque<node> queue;
476
477         grid[s.pos.x][s.pos.y] = 0;
478         queue.push_front( {0.0, s.pos} );
479
480         while (!queue.empty()) {
481                 auto n = queue.front();
482                 queue.pop_front();
483
484                 auto d = n.distance;
485                 auto pos = n.pos;
486
487                 if (pos.x < 0 || pos.x > 10)
488                         continue;
489                 if (pos.y < 0 || pos.y > 10)
490                         continue;
491                 if (int(d) > (s.creature.speed+1))
492                         continue;
493                 if (grid[pos.x][pos.y] == occupied) {
494                         grid[pos.x][pos.y] = reachable_occupied;
495                         continue;
496                 }
497                 if (grid[pos.x][pos.y] != free)
498                         continue;
499
500                 grid[pos.x][pos.y] = reachable;
501
502                 queue.push_back( {d+1,pos.x+1,pos.y} );
503                 queue.push_back( {d+1,pos.x,pos.y+1} );
504                 queue.push_back( {d+1,pos.x-1,pos.y} );
505                 queue.push_back( {d+1,pos.x,pos.y-1} );
506
507                 queue.push_back( {d+std::sqrt(2),pos.x+1,pos.y} );
508                 queue.push_back( {d+std::sqrt(2),pos.x,pos.y+1} );
509                 queue.push_back( {d+std::sqrt(2),pos.x-1,pos.y} );
510                 queue.push_back( {d+std::sqrt(2),pos.x,pos.y-1} );
511         }
512
513         auto team = s.team;
514         for (auto& s : ss)
515                 if (grid[s.pos.x][s.pos.y] == reachable_occupied)
516                         if (!s.dead() && s.team != team)
517                                 result.push_back(&s);
518         return result;
519 }
520
521 point2d bfs( creature_stack& s, point2d target, std::vector<creature_stack> const& ss )
522 {
523         double grid[11][11];
524
525         for (int i = 0; i < 11; ++i)
526                 for (int j = 0; j < 11; ++j)
527                         grid[i][j] = -1;
528
529         for (auto& s : ss)
530                 if (!s.dead())
531                 grid[s.pos.x][s.pos.y] = -2;
532
533         struct node {
534                 double distance;
535                 point2d pos;
536                 point2d prev;
537         };
538
539         std::deque<node> queue;
540         auto origin = s.pos;
541         grid[origin.x][origin.y] = -1;
542
543         queue.push_front( {0.0, origin, origin} );
544
545         while (!queue.empty()) {
546                 auto n = queue.front();
547                 queue.pop_front();
548
549                 auto pos = n.pos;
550                 if (pos == target)
551                         return n.prev;
552
553                 if (pos.x < 0 || pos.x > 10) continue;
554                 if (pos.y < 0 || pos.y > 10) continue;
555
556                 auto d = n.distance;
557                 if (int(d) > s.creature.speed)
558                         continue;
559                 if (grid[pos.x][pos.y] == -2)
560                         continue;
561                 if (grid[pos.x][pos.y] > d)
562                         grid[pos.x][pos.y] = d;
563                 if (grid[pos.x][pos.y] != -1)
564                         continue;
565                 grid[pos.x][pos.y] = d;
566
567                 queue.push_back( {d+1,{pos.x+1,pos.y},pos} );
568                 queue.push_back( {d+1,{pos.x,pos.y+1},pos} );
569                 queue.push_back( {d+1,{pos.x-1,pos.y},pos} );
570                 queue.push_back( {d+1,{pos.x,pos.y-1},pos} );
571
572                 queue.push_back( {d+std::sqrt(2),{pos.x+1,pos.y},pos} );
573                 queue.push_back( {d+std::sqrt(2),{pos.x,pos.y+1},pos} );
574                 queue.push_back( {d+std::sqrt(2),{pos.x-1,pos.y},pos} );
575                 queue.push_back( {d+std::sqrt(2),{pos.x,pos.y-1},pos} );
576         }
577
578         return origin;
579 }
580
581 point2d bfs2( creature_stack& s, point2d target, std::vector<creature_stack> const& ss )
582 {
583         double grid[11][11];
584
585         for (int i = 0; i < 11; ++i)
586                 for (int j = 0; j < 11; ++j)
587                         grid[i][j] = -1;
588
589         for (auto& s : ss)
590                 if (!s.dead())
591                 grid[s.pos.x][s.pos.y] = -2;
592
593         struct node {
594                 double distance;
595                 point2d pos;
596                 point2d prev;
597         };
598
599         auto origin = s.pos;
600         std::deque<node> queue;
601         grid[origin.x][origin.y] = -1;
602
603         queue.push_front( {0.0, origin, origin} );
604
605         double dist = (target-origin).length();
606         point2d closest = origin;
607
608         while (!queue.empty()) {
609                 auto n = queue.front();
610                 queue.pop_front();
611
612                 auto pos = n.pos;
613                 if (pos.x < 0 || pos.x > 10) continue;
614                 if (pos.y < 0 || pos.y > 10) continue;
615
616                 auto d = n.distance;
617                 if (int(d) > s.creature.speed)
618                         continue;
619                 if (grid[pos.x][pos.y] == -2)
620                         continue;
621
622                 auto quy = (target-pos).length();
623                 if (quy < dist) {
624                         quy = dist;
625                         closest = pos;
626                 }
627
628                 if (grid[pos.x][pos.y] != -1)
629                         continue;
630                 grid[pos.x][pos.y] = d;
631
632                 queue.push_back( {d+1,{pos.x+1,pos.y},pos} );
633                 queue.push_back( {d+1,{pos.x,pos.y+1},pos} );
634                 queue.push_back( {d+1,{pos.x-1,pos.y},pos} );
635                 queue.push_back( {d+1,{pos.x,pos.y-1},pos} );
636
637                 queue.push_back( {d+std::sqrt(2),{pos.x+1,pos.y},pos} );
638                 queue.push_back( {d+std::sqrt(2),{pos.x,pos.y+1},pos} );
639                 queue.push_back( {d+std::sqrt(2),{pos.x-1,pos.y},pos} );
640                 queue.push_back( {d+std::sqrt(2),{pos.x,pos.y-1},pos} );
641         }
642
643         return closest;
644 }
645
646 #include <fstream>
647 int main()
648 {
649         creature BLACK_DRAGON = {
650                 "black_dragon", 30, 30, 240, {45, 70}, 10, 10,
651                 { }
652         };
653
654         creature EMERALD_DRAGON = {
655                 "emerald_dragon", 31, 27, 200, {33, 57}, 9, 14,
656                 { }
657         };
658
659         creature HYDRA = {
660                 "hydra", 15, 15, 125, {9, 14}, 5, 7,
661                 {
662                         &no_retaliation,
663                         &regeneration
664                         //&all_targets
665                 }
666         };
667
668         creature MINOTAUR = {
669                 "minotaur", 5, 2, 35, {4, 7}, 5, 8,
670                 {
671                         &double_attack,
672                 }
673         };
674
675         creature ASSASIN = {
676                 "assasin", 4, 3, 14, {2, 4}, 5, 12,
677                 {
678                         &poisoner,
679                 }
680         };
681
682         creature UNICORN = {
683                 "unicorn", 17, 17, 77, {10, 20}, 7, 12,
684                 {
685                         &blind
686                 }
687         };
688
689         creature TROGLODYTE = {
690                 "troglodyte", 5, 4, 6, {1, 3}, 5, 9,
691                 {
692                         //&blind_immunity
693                 }
694         };
695
696         creature CENTAUR = {
697                 "centaur", 6, 3, 10, {2, 3}, 6, 11,
698                 {
699                 }
700         };
701
702         creature PEGASUS = {
703                 "pegasus", 9, 10, 30, {5, 9}, 9, 12,
704                 {
705                 }
706         };
707
708         creature BEHEMOTH = {
709                 "behemoth", 19, 19, 300, {30, 50}, 5, 9,
710                 {
711                         &ignoredef80p
712                 }
713         };
714
715
716
717         creature AZURE_DRAGON = {
718                 "azure_dragon", 50, 50, 1000, {70, 80}, 10, 15,
719                 {
720                 }
721         };
722
723         creature BLACK_DRAGON_HOMM4 = {
724                 "B4lack_dragon", 40, 40, 240, {55, 110}, 17, 14,
725                 { }
726         };
727
728         creature TROGLODYTE_HOMM4 = {
729                 "T4roglodyte", 11, 9, 14, {2, 3}, 6, 10,
730                 { }
731         };
732
733         int turn = 0;
734         int turnini = 0;
735
736         std::vector<creature_stack> stacks {
737                 {BLACK_DRAGON, 1, {0,0}},
738                 {ASSASIN, 45, {0,1}},
739                 {HYDRA, 5, {0,2}},
740                 {MINOTAUR, 24,{0,3}},
741                 {EMERALD_DRAGON, 2,{10,0}},
742                 {UNICORN, 7,{10,1}},
743                 {CENTAUR, 40,{10,2}},
744                 {PEGASUS, 14,{10,3}},
745                 {BEHEMOTH, 2,{0,10}},
746                 {AZURE_DRAGON, 1,{2,10}},
747                 {BLACK_DRAGON_HOMM4, 1,{10,10}},
748                 {TROGLODYTE, 60,{9,10}},
749                 {TROGLODYTE_HOMM4, 40,{10,9}},
750         };
751
752         stacks[0].team = 1;
753         stacks[1].team = 1;
754         stacks[2].team = 1;
755         stacks[3].team = 1;
756         stacks[4].team = 2;
757         stacks[5].team = 2;
758         stacks[6].team = 2;
759         stacks[7].team = 2;
760         stacks[8].team = 3;
761         stacks[9].team = 4;
762         stacks[10].team = 5;
763         stacks[11].team = 5;
764         stacks[12].team = 5;
765
766         auto teams = [&] {
767                 auto not_dead = [] (auto s) { return !s.dead(); };
768                 auto it1 = std::find_if( begin(stacks), end(stacks), not_dead );
769                 if (it1 == end(stacks))
770                         return false;
771                 auto team = [team=it1->team] (auto s) { return !s.dead() && s.team!=team; };
772                 auto it = std::find_if( begin(stacks),end(stacks), team);
773                 return it != end(stacks);
774         };
775
776         {
777                 std::uniform_int_distribution<int> dist(0,25);
778                 for (auto& s : stacks)
779                         s.initiative += dist(rng);
780         }
781
782         auto print_field = [&] {
783                 //std::ofstream ofs{"grid", std::ios::trunc|std::ios::out};
784                 char grid[11][11];
785                 int  team[11][11] = {};
786
787                 for (int i = 0; i < 11; ++i)
788                         for (int j = 0; j < 11; ++j)
789                                 grid[i][j] = '.';
790                 for (auto& s:stacks)
791                         if (!s.dead()) {
792                                 grid[s.pos.x][s.pos.y] = s.creature.name[0];
793                                 team[s.pos.x][s.pos.y] = s.team;
794                         }
795                 system("echo ---------------------------");
796                 system("clear&&clear");
797                 for (int i = 0; i < 11; ++i) {
798                         for (int j = 0; j < 11; ++j) {
799                                 std::cerr << ' ';
800                                 if (team[i][j] == 1) std::cerr << "\033[1;35m";
801                                 if (team[i][j] == 2) std::cerr << "\033[1;32m";
802                                 if (team[i][j] == 3) std::cerr << "\033[1;33m";
803                                 if (team[i][j] == 4) std::cerr << "\033[1;34m";
804                                 if (team[i][j] == 5) std::cerr << "\033[1;31m";
805                                 //if (team[i][j] == 1) ofs << "#5-";
806                                 //if (team[i][j] == 2) ofs << "#2-";
807                                 //if (team[i][j] == 3) ofs << "#3-";
808                                 //if (team[i][j] == 4) ofs << "#4-";
809                                 //if (team[i][j] == 5) ofs << "#1-";
810                                 std::cerr << grid[i][j];
811                                 if (team[i][j] != 0) std::cerr << "\033[0m";
812                                 //if (team[i][j] != 0) ofs << "#";
813                         }
814                         std::cerr << '\n';
815                 }
816         };
817
818         while ( teams() ) {
819                 for (auto& stack : stacks)
820                         stack.step_turn();
821                 turnini += 10;
822                 if (turnini >= 100) {
823                         ++turn;
824                         std::cout << "-----------------------\n";
825                         std::cout << "turn: " << turn << '\n';
826                         turnini -= 100;
827                 }
828
829                 std::sort( begin(stacks), end(stacks), [] (auto a, auto b) {
830                         return a.initiative > b.initiative;
831                 });
832
833                 for (auto& stack : stacks) {
834                         if (stack.dead())
835                                 continue;
836                         if (stack.initiative < 100)
837                                 break;
838                         stack.initiative -= 100;
839                         stack.begin_turn();
840                         if (stack.dead())
841                                 continue;
842
843                         std::vector<creature_stack*> can_reach = find_nearby_targets( stack, stacks );
844
845                         auto no_retali = [team=stack.team] (auto ss) {
846                                 auto s = *ss;
847                                 return team!=s.team&&!s.disabled && !s.dead() && s.retaliations == 0;
848                         };
849                         auto no_dis = [team=stack.team] (auto ss) {
850                                 auto s = *ss;
851                                 return team!=s.team&&!s.disabled && !s.dead();
852                         };
853                         auto enemy_team = [team=stack.team] (auto ss) {
854                                 auto s = *ss;
855                                 return team!=s.team&&!s.dead();
856                         };
857                         auto enemymy_team = [team=stack.team] (auto ss) {
858                                 auto s = ss;
859                                 return team!=s.team&&!s.dead();
860                         };
861                         auto it = std::find_if(begin(can_reach), end(can_reach), no_retali);
862                         if (it == end(can_reach))
863                         it = std::find_if(begin(can_reach), end(can_reach), no_dis);
864                         if (it == end(can_reach))
865                         it = std::find_if(begin(can_reach), end(can_reach), enemy_team);
866                         if (it == end(can_reach)) {
867                                 auto it = std::find_if(begin(stacks), end(stacks), enemymy_team);
868                                 if (it ==end(stacks)) {
869                                         stack.initiative += 50;
870                                         std::cout << stack.describe() << " waits\n";
871                                         continue;
872                                 }
873                                 auto pos = bfs2(stack,it->pos,stacks);
874                                 if (pos != stack.pos)
875                                 {
876                                         std::cout << stack.describe() << " moves to " << pos.x << ',' << pos.y << '\n';
877                                         stack.pos = pos;
878                                 } else {
879                                         stack.initiative += 50;
880                                         std::cout << stack.describe() << " waits\n";
881                                 }
882                                 print_field();
883                                 continue;
884                         }
885
886                         auto& target = **it;
887
888                         auto pos = bfs( stack, target.pos,stacks );
889                         if (pos != stack.pos)
890                         {
891                                 std::cout << stack.describe() << " moves to " << pos.x << ',' << pos.y << '\n';
892                                 stack.pos = pos;
893                         }
894
895                         attack( stack, target );
896                         print_field();
897                         /*if (stack.creature.name == "hydra") {
898                                 for (auto& e : stacks)
899                                         if (e.team != stack.team && &e != &target)
900                                                 apply_damage( stack, e );
901
902                         }*/
903
904                         using namespace std::chrono_literals;
905                         std::this_thread::sleep_for(3.50s);
906                 }
907                         //std::cout << "=\n";
908                         //for (auto& stack : stacks)
909                         //      std::cout << stack.describe() << '[' << stack.hp << '/' << stack.max_hp << ']' << '\n';
910                         //std::cout << "=\n";
911         }
912
913         std::partition( begin(stacks), end(stacks), [] (auto s) {
914                 return !s.dead();
915         });
916
917         auto team = stacks[0].team;
918         std::cout << "-----------------------\n";
919         std::cout << "winner: team " << team << ' ';
920         switch(team) {
921         case 1: std::cout << "H5 dungeon"; break;
922         case 2: std::cout << "H3 rampart + H5 sylvan"; break;
923         case 3: std::cout << "H3 stronghold"; break;
924         case 4: std::cout << "H3 azure dragon"; break;
925         case 5: std::cout << "H4 chaos + H3 dungeon"; break;
926         };
927         std::cout << '\n';
928         for (auto& s : stacks) {
929                 if (s.dead()) break;
930                 std::cout  << s.describe() << " with "<< s.hp << " hit points \n";
931         }
932
933 }