-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMyBot.cpp
1495 lines (1342 loc) · 60 KB
/
MyBot.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "hlt/hlt.hpp"
#include "hlt/navigation.hpp"
#include <math.h>
#define NO_RADIUS 100000000
#define SET_TARGET_LAST 500.0
//#define DEFENSE_MULTIPLIER 3.0
/*
Folder Name: Last Version + Distraction Refactor
Last Versions: Last Version - Strategic Attacks Refactor (Rank 11)
Last Version + Strategic Attacks Refactor + No Collision (Rank 12)
Last Version + Ally/Enemy Bunny Ship Fixes + Collision Refactor (Rank 16)
Rank 22 Bot + Distraction Prevention (Rank 21)
*/
/*
Terms referenced:
-Bunny ship: A ship that "hops away" from enemy ships, distracting enemies by being uncatchable yet likely the closest ship they're chasing.
-Strafe: "Strafing" is used, in all seriousness, in reference to the Minecraft term. Basically, you attack while moving perpendicular to your target.
It's like "kiting" but staying just as close to your real target, the enemy docked ships.
-Noob-rush: A reference to Rise of Nations, basically when a bot rushes your ships at the beginning of the game. A low-skill-highly-obnoxious strategy in RoN.
*/
/*
Next to fix:
1. Set target last issues (see below).
2. Noob rush detection.
*/
/*
Major issues:
Note: Code needs major refactoring and the function groups need their own file.
Idea: Don't! Takes too much time!
Note: The current Outnumbered navigation scheme can leave ships behind due to being unable to find a safe location.
Idea: Sort the Outnumbered ships by distance to target after they're assigned separate from the rest of the bubble sort.
Note: This failed.
Idea: Try some crazy idea at the last minute of submission.
Note: Didn't come up with one. :(
*/
/*
Minor issues:
Idea: Functions looping through all ships in the game can be simplified by checking if it matches player_id before the second for loop.
Note: Heading to an allied planet to dock there does not take into account if the planet will be full because of production before the ship gets there, only closer ships
that already exist.
Idea: Nobody seems to have implemented this, why bother? It might be important to getting an early ship lead though, idk.
*/
//
//Commented functions have been phased out and not used in the current version, but were recent enough I might've wanted them again/didn't take the time to remove them.
//
//Special Case Functions
bool NoobRush(const hlt::Ship& ship, const hlt::Ship& enemy, const hlt::Planet& planet, const unsigned int& players);
hlt::Ship FarthestShip(const hlt::Map& map, hlt::PlayerId player_id);
bool InFormation(const std::vector<hlt::Ship>& ships);
hlt::Ship ShipClosestToPlanet(const std::vector<hlt::Ship>& ships, const std::vector<hlt::Planet>& planets);
bool GoAround(const hlt::Ship& unit_leader, const hlt::Location& ship_target, const std::vector<hlt::Planet>& planets);
//hlt::Ship ClosestUndockedEnemy(const hlt::Ship& ship, const hlt::Map& map, hlt::PlayerId enemy_id);
hlt::Ship ClosestDockedEnemy(const hlt::Ship& ship, const hlt::Map& map, hlt::PlayerId player_id);
//hlt::Location Strafe(const hlt::Ship& ship, const hlt::Ship& target, const hlt::Ship& hunter, const hlt::Map& map);
//bool HunterDanger(const hlt::Ship& ship, const hlt::Location& target, const hlt::Map& map, hlt::PlayerId player_id);
hlt::Location NearestCorner(const hlt::Ship& ship, const hlt::Map& map);
//Victory Shape Functions
bool IsInVector(const hlt::EntityId& ship_id, const std::vector<hlt::EntityId>& vector);
//Defensive Functions
hlt::Planet FindClosestAnyPlanet(const hlt::Ship& ship, std::vector<hlt::Planet> planets);
//bool EnemyNearPlanet(const hlt::Planet& planet, const hlt::Map& map, hlt::PlayerId player_id);
//bool EnemyIsolated(const hlt::Ship& enemy, const hlt::Map& map);
//bool EnoughShipsCloser(const hlt::Ship& ship, const hlt::Ship& enemy, const std::vector<hlt::Ship> my_ships);
//Distraction Prevention
//double DistanceToAllyPlanet(const hlt::Ship enemy, const std::vector<hlt::Planet> planets, const hlt::PlayerId player_id);
//bool ClosestAllyToEnemy(const hlt::Ship ship, const std::vector<hlt::Ship> ships, const hlt::Ship enemy, const std::vector<hlt::EntityId> defending_allies);
hlt::Ship ClosestAlly(const hlt::Ship enemy, const std::vector<hlt::Ship> ships, const std::vector<hlt::EntityId> defending_allies, hlt::EntityId bunny_id);
hlt::Ship FindNearestNotAssignedShip(const hlt::Ship ship, const hlt::Map map, hlt::PlayerId player_id, const std::vector<hlt::Ship> attacking_enemies, const std::vector<hlt::Ship> neutral_enemies);
bool IsInShipVector(hlt::Ship ship, std::vector<hlt::Ship> vector);
hlt::Ship NearestAnyAlly(hlt::Ship ship, std::vector<hlt::Ship> ships);
hlt::Ship ClosestDockedAlly(hlt::Ship ship, std::vector<hlt::Ship> ships);
//Offensive Functions
hlt::Ship FindClosestShip(const hlt::Ship& ship, const hlt::Map& map, hlt::PlayerId player_id, double radius);
//Credit to skylarh for FindClosestPlanet (not neccessarily in its original form).
hlt::Planet FindClosestPlanet(const hlt::Ship& ship, std::vector<hlt::Planet> planets, hlt::PlayerId player_id);
bool EnoughAlliesCloser(const hlt::Planet& planet, const hlt::Ship& ship, const hlt::Map& map, hlt::PlayerId player_id);
hlt::Planet FindNextPlanet(const hlt::Planet& planet, const hlt::Ship& ship, std::vector<hlt::Planet> planets, hlt::PlayerId player_id);
hlt::Ship NearestDockedShip(const hlt::Ship& ship, const hlt::Planet& planet, const hlt::Map& map);
hlt::Planet NearestEnemyPlanet(const hlt::Ship& ship, const hlt::Planet& target_planet, const std::vector<hlt::Planet>& planets, hlt::PlayerId player_id);
//Grouping Functions
bool Outnumbered(const hlt::Ship& ship, const hlt::Map& map, hlt::PlayerId player_id);
hlt::Location ClosestTarget(const hlt::Ship& ship, std::vector<hlt::Location> targets, std::vector<double> distances);
//Navigation Functions
hlt::Location SetTarget(const hlt::Ship& ship, const hlt::Location& target);
bool TargetOrPlanetInTheWay(const hlt::Location& location, const hlt::Location& target, const hlt::Map& map, std::vector<hlt::Ship> ships, std::vector<hlt::Location> targets, std::vector<double> distances, unsigned int index, bool unit_leader);
//bool CrissCross(const hlt::Location& location, const hlt::Location& target, std::vector<hlt::Ship> ships, std::vector<hlt::Location> targets, unsigned int index);
//int orientation(const hlt::Location& p, const hlt::Location& q, const hlt::Location& r);
bool OutOfBounds(const hlt::Location& target, const hlt::Map& map);
int main() {
const hlt::Metadata metadata = hlt::initialize("JustinianAndTheodora");
const hlt::PlayerId player_id = metadata.player_id;
const hlt::Map& initial_map = metadata.initial_map;
//We now have 1 full minute to analyse the initial map.
std::ostringstream initial_map_intelligence;
initial_map_intelligence
<< "width: " << initial_map.map_width
<< "; height: " << initial_map.map_height
<< "; players: " << initial_map.ship_map.size()
<< "; my ships: " << initial_map.ship_map.at(player_id).size()
<< "; planets: " << initial_map.planets.size();
hlt::Log::log(initial_map_intelligence.str());
std::vector<hlt::Move> moves;
//By using push_back's together, each index "i" will correspond to the other vector's index "i."
//Instead of storing the ship's location, the whole ship has to be stored just for the movement command at the end.
std::vector<hlt::Location> targets;
std::vector<hlt::Ship> ships;
std::vector<double> distances;
//Defense related vectors
std::vector<hlt::Ship> attacking_enemies;
std::vector<hlt::Ship> neutral_enemies;
//Entity ID is stored for IsInVector.
std::vector<hlt::EntityId> defending_allies;
//Special case variables
//Bunny_id was used when my defense against noob rushes was to assign one ship to be a bunny ship and the other two to go dock. It's not used for that anymore,
//but it's passed into another function so I have to intialize it/bother to change the function.
hlt::EntityId bunny_id = 999;
int move_number = 0;
double enemy_distance = 0;
double previous_distance = 0;
bool noob_rush = false;
hlt::EntityId unit_leader_id = 999;
bool defeated = false;
//These next two variables make a nice "1" in the bottom-left corner of the map when I'm certainly going to win. Old versions of my code would glitch out at that point and
//give me enough turns to make some art.
std::vector<hlt::EntityId> victory_ships;
hlt::Location victory_shape[7] = { { 9.0, initial_map.map_height - 6.0 },{ 12.0, initial_map.map_height - 6.0 }, { 15.0, initial_map.map_height - 6.0 },{ 12.0, initial_map.map_height - 9.0 },{ 12.0, initial_map.map_height - 12.0 },{ 12.0, initial_map.map_height - 15.0 },{ 9.0, initial_map.map_height - 12.0 } };
bool winning = false;
for (;;) {
moves.clear();
const hlt::Map map = hlt::in::get_map();
//Reset targets and vectors.
targets.clear();
ships.clear();
distances.clear();
attacking_enemies.clear();
neutral_enemies.clear();
defending_allies.clear();
victory_ships.clear();
//
//Check for special cases and give unique instructions.
//
//
//Noob rush
//Detects if there's a noob rush then handles it accordingly. Current strategy: group up and charge in.
//
if (move_number < 3 || noob_rush) {
move_number++;
hlt::Ship farthest_ship = FarthestShip(map, player_id);
hlt::Ship nearest_enemy = FindClosestShip(farthest_ship, map, player_id, NO_RADIUS);
if (map.ships.at(player_id).size() > map.ships.at(nearest_enemy.owner_id).size()) {
noob_rush = false;
}
else {
enemy_distance = farthest_ship.location.get_distance_to(nearest_enemy.location);
double units_closer = previous_distance - enemy_distance;
previous_distance = enemy_distance;
std::ostringstream turn_information;
turn_information << "units closer: " << units_closer;
hlt::Log::log(turn_information.str());
if ((units_closer > 0 && enemy_distance < 98) || enemy_distance < 70) {
noob_rush = true;
if (unit_leader_id == 999) {
unit_leader_id = farthest_ship.entity_id;
}
hlt::Ship unit_leader;
for (hlt::Ship ally : map.ships.at(player_id)) {
if (ally.entity_id == unit_leader_id) {
unit_leader = ally;
}
}
if (InFormation(map.ships.at(player_id))) {
unit_leader = ShipClosestToPlanet(map.ships.at(player_id), map.planets);
unit_leader_id = unit_leader.entity_id;
}
std::ostringstream turn_information;
turn_information << "unit leader: " << unit_leader_id;
hlt::Log::log(turn_information.str());
hlt::Ship nearest_docked_enemy = ClosestDockedEnemy(unit_leader, map, player_id);
if (nearest_docked_enemy.owner_id == nearest_enemy.owner_id) {
nearest_enemy = nearest_docked_enemy;
}
// was nearest_enemy.radius - 2
hlt::Location ship_target = unit_leader.location.get_closest_point(nearest_enemy.location, 1.9);
ship_target = SetTarget(unit_leader, ship_target);
if (!InFormation(map.ships.at(player_id))) {
ship_target = { unit_leader.location.pos_x + 5.0 / 7.0 * (ship_target.pos_x - unit_leader.location.pos_x), unit_leader.location.pos_y + 5.0 / 7.0 * (ship_target.pos_y - unit_leader.location.pos_y) };
}
bool go_around = GoAround(unit_leader, ship_target, map.planets);
int max_corrections = 181;
bool left = true;
while ((TargetOrPlanetInTheWay(unit_leader.location, ship_target, map, ships, targets, distances, 0, go_around) || OutOfBounds(ship_target, map)) && max_corrections) {
max_corrections--;
const double distance = unit_leader.location.get_distance_to(ship_target);
const double angle_rad = unit_leader.location.orient_towards_in_rad(ship_target);
double new_target_dx;
double new_target_dy;
if (left) {
new_target_dx = cos(angle_rad + (M_PI / 180.0)*(181.0 - max_corrections)) * distance;
new_target_dy = sin(angle_rad + (M_PI / 180.0)*(181.0 - max_corrections)) * distance;
left = false;
}
else {
new_target_dx = cos(angle_rad - (M_PI / 180.0)*(181.0 - max_corrections)) * distance;
new_target_dy = sin(angle_rad - (M_PI / 180.0)*(181.0 - max_corrections)) * distance;
left = true;
}
ship_target = { unit_leader.location.pos_x + new_target_dx, unit_leader.location.pos_y + new_target_dy };
}
targets.push_back(SetTarget(unit_leader, ship_target));
ships.push_back(unit_leader);
distances.push_back(0.0);
for (hlt::Ship ally : map.ships.at(player_id)) {
if (ally.entity_id != unit_leader_id) {
if (InFormation(map.ships.at(player_id))) {
hlt::Location difference = { unit_leader.location.pos_x - ally.location.pos_x, unit_leader.location.pos_y - ally.location.pos_y };
hlt::Location ship_target = { targets[0].pos_x - difference.pos_x, targets[0].pos_y - difference.pos_y };
targets.push_back(SetTarget(ally, ship_target));
ships.push_back(ally);
distances.push_back(ally.location.get_distance_to(ship_target));
}
else {
hlt::Location ship_target = ally.location.get_closest_point(targets[0], -4);
targets.push_back(SetTarget(ally, ship_target));
ships.push_back(ally);
distances.push_back(ally.location.get_distance_to(ship_target));
}
}
}
}
else {
noob_rush = false;
}
}
}
//
//Imminent defeat/victory.
//Sends my ships to the nearest corner if I'm certain to lose/starts making art if I'm certain to win.
//
if (map.ships.size() == 4) {
int owned_by_me = 0;
int owned_by_enemy = 0;
int planets = 0;
for (const hlt::Planet& p : map.planets) {
planets++;
if (p.owned) {
if (p.owner_id == player_id) {
owned_by_me++;
}
else {
owned_by_enemy++;
}
}
}
//Defeat
if ((owned_by_me <= 3 && owned_by_enemy >= (planets * 3.0) / 4.0) || (owned_by_me <= 3 && owned_by_enemy >= 3.0 * (owned_by_me + 2.0)) || defeated) {
defeated = true;
for (const hlt::Ship& ship : map.ships.at(player_id)) {
if (ship.docking_status != hlt::ShipDockingStatus::Undocked) {
targets.push_back(ship.location);
ships.push_back(ship);
distances.push_back(0.0);
}
else {
hlt::Location ship_target = NearestCorner(ship, map);
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));
}
}
}
//Victory
if (owned_by_enemy == 0 && owned_by_me >= planets / 2.0) {
int index = 0;
for (const hlt::Ship& ship : map.ships.at(player_id)) {
if (ship.docking_status == hlt::ShipDockingStatus::Undocked && index < sizeof(victory_ships)) {
hlt::Location ship_target = victory_shape[index];
index++;
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));
victory_ships.push_back(ship.entity_id);
}
}
}
}
//
//Attacking enemies.
//Finds ships closest to my planets/neutral planets and assigns a ship to each one. This uses the offensively-ineffective Outnumbered() function to ensure that the
//ships never really attack their targets, and get cover if they're under attack.
//
for (const auto& player_ship : map.ships) {
//Find attacking and neutral enemies.
for (const hlt::Ship& s : player_ship.second) {
if (s.owner_id != player_id && !noob_rush && !defeated) {
hlt::Planet closest_planet = FindClosestAnyPlanet(s, map.planets);
if (closest_planet.owner_id == player_id && s.location.get_distance_to(closest_planet.location) < 50) {
attacking_enemies.push_back(s);
}
else if (!closest_planet.owned && s.location.get_distance_to(closest_planet.location) < 50) {
neutral_enemies.push_back(s);
}
}
}
}
std::ostringstream turn_information;
for (hlt::Ship s : attacking_enemies) {
turn_information << "attacking ship id: " << s.entity_id << " ";
}
hlt::Log::log(turn_information.str());
for (hlt::Ship s : neutral_enemies) {
turn_information << "neutral ship id: " << s.entity_id << " ";
}
hlt::Log::log(turn_information.str());
//Assign an ally to the attacking and neutral ships and set its target.
for (const hlt::Ship& enemy : attacking_enemies) {
const hlt::Ship closest_ally = ClosestAlly(enemy, map.ships.at(player_id), defending_allies, bunny_id);
if (closest_ally.entity_id != enemy.entity_id) {
if (Outnumbered(closest_ally, map, player_id)) {
hlt::Ship nearest_ally = NearestAnyAlly(closest_ally, map.ships.at(player_id));
if (nearest_ally.docking_status != hlt::ShipDockingStatus::Undocked) {
hlt::Ship closest_docked_ally_for_enemy = ClosestDockedAlly(enemy, map.ships.at(player_id));
hlt::Location ship_target = closest_docked_ally_for_enemy.location;
targets.push_back(SetTarget(closest_ally, ship_target));
ships.push_back(closest_ally);
distances.push_back(closest_ally.location.get_distance_to(ship_target));
}
else {
targets.push_back({ 0.0, 0.0 });
ships.push_back(closest_ally);
distances.push_back(SET_TARGET_LAST);
}
}
else {
hlt::Location ship_target = closest_ally.location.get_closest_point(enemy.location, enemy.radius - 2);
targets.push_back(SetTarget(closest_ally, ship_target));
ships.push_back(closest_ally);
distances.push_back(closest_ally.location.get_distance_to(ship_target));
}
defending_allies.push_back(closest_ally.entity_id);
}
}
for (const hlt::Ship& enemy : neutral_enemies) {
const hlt::Ship closest_ally = ClosestAlly(enemy, map.ships.at(player_id), defending_allies, bunny_id);
if (closest_ally.entity_id != enemy.entity_id) {
if (Outnumbered(closest_ally, map, player_id)) {
targets.push_back({ 0.0, 0.0 });
ships.push_back(closest_ally);
distances.push_back(SET_TARGET_LAST);
}
else {
hlt::Location ship_target = closest_ally.location.get_closest_point(enemy.location, enemy.radius - 2);
targets.push_back(SetTarget(closest_ally, ship_target));
ships.push_back(closest_ally);
distances.push_back(closest_ally.location.get_distance_to(ship_target));
}
defending_allies.push_back(closest_ally.entity_id);
}
}
//
//Ship advantage - use it!
//If I have 5 more ships than the everyone, I don't care about ensuring each ship only goes in when it outnumbers the enemy - just attack!
//
int my_ships = 0;
int their_ships = 0;
for (const auto player_ship : map.ships) {
if (player_ship.first == player_id) {
my_ships = player_ship.second.size();
}
else {
if (player_ship.second.size() > their_ships) {
their_ships = player_ship.second.size();
}
}
}
if (my_ships >= 50 && their_ships + 5 <= my_ships) {
//This bool makes my attacking ships not care about numbers - I have more! Time to blitz!
winning = true;
}
//
//Outnumbered Refactor
//This code sorts ships by their distance to their closest enemy, has ship, by distance and if not already checked, identify any clusters around it, determine if the
//allied cluster is outnumbered, then sets each ship in that identified cluster to know it's been checked so that the loop really only does a small fraction of the
//ships. Works pretty well, written pretty poorly and quickly!
//
//No detailed comments, just written all at once. Not my problem! Written 2 hours before Halite ended, so the key-phrase "EXPERIMENTAL, MOSTLY MENTAL" is commented
//throughout the code I used it in so I could efficiently remove this if it failed. It worked nicely for being a first implementation in my final bot!
//EXPERIMENTAL, MOSTLY MENTAL.
std::vector<double> enemy_distances;
std::vector<hlt::Ship> each_ship;
std::vector<bool> outnumbered;
for (hlt::Ship s : map.ships.at(player_id)) {
hlt::Ship nearest_enemy = FindClosestShip(s, map, player_id, NO_RADIUS);
enemy_distances.push_back(s.location.get_distance_to(nearest_enemy.location));
each_ship.push_back(s);
outnumbered.push_back(true);
}
for (unsigned int i = 0; i < enemy_distances.size() - 1; i++) {
for (unsigned int j = 0; j < enemy_distances.size() - i - 1; j++) {
if (enemy_distances[j] > enemy_distances[j + 1]) {
hlt::Ship ship_holder = each_ship[j];
double distance_holder = enemy_distances[j];
each_ship[j] = each_ship[j + 1];
enemy_distances[j] = enemy_distances[j + 1];
each_ship[j + 1] = ship_holder;
enemy_distances[j + 1] = distance_holder;
}
}
}
std::vector<hlt::Ship> checked;
for (unsigned int i = 0; i < each_ship.size(); i++) {
if (!IsInShipVector(each_ship[i], checked)) {
std::vector<double> ally_distances;
std::vector<hlt::Ship> allies;
for (unsigned int j = 0; j < each_ship.size(); j++) {
ally_distances.push_back(each_ship[i].location.get_distance_to(each_ship[j].location));
allies.push_back(each_ship[j]);
}
for (unsigned int k = 0; k < ally_distances.size() - 1; k++) {
for (unsigned int l = 0; l < ally_distances.size() - k - 1; l++) {
if (ally_distances[l] > ally_distances[l + 1]) {
hlt::Ship ship_holder = allies[l];
double distance_holder = ally_distances[l];
allies[l] = allies[l + 1];
ally_distances[l] = ally_distances[l + 1];
allies[l + 1] = ship_holder;
ally_distances[l + 1] = distance_holder;
}
}
}
unsigned int cluster_number = 0;
bool end_early = false;
for (unsigned int k = 0; k < ally_distances.size() - 1; k++) {
if (!end_early) {
cluster_number++;
}
if (ally_distances[k + 1] > 3 + ally_distances[k]) {
end_early = true;
}
}
std::ostringstream info;
info << cluster_number;
hlt::Log::log(info.str());
for (unsigned int y = 0; y < cluster_number; y++) {
checked.push_back(allies[y]);
}
unsigned int enemies = 0;
for (const auto& player_ship : map.ships) {
for (const hlt::Ship& s : player_ship.second) {
if (s.owner_id != player_id && each_ship[i].location.get_distance_to(s.location) <= (hlt::constants::MAX_SPEED * 2.0) + hlt::constants::WEAPON_RADIUS && s.docking_status == hlt::ShipDockingStatus::Undocked) {
enemies++;
}
}
}
if (cluster_number > enemies) {
for (unsigned int x = 0; x < cluster_number; x++) {
for (unsigned int z = 0; z < allies.size(); z++) {
if (each_ship[z].entity_id == allies[x].entity_id) {
outnumbered[z] = false;
}
}
}
}
}
}
//
//Standard logic for each ship not used in a special case (except the Outnumbered Refactor, that just sets a boolean for the ship).
//
//Set targets.
for (const hlt::Ship& ship : map.ships.at(player_id)) {
//Check if the ship was already given a target in a special case.
if (noob_rush || ship.entity_id == bunny_id || defeated || IsInVector(ship.entity_id, victory_ships) || IsInVector(ship.entity_id, defending_allies)) {
continue;
}
//Stay docked if you are.
if (ship.docking_status != hlt::ShipDockingStatus::Undocked) {
targets.push_back(ship.location);
ships.push_back(ship);
distances.push_back(0.0);
continue;
}
/*
Defense strategy:
1. Is there a near enemy?
2. Defend against it with the previous assignments in attacking_enemies.
(There used to be logic here that was since removed by the "Attacking Ships" special case.)
*/
/*
The current offensive strategy is to locate the closest planet that is either not full or not owned by me, and run a unique strategy for
each three cases of ownership:
1. Not owned.
- If there are nearby enemies (scaling with planet radius), target the nearest docked enemy.
- Otherwise, try to dock.
- Otherwise, if you have no enemies in a really large radius and there are more than enough ships heading to the planet, find a new planet to target.
- Otherwise, continue moving towards the planet.
2. Owned by me but it needs more docked ships.
- If there are enough allies closer, go to the next planet.
- Otherwise dock or move to it.
3. Owned by someone else.
- Attack the closest docked ship.
*/
//Find the closest planet that is not fully docked by me.
const hlt::Planet& target_planet = FindClosestPlanet(ship, map.planets, player_id);
//EXPERIMENTAL, MOSTLY MENTAL.
bool get_help = false;
for (unsigned int i = 0; i < each_ship.size(); i++) {
if (each_ship[i].entity_id == ship.entity_id) {
if (outnumbered[i]) {
get_help = true;
}
}
}
//If nobody owns the planet,
if (!target_planet.owned) {
//find the nearest enemy,
//hlt::Ship nearest_enemy = FindClosestShip(ship, map, player_id, NO_RADIUS);
hlt::Ship nearest_enemy = FindNearestNotAssignedShip(ship, map, player_id, attacking_enemies, neutral_enemies);
hlt::Ship actual_nearest = FindClosestShip(ship, map, player_id, NO_RADIUS);
//if there are no nearby enemies within a certain range,
//EXPERIMENTAL, MOSTLY MENTAL.
//if (ship.location.get_distance_to(nearest_enemy.location) > (19.1) || ship.location.get_distance_to(actual_nearest.location) > (target_planet.radius * 3.0 + 15)) {
if (ship.location.get_distance_to(nearest_enemy.location) > (target_planet.radius * 3.0 + 15) || ship.location.get_distance_to(actual_nearest.location) > (19.1)) {
//try to dock,
if (ship.can_dock(target_planet)) {
targets.push_back(ship.location);
ships.push_back(ship);
distances.push_back(0.0);
moves.push_back(hlt::Move::dock(ship.entity_id, target_planet.entity_id));
}
//or if I can't dock,
else {
//check if there is a need for more ships to go to the planet,
if ((target_planet.location.get_distance_to(nearest_enemy.location) < 50.0 && ship.entity_id != nearest_enemy.entity_id) || !EnoughAlliesCloser(target_planet, ship, map, player_id)) {
hlt::Location ship_target = ship.location.get_closest_point(target_planet.location, target_planet.radius);
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));
}
//otherwise locate the second closest planet,
else {
const hlt::Planet& new_planet = FindNextPlanet(target_planet, ship, map.planets, player_id);
hlt::Location ship_target = ship.location.get_closest_point(new_planet.location, new_planet.radius);
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));
}
}
}
//otherwise there is a nearby enemy,
else {
//Outnumbered() is an inefficient means of detecting a number advantage, mostly phased-out.
//if (Outnumbered(ship, map, player_id)) {
if (get_help) {
targets.push_back({ 0, 0 });
ships.push_back(ship);
distances.push_back(SET_TARGET_LAST);
}
else {
hlt::Planet nearest_enemy_planet = NearestEnemyPlanet(ship, target_planet, map.planets, player_id);
hlt::Ship nearest_docked_ship = NearestDockedShip(ship, nearest_enemy_planet, map);
hlt::Location ship_target = ship.location.get_closest_point(nearest_docked_ship.location, nearest_docked_ship.radius - 2);
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));
//Other logic that attacks the nearest ship. That's no fun, though, because I want to rush the nearest docked ship.
/*hlt::Location ship_target = ship.location.get_closest_point(nearest_enemy.location, nearest_enemy.radius - 2);
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));*/
}
}
}
//or I need more ships docked to mine,
else if (target_planet.owner_id == player_id) {
if (!EnoughAlliesCloser(target_planet, ship, map, player_id)) {
if (ship.can_dock(target_planet)) {
targets.push_back(ship.location);
ships.push_back(ship);
distances.push_back(0.0);
moves.push_back(hlt::Move::dock(ship.entity_id, target_planet.entity_id));
}
else {
hlt::Location ship_target = ship.location.get_closest_point(target_planet.location, target_planet.radius);
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));
}
}
else {
//Outnumbered() is an inefficient means of detecting a number advantage, mostly phased-out.
//if (Outnumbered(ship, map, player_id)) {
if (get_help) {
targets.push_back({ 0, 0 });
ships.push_back(ship);
distances.push_back(SET_TARGET_LAST);
}
else {
const hlt::Planet& new_planet = FindNextPlanet(target_planet, ship, map.planets, player_id);
hlt::Location ship_target = ship.location.get_closest_point(new_planet.location, new_planet.radius);
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));
}
}
}
//otherwise it's not mine,
else {
//so find the closest ship docked to it,
hlt::Ship nearest_docked_ship = NearestDockedShip(ship, target_planet, map);
//Outnumbered() is an inefficient means of detecting a number advantage, mostly phased-out.
//if (Outnumbered(ship, map, player_id) && !winning) {
//Winning, from the special case "Ship Advantage," makes the ships ignore how outnumbered they are if I have more total ships.
if (get_help && !winning) {
targets.push_back({ 0, 0 });
ships.push_back(ship);
distances.push_back(SET_TARGET_LAST);
}
else {
hlt::Location ship_target = ship.location.get_closest_point(nearest_docked_ship.location, nearest_docked_ship.radius - 2);
targets.push_back(SetTarget(ship, ship_target));
ships.push_back(ship);
distances.push_back(ship.location.get_distance_to(ship_target));
}
}
}
//Set Outnumbered targets and prioritize them between stationary and the rest of the ships.
for (unsigned int i = 0; i < distances.size(); i++) {
if (distances[i] == SET_TARGET_LAST) {
//Go to the target of the closest ship that isn't outnumbered/is docked.
targets[i] = ClosestTarget(ships[i], targets, distances);
//A small number that sorts these distances between stationary and other moving ships.
distances[i] = 0.01;
}
}
//Rearrange the ships by distance to their target with a bubble sort.
for (unsigned int i = 0; i < distances.size() - 1; i++) {
for (unsigned int j = 0; j < distances.size() - i - 1; j++) {
if (distances[j] > distances[j + 1]) {
hlt::Location target_holder = targets[j];
hlt::Ship ship_holder = ships[j];
double distance_holder = distances[j];
targets[j] = targets[j + 1];
ships[j] = ships[j + 1];
distances[j] = distances[j + 1];
targets[j + 1] = target_holder;
ships[j + 1] = ship_holder;
distances[j + 1] = distance_holder;
}
//Sorts the Outnumbered ships by farthest-first. The idea is that they become more likely to be able to move away from the danger they're closer to.
//Doesn't really work.
else if (distances[j] == 0.01 && distances[j + 1] == 0.01) {
if (ships[j].location.get_distance_to(targets[j]) < ships[j + 1].location.get_distance_to(targets[j + 1])) {
hlt::Location target_holder = targets[j];
hlt::Ship ship_holder = ships[j];
double distance_holder = distances[j];
targets[j] = targets[j + 1];
ships[j] = ships[j + 1];
distances[j] = distances[j + 1];
targets[j + 1] = target_holder;
ships[j + 1] = ship_holder;
distances[j + 1] = distance_holder;
}
}
}
}
//Manage any collision between ally ships.
bool correction;
do {
correction = false;
for (unsigned int i = 0; i < targets.size(); i++) {
//If the ship is already docked/has been commanded to dock, skip this loop.
if (targets[i] == ships[i].location) {
continue;
}
//Move targets that will hit planets or other targets.
int max_corrections;
//Special 360 degree navigation check for bunny ships, including some altered collision checks for docked enemies/nearby enemies.
//I don't have bunny ships in this version.
if (ships[i].entity_id == bunny_id) {
/* This is phased out, as this version does not set a bunny id.
max_corrections = 361;
bool left = true;
//The loop now only checks ally targets with a distance closer than the ship target.
//while ((HunterDanger(ships[i], targets[i], map, player_id) || TargetOrPlanetInTheWay(ships[i].location, targets[i], map, ships, targets, distances, i) || CrissCross(ships[i].location, targets[i], ships, targets, i) || OutOfBounds(targets[i], map)) && max_corrections) {
while ((HunterDanger(ships[i], targets[i], map, player_id) || TargetOrPlanetInTheWay(ships[i].location, targets[i], map, ships, targets, distances, i, false) || OutOfBounds(targets[i], map)) && max_corrections) {
correction = true;
max_corrections--;
const double distance = ships[i].location.get_distance_to(targets[i]);
const double angle_rad = ships[i].location.orient_towards_in_rad(targets[i]);
double new_target_dx;
double new_target_dy;
//Alternate checking 1 degree left/right.
if (left) {
new_target_dx = cos(angle_rad + (M_PI / 180.0)*(361.0 - max_corrections)) * distance;
new_target_dy = sin(angle_rad + (M_PI / 180.0)*(361.0 - max_corrections)) * distance;
left = false;
}
else {
new_target_dx = cos(angle_rad - (M_PI / 180.0)*(361.0 - max_corrections)) * distance;
new_target_dy = sin(angle_rad - (M_PI / 180.0)*(361.0 - max_corrections)) * distance;
left = true;
}
targets[i] = { ships[i].location.pos_x + new_target_dx, ships[i].location.pos_y + new_target_dy };
}*/
}
//Regular ship collision check.
else {
//The corrections go 90 degrees either way, and 1 is added because of the initial decrement.
max_corrections = 181;
bool left = true;
//The loop now only checks ally targets with a distance closer than the ship target.
//while ((TargetOrPlanetInTheWay(ships[i].location, targets[i], map, ships, targets, distances, i) || CrissCross(ships[i].location, targets[i], ships, targets, i) || OutOfBounds(targets[i], map)) && max_corrections) {
while ((TargetOrPlanetInTheWay(ships[i].location, targets[i], map, ships, targets, distances, i, false) || OutOfBounds(targets[i], map)) && max_corrections) {
correction = true;
max_corrections--;
const double distance = ships[i].location.get_distance_to(targets[i]);
const double angle_rad = ships[i].location.orient_towards_in_rad(targets[i]);
double new_target_dx;
double new_target_dy;
//Alternate checking 1 degree left/right.
if (left) {
new_target_dx = cos(angle_rad + (M_PI / 180.0)*(181.0 - max_corrections)) * distance;
new_target_dy = sin(angle_rad + (M_PI / 180.0)*(181.0 - max_corrections)) * distance;
left = false;
}
else {
new_target_dx = cos(angle_rad - (M_PI / 180.0)*(181.0 - max_corrections)) * distance;
new_target_dy = sin(angle_rad - (M_PI / 180.0)*(181.0 - max_corrections)) * distance;
left = true;
}
targets[i] = { ships[i].location.pos_x + new_target_dx, ships[i].location.pos_y + new_target_dy };
}
}
//If a safe target could not be found, don't move, resort the vectors.
if (!max_corrections) {
targets[i] = ships[i].location;
targets.insert(targets.begin(), targets[i]);
ships.insert(ships.begin(), ships[i]);
distances.insert(distances.begin(), 0.0);
targets.erase(targets.begin() + i + 1);
ships.erase(ships.begin() + i + 1);
distances.erase(distances.begin() + i + 1);
continue;
}
}
} while (correction);
//Move each target/log info.
for (unsigned int i = 0; i < targets.size(); i++) {
std::ostringstream turn_information;
turn_information << "ship id: " << ships[i].entity_id << " target x: " << targets[i].pos_x << " target y: " << targets[i].pos_y << " distance: " << distances[i];
hlt::Log::log(turn_information.str());
if (ships[i].location == targets[i]) {
continue;
}
//Move.
const hlt::possibly<hlt::Move> move =
hlt::navigation::navigate_ship_towards_target(map, ships[i], targets[i], hlt::constants::MAX_SPEED, false, hlt::constants::MAX_NAVIGATION_CORRECTIONS, 0.0);
if (move.second) {
moves.push_back(move.first);
}
}
if (!hlt::out::send_moves(moves)) {
hlt::Log::log("send_moves failed; exiting");
break;
}
}
}
//
//Special Case functions
//
bool NoobRush(const hlt::Ship& ship, const hlt::Ship& enemy, const hlt::Planet& planet, const unsigned int& players) {
if (ship.entity_id == enemy.entity_id) {
return false;
}
hlt::Location docking_spot = ship.location.get_closest_point(planet.location, planet.radius);
if (players == 2) {
if ((int)(enemy.location.get_distance_to(docking_spot) / 7) - (int)(ship.location.get_distance_to(docking_spot) / 7) <= 12) {
return true;
}
}
else {
if ((int)(enemy.location.get_distance_to(docking_spot) / 7) - (int)(ship.location.get_distance_to(docking_spot) / 7) <= 9) {
return true;
}
}
return false;
}
hlt::Ship FarthestShip(const hlt::Map& map, hlt::PlayerId player_id) {
hlt::Ship farthest_ally = map.ships.at(player_id)[0];
hlt::Ship closest_enemy;
double range = NO_RADIUS;
for (const auto& player_ship : map.ships) {
for (const hlt::Ship& potential_ship : player_ship.second) {
if (potential_ship.owner_id != player_id) {
double dist = potential_ship.location.get_distance_to(farthest_ally.location);
if (dist < range) {
range = dist;
closest_enemy = potential_ship;
}
}
}
}
for (const hlt::Ship& ally : map.ships.at(player_id)) {
// for a ghost ship that appears when you're at 1 ship
if (ally.docking_status == hlt::ShipDockingStatus::Undocked && ally.health != 15) {
double dist = ally.location.get_distance_to(closest_enemy.location);
if (dist > range) {
range = dist;
farthest_ally = ally;
}
}
}
return farthest_ally;
}
bool InFormation(const std::vector<hlt::Ship>& ships) {
for (hlt::Ship ship : ships) {
double range = NO_RADIUS;
for (hlt::Ship ally : ships) {
if (ally.entity_id != ship.entity_id) {
double dist = ally.location.get_distance_to(ship.location);
if (dist < range) {
range = dist;
}
}
}
if (range > 1.5) {
return false;
}
}
return true;
}
hlt::Ship ShipClosestToPlanet(const std::vector<hlt::Ship>& ships, const std::vector<hlt::Planet>& planets) {
double range = NO_RADIUS;
hlt::Ship current_ship;
for (hlt::Ship ally : ships) {
for (hlt::Planet p : planets) {
double dist = ally.location.get_distance_to(p.location);
if (dist < range) {
range = dist;
current_ship = ally;
}
}
}
return current_ship;
}
bool GoAround(const hlt::Ship& unit_leader, const hlt::Location& ship_target, const std::vector<hlt::Planet>& planets) {
for (hlt::Planet p : planets) {
if (hlt::navigation::check_planet_between(unit_leader.location, ship_target, p, false)) {
return true;
}
}
return false;
}
//Phased-out function that was used in some logic for noob rushes.
/*hlt::Ship ClosestUndockedEnemy(const hlt::Ship& ship, const hlt::Map& map, hlt::PlayerId enemy_id) {
//Returns the ship if there are no near enemies.
hlt::Ship current_ship = ship;
double radius = NO_RADIUS;
for (const hlt::Ship& potential_ship : map.ships.at(enemy_id)) {
if (potential_ship.docking_status == hlt::ShipDockingStatus::Undocked) {
double dist = ship.location.get_distance_to(potential_ship.location);
if (dist < radius) {
current_ship = potential_ship;
radius = dist;
}
}
}
return current_ship;
}*/
hlt::Ship ClosestDockedEnemy(const hlt::Ship& ship, const hlt::Map& map, hlt::PlayerId player_id) {
//Returns the ship if there are no near enemies.
hlt::Ship current_ship = ship;
double radius = NO_RADIUS;
for (const auto& player_ship : map.ships) {
for (const hlt::Ship& potential_ship : player_ship.second) {
if (potential_ship.owner_id != player_id && potential_ship.docking_status != hlt::ShipDockingStatus::Undocked) {
double dist = ship.location.get_distance_to(potential_ship.location);
if (dist < radius) {
current_ship = potential_ship;
radius = dist;
}
}
}
}
return current_ship;
}
//Phased-out function that helped my bunny ship get around a defender and closer to enemy docked ships.
/*hlt::Location Strafe(const hlt::Ship& ship, const hlt::Ship& target, const hlt::Ship& hunter, const hlt::Map& map) {
int angle_deg = ship.location.orient_towards_in_deg(hunter.location);
double left_dx = cos(angle_deg * (M_PI / 180) + (M_PI / 2.0)) * 7.0;
double left_dy = sin(angle_deg * (M_PI / 180) + (M_PI / 2.0)) * 7.0;
double right_dx = cos(angle_deg * (M_PI / 180) - (M_PI / 2.0)) * 7.0;
double right_dy = sin(angle_deg * (M_PI / 180) - (M_PI / 2.0)) * 7.0;
hlt::Location left_strafe = { ship.location.pos_x + left_dx, ship.location.pos_y + left_dy };
hlt::Location right_strafe = { ship.location.pos_x + right_dx, ship.location.pos_y + right_dy };
if (left_strafe.get_distance_to(target.location) <= right_strafe.get_distance_to(target.location)) {
return left_strafe;
}
return right_strafe;
}*/
//Phased-out function that most-nearly kept the bunny ship out of harm's way.
/*bool HunterDanger(const hlt::Ship& ship, const hlt::Location& target, const hlt::Map& map, hlt::PlayerId player_id) {
for (const auto& player_ship : map.ships) {
if (player_ship.first != player_id) {
for (const hlt::Ship& s : player_ship.second) {
if (s.docking_status != hlt::ShipDockingStatus::Undocked) {
if (hlt::navigation::check_target_between(ship.location, target, s.location)) {
return true;
}
}
else {
if (target.get_distance_to(s.location) <= 12.1) {
return true;