mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 12:15:44 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfc32e8068 | ||
|
|
549cf37127 | ||
|
|
237d5ce7c3 | ||
|
|
05fc72c611 | ||
|
|
5d889b71aa | ||
|
|
8641d15fc7 | ||
|
|
e4fc742990 | ||
|
|
a27dfe895f | ||
|
|
098d4b4dcc | ||
|
|
765ca02255 | ||
|
|
186dec0ad9 | ||
|
|
14a6d143bc | ||
|
|
ade5a4fa10 | ||
|
|
85d3ae8fce | ||
|
|
fb200278ae | ||
|
|
34da29bef0 | ||
|
|
f164fbb150 | ||
|
|
fe1537bb12 | ||
|
|
69cc336cef | ||
|
|
d05030f99a | ||
|
|
9699d095c5 | ||
|
|
6ca2d7a37c | ||
|
|
b0c4944a6c | ||
|
|
d25925d525 | ||
|
|
07a6834216 | ||
|
|
69f1f51460 | ||
|
|
8b30a2c5f2 | ||
|
|
65a534ebae | ||
|
|
ac0a154fc4 | ||
|
|
1b510007fd | ||
|
|
692c363264 | ||
|
|
cf5d88c69d | ||
|
|
109f49e522 | ||
|
|
53e3f7a1b0 | ||
|
|
c7d3e162b9 | ||
|
|
7b2bb34a36 | ||
|
|
c7b1082954 | ||
|
|
aa18d0dd1a | ||
|
|
711d0382f3 | ||
|
|
7b02c39172 | ||
|
|
b178f11bea | ||
|
|
42da8a7d15 | ||
|
|
bd9b62d7de | ||
|
|
98fcd94614 | ||
|
|
06e7112db1 | ||
|
|
9ba023b0fd | ||
|
|
ff63c0fff2 | ||
|
|
421dbcae80 | ||
|
|
4c321967f4 | ||
|
|
b3c0138074 | ||
|
|
4b7c862ac8 | ||
|
|
cf4b99710c | ||
|
|
d25e0121a6 | ||
|
|
213a339d34 | ||
|
|
d2205dc361 | ||
|
|
21e0586add | ||
|
|
cbbe200c98 | ||
|
|
fbeddc7b19 | ||
|
|
1ed6c85b5d | ||
|
|
c7f10dbcbc | ||
|
|
81f747c75b | ||
|
|
a76dc0515c | ||
|
|
bb77ecbd73 | ||
|
|
5d35e7d78b | ||
|
|
e20c4e7027 | ||
|
|
3977df70f4 | ||
|
|
7f72a8bb39 | ||
|
|
de27e9fcb8 | ||
|
|
76bd94ddb5 | ||
|
|
651e209f07 | ||
|
|
448ec1e0ee | ||
|
|
6e2afeee3c | ||
|
|
edce49467c | ||
|
|
2934027437 | ||
|
|
b2250cda79 | ||
|
|
96d69b349f | ||
|
|
4a6ae0380a | ||
|
|
e339ddd227 | ||
|
|
afaf6c9e19 | ||
|
|
e21eaf8606 | ||
|
|
a43a650d17 | ||
|
|
17cb742964 | ||
|
|
9d8231b253 | ||
|
|
5bf2ea95e5 | ||
|
|
2f48330d74 | ||
|
|
b0b516a990 | ||
|
|
1057271163 | ||
|
|
296d5df16f | ||
|
|
b2abe00b14 | ||
|
|
793d665d80 | ||
|
|
bbbe596e72 | ||
|
|
c3c148f5db | ||
|
|
4b8f1d2d20 | ||
|
|
8a3222ed54 | ||
|
|
9f82af2e12 | ||
|
|
2536e5146c | ||
|
|
98b0502e20 | ||
|
|
25c1198c0a | ||
|
|
a6e31596d6 | ||
|
|
c9b3cd6fc0 | ||
|
|
2c18d1d39b | ||
|
|
28895e3e04 | ||
|
|
f8ef044226 | ||
|
|
b8da5750c7 | ||
|
|
fbdc0f79a8 | ||
|
|
ea7f666e36 | ||
|
|
48ff8a7e1b | ||
|
|
033a378e14 | ||
|
|
a3d19f246e | ||
|
|
ec67215bc2 | ||
|
|
f207bef4a8 | ||
|
|
41bb944c26 | ||
|
|
38eb33367d | ||
|
|
5ae59006fb | ||
|
|
e66e7d4a03 | ||
|
|
34bbfaf256 | ||
|
|
abd323a232 | ||
|
|
97ba1d0be1 | ||
|
|
deb211a420 | ||
|
|
9481eed7b8 | ||
|
|
50c457d877 | ||
|
|
a544a0d15e | ||
|
|
8c26ac5fca | ||
|
|
d56e0de9fd | ||
|
|
5132152334 | ||
|
|
ef6ba7e585 | ||
|
|
695a9d19d4 | ||
|
|
45682dff9b | ||
|
|
47c5653bc0 | ||
|
|
033ebc802a | ||
|
|
14e0fb66fb | ||
|
|
18d9756fc5 | ||
|
|
f976548fa1 | ||
|
|
a3fed6836d | ||
|
|
78d89f83da | ||
|
|
66b93b0c83 | ||
|
|
fe06ead2e2 | ||
|
|
71e5b1b866 | ||
|
|
35ac2d6297 | ||
|
|
ff33d13822 | ||
|
|
ffb02d1d6c | ||
|
|
86f7e85ce9 | ||
|
|
cd6318ba84 | ||
|
|
7385b8ced2 | ||
|
|
a36ccd4157 | ||
|
|
29a3459649 | ||
|
|
8dd7df989d | ||
|
|
d40b9a9ba6 | ||
|
|
dd5d34f7aa | ||
|
|
5685c8430f | ||
|
|
723a195d89 | ||
|
|
68a20cba27 | ||
|
|
27802e54e6 | ||
|
|
3e91ca3bc3 | ||
|
|
5a4e98bd71 | ||
|
|
225fe66afa | ||
|
|
619f0ca3e9 | ||
|
|
fa3f4cd06b | ||
|
|
7694d2e676 | ||
|
|
ea6792f21b | ||
|
|
5e4dace755 | ||
|
|
276dbf5d42 | ||
|
|
0ca2685bf3 | ||
|
|
4bab97ec62 | ||
|
|
47bd5f6291 | ||
|
|
7ee1441b8e | ||
|
|
034270a119 | ||
|
|
9e4bff03d9 | ||
|
|
29b9954954 | ||
|
|
3aa62b9eeb | ||
|
|
d8bd976204 | ||
|
|
3ccb8f5dc8 | ||
|
|
cb05990260 | ||
|
|
8a2c21c88c | ||
|
|
204cd81462 | ||
|
|
71505b5419 | ||
|
|
50e958e2e3 | ||
|
|
3ffab58cbf | ||
|
|
febb9e2fe0 | ||
|
|
b1af1b7254 | ||
|
|
32a4da1826 | ||
|
|
cc44eef174 | ||
|
|
c608a5890a | ||
|
|
52253908eb | ||
|
|
a50956cdb0 | ||
|
|
ee55e4b216 | ||
|
|
9d041926d7 | ||
|
|
72ea2de25c | ||
|
|
4b559c2278 | ||
|
|
327ecf1aed | ||
|
|
8fc893ce08 | ||
|
|
0ec0e10c0f | ||
|
|
38c7a01743 | ||
|
|
a481b5ce60 | ||
|
|
7786db1040 | ||
|
|
200aa0d319 | ||
|
|
c8bb543907 | ||
|
|
de7ecf45d1 | ||
|
|
f25298ed9e | ||
|
|
d4ea2fbd93 | ||
|
|
79c51fb21a | ||
|
|
c57c9b5f18 | ||
|
|
823cd73d33 | ||
|
|
54db4b6e34 | ||
|
|
112399d67c | ||
|
|
01b80d8798 | ||
|
|
f6a3f36a13 | ||
|
|
d674e23987 | ||
|
|
51aebf5e40 | ||
|
|
2ec6d2f1d7 | ||
|
|
9f94917a2d | ||
|
|
06f2104750 | ||
|
|
b1e1e36236 | ||
|
|
9eb2056ac6 | ||
|
|
0c3cb22e83 | ||
|
|
92f27ba273 | ||
|
|
47d682e7c1 | ||
|
|
75d84b67ff | ||
|
|
be8f5ca5da | ||
|
|
c7910a6183 | ||
|
|
98afc28762 | ||
|
|
4cd1349a5a | ||
|
|
cebac64264 | ||
|
|
172125e03b | ||
|
|
15006091b4 | ||
|
|
2175f1d957 | ||
|
|
d445cbfe25 | ||
|
|
5fc56c342d | ||
|
|
e5350d03a8 | ||
|
|
097daf3dc1 | ||
|
|
3c3dc85187 | ||
|
|
a775a667f6 | ||
|
|
a1a7a69aeb | ||
|
|
ff549ab337 | ||
|
|
464651503e | ||
|
|
2f93250c8e | ||
|
|
22ce7ba750 | ||
|
|
acbb09f6a1 | ||
|
|
900dd23ae7 | ||
|
|
0f391776aa | ||
|
|
817fb613ab | ||
|
|
2f57ea0160 | ||
|
|
f5d5aedf9d | ||
|
|
c5114e9bf1 | ||
|
|
7514aed437 | ||
|
|
f4d381c9d4 | ||
|
|
8698036f7c | ||
|
|
21f04cfd46 | ||
|
|
074cf62cc9 | ||
|
|
a66f833c90 | ||
|
|
d704d23d39 | ||
|
|
e2570aec4a | ||
|
|
6b97fde9ab | ||
|
|
30adf1bcbf | ||
|
|
f205b49a54 | ||
|
|
5c7e18696e | ||
|
|
c78b00b0fc | ||
|
|
b05d6e1e1b | ||
|
|
da0bce655c | ||
|
|
d4dcbcc9d5 | ||
|
|
f132e5b87b | ||
|
|
872475e82b | ||
|
|
66d062bfda | ||
|
|
a514ceb62f | ||
|
|
b0d8a5bc41 | ||
|
|
8f4bb5130e | ||
|
|
9386589558 | ||
|
|
5945289a81 | ||
|
|
53346cc85b | ||
|
|
a6fad3ac4e | ||
|
|
cb7e2b5d5c | ||
|
|
0f764416a3 | ||
|
|
c9e0012bf7 | ||
|
|
457f2a677a | ||
|
|
ced56ee41f | ||
|
|
4e910f1f22 | ||
|
|
34bceebace | ||
|
|
b4f802ba77 | ||
|
|
3d9d464870 | ||
|
|
a7e21427f9 | ||
|
|
bdbf10294b | ||
|
|
8588430cf0 | ||
|
|
d920bb5c17 | ||
|
|
64fe9bf59d | ||
|
|
101b0bf41f | ||
|
|
019865b5ee | ||
|
|
799d34a1f1 | ||
|
|
951d533ed3 | ||
|
|
459eb0ef7b | ||
|
|
f710105bd9 | ||
|
|
4998e3ce40 | ||
|
|
d739b374a4 | ||
|
|
bd61ebcbb6 | ||
|
|
3963275092 | ||
|
|
a796e0fe67 | ||
|
|
52ac61fa1d | ||
|
|
29ca7665b1 | ||
|
|
d9bdb814f4 | ||
|
|
3dfb094a08 | ||
|
|
7ce1818a7f | ||
|
|
6e6eff7991 | ||
|
|
aa9c98f081 | ||
|
|
cdf0ad45f0 | ||
|
|
1b97b6673f | ||
|
|
721d432bc6 | ||
|
|
e513a784eb | ||
|
|
ff8da78992 | ||
|
|
84a69ce982 | ||
|
|
7d743f8678 | ||
|
|
3f24e15392 | ||
|
|
0f86bb6874 | ||
|
|
05ee7a2ab2 | ||
|
|
b9321433d0 | ||
|
|
62791401e9 | ||
|
|
4d28bba9cc | ||
|
|
1f3660e1f3 | ||
|
|
95ff44e205 | ||
|
|
987a164092 | ||
|
|
1b5b11fd5b | ||
|
|
ddcde52e27 | ||
|
|
ea2da84535 | ||
|
|
abed8ee574 | ||
|
|
556c59a9c7 | ||
|
|
ad2d085b24 | ||
|
|
d7209102b8 | ||
|
|
14e1ba6795 | ||
|
|
65b56081ce | ||
|
|
eafb51cbfa | ||
|
|
3444ae296e | ||
|
|
4f2fff5da6 | ||
|
|
144483a45d | ||
|
|
53345b2bb0 | ||
|
|
5d6bc429b0 | ||
|
|
a4faf61613 | ||
|
|
fdd516598d | ||
|
|
70da6990c4 | ||
|
|
e8b18d0805 | ||
|
|
1ca9e8678c | ||
|
|
94670149fe | ||
|
|
8e253c2972 | ||
|
|
6e15c6d9cb | ||
|
|
ca00e90ad6 | ||
|
|
b1d76ea661 | ||
|
|
059d428cfa | ||
|
|
a4b50eb357 | ||
|
|
0dfd5aa11d | ||
|
|
2b63ccd145 | ||
|
|
97cec5ec38 | ||
|
|
31fb5a3616 | ||
|
|
7b7130d67d | ||
|
|
6707ad55ff | ||
|
|
89aa73b63a | ||
|
|
bb7453a002 | ||
|
|
8cea29dcb0 | ||
|
|
d5d74ec8d6 | ||
|
|
9b000b94ac | ||
|
|
82ed9fcc83 | ||
|
|
cd53283935 | ||
|
|
3faddf76f8 | ||
|
|
6e24ae2f4f | ||
|
|
66912580c8 | ||
|
|
219dd830e1 | ||
|
|
3d5cdcc78e | ||
|
|
ab1d43fae3 | ||
|
|
2d51f3c23d | ||
|
|
f9447cf7aa | ||
|
|
c6ed0bfc7d | ||
|
|
2d4621b1a6 | ||
|
|
3bf929b4a5 | ||
|
|
ebed01ba86 | ||
|
|
880d005347 | ||
|
|
e8ef216de8 | ||
|
|
baa453594a | ||
|
|
9c767d5c49 | ||
|
|
e0892bd2fa | ||
|
|
315be7c465 | ||
|
|
9d34dc0954 | ||
|
|
bdedd43e3c | ||
|
|
97e779675e | ||
|
|
2ef4216886 | ||
|
|
8c9764a57e | ||
|
|
eb94eb1a6d | ||
|
|
12e3fb99eb | ||
|
|
2bd90a26a8 | ||
|
|
24db3a4cb1 | ||
|
|
0d05ea1730 | ||
|
|
9c13b3c1bf | ||
|
|
492d9d16eb | ||
|
|
c4cd0eb683 | ||
|
|
287f7b9818 | ||
|
|
15b5a3e0c1 | ||
|
|
cf93c43ef2 | ||
|
|
380c24537b | ||
|
|
6200bc7e02 | ||
|
|
e92465c894 | ||
|
|
6f432d221a | ||
|
|
b8ddca4e76 | ||
|
|
f343ee9f7f | ||
|
|
9a8c4c6fdd | ||
|
|
96f2dc0454 | ||
|
|
e0988ef186 | ||
|
|
a154174fa1 | ||
|
|
042861f065 | ||
|
|
0f776af050 | ||
|
|
166e50924f | ||
|
|
efb34ede56 | ||
|
|
22b55eb223 | ||
|
|
f5d9027206 | ||
|
|
9dcbeff21a | ||
|
|
0cc7c4d0a2 | ||
|
|
b0ca65b720 | ||
|
|
e3eb3eb7bf | ||
|
|
b8b2c92227 | ||
|
|
424dde5da6 | ||
|
|
ce7e636987 | ||
|
|
c51f61ba56 | ||
|
|
91623bcf7a | ||
|
|
34e14a3521 | ||
|
|
6c1a79b342 | ||
|
|
6af5014866 | ||
|
|
16f53dc1b1 | ||
|
|
df09fe0684 | ||
|
|
e765764cea | ||
|
|
2e63bcb5c3 | ||
|
|
e231f0c34e | ||
|
|
8155fe7b3f | ||
|
|
6fe1bcdff2 | ||
|
|
b6b488bd46 | ||
|
|
d97cc0cadc | ||
|
|
2a3c742be9 | ||
|
|
cdee86dbcb | ||
|
|
60264e2bfc | ||
|
|
19fb315500 | ||
|
|
66710659c9 | ||
|
|
de5a34c485 | ||
|
|
e0c7ba3874 | ||
|
|
9e58fbee56 | ||
|
|
5315941f88 | ||
|
|
3ffeb0250b | ||
|
|
451e6bb406 | ||
|
|
58d680bd59 | ||
|
|
79eb708adb | ||
|
|
1282e62781 | ||
|
|
4af501feee | ||
|
|
ef7c24f6de | ||
|
|
62edd02a22 | ||
|
|
6f22edc8f3 | ||
|
|
27bba1c2da | ||
|
|
c95bc22f0e | ||
|
|
844bab253d | ||
|
|
b8372487cf | ||
|
|
8cc38466e6 | ||
|
|
14935b1a52 | ||
|
|
f7b14cd9ba | ||
|
|
9d1e61ef8d | ||
|
|
8c912c1b3e | ||
|
|
1ff6ae0195 | ||
|
|
641bf456fa | ||
|
|
f21eaabb15 | ||
|
|
7d38336e67 | ||
|
|
f37ec78d10 | ||
|
|
eb0f92dcd0 | ||
|
|
ee06235cf4 | ||
|
|
48dfe9054d | ||
|
|
c7e39e67aa | ||
|
|
86e51a11b7 | ||
|
|
3c0e98280d | ||
|
|
c54c9f7955 | ||
|
|
614c404bf4 | ||
|
|
c1ebcd49fc | ||
|
|
49551614fe | ||
|
|
b2b8603f23 | ||
|
|
4a6ecda8f8 | ||
|
|
b65d3c25e1 | ||
|
|
65f5ec1c38 | ||
|
|
b8b225eb84 | ||
|
|
c20239b9e8 | ||
|
|
a8df7228b3 | ||
|
|
b8ebc0efd8 | ||
|
|
5ee86faf80 | ||
|
|
3d6524b2fa | ||
|
|
82b671b234 | ||
|
|
3ad6de784f | ||
|
|
6a9e1d7ad7 | ||
|
|
84f182d74f | ||
|
|
d08402c502 | ||
|
|
b6a7c38a70 | ||
|
|
9513c1e404 | ||
|
|
d67d8b9784 | ||
|
|
fdc643bac0 | ||
|
|
9e7890a1a6 | ||
|
|
e9d876e5c9 | ||
|
|
524c15b824 | ||
|
|
0891ccb0af | ||
|
|
179288fa25 | ||
|
|
4a8b6df484 | ||
|
|
73b5e1a8cc | ||
|
|
8fa012cb73 | ||
|
|
8561c606eb | ||
|
|
abed097cad | ||
|
|
ccd7e6db18 | ||
|
|
e6c648c03e | ||
|
|
a4278cb6f2 | ||
|
|
ce9ddc049e | ||
|
|
3646fd868a | ||
|
|
288428c874 | ||
|
|
1f30d13454 | ||
|
|
6712a495ff | ||
|
|
8ac26578e3 | ||
|
|
049c7abb81 | ||
|
|
123e0066d7 | ||
|
|
3cff277132 | ||
|
|
ebb6a55ef8 | ||
|
|
a2d371fcfc | ||
|
|
b1d30c4f03 | ||
|
|
67911c12f6 | ||
|
|
25db6649f3 | ||
|
|
6a5d9df1a4 | ||
|
|
8254600b5c | ||
|
|
8b0d43a5ee | ||
|
|
99b1827048 | ||
|
|
8368f4f762 | ||
|
|
eda94bd881 | ||
|
|
2c8b3e6089 | ||
|
|
d9376ee870 | ||
|
|
94473eaac6 | ||
|
|
ff2caaac1c | ||
|
|
2fc7d91b6b | ||
|
|
f74ef641d8 | ||
|
|
1aa23d2992 | ||
|
|
2fd86301ce | ||
|
|
2c2a6a083b | ||
|
|
3462d7e6b3 | ||
|
|
d9a56d5cc5 | ||
|
|
db8a50edab | ||
|
|
e815438212 | ||
|
|
a31ce39009 | ||
|
|
8d8abf0f90 | ||
|
|
c9faf440ce | ||
|
|
14aec4f6d0 | ||
|
|
5b11135abb | ||
|
|
3ce4ad3770 | ||
|
|
65bc952a5a | ||
|
|
01be354271 | ||
|
|
2634c7c3c8 | ||
|
|
30789f86f6 | ||
|
|
404028bb59 | ||
|
|
ea25b9ea79 | ||
|
|
423940ad3b | ||
|
|
44d8ba6c40 | ||
|
|
87ea1f1727 | ||
|
|
76c70166ee | ||
|
|
40706f96a3 | ||
|
|
f04bcd61f7 | ||
|
|
d415e97672 | ||
|
|
e22a6d987c | ||
|
|
ba60421024 | ||
|
|
0dd7d75878 | ||
|
|
e77afba638 | ||
|
|
2d69336245 | ||
|
|
934f3c594e | ||
|
|
4cb56f80c6 | ||
|
|
212f19ba40 | ||
|
|
9e4f052653 | ||
|
|
afa85afebf | ||
|
|
c6514be984 | ||
|
|
719323d3c7 | ||
|
|
47fd3733b2 | ||
|
|
d7a6241a90 | ||
|
|
ce37e37fb6 | ||
|
|
536af1f547 | ||
|
|
7c69a9da0e | ||
|
|
548f72e6fc | ||
|
|
1d5aa0c7aa | ||
|
|
c00631d6dc | ||
|
|
f3e6285828 | ||
|
|
866c39283d |
@@ -22,6 +22,32 @@ common --per_file_copt=src/test/cpp/.*@-Wno-sign-compare
|
||||
common --per_file_copt=src/test/cpp/.*@-Wno-unknown-warning-option
|
||||
common --host_cxxopt="--std=c++23"
|
||||
|
||||
# C++ sanitizer configs for targeted Shardok safety checks.
|
||||
# Example: bazel test --config=asan //src/test/cpp/net/eagle0/shardok/library/...
|
||||
build:asan --compilation_mode=dbg
|
||||
build:asan --strip=never
|
||||
build:asan --copt=-fno-omit-frame-pointer
|
||||
build:asan --copt=-Wno-macro-redefined
|
||||
build:asan --copt=-fsanitize=address
|
||||
build:asan --linkopt=-fsanitize=address
|
||||
test:asan --test_env=ASAN_OPTIONS=detect_leaks=0:strict_init_order=1
|
||||
|
||||
build:ubsan --compilation_mode=dbg
|
||||
build:ubsan --strip=never
|
||||
build:ubsan --copt=-fno-omit-frame-pointer
|
||||
build:ubsan --copt=-Wno-macro-redefined
|
||||
build:ubsan --copt=-fsanitize=undefined
|
||||
build:ubsan --linkopt=-fsanitize=undefined
|
||||
test:ubsan --test_env=UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1
|
||||
|
||||
build:tsan --compilation_mode=dbg
|
||||
build:tsan --strip=never
|
||||
build:tsan --copt=-fno-omit-frame-pointer
|
||||
build:tsan --copt=-Wno-macro-redefined
|
||||
build:tsan --copt=-fsanitize=thread
|
||||
build:tsan --linkopt=-fsanitize=thread
|
||||
test:tsan --test_env=TSAN_OPTIONS=halt_on_error=1
|
||||
|
||||
common --javacopt="-Xlint:-options"
|
||||
|
||||
# Prefer protobuf's prebuilt protoc toolchain instead of building protoc from
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
Checks: >
|
||||
-*,
|
||||
bugprone-*,
|
||||
performance-*,
|
||||
readability-*,
|
||||
modernize-*,
|
||||
cppcoreguidelines-*,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cppcoreguidelines-pro-type-const-cast,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-modernize-use-trailing-return-type,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-identifier-length,
|
||||
-readability-magic-numbers
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: 'src/(main|test)/cpp/net/eagle0/(shardok|common)/.*'
|
||||
FormatStyle: file
|
||||
CheckOptions:
|
||||
readability-braces-around-statements.ShortStatementLines: '1'
|
||||
readability-function-size.LineThreshold: '160'
|
||||
readability-function-size.StatementThreshold: '80'
|
||||
readability-function-size.BranchThreshold: '20'
|
||||
modernize-use-nullptr.NullMacros: 'NULL'
|
||||
@@ -255,7 +255,7 @@ jobs:
|
||||
echo "=== All images pushed successfully ==="
|
||||
|
||||
deploy:
|
||||
runs-on: [self-hosted, bazel]
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-all]
|
||||
if: needs.build-all.outputs.skip_build != 'true' && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true'))
|
||||
environment: production
|
||||
@@ -334,25 +334,11 @@ jobs:
|
||||
ssh-keyscan -H "$DO_DROPLET_IP" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
|
||||
- name: Download warmup binary
|
||||
id: download-warmup
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: warmup-binary
|
||||
path: scripts/bin/
|
||||
|
||||
- name: Build warmup binary fallback
|
||||
if: steps.download-warmup.outcome != 'success'
|
||||
run: |
|
||||
echo "::warning::warmup-binary artifact was unavailable; rebuilding warmup binary before deploy"
|
||||
./ci/github_actions/ensure_bazel_installed.sh
|
||||
bazel build \
|
||||
--platforms=//:linux_x86_64 \
|
||||
--extra_toolchains=@llvm_toolchain_linux//:cc-toolchain-x86_64-linux \
|
||||
//ci:warmup_tar
|
||||
mkdir -p scripts/bin
|
||||
tar -xf bazel-bin/ci/warmup_tar.tar -C scripts/bin --strip-components=1
|
||||
|
||||
- name: Copy config files to droplet
|
||||
run: |
|
||||
# Create directory structure on remote
|
||||
|
||||
@@ -177,10 +177,12 @@ jobs:
|
||||
env:
|
||||
DO_CDN_PAT: ${{ secrets.DO_CDN_PAT }}
|
||||
run: |
|
||||
UNITY_VERSION=$(grep "m_EditorVersion:" src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt | head -1 | sed 's/m_EditorVersion: //')
|
||||
UNITY_MAJOR_MINOR=$(echo "$UNITY_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1.\2/')
|
||||
curl -s -X DELETE "https://api.digitalocean.com/v2/cdn/endpoints/8c98df29-6f0c-4704-8e82-ca40a2b81aa8/cache" \
|
||||
-H "Authorization: Bearer $DO_CDN_PAT" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"files": ["addressables/iOS/*"]}' \
|
||||
-d "{\"files\": [\"addressables/iOS/$UNITY_MAJOR_MINOR/*\"]}" \
|
||||
--fail || echo "Warning: CDN purge failed (non-fatal)"
|
||||
|
||||
- name: Archive Build Log
|
||||
|
||||
@@ -267,6 +267,7 @@ jobs:
|
||||
runs-on: [self-hosted, macOS, notarize]
|
||||
outputs:
|
||||
deployed_version: ${{ steps.deploy-mac.outputs.deployed_version }}
|
||||
deployed_unity_major_minor: ${{ steps.deploy-mac.outputs.deployed_unity_major_minor }}
|
||||
concurrency:
|
||||
group: mac-deploy-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
@@ -495,6 +496,9 @@ jobs:
|
||||
rm "$SPARKLE_PRIVATE_KEY_PATH"
|
||||
|
||||
echo "deployed_version=$VERSION" >> $GITHUB_OUTPUT
|
||||
UNITY_VERSION=$(grep "m_EditorVersion:" src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt | head -1 | sed 's/m_EditorVersion: //')
|
||||
UNITY_MAJOR_MINOR=$(echo "$UNITY_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1.\2/')
|
||||
echo "deployed_unity_major_minor=$UNITY_MAJOR_MINOR" >> $GITHUB_OUTPUT
|
||||
|
||||
# Artifact cleanup is handled by the cleanup job on ubuntu-latest
|
||||
- name: Cleanup build directory
|
||||
@@ -515,7 +519,7 @@ jobs:
|
||||
curl -s -X DELETE "https://api.digitalocean.com/v2/cdn/endpoints/8c98df29-6f0c-4704-8e82-ca40a2b81aa8/cache" \
|
||||
-H "Authorization: Bearer $DO_CDN_PAT" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"files": ["mac/*", "addressables/StandaloneOSX/*"]}' \
|
||||
-d '{"files": ["mac/*", "addressables/StandaloneOSX/${{ needs.deploy-mac.outputs.deployed_unity_major_minor }}/*"]}' \
|
||||
--fail || echo "Warning: CDN purge failed (non-fatal)"
|
||||
|
||||
curl -X POST "https://admin.eagle0.net/notify-update?platform=mac&version=${{ needs.deploy-mac.outputs.deployed_version }}&required=false" \
|
||||
|
||||
@@ -20,6 +20,7 @@ on:
|
||||
- 'ci/github_actions/ensure_bazel_installed.sh'
|
||||
- 'scripts/build_eagle_ci.sh'
|
||||
- 'scripts/build_shardok_ci.sh'
|
||||
- 'scripts/build_shardok_linux_arm64_ci.sh'
|
||||
- '.github/actions/setup-bazel/**'
|
||||
- '.github/workflows/server_build.yml'
|
||||
|
||||
@@ -83,6 +84,7 @@ jobs:
|
||||
'src/main/cpp/',
|
||||
'src/main/protobuf/net/eagle0/shardok/',
|
||||
'scripts/build_shardok_ci.sh',
|
||||
'scripts/build_shardok_linux_arm64_ci.sh',
|
||||
];
|
||||
|
||||
const shared = matches(sharedPatterns);
|
||||
@@ -106,6 +108,10 @@ jobs:
|
||||
name: 'Shardok server',
|
||||
command: './scripts/build_shardok_ci.sh',
|
||||
});
|
||||
include.push({
|
||||
name: 'Shardok Linux ARM64 server',
|
||||
command: './scripts/build_shardok_linux_arm64_ci.sh',
|
||||
});
|
||||
}
|
||||
core.setOutput('build_matrix', JSON.stringify({include}));
|
||||
|
||||
|
||||
@@ -196,7 +196,9 @@ jobs:
|
||||
echo "Also tagged as: registry.digitalocean.com/eagle0/shardok-server:arm64-latest"
|
||||
|
||||
deploy-hetzner:
|
||||
runs-on: [self-hosted, bazel]
|
||||
# Keep this on a self-hosted Mac runner for IPv6 reachability to Hetzner,
|
||||
# but do not require a Bazel runner slot for artifact transport/deploy work.
|
||||
runs-on: [self-hosted, macOS, ARM64]
|
||||
needs: [build-shardok-arm64]
|
||||
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
|
||||
environment: production
|
||||
|
||||
@@ -287,7 +287,7 @@ jobs:
|
||||
|
||||
./ci/github_actions/detect_addressables_changes.sh "$BASE_SHA" "${{ github.sha }}"
|
||||
- name: Build Windows unity
|
||||
run: ./ci/github_actions/build_unity.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0WIN" "${{ steps.addressables.outputs.changed }}" "${{ steps.addressables.outputs.changed }}"
|
||||
run: ./ci/github_actions/build_unity.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0WIN" true "${{ steps.addressables.outputs.changed }}"
|
||||
|
||||
- name: Save build SHA for Bee/ cache invalidation
|
||||
if: success()
|
||||
|
||||
@@ -29,6 +29,16 @@ If you catch yourself about to run `git push origin main` or `git push origin <b
|
||||
|
||||
If you catch yourself about to run `gh pr merge`, STOP. Only the user merges PRs.
|
||||
|
||||
## Commit History During Review
|
||||
|
||||
Prefer new follow-up commits over amending existing commits once a PR is open and the user is actively reviewing or
|
||||
testing it. This preserves working interim states so the user can inspect, compare, or return to a known-good point
|
||||
while improvements continue.
|
||||
|
||||
Use `git commit --amend` and force-push only when the user explicitly asks for history cleanup, when fixing metadata
|
||||
before review has started, or when repairing a local commit that has not been pushed. If you believe squashing is the
|
||||
right choice despite an open review, explain why and ask first.
|
||||
|
||||
## Worktree Usage
|
||||
|
||||
Prefer using the current worktree for small requested fixes. Create a separate worktree only when the user asks for one,
|
||||
@@ -58,6 +68,15 @@ For PRs expected to trigger substantial Bazel work in CI, run the relevant Bazel
|
||||
workstation is fast, and local Bazel runs can help hydrate the remote cache for the CI builders while still giving
|
||||
earlier signal on failures.
|
||||
|
||||
## Cleanup PR Batching
|
||||
|
||||
When making mechanical cleanup changes, group 3-5 files per PR when the files are receiving the same kind of change
|
||||
and can be reviewed as one pattern. Do not open one PR per file for nearly identical few-line cleanups, such as replacing
|
||||
the same unsafe accessor pattern with the same contextual failure pattern across multiple Scala files.
|
||||
|
||||
Keep separate PRs for changes that involve different semantics, materially different risk, unrelated subsystems, or
|
||||
files whose tests/validation strategy makes them better reviewed independently.
|
||||
|
||||
## GitHub CLI PR Bodies
|
||||
|
||||
When creating or editing PR descriptions with multiline bodies, write the body to a temporary markdown file and pass it
|
||||
|
||||
+1
-1
@@ -282,7 +282,7 @@ oci.pull(
|
||||
# Base image for Admin Server (Alpine for lightweight Go binary)
|
||||
oci.pull(
|
||||
name = "alpine_linux",
|
||||
digest = "sha256:a2d49ea686c2adfe3c992e47dc3b5e7fa6e6b5055609400dc2acaeb241c829f4",
|
||||
digest = "sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b",
|
||||
image = "docker.io/library/alpine",
|
||||
platforms = ["linux/amd64"],
|
||||
tag = "3.24",
|
||||
|
||||
Generated
+8
-8
@@ -904,7 +904,7 @@
|
||||
"@@rules_oci+//oci:extensions.bzl%oci": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "H23W9dvwCQM6h+Wt8PjIahx08EkAU5eSbUxAyx9Mopc=",
|
||||
"usagesDigest": "tbQ7wHHObzzrBzSRv652104GY3xxiaQDK5OUp2YeEbg=",
|
||||
"usagesDigest": "k6DBHAkb5br/tpwCPUMJgqwb+7U4DXyCmBZQBgLnHJ8=",
|
||||
"recordedInputs": [
|
||||
"REPO_MAPPING:aspect_bazel_lib+,bazel_tools bazel_tools",
|
||||
"REPO_MAPPING:bazel_features+,bazel_tools bazel_tools",
|
||||
@@ -922,7 +922,7 @@
|
||||
"scheme": "https",
|
||||
"registry": "index.docker.io",
|
||||
"repository": "library/eclipse-temurin",
|
||||
"identifier": "sha256:db3011b348ad87ced055afe5145238d87a3d8f0e8f23eb4d0cd8cecb4659b085",
|
||||
"identifier": "sha256:0beb258e10ad28d2bae0f74349a170bdfa67da0ef87c028cbf3d95c3e1170120",
|
||||
"platform": "linux/amd64",
|
||||
"target_name": "eclipse_temurin_25_jre_linux_amd64",
|
||||
"bazel_tags": []
|
||||
@@ -936,7 +936,7 @@
|
||||
"scheme": "https",
|
||||
"registry": "index.docker.io",
|
||||
"repository": "library/eclipse-temurin",
|
||||
"identifier": "sha256:db3011b348ad87ced055afe5145238d87a3d8f0e8f23eb4d0cd8cecb4659b085",
|
||||
"identifier": "sha256:0beb258e10ad28d2bae0f74349a170bdfa67da0ef87c028cbf3d95c3e1170120",
|
||||
"platforms": {
|
||||
"@@platforms//cpu:x86_64": "@eclipse_temurin_25_jre_linux_amd64"
|
||||
},
|
||||
@@ -980,7 +980,7 @@
|
||||
"scheme": "https",
|
||||
"registry": "index.docker.io",
|
||||
"repository": "library/ubuntu",
|
||||
"identifier": "24.04",
|
||||
"identifier": "26.04",
|
||||
"platform": "linux/amd64",
|
||||
"target_name": "ubuntu_24_04_linux_amd64",
|
||||
"bazel_tags": []
|
||||
@@ -993,7 +993,7 @@
|
||||
"scheme": "https",
|
||||
"registry": "index.docker.io",
|
||||
"repository": "library/ubuntu",
|
||||
"identifier": "24.04",
|
||||
"identifier": "26.04",
|
||||
"platform": "linux/arm64/v8",
|
||||
"target_name": "ubuntu_24_04_linux_arm64_v8",
|
||||
"bazel_tags": []
|
||||
@@ -1007,7 +1007,7 @@
|
||||
"scheme": "https",
|
||||
"registry": "index.docker.io",
|
||||
"repository": "library/ubuntu",
|
||||
"identifier": "24.04",
|
||||
"identifier": "26.04",
|
||||
"platforms": {
|
||||
"@@platforms//cpu:x86_64": "@ubuntu_24_04_linux_amd64",
|
||||
"@@platforms//cpu:arm64": "@ubuntu_24_04_linux_arm64_v8"
|
||||
@@ -1023,7 +1023,7 @@
|
||||
"scheme": "https",
|
||||
"registry": "index.docker.io",
|
||||
"repository": "library/alpine",
|
||||
"identifier": "sha256:a2d49ea686c2adfe3c992e47dc3b5e7fa6e6b5055609400dc2acaeb241c829f4",
|
||||
"identifier": "sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b",
|
||||
"platform": "linux/amd64",
|
||||
"target_name": "alpine_linux_linux_amd64",
|
||||
"bazel_tags": []
|
||||
@@ -1037,7 +1037,7 @@
|
||||
"scheme": "https",
|
||||
"registry": "index.docker.io",
|
||||
"repository": "library/alpine",
|
||||
"identifier": "sha256:a2d49ea686c2adfe3c992e47dc3b5e7fa6e6b5055609400dc2acaeb241c829f4",
|
||||
"identifier": "sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b",
|
||||
"platforms": {
|
||||
"@@platforms//cpu:x86_64": "@alpine_linux_linux_amd64"
|
||||
},
|
||||
|
||||
@@ -5,8 +5,7 @@ set -x
|
||||
|
||||
COMMAND_LOG="$(bazel info command_log)"
|
||||
bazel build \
|
||||
//src/main/cpp/net/eagle0/shardok:shardok-server \
|
||||
//src/main/cpp/net/eagle0/shardok:shardok-inspector
|
||||
//src/main/cpp/net/eagle0/shardok:shardok-server
|
||||
STATUS="$?"
|
||||
|
||||
if [ $STATUS -eq 0 ]
|
||||
@@ -26,7 +25,6 @@ echo "Setting up Shardok files..."
|
||||
/bin/cp -R ./bazel-bin/src/main/cpp/net/eagle0/shardok/shardok-server ./shardok/
|
||||
/bin/mkdir -p ./shardok/shardok-server.runfiles/net_eagle0/src/main/
|
||||
/bin/cp -R ./bazel-bin/src/main/cpp/net/eagle0/shardok/shardok-server.runfiles/net_eagle0/src/main/resources ./shardok/shardok-server.runfiles/net_eagle0/src/main/
|
||||
/bin/cp -R ./bazel-bin/src/main/cpp/net/eagle0/shardok/shardok-inspector ./shardok/
|
||||
|
||||
echo "tar & zip shardok files..."
|
||||
/bin/tar cfh - shardok | /usr/bin/pigz > ./deploy/shardok.tar.gz
|
||||
|
||||
@@ -40,19 +40,6 @@ if [ -f "$INFO_PLIST" ]; then
|
||||
/usr/libexec/PlistBuddy -c "Set :ITSAppUsesNonExemptEncryption false" "$INFO_PLIST" 2>/dev/null \
|
||||
|| /usr/libexec/PlistBuddy -c "Add :ITSAppUsesNonExemptEncryption bool false" "$INFO_PLIST"
|
||||
echo "Set ITSAppUsesNonExemptEncryption=false in Info.plist"
|
||||
|
||||
# Remove UIApplicationSceneManifest if present.
|
||||
# Unity 6 generates this key, which opts the app into the iOS scene-based
|
||||
# lifecycle. This breaks Application.deepLinkActivated because iOS routes
|
||||
# deep link URLs through UISceneDelegate instead of UIApplicationDelegate,
|
||||
# and Unity doesn't implement the scene delegate path.
|
||||
# See: https://issuetracker.unity3d.com/issues/in-135632
|
||||
if /usr/libexec/PlistBuddy -c "Print :UIApplicationSceneManifest" "$INFO_PLIST" 2>/dev/null; then
|
||||
echo "Found UIApplicationSceneManifest in Info.plist — removing to fix deep link handling"
|
||||
/usr/libexec/PlistBuddy -c "Delete :UIApplicationSceneManifest" "$INFO_PLIST"
|
||||
else
|
||||
echo "No UIApplicationSceneManifest in Info.plist (deep links should work)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Unity always generates "Unity-iPhone" as the main app scheme
|
||||
|
||||
@@ -59,6 +59,20 @@ if [ $UNITY_EXIT_CODE -ne 0 ]; then
|
||||
exit $UNITY_EXIT_CODE
|
||||
fi
|
||||
|
||||
if [ ! -f "$LOG_PATH" ]; then
|
||||
echo ""
|
||||
echo "ERROR: Unity exited successfully but did not write an editor log at $LOG_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$BUILD_PATH/Data/Raw/aa/settings.json" ]; then
|
||||
echo ""
|
||||
echo "ERROR: iOS player is missing Addressables runtime data at:"
|
||||
echo " $BUILD_PATH/Data/Raw/aa/settings.json"
|
||||
echo "Build Addressables before packaging the player so Addressables can initialize at runtime."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fail the build if any prefab references are broken — this produces a player
|
||||
# that launches but has null Inspector fields, which is hard to debug.
|
||||
if grep -q "Missing Prefab" "$LOG_PATH"; then
|
||||
|
||||
@@ -57,6 +57,14 @@ if [ ! -f "$LOG_PATH" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$BUILD_DIR/eagle0_Data/StreamingAssets/aa/settings.json" ]; then
|
||||
echo ""
|
||||
echo "ERROR: Windows player is missing Addressables runtime data at:"
|
||||
echo " $BUILD_DIR/eagle0_Data/StreamingAssets/aa/settings.json"
|
||||
echo "Build Addressables before packaging the player so Addressables can initialize at runtime."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fail the build if any prefab references are broken — this produces a player
|
||||
# that launches but has null Inspector fields, which is hard to debug.
|
||||
if grep -q "Missing Prefab" "$LOG_PATH"; then
|
||||
|
||||
@@ -13,7 +13,10 @@ set -euxo pipefail
|
||||
BUILD_TARGET=$1
|
||||
WORKSPACE=$(pwd)
|
||||
UNITY_PROJECT="$WORKSPACE/src/main/csharp/net/eagle0/clients/unity/eagle0"
|
||||
SERVER_DATA="$UNITY_PROJECT/ServerData/$BUILD_TARGET"
|
||||
UNITY_VERSION=$(grep "m_EditorVersion:" "$UNITY_PROJECT/ProjectSettings/ProjectVersion.txt" | head -1 | sed 's/m_EditorVersion: //')
|
||||
UNITY_MAJOR_MINOR=$(echo "$UNITY_VERSION" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1.\2/')
|
||||
ADDRESSABLES_PREFIX="addressables/$BUILD_TARGET/$UNITY_MAJOR_MINOR"
|
||||
SERVER_DATA="$UNITY_PROJECT/ServerData/$BUILD_TARGET/$UNITY_MAJOR_MINOR"
|
||||
|
||||
# DigitalOcean Spaces configuration (same region as other eagle0 buckets)
|
||||
DO_ENDPOINT="https://sfo3.digitaloceanspaces.com"
|
||||
@@ -75,7 +78,7 @@ summarize_pending_uploads() {
|
||||
sorted_uploads_file=$(mktemp)
|
||||
trap 'rm -f "$sync_args_file" "$uploads_file" "$sorted_uploads_file"' RETURN
|
||||
|
||||
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/addressables/$BUILD_TARGET/" \
|
||||
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
|
||||
--endpoint-url "$DO_ENDPOINT" \
|
||||
--acl public-read \
|
||||
--size-only \
|
||||
@@ -178,7 +181,7 @@ if [ ! -d "$SERVER_DATA" ]; then
|
||||
fi
|
||||
|
||||
echo "Uploading Addressables bundles from $SERVER_DATA"
|
||||
echo "Target: s3://$DO_BUCKET/addressables/$BUILD_TARGET/"
|
||||
echo "Target: s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/"
|
||||
|
||||
# Configure AWS CLI for DigitalOcean Spaces
|
||||
export AWS_ACCESS_KEY_ID="$ACCESS_KEY_ID"
|
||||
@@ -193,13 +196,13 @@ summarize_pending_uploads
|
||||
#
|
||||
# --delete removes files in destination that don't exist in source.
|
||||
# --acl public-read makes files publicly accessible.
|
||||
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/addressables/$BUILD_TARGET/" \
|
||||
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
|
||||
--endpoint-url "$DO_ENDPOINT" \
|
||||
--acl public-read \
|
||||
--size-only \
|
||||
--delete
|
||||
|
||||
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/addressables/$BUILD_TARGET/" \
|
||||
aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/$ADDRESSABLES_PREFIX/" \
|
||||
--endpoint-url "$DO_ENDPOINT" \
|
||||
--acl public-read \
|
||||
--exclude "*" \
|
||||
@@ -208,4 +211,4 @@ aws s3 sync "$SERVER_DATA" "s3://$DO_BUCKET/addressables/$BUILD_TARGET/" \
|
||||
--include "*.json"
|
||||
|
||||
echo "Addressables upload complete"
|
||||
echo "Files available at: https://assets.eagle0.net/addressables/$BUILD_TARGET/"
|
||||
echo "Files available at: https://assets.eagle0.net/$ADDRESSABLES_PREFIX/"
|
||||
|
||||
Binary file not shown.
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
bazel_cmd=(bazel)
|
||||
if [ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]; then
|
||||
bazel_cmd+=("--output_user_root=${BAZEL_OUTPUT_USER_ROOT}")
|
||||
fi
|
||||
if [ -n "${BAZEL_OUTPUT_BASE:-}" ]; then
|
||||
bazel_cmd+=("--output_base=${BAZEL_OUTPUT_BASE}")
|
||||
fi
|
||||
|
||||
"${bazel_cmd[@]}" build \
|
||||
--platforms=//:linux_arm64 \
|
||||
--extra_toolchains=@llvm_toolchain_linux_arm64//:cc-toolchain-aarch64-linux \
|
||||
"$@" \
|
||||
//src/main/cpp/net/eagle0/shardok:shardok-server
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
# Runs the repository-pinned clang-tidy binary from the workspace root.
|
||||
#
|
||||
# Bazel's `run` command executes clang-tidy from its runfiles tree, which makes
|
||||
# workspace-relative source paths and .clang-tidy discovery fail. This wrapper
|
||||
# builds the tool and then invokes the produced binary directly.
|
||||
|
||||
set -e
|
||||
|
||||
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
|
||||
|
||||
repo_root="$(git rev-parse --show-toplevel)"
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "Usage: scripts/run-clang-tidy.sh [clang-tidy args] <files> [-- <compiler args>]"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " scripts/run-clang-tidy.sh --verify-config"
|
||||
echo " scripts/run-clang-tidy.sh src/main/cpp/net/eagle0/shardok/library/CombatDamage.hpp -- -I$repo_root -std=c++23"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
bazel build @llvm_toolchain//:clang-tidy
|
||||
|
||||
bazel_bin="$(bazel info bazel-bin)"
|
||||
clang_tidy="$bazel_bin/external/toolchains_llvm++llvm+llvm_toolchain/clang-tidy"
|
||||
|
||||
if [ ! -x "$clang_tidy" ]; then
|
||||
echo "Unable to find clang-tidy binary at $clang_tidy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
"$clang_tidy" --config-file="$repo_root/.clang-tidy" "$@"
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
|
||||
#include "tools/cpp/runfiles/runfiles.h"
|
||||
@@ -37,7 +38,7 @@ auto rloc(const string& execPath) -> string {
|
||||
const std::unique_ptr<Runfiles> runfiles(Runfiles::Create(execPath, &error));
|
||||
|
||||
if (runfiles == nullptr) {
|
||||
fprintf(stderr, "Error! %s\n", error.c_str());
|
||||
std::cerr << "Error! " << error << '\n';
|
||||
abort();
|
||||
// error handling
|
||||
}
|
||||
@@ -78,9 +79,9 @@ auto FilesystemUtils::MapFilesDirectory() -> string {
|
||||
|
||||
void FilesystemUtils::MakeDirectoryIfNecessary(const string& directoryPath) {
|
||||
if (fs::create_directories(directoryPath))
|
||||
fprintf(stderr, "Directory %s created\n", directoryPath.c_str());
|
||||
std::cerr << "Directory " << directoryPath << " created\n";
|
||||
else
|
||||
fprintf(stderr, "No new directory created for %s\n", directoryPath.c_str());
|
||||
std::cerr << "No new directory created for " << directoryPath << '\n';
|
||||
}
|
||||
|
||||
auto FilesystemUtils::SaveFilesDirectory() -> string {
|
||||
@@ -140,11 +141,11 @@ auto FilesystemUtils::AtomicallySaveToPath(const string& path, const byte_vector
|
||||
if (ostr.good()) {
|
||||
const int err = rename(tempPath.c_str(), path.c_str());
|
||||
if (err == -1) {
|
||||
fprintf(stderr, "Failed to move file to %s! Errno %d\n", path.c_str(), errno);
|
||||
std::cerr << "Failed to move file to " << path << "! Errno " << errno << '\n';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Failed writing to %s!\n", tempPath.c_str());
|
||||
std::cerr << "Failed writing to " << tempPath << "!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// Copyright © 2017 none. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef FilesystemUtils_hpp
|
||||
#define FilesystemUtils_hpp
|
||||
#ifndef EAGLE0_COMMON_FILESYSTEM_UTILS_HPP
|
||||
#define EAGLE0_COMMON_FILESYSTEM_UTILS_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -36,4 +36,4 @@ public:
|
||||
static auto LoadFromPath(const string& path) -> byte_vector;
|
||||
};
|
||||
|
||||
#endif /* FilesystemUtils_hpp */
|
||||
#endif // EAGLE0_COMMON_FILESYSTEM_UTILS_HPP
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// Copyright © 2018 none. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MapUtils_hpp
|
||||
#define MapUtils_hpp
|
||||
#ifndef EAGLE0_COMMON_MAP_UTILS_HPP
|
||||
#define EAGLE0_COMMON_MAP_UTILS_HPP
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -19,4 +19,4 @@ auto IntForKey(const std::unordered_map<std::string, std::string>& map, const st
|
||||
auto BoolForKey(const std::unordered_map<std::string, std::string>& map, const std::string& key)
|
||||
-> bool;
|
||||
|
||||
#endif /* MapUtils_hpp */
|
||||
#endif // EAGLE0_COMMON_MAP_UTILS_HPP
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// Copyright © 2019 none. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ProtobufWarningSuppression_h
|
||||
#define ProtobufWarningSuppression_h
|
||||
#ifndef EAGLE0_COMMON_PROTOBUF_WARNING_SUPPRESSION_HPP
|
||||
#define EAGLE0_COMMON_PROTOBUF_WARNING_SUPPRESSION_HPP
|
||||
|
||||
#define SUPPRESS_PROTOBUF_WARNINGS \
|
||||
_Pragma("GCC diagnostic ignored \"-Wpragmas\"") \
|
||||
@@ -19,4 +19,4 @@
|
||||
"GCC diagnostic ignored \"-Wshorten-64-to-32\"") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wnested-anon-types\"")
|
||||
|
||||
#endif /* ProtobufWarningSuppression_h */
|
||||
#endif // EAGLE0_COMMON_PROTOBUF_WARNING_SUPPRESSION_HPP
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
// Copyright (c) 2015 none. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __eagle0__ShardokRandomGenerator__
|
||||
#define __eagle0__ShardokRandomGenerator__
|
||||
#ifndef EAGLE0_COMMON_RANDOM_GENERATOR_HPP
|
||||
#define EAGLE0_COMMON_RANDOM_GENERATOR_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
class RandomGenerator;
|
||||
|
||||
@@ -22,6 +24,10 @@ private:
|
||||
|
||||
public:
|
||||
RandomGenerator() = default;
|
||||
RandomGenerator(const RandomGenerator&) = default;
|
||||
auto operator=(const RandomGenerator&) -> RandomGenerator& = default;
|
||||
RandomGenerator(RandomGenerator&&) = default;
|
||||
auto operator=(RandomGenerator&&) -> RandomGenerator& = default;
|
||||
|
||||
virtual ~RandomGenerator() = default;
|
||||
|
||||
@@ -35,7 +41,7 @@ public:
|
||||
|
||||
template<class T>
|
||||
auto RandomElement(const std::vector<T>& vec) -> T {
|
||||
return vec[IntBelow((int)vec.size())];
|
||||
return vec.at(static_cast<std::size_t>(IntBelow(static_cast<int>(vec.size()))));
|
||||
}
|
||||
|
||||
static auto ChanceOpenEndedPercentileAtOrAbove(double value) -> double;
|
||||
@@ -56,4 +62,4 @@ public:
|
||||
auto IntBetween(int min, int max) -> int override; // inclusive, exclusive (min<=return<max)
|
||||
};
|
||||
|
||||
#endif /* defined(__eagle0__ShardokRandomGenerator__) */
|
||||
#endif // EAGLE0_COMMON_RANDOM_GENERATOR_HPP
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
// Copyright © 2018 none. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SequenceRandomGenerator_h
|
||||
#define SequenceRandomGenerator_h
|
||||
#ifndef EAGLE0_COMMON_SEQUENCE_RANDOM_GENERATOR_HPP
|
||||
#define EAGLE0_COMMON_SEQUENCE_RANDOM_GENERATOR_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/common/RandomGenerator.hpp"
|
||||
@@ -24,22 +25,22 @@
|
||||
// which produces: initial=2 (triggers open-ended), accumulated=52, final=2-52=-50
|
||||
class SequenceRandomGenerator : public ::RandomGenerator {
|
||||
private:
|
||||
const std::vector<double> sequence;
|
||||
std::vector<double> sequence;
|
||||
size_t position = 0;
|
||||
|
||||
auto DoubleZeroToOne() -> double override {
|
||||
const double nextVal = sequence[position];
|
||||
const double nextVal = sequence.at(position);
|
||||
position++;
|
||||
if (position >= sequence.size()) { position = 0; }
|
||||
return nextVal;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit SequenceRandomGenerator(std::vector<double> s) : sequence(std::move(s)), position(0) {}
|
||||
explicit SequenceRandomGenerator(std::vector<double> s) : sequence(std::move(s)) {}
|
||||
static auto WithSequence(const std::vector<double>& s)
|
||||
-> std::shared_ptr<SequenceRandomGenerator> {
|
||||
return std::make_shared<SequenceRandomGenerator>(s);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* SequenceRandomGenerator_h */
|
||||
#endif // EAGLE0_COMMON_SEQUENCE_RANDOM_GENERATOR_HPP
|
||||
|
||||
@@ -80,7 +80,7 @@ void EmitShardokLatencyTrace(
|
||||
out << "}";
|
||||
|
||||
std::scoped_lock lock(LogMutex());
|
||||
std::cerr << out.str() << std::endl;
|
||||
std::cerr << out.str() << '\n';
|
||||
}
|
||||
|
||||
ScopedShardokLatencyTrace::ScopedShardokLatencyTrace(
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
@@ -171,7 +172,7 @@ public:
|
||||
// Get detailed queue information for debugging
|
||||
void debug_queue_state() const {
|
||||
std::unique_lock<std::mutex> lock(const_cast<std::mutex&>(queue_mutex));
|
||||
printf("ThreadPool: Queue size: %zu\n", tasks.size());
|
||||
std::cout << "ThreadPool: Queue size: " << tasks.size() << '\n';
|
||||
if (!tasks.empty()) {
|
||||
// Create a copy to inspect priorities without modifying queue
|
||||
auto queue_copy = tasks;
|
||||
@@ -180,9 +181,9 @@ public:
|
||||
priorities.push_back(queue_copy.top().priority);
|
||||
queue_copy.pop();
|
||||
}
|
||||
printf("ThreadPool: Priorities in queue: ");
|
||||
for (int p : priorities) { printf("%d ", p); }
|
||||
printf("\n");
|
||||
std::cout << "ThreadPool: Priorities in queue: ";
|
||||
for (int p : priorities) { std::cout << p << ' '; }
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,4 +198,4 @@ public:
|
||||
|
||||
} // namespace eagle0::common
|
||||
|
||||
#endif // EAGLE0_THREADPOOL_HPP
|
||||
#endif // EAGLE0_THREADPOOL_HPP
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
// Copyright © 2018 none. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef TsvParser_hpp
|
||||
#define TsvParser_hpp
|
||||
#ifndef EAGLE0_COMMON_TSV_PARSER_HPP
|
||||
#define EAGLE0_COMMON_TSV_PARSER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
typedef std::unordered_map<std::string, std::string> StringMap;
|
||||
using StringMap = std::unordered_map<std::string, std::string>;
|
||||
|
||||
// Parses a TSV, with the first column becoming map keys and subsequent columns becoming values
|
||||
// for different maps in the returned vector.
|
||||
@@ -23,4 +23,4 @@ public:
|
||||
std::vector<StringMap> ParseColumnEntryTsv(std::string tsv);
|
||||
};
|
||||
|
||||
#endif /* TsvParser_hpp */
|
||||
#endif // EAGLE0_COMMON_TSV_PARSER_HPP
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// Copyright © 2017 none. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef byte_vector_h
|
||||
#define byte_vector_h
|
||||
#ifndef EAGLE0_COMMON_BYTE_VECTOR_HPP
|
||||
#define EAGLE0_COMMON_BYTE_VECTOR_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@@ -184,4 +184,4 @@ inline auto operator<<(std::ostream& ostr, byte_vector vec) -> std::ostream& {
|
||||
return ostr;
|
||||
}
|
||||
|
||||
#endif /* byte_vector_h */
|
||||
#endif // EAGLE0_COMMON_BYTE_VECTOR_HPP
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
@@ -862,12 +864,12 @@ auto AbstractMCTSAI::LogSearchResults(
|
||||
// Log selected command
|
||||
const std::string selectedDesc =
|
||||
bestChild->action ? bestChild->action->getDescription() : "Unknown";
|
||||
printf("MCTS: Selected %s (visit:%d, reward:%.2f, lookahead:%.2f) from %zu options\n",
|
||||
selectedDesc.c_str(),
|
||||
bestChild->visitCount,
|
||||
bestChild->averageReward,
|
||||
bestChild->lookaheadScore,
|
||||
rootNode->children.size());
|
||||
std::ostringstream selectedLine;
|
||||
selectedLine << std::fixed << std::setprecision(2) << "MCTS: Selected " << selectedDesc
|
||||
<< " (visit:" << bestChild->visitCount << ", reward:" << bestChild->averageReward
|
||||
<< ", lookahead:" << bestChild->lookaheadScore << ") from "
|
||||
<< rootNode->children.size() << " options";
|
||||
std::cout << selectedLine.str() << '\n';
|
||||
|
||||
// Log top 3 actions by visits
|
||||
std::vector<const MCTSNode*> sortedChildren;
|
||||
@@ -879,20 +881,17 @@ auto AbstractMCTSAI::LogSearchResults(
|
||||
|
||||
// Show all actions if there are <= 10, otherwise top 5
|
||||
const size_t numToShow = sortedChildren.size() <= 10 ? sortedChildren.size() : 5;
|
||||
printf("MCTS: Top %zu actions by visits (out of %zu total):\n",
|
||||
numToShow,
|
||||
sortedChildren.size());
|
||||
std::cout << "MCTS: Top " << numToShow << " actions by visits (out of " << sortedChildren.size()
|
||||
<< " total):\n";
|
||||
for (size_t i = 0; i < numToShow; ++i) {
|
||||
const auto* child = sortedChildren[i];
|
||||
printf(" [%zu] visits:%d avgReward:%.2f immediate:%.2f lookahead:%.2f",
|
||||
i,
|
||||
child->visitCount,
|
||||
child->averageReward,
|
||||
child->immediateScore,
|
||||
child->lookaheadScore);
|
||||
std::ostringstream line;
|
||||
line << std::fixed << std::setprecision(2) << " [" << i << "] visits:" << child->visitCount
|
||||
<< " avgReward:" << child->averageReward << " immediate:" << child->immediateScore
|
||||
<< " lookahead:" << child->lookaheadScore;
|
||||
|
||||
// Show the action's own description
|
||||
if (child->action) { printf(" %s", child->action->getDescription().c_str()); }
|
||||
if (child->action) { line << ' ' << child->action->getDescription(); }
|
||||
|
||||
// Show sequence preview
|
||||
if (!child->children.empty()) {
|
||||
@@ -905,16 +904,15 @@ auto AbstractMCTSAI::LogSearchResults(
|
||||
}
|
||||
}
|
||||
if (bestNext && bestNext->action) {
|
||||
printf(" -> %s", bestNext->action->getDescription().c_str());
|
||||
line << " -> " << bestNext->action->getDescription();
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
std::cout << line.str() << '\n';
|
||||
}
|
||||
|
||||
printf("MCTS: Tree stats - max depth:%d, total nodes:%d, root visits:%d\n",
|
||||
result.searchDepth,
|
||||
result.nodesEvaluated,
|
||||
rootNode->visitCount);
|
||||
std::cout << "MCTS: Tree stats - max depth:" << result.searchDepth
|
||||
<< ", total nodes:" << result.nodesEvaluated
|
||||
<< ", root visits:" << rootNode->visitCount << '\n';
|
||||
|
||||
// Log best sequence from chosen action (following most-visited path)
|
||||
std::vector<const MCTSNode*> bestSequence;
|
||||
@@ -940,7 +938,10 @@ auto AbstractMCTSAI::LogSearchResults(
|
||||
}
|
||||
|
||||
if (!bestSequence.empty()) {
|
||||
printf("MCTS: Best sequence from chosen action (final: %.2f):\n", sequenceScore);
|
||||
std::ostringstream sequenceLine;
|
||||
sequenceLine << std::fixed << std::setprecision(2)
|
||||
<< "MCTS: Best sequence from chosen action (final: " << sequenceScore << "):";
|
||||
std::cout << sequenceLine.str() << '\n';
|
||||
int displayedStep = 0;
|
||||
for (size_t i = 0; i < bestSequence.size(); ++i) {
|
||||
const auto* node = bestSequence[i];
|
||||
@@ -949,29 +950,31 @@ auto AbstractMCTSAI::LogSearchResults(
|
||||
if (i > 0 && node->parent && node->parent->IsChanceNode()) { continue; }
|
||||
|
||||
displayedStep++;
|
||||
printf(" %d.", displayedStep);
|
||||
if (node->action) { printf(" %s", node->action->getDescription().c_str()); }
|
||||
std::ostringstream line;
|
||||
line << " " << displayedStep << ".";
|
||||
if (node->action) { line << ' ' << node->action->getDescription(); }
|
||||
|
||||
// If this is a chance node, display outcome probabilities and scores
|
||||
if (node->IsChanceNode() && !node->outcomeProbabilities.empty()) {
|
||||
printf(" [");
|
||||
line << " [";
|
||||
for (size_t j = 0; j < node->outcomeProbabilities.size(); ++j) {
|
||||
if (j > 0) printf(", ");
|
||||
if (j > 0) { line << ", "; }
|
||||
const double prob = node->outcomeProbabilities[j] * 100;
|
||||
// Show lookahead score for each outcome if child exists
|
||||
if (j < node->children.size() && node->children[j]->visitCount > 0) {
|
||||
printf("%.0f%%->%.1f", prob, node->children[j]->lookaheadScore);
|
||||
line << std::fixed << std::setprecision(0) << prob << "%->"
|
||||
<< std::setprecision(1) << node->children[j]->lookaheadScore;
|
||||
} else {
|
||||
printf("%.0f%%->?", prob);
|
||||
line << std::fixed << std::setprecision(0) << prob << "%->?";
|
||||
}
|
||||
}
|
||||
printf("]");
|
||||
line << "]";
|
||||
}
|
||||
|
||||
printf(" (visits:%d, immediate:%.2f, lookahead:%.2f)\n",
|
||||
node->visitCount,
|
||||
node->immediateScore,
|
||||
node->lookaheadScore);
|
||||
line << std::fixed << std::setprecision(2) << " (visits:" << node->visitCount
|
||||
<< ", immediate:" << node->immediateScore << ", lookahead:" << node->lookaheadScore
|
||||
<< ")";
|
||||
std::cout << line.str() << '\n';
|
||||
|
||||
// For non-root nodes in the sequence, show what the top alternatives were
|
||||
// Skip showing alternatives for chance nodes (they have outcome children, not action
|
||||
@@ -991,17 +994,17 @@ auto AbstractMCTSAI::LogSearchResults(
|
||||
});
|
||||
|
||||
// Show top 3 alternatives at this decision point
|
||||
printf(" Alternatives at this node (%zu total):\n", siblings.size());
|
||||
std::cout << " Alternatives at this node (" << siblings.size() << " total):\n";
|
||||
const size_t topN = std::min(siblings.size(), size_t(3));
|
||||
for (size_t j = 0; j < topN; ++j) {
|
||||
const auto* alt = siblings[j];
|
||||
printf(" [%zu] visits:%d immediate:%.2f lookahead:%.2f",
|
||||
j,
|
||||
alt->visitCount,
|
||||
alt->immediateScore,
|
||||
alt->lookaheadScore);
|
||||
if (alt->action) { printf(" %s", alt->action->getDescription().c_str()); }
|
||||
printf("\n");
|
||||
std::ostringstream altLine;
|
||||
altLine << std::fixed << std::setprecision(2) << " [" << j
|
||||
<< "] visits:" << alt->visitCount
|
||||
<< " immediate:" << alt->immediateScore
|
||||
<< " lookahead:" << alt->lookaheadScore;
|
||||
if (alt->action) { altLine << ' ' << alt->action->getDescription(); }
|
||||
std::cout << altLine.str() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1013,7 +1016,7 @@ auto AbstractMCTSAI::DumpTreeToFile(const MCTSNode* root, const std::string& fil
|
||||
|
||||
std::ofstream out(filepath);
|
||||
if (!out) {
|
||||
fprintf(stderr, "Failed to open dump file: %s\n", filepath.c_str());
|
||||
std::cerr << "Failed to open dump file: " << filepath << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1042,7 +1045,7 @@ auto AbstractMCTSAI::DumpTreeToFile(const MCTSNode* root, const std::string& fil
|
||||
out << "\n=== End of Tree Dump ===\n";
|
||||
out.close();
|
||||
|
||||
printf("MCTS: Tree dumped to %s\n", filepath.c_str());
|
||||
std::cout << "MCTS: Tree dumped to " << filepath << '\n';
|
||||
}
|
||||
|
||||
auto AbstractMCTSAI::DumpNodeRecursive(
|
||||
|
||||
@@ -141,9 +141,8 @@ public:
|
||||
// Default implementation: no filtering, so filtered index = original index
|
||||
[[nodiscard]] virtual size_t mapFilteredIndexToOriginal(
|
||||
size_t filteredIndex,
|
||||
const MCTSGameState& state) const {
|
||||
const MCTSGameState& /*state*/) const {
|
||||
// Default: no filtering, index stays the same
|
||||
(void)state; // Suppress unused parameter warning
|
||||
return filteredIndex;
|
||||
}
|
||||
|
||||
@@ -158,4 +157,4 @@ public:
|
||||
} // namespace mcts
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_MCTS_GAME_ENGINE_HPP
|
||||
#endif // EAGLE0_MCTS_GAME_ENGINE_HPP
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
#define EAGLE0_ABSTRACT_MCTSNODE_HPP
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "MCTSAction.hpp"
|
||||
@@ -205,13 +208,13 @@ struct MCTSNode {
|
||||
isMaximizingPlayer ? child->lookaheadScore : -child->lookaheadScore;
|
||||
const double explorationValue =
|
||||
explorationConstant * std::sqrt(std::log(visitCount) / child->visitCount);
|
||||
printf(" UCB: %s lookahead=%.2f expl=%.2f (+%.2f) = %.2f [%s]\n",
|
||||
isMaximizingPlayer ? "MAX" : "MIN",
|
||||
child->lookaheadScore,
|
||||
exploitationValue,
|
||||
explorationValue,
|
||||
value,
|
||||
child->action ? child->action->getDescription().c_str() : "root");
|
||||
std::ostringstream line;
|
||||
line << std::fixed << std::setprecision(2)
|
||||
<< " UCB: " << (isMaximizingPlayer ? "MAX" : "MIN")
|
||||
<< " lookahead=" << child->lookaheadScore << " expl=" << exploitationValue
|
||||
<< " (+" << explorationValue << ") = " << value << " ["
|
||||
<< (child->action ? child->action->getDescription() : "root") << "]";
|
||||
std::cout << line.str() << '\n';
|
||||
}
|
||||
|
||||
if (value > bestValue) {
|
||||
|
||||
@@ -28,36 +28,6 @@ cc_binary(
|
||||
deps = [":shardok-server-lib"],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "shardok-inspector",
|
||||
srcs = ["shardok_inspector.cpp"],
|
||||
copts = COPTS,
|
||||
data = [
|
||||
"//src/main/resources/net/eagle0/shardok:battalion_types",
|
||||
"//src/main/resources/net/eagle0/shardok:settings",
|
||||
"//src/main/resources/net/eagle0/shardok/maps",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//src/main/cpp/net/eagle0/common:byte_vector",
|
||||
"//src/main/cpp/net/eagle0/common:filesystem_utils",
|
||||
"//src/main/cpp/net/eagle0/common:tsv_parser",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:engine",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "corrupt-game-checker",
|
||||
srcs = ["CorruptGameChecker.cpp"],
|
||||
copts = COPTS,
|
||||
deps = [
|
||||
"//src/main/cpp/net/eagle0/common:byte_vector",
|
||||
"//src/main/cpp/net/eagle0/common:filesystem_utils",
|
||||
"//src/main/cpp/net/eagle0/shardok/library:game_state_w",
|
||||
"//src/main/protobuf/net/eagle0/common:shardok_internal_interface_cc_grpc",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "hex-map-migrator",
|
||||
srcs = ["HexMapMigrator.cpp"],
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// Created by Dan Crosby on 1/9/22.
|
||||
//
|
||||
|
||||
#include "src/main/cpp/net/eagle0/common/byte_vector.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
#include "src/main/protobuf/net/eagle0/common/shardok_internal_interface.pb.h"
|
||||
#include "src/main/protobuf/net/eagle0/shardok/storage/game.pb.h"
|
||||
|
||||
auto main(int argc, char** argv) -> int {
|
||||
char* path = argv[1];
|
||||
|
||||
printf("Checking file %s\n", path);
|
||||
|
||||
auto bytes = byte_vector::FromPath(path);
|
||||
|
||||
net::eagle0::shardok::storage::Game game;
|
||||
game.ParseFromArray(bytes.data(), bytes.size());
|
||||
|
||||
printf("The game ID is %s\n", game.game_id().c_str());
|
||||
printf("The size is %lu\n", bytes.size());
|
||||
|
||||
const int arCount = game.action_result_size();
|
||||
printf("There are %d results\n", arCount);
|
||||
|
||||
for (int arIndex = 0; arIndex < arCount; arIndex++) {
|
||||
shardok::GameStateW gameState =
|
||||
shardok::GameStateW::FromByteString(game.action_result(arIndex).state_after_fb());
|
||||
const auto* hexMap = gameState->hex_map();
|
||||
|
||||
for (int terrainIndex = 0; terrainIndex < hexMap->terrain()->size(); terrainIndex++) {
|
||||
const auto* terrain = hexMap->terrain()->Get(terrainIndex);
|
||||
if (terrain->type() == net::eagle0::shardok::storage::fb::Terrain_::Type_STILL_WATER)
|
||||
continue;
|
||||
if (terrain->type() == net::eagle0::shardok::storage::fb::Terrain_::Type_RIVER)
|
||||
continue;
|
||||
|
||||
if (terrain->modifier().bridge().present()) {
|
||||
printf("Caught one! arIndex %d, terrainIndex %d\n", arIndex, terrainIndex);
|
||||
printf("Action result was %s\n",
|
||||
game.action_result(arIndex).action_result().DebugString().c_str());
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Created by Dan Crosby on 3/19/20.
|
||||
//
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
|
||||
#include "src/main/cpp/net/eagle0/common/byte_vector.hpp"
|
||||
@@ -14,7 +14,7 @@ void ModifyMap(HexMapProto& map);
|
||||
|
||||
int main(const int argc, char** argv) {
|
||||
if (argc < 3) {
|
||||
printf("Usage: %s input_path output_path\n", argv[0]);
|
||||
std::cerr << "Usage: " << argv[0] << " input_path output_path\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -22,21 +22,25 @@ int main(const int argc, char** argv) {
|
||||
const std::string outputPath = argv[2];
|
||||
const byte_vector mapProtoBytes = byte_vector::FromPath(inputPath);
|
||||
net::eagle0::shardok::common::HexMap mapProto;
|
||||
mapProto.ParseFromArray(mapProtoBytes.data(), (int)mapProtoBytes.size());
|
||||
if (!mapProto.ParseFromArray(mapProtoBytes.data(), static_cast<int>(mapProtoBytes.size()))) {
|
||||
std::cerr << "Failed to parse hex map from " << inputPath << '\n';
|
||||
return -1;
|
||||
}
|
||||
|
||||
ModifyMap(mapProto);
|
||||
const std::string outputString = mapProto.SerializeAsString();
|
||||
if (FilesystemUtils::AtomicallySaveToPath(outputPath, byte_vector(outputString))) return 0;
|
||||
|
||||
printf("Failed to save.\n");
|
||||
std::cerr << "Failed to save.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ModifyMap(HexMapProto& map) {
|
||||
for (int i = 0; i < map.terrain_size(); i++) {
|
||||
const int terrainCount = map.terrain_size();
|
||||
for (int i = 0; i < terrainCount; i++) {
|
||||
const auto existingTerrainType = map.terrain(i).type();
|
||||
const auto newTerrain =
|
||||
(net::eagle0::shardok::common::Terrain_Type)(existingTerrainType + 2);
|
||||
static_cast<net::eagle0::shardok::common::Terrain_Type>(existingTerrainType + 2);
|
||||
map.mutable_terrain(i)->set_type(newTerrain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,50 +4,52 @@
|
||||
|
||||
#include "MapInfoCalculator.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIWaterCrossingCalculator.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/HexMapHelpers.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/util/MapLoader.hpp"
|
||||
|
||||
using namespace shardok;
|
||||
using std::endl;
|
||||
|
||||
auto LoadMapFb(const string& mapName) -> Wrapper<HexMap> {
|
||||
auto LoadMapFb(const std::string& mapName) -> shardok::Wrapper<shardok::HexMap> {
|
||||
const HexMapProto mapProto = LoadMap(mapName);
|
||||
|
||||
flatbuffers::FlatBufferBuilder fbb{};
|
||||
const auto hexMapOffset = fb::ConvertHexMapProto(fbb, mapProto);
|
||||
const auto hexMapOffset = shardok::fb::ConvertHexMapProto(fbb, mapProto);
|
||||
fbb.Finish(hexMapOffset);
|
||||
return Wrapper<HexMap>(fbb);
|
||||
return shardok::Wrapper<shardok::HexMap>(fbb);
|
||||
}
|
||||
|
||||
auto CalculateMap(
|
||||
const string& mapName,
|
||||
const std::shared_ptr<ActionPointDistancesCache>& apdCache,
|
||||
const BattalionTypeSPtr& battalionType) -> OneMapInfo {
|
||||
const std::string& mapName,
|
||||
const std::shared_ptr<shardok::ActionPointDistancesCache>& apdCache,
|
||||
const shardok::BattalionTypeSPtr& battalionType) -> OneMapInfo {
|
||||
auto hexMap = LoadMapFb(mapName);
|
||||
|
||||
const auto mapId = ActionPointDistancesCache::GetMapId(hexMap);
|
||||
const auto mapId = shardok::ActionPointDistancesCache::GetMapId(hexMap);
|
||||
|
||||
const CoordsSet criticalTileLocations = GetCriticalTileLocations(hexMap);
|
||||
const shardok::CoordsSet criticalTileLocations = shardok::GetCriticalTileLocations(hexMap);
|
||||
|
||||
OneMapInfo info{
|
||||
.castleCount = criticalTileLocations.size(),
|
||||
.name = mapName,
|
||||
.positionsRequiringCrossing = {}};
|
||||
|
||||
for (unsigned int i = 0; i < hexMap->attacker_starting_positions()->size(); i++) {
|
||||
for (std::size_t i = 0; i < hexMap->attacker_starting_positions()->size(); i++) {
|
||||
const auto* positionList = hexMap->attacker_starting_positions()->Get(i);
|
||||
if (positionList->positions()->size() < 1) continue;
|
||||
if (positionList->positions()->size() != 10) {
|
||||
printf("Should have 10 starting positions!");
|
||||
abort();
|
||||
const auto* positions = positionList->positions();
|
||||
if (positions->empty()) continue;
|
||||
if (positions->size() != 10) {
|
||||
std::cerr << "Should have 10 starting positions!" << '\n';
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int positionsRequiringCrossing = 0;
|
||||
for (const auto* startingPosition : *positionList->positions()) {
|
||||
for (const Coords& castlePosition : criticalTileLocations) {
|
||||
if (!CanReach(
|
||||
for (const auto* startingPosition : *positions) {
|
||||
for (const shardok::Coords& castlePosition : criticalTileLocations) {
|
||||
if (!shardok::CanReach(
|
||||
*startingPosition,
|
||||
castlePosition,
|
||||
hexMap,
|
||||
@@ -56,11 +58,11 @@ auto CalculateMap(
|
||||
battalionType)) {
|
||||
++positionsRequiringCrossing;
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
info.positionsRequiringCrossing[i] = positionsRequiringCrossing;
|
||||
info.positionsRequiringCrossing[static_cast<int>(i)] = positionsRequiringCrossing;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,15 @@
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistancesCache.hpp"
|
||||
|
||||
using namespace shardok;
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
struct OneMapInfo {
|
||||
struct alignas(64) OneMapInfo {
|
||||
uint32_t castleCount;
|
||||
string name;
|
||||
map<int, int> positionsRequiringCrossing;
|
||||
} __attribute__((aligned(64)));
|
||||
std::string name;
|
||||
std::map<int, int> positionsRequiringCrossing;
|
||||
};
|
||||
|
||||
auto CalculateMap(
|
||||
const string& mapName,
|
||||
const std::shared_ptr<ActionPointDistancesCache>& apdCache,
|
||||
const BattalionTypeSPtr& battalionType) -> OneMapInfo;
|
||||
const std::string& mapName,
|
||||
const std::shared_ptr<shardok::ActionPointDistancesCache>& apdCache,
|
||||
const shardok::BattalionTypeSPtr& battalionType) -> OneMapInfo;
|
||||
|
||||
#endif // EAGLE0_MAPINFOCALCULATOR_HPP
|
||||
|
||||
@@ -2,37 +2,42 @@
|
||||
// Created by Dan Crosby on 4/18/22.
|
||||
//
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "MapInfoCalculator.hpp"
|
||||
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/settings/GameSettings.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/util/BattalionTypeRegistrar.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/util/MapLoader.hpp"
|
||||
|
||||
using namespace shardok;
|
||||
using std::endl;
|
||||
|
||||
auto main(const int argc, char** argv) -> int {
|
||||
FilesystemUtils::SetExecPath(argv[0]);
|
||||
|
||||
std::ostream* outputStreamPtr = &std::cout;
|
||||
std::ofstream outputFile{};
|
||||
if (argc > 1) {
|
||||
const string outputPath = argv[1];
|
||||
outputStreamPtr = new std::ofstream(outputPath);
|
||||
const std::string outputPath = argv[1];
|
||||
outputFile.open(outputPath);
|
||||
outputStreamPtr = &outputFile;
|
||||
}
|
||||
std::ostream& outputStream = *outputStreamPtr;
|
||||
|
||||
const auto gameSettings = std::make_shared<GameSettings>();
|
||||
const auto gameSettings = std::make_shared<shardok::GameSettings>();
|
||||
auto setter = gameSettings->GetSetter();
|
||||
BattalionTypeRegistrar::RegisterBattalionTypes(setter);
|
||||
const BattalionTypeSPtr& battalionType = gameSettings->GetGetter().GetBattalionType(
|
||||
shardok::BattalionTypeRegistrar::RegisterBattalionTypes(setter);
|
||||
const shardok::BattalionTypeSPtr& battalionType = gameSettings->GetGetter().GetBattalionType(
|
||||
net::eagle0::shardok::storage::fb::BattalionTypeId_LIGHT_INFANTRY);
|
||||
|
||||
vector<OneMapInfo> mapInfos{};
|
||||
for (const string& mapName : GetMapNames()) {
|
||||
std::shared_ptr<ActionPointDistancesCache> apdCache =
|
||||
std::make_shared<ActionPointDistancesCache>();
|
||||
const std::vector<std::string> mapNames = GetMapNames();
|
||||
std::vector<OneMapInfo> mapInfos{};
|
||||
mapInfos.reserve(mapNames.size());
|
||||
for (const std::string& mapName : mapNames) {
|
||||
std::shared_ptr<shardok::ActionPointDistancesCache> apdCache =
|
||||
std::make_shared<shardok::ActionPointDistancesCache>();
|
||||
|
||||
mapInfos.push_back(CalculateMap(mapName, apdCache, battalionType));
|
||||
}
|
||||
@@ -41,29 +46,29 @@ auto main(const int argc, char** argv) -> int {
|
||||
bool firstMap = true;
|
||||
for (const OneMapInfo& mapInfo : mapInfos) {
|
||||
if (firstMap) {
|
||||
outputStream << endl;
|
||||
outputStream << '\n';
|
||||
firstMap = false;
|
||||
} else {
|
||||
outputStream << "," << endl;
|
||||
outputStream << "," << '\n';
|
||||
}
|
||||
outputStream << " {" << endl;
|
||||
outputStream << R"( "name": ")" << mapInfo.name << "\"," << endl;
|
||||
outputStream << " {" << '\n';
|
||||
outputStream << R"( "name": ")" << mapInfo.name << "\"," << '\n';
|
||||
|
||||
outputStream << " \"castleCount\": " << mapInfo.castleCount << "," << endl;
|
||||
outputStream << " \"castleCount\": " << mapInfo.castleCount << "," << '\n';
|
||||
outputStream << " \"positions\": {";
|
||||
|
||||
bool firstPosition = true;
|
||||
for (const auto& [position, count] : mapInfo.positionsRequiringCrossing) {
|
||||
if (firstPosition) {
|
||||
outputStream << endl;
|
||||
outputStream << '\n';
|
||||
firstPosition = false;
|
||||
} else {
|
||||
outputStream << "," << endl;
|
||||
outputStream << "," << '\n';
|
||||
}
|
||||
|
||||
outputStream << " \"" << position << "\": " << count;
|
||||
}
|
||||
outputStream << endl << " }" << endl << " }";
|
||||
outputStream << '\n' << " }" << '\n' << " }";
|
||||
}
|
||||
outputStream << endl << "]" << endl;
|
||||
outputStream << '\n' << "]" << '\n';
|
||||
}
|
||||
|
||||
@@ -2,10 +2,17 @@
|
||||
// Created by Dan Crosby on 3/29/21.
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
|
||||
#include "src/main/cpp/net/eagle0/common/UnitConversions.hpp"
|
||||
@@ -14,8 +21,6 @@
|
||||
#include "src/main/protobuf/net/eagle0/common/random_units.pb.h"
|
||||
|
||||
using net::eagle0::common::RandomUnits;
|
||||
using std::shared_ptr;
|
||||
using std::vector;
|
||||
|
||||
constexpr int THREAD_COUNT = 16;
|
||||
|
||||
@@ -28,96 +33,130 @@ std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
#endif
|
||||
|
||||
vector<string> fileNames{};
|
||||
std::mutex randomMutex;
|
||||
std::vector<std::string> fileNames{};
|
||||
|
||||
using namespace shardok;
|
||||
|
||||
ShardokGamesManager* manager;
|
||||
std::unique_ptr<shardok::ShardokGamesManager> manager;
|
||||
|
||||
auto randInt(const int lowInc, const int hiInc) {
|
||||
std::lock_guard<std::mutex> lock(randomMutex);
|
||||
return std::uniform_int_distribution(lowInc, hiInc)(g);
|
||||
}
|
||||
|
||||
void runBattle(const vector<shared_ptr<const UnitProto>>& units) {
|
||||
auto mapName = fileNames[randInt(0, (int)fileNames.size() - 1)];
|
||||
auto month = randInt(1, 12);
|
||||
void runBattle(const std::vector<shardok::Unit>& units) {
|
||||
if (fileNames.empty()) { throw std::runtime_error("No map files found"); }
|
||||
if (units.size() < 2) { throw std::runtime_error("Need at least two units to run a battle"); }
|
||||
|
||||
auto p1UnitCount = randInt(1, 20);
|
||||
vector<UnitProto> p1Units{};
|
||||
const auto mapName = fileNames[randInt(0, static_cast<int>(fileNames.size()) - 1)];
|
||||
const auto month = randInt(1, 12);
|
||||
|
||||
const auto maxP1UnitCount = std::min<size_t>(20, units.size() - 1);
|
||||
const auto p1UnitCount = randInt(1, static_cast<int>(maxP1UnitCount));
|
||||
std::vector<shardok::Unit> p1Units{};
|
||||
p1Units.reserve(p1UnitCount);
|
||||
for (int i = 0; i < p1UnitCount; i++) {
|
||||
auto unit = *units[i];
|
||||
unit.set_player_id(0);
|
||||
p1Units.push_back(unit);
|
||||
auto unit = units[i];
|
||||
unit.mutate_player_id(0);
|
||||
p1Units.push_back(std::move(unit));
|
||||
}
|
||||
|
||||
auto p2UnitCount = randInt(1, 20);
|
||||
vector<UnitProto> p2Units{};
|
||||
const auto maxP2UnitCount = std::min<size_t>(20, units.size() - p1UnitCount);
|
||||
const auto p2UnitCount = randInt(1, static_cast<int>(maxP2UnitCount));
|
||||
std::vector<shardok::Unit> p2Units{};
|
||||
p2Units.reserve(p2UnitCount);
|
||||
for (int i = p1UnitCount; i < p1UnitCount + p2UnitCount; i++) {
|
||||
auto unit = *units[i];
|
||||
unit.set_player_id(1);
|
||||
p2Units.push_back(unit);
|
||||
auto unit = units[i];
|
||||
unit.mutate_player_id(1);
|
||||
p2Units.push_back(std::move(unit));
|
||||
}
|
||||
|
||||
printf("map name is %s, %d attacking %d\n", mapName.c_str(), p1UnitCount, p2UnitCount);
|
||||
std::cout << "map name is " << mapName << ", " << p1UnitCount << " attacking " << p2UnitCount
|
||||
<< '\n';
|
||||
|
||||
vector<VictoryConditionProto> attVictoryConditions{
|
||||
std::vector<VictoryConditionProto> attVictoryConditions{
|
||||
VictoryConditionProto::VICTORY_CONDITION_HOLDS_CRITICAL_TILES,
|
||||
VictoryConditionProto::VICTORY_CONDITION_LAST_PLAYER_STANDING};
|
||||
vector<VictoryConditionProto> defVictoryConditions{
|
||||
std::vector<VictoryConditionProto> defVictoryConditions{
|
||||
VictoryConditionProto::VICTORY_CONDITION_LAST_PLAYER_STANDING,
|
||||
VictoryConditionProto::VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS};
|
||||
|
||||
auto p1 = PlayerInfoWithUnits(0, true, 5, false, 10000, attVictoryConditions, {}, p1Units);
|
||||
auto p2 = PlayerInfoWithUnits(1, false, 7, false, 10000, defVictoryConditions, {}, p2Units);
|
||||
std::vector<shardok::PlayerInfoWithUnits> playerInfos;
|
||||
playerInfos.reserve(2);
|
||||
playerInfos.emplace_back(
|
||||
0,
|
||||
true,
|
||||
5,
|
||||
false,
|
||||
10000,
|
||||
attVictoryConditions,
|
||||
std::vector<shardok::PlayerId>{},
|
||||
p1Units,
|
||||
false);
|
||||
playerInfos.emplace_back(
|
||||
1,
|
||||
true,
|
||||
7,
|
||||
true,
|
||||
10000,
|
||||
defVictoryConditions,
|
||||
std::vector<shardok::PlayerId>{},
|
||||
p2Units,
|
||||
false);
|
||||
|
||||
string gameId = std::to_string(randInt(INT_MIN, INT_MAX));
|
||||
const std::string gameId = std::to_string(randInt(INT_MIN, INT_MAX));
|
||||
|
||||
manager->LockedCreateSpecifiedGame(gameId, {p1, p2}, mapName, "", month, "");
|
||||
manager->UnlockedCreateSpecifiedGame(
|
||||
gameId,
|
||||
playerInfos,
|
||||
mapName,
|
||||
"",
|
||||
month,
|
||||
TutorialBattleConfigProto{},
|
||||
"");
|
||||
}
|
||||
|
||||
auto LoadUnits() -> vector<shared_ptr<const UnitProto>> {
|
||||
auto LoadUnits() -> std::vector<shardok::Unit> {
|
||||
const auto bytes = byte_vector::FromPath("/tmp/random_heroes.bin");
|
||||
|
||||
RandomUnits commonUnits;
|
||||
commonUnits.ParseFromArray(bytes.data(), (int)bytes.size());
|
||||
printf("Loaded %d units\n", commonUnits.units_size());
|
||||
if (!commonUnits.ParseFromArray(bytes.data(), static_cast<int>(bytes.size()))) {
|
||||
throw std::runtime_error("Failed to parse /tmp/random_heroes.bin");
|
||||
}
|
||||
std::cout << "Loaded " << commonUnits.units_size() << " units\n";
|
||||
|
||||
vector<shared_ptr<const UnitProto>> units{};
|
||||
std::vector<shardok::Unit> units{};
|
||||
units.reserve(commonUnits.units_size());
|
||||
|
||||
for (const auto& commonUnit : commonUnits.units()) {
|
||||
// FIXME: assumes 2 players
|
||||
units.push_back(std::make_shared<const UnitProto>(ConvertUnit(commonUnit, 0, {0, 1})));
|
||||
units.push_back(shardok::ConvertUnit(commonUnit, 0, {0, 1}));
|
||||
}
|
||||
std::shuffle(units.begin(), units.end(), g);
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
[[noreturn]] void runBattlesThread(const vector<shared_ptr<const UnitProto>>& units) {
|
||||
[[noreturn]] void runBattlesThread(const std::vector<shardok::Unit>& units) {
|
||||
while (true) { runBattle(units); }
|
||||
}
|
||||
|
||||
auto main(int argc, char** argv) -> int {
|
||||
auto main(int /*argc*/, char** argv) -> int {
|
||||
#if FIXED_SEED
|
||||
std::srand(4564564);
|
||||
g.seed(378473);
|
||||
#else
|
||||
std::srand(std::time(nullptr));
|
||||
#endif
|
||||
|
||||
FilesystemUtils::SetExecPath(argv[0]);
|
||||
|
||||
auto randomUnits = LoadUnits();
|
||||
|
||||
printf("Map file directory is %s\n", FilesystemUtils::MapFilesDirectory().c_str());
|
||||
std::cout << "Map file directory is " << FilesystemUtils::MapFilesDirectory() << '\n';
|
||||
fileNames = FilesystemUtils::FilesInDirectory(FilesystemUtils::MapFilesDirectory(), ".e0m");
|
||||
|
||||
for (auto& fileName : fileNames) { fileName.resize(fileName.size() - 4); }
|
||||
|
||||
manager = new ShardokGamesManager(nullptr, std::vector<std::string>());
|
||||
manager = std::make_unique<shardok::ShardokGamesManager>(std::vector<std::string>());
|
||||
|
||||
std::thread threads[THREAD_COUNT];
|
||||
std::array<std::thread, THREAD_COUNT> threads;
|
||||
|
||||
for (int i = 0; i < THREAD_COUNT; i++) {
|
||||
threads[i] = std::thread(runBattlesThread, randomUnits);
|
||||
|
||||
@@ -16,9 +16,6 @@ namespace shardok {
|
||||
constexpr double kOverpowerRatio = 2.0;
|
||||
constexpr double kBraveWaterCostMultiplier = 1.2;
|
||||
|
||||
using std::pair;
|
||||
using std::shared_ptr;
|
||||
|
||||
DIST_T NormalizedCostWhenBraving(const DIST_T cost) {
|
||||
if (cost >= static_cast<double>(ActionPointDistances::IMPOSSIBLE) / kBraveWaterCostMultiplier)
|
||||
return ActionPointDistances::IMPOSSIBLE;
|
||||
@@ -156,6 +153,7 @@ auto GenerateTargetPriorities(
|
||||
vector<TargetAndAttackLocations>{});
|
||||
|
||||
vector<TargetAndDistance> targetsWithDistance;
|
||||
targetsWithDistance.reserve(targets.size());
|
||||
|
||||
// Get APDs directly from cache (now with built-in thread-local optimization)
|
||||
const auto& battType = battalionTypeGetter(unit->battalion().type());
|
||||
@@ -235,4 +233,4 @@ auto GenerateTargetPriorities(
|
||||
return allTargetsUnitsAndDistances;
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
} // namespace shardok
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 6/19/23.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIATTACKGROUPS_HPP
|
||||
#define EAGLE0_AIATTACKGROUPS_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_ATTACK_GROUPS_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_ATTACK_GROUPS_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
@@ -20,7 +20,6 @@ namespace shardok {
|
||||
using GameState = net::eagle0::shardok::storage::fb::GameState;
|
||||
using Unit = net::eagle0::shardok::storage::fb::Unit;
|
||||
using net::eagle0::shardok::storage::fb::PlayerInfo;
|
||||
using std::vector;
|
||||
|
||||
using BattalionTypeGetter = std::function<BattalionTypeSPtr(BattalionTypeId)>;
|
||||
|
||||
@@ -33,9 +32,9 @@ struct TargetAndAttackLocations {
|
||||
|
||||
struct TargetPriorityList {
|
||||
UnitId attackingUnitId;
|
||||
vector<TargetAndAttackLocations> priorityOrder;
|
||||
std::vector<TargetAndAttackLocations> priorityOrder;
|
||||
|
||||
TargetPriorityList(const UnitId uid, const vector<TargetAndAttackLocations>& po)
|
||||
TargetPriorityList(const UnitId uid, const std::vector<TargetAndAttackLocations>& po)
|
||||
: attackingUnitId(uid),
|
||||
priorityOrder(po) {}
|
||||
};
|
||||
@@ -64,17 +63,17 @@ auto EffectiveDistance(
|
||||
|
||||
// Chooses a list of targets in priority order for each unit.
|
||||
auto GenerateTargetPriorities(
|
||||
const vector<const Unit*>& occupants,
|
||||
const std::vector<const Unit*>& occupants,
|
||||
const HexMap* map,
|
||||
const CoordsSet& targets,
|
||||
PlayerId attackerId,
|
||||
const vector<const Unit*>& remainingUnits,
|
||||
const std::vector<const Unit*>& remainingUnits,
|
||||
const APDCache& apdCache,
|
||||
const ALCache& alCache,
|
||||
const BattalionTypeGetter& battalionTypeGetter,
|
||||
ActionPoints braveWaterCost,
|
||||
bool isLateGame = false) -> vector<TargetPriorityList>;
|
||||
bool isLateGame = false) -> std::vector<TargetPriorityList>;
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AIATTACKGROUPS_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_ATTACK_GROUPS_HPP
|
||||
|
||||
@@ -17,10 +17,8 @@
|
||||
namespace shardok {
|
||||
|
||||
using Unit = net::eagle0::shardok::storage::fb::Unit;
|
||||
using std::shared_ptr;
|
||||
using std::vector;
|
||||
|
||||
class AttackLocations {
|
||||
class alignas(128) AttackLocations {
|
||||
private:
|
||||
const MapIndex rowCount;
|
||||
const MapIndex columnCount;
|
||||
@@ -147,9 +145,9 @@ public:
|
||||
adjacentLocations += cs;
|
||||
allLocations += cs;
|
||||
}
|
||||
} __attribute__((aligned(128)));
|
||||
};
|
||||
|
||||
struct AttackLocationsCache {
|
||||
struct alignas(128) AttackLocationsCache {
|
||||
private:
|
||||
const MapIndex rowCount;
|
||||
const MapIndex columnCount;
|
||||
@@ -165,8 +163,8 @@ public:
|
||||
|
||||
auto CachedLocations(const Coords& singleTarget, bool lateGame) -> const AttackLocations&;
|
||||
auto CachedLocations(const CoordsSet& targets, bool lateGame) -> AttackLocations;
|
||||
auto CachedLocations(const vector<const Unit*>& enemies, bool lateGame) -> AttackLocations;
|
||||
} __attribute__((aligned(128)));
|
||||
auto CachedLocations(const std::vector<const Unit*>& enemies, bool lateGame) -> AttackLocations;
|
||||
};
|
||||
|
||||
using ALCache = std::unique_ptr<AttackLocationsCache>;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ auto AIAttackerStrategySelector::BestAttackerStrategy(
|
||||
bool canFlee = false;
|
||||
|
||||
vector<const Unit*> attackerUnits{};
|
||||
attackerUnits.reserve(gameState->units()->size());
|
||||
CoordsSet defenderPositions(gameState->hex_map());
|
||||
for (const Unit* unit : *gameState->units()) {
|
||||
if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT &&
|
||||
@@ -131,4 +132,4 @@ auto AIAttackerStrategySelector::BestAttackerStrategy(
|
||||
return chosenStrategy;
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
} // namespace shardok
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 1/9/22.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIATTACKERSTRATEGYSELECTOR_HPP
|
||||
#define EAGLE0_AIATTACKERSTRATEGYSELECTOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_ATTACKER_STRATEGY_SELECTOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_ATTACKER_STRATEGY_SELECTOR_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AICommonTypes.hpp"
|
||||
@@ -32,4 +32,4 @@ public:
|
||||
};
|
||||
|
||||
} // namespace shardok
|
||||
#endif // EAGLE0_AIATTACKERSTRATEGYSELECTOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_ATTACKER_STRATEGY_SELECTOR_HPP
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <future>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#include "AICommandFilter.hpp"
|
||||
@@ -22,11 +24,11 @@ namespace shardok {
|
||||
// No need to forward declare internal functions - use the public interface instead
|
||||
|
||||
// Helper constants and static variables
|
||||
static const std::vector<double> _averageSequence = {0.5};
|
||||
static const auto _averageGenerator = std::make_shared<SequenceRandomGenerator>(_averageSequence);
|
||||
static const std::vector<double> kAverageSequence = {0.5};
|
||||
static const auto kAverageGenerator = std::make_shared<SequenceRandomGenerator>(kAverageSequence);
|
||||
|
||||
#define MULTITHREAD true
|
||||
#define LOGGING_ 0
|
||||
constexpr bool kMultithread = true;
|
||||
constexpr bool kLogging = false;
|
||||
|
||||
// Helper function to determine if a command type is deterministic
|
||||
static auto IsDeterministic(const CommandType type) -> bool {
|
||||
@@ -237,15 +239,16 @@ auto AICommandEvaluator::EvaluateWithRandomness(
|
||||
return lookaheadFuture.get();
|
||||
};
|
||||
|
||||
#if MULTITHREAD
|
||||
auto launchPolicy = remainingLookahead == 1 ? std::launch::async : std::launch::deferred;
|
||||
returnValue.lookaheadScore = std::async(launchPolicy, lookaheadLambda);
|
||||
#else
|
||||
std::promise<EvaluationResult> p;
|
||||
returnValue.lookaheadScore = p.get_future();
|
||||
auto lambdaResult = lookaheadLambda();
|
||||
p.set_value(lambdaResult);
|
||||
#endif
|
||||
if constexpr (kMultithread) {
|
||||
auto launchPolicy =
|
||||
remainingLookahead == 1 ? std::launch::async : std::launch::deferred;
|
||||
returnValue.lookaheadScore = std::async(launchPolicy, lookaheadLambda);
|
||||
} else {
|
||||
std::promise<EvaluationResult> p;
|
||||
returnValue.lookaheadScore = p.get_future();
|
||||
auto lambdaResult = lookaheadLambda();
|
||||
p.set_value(lambdaResult);
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
@@ -302,30 +305,31 @@ auto AICommandEvaluator::FindBestCommand(
|
||||
minDistToEnemies = 0.0; // No enemies found
|
||||
}
|
||||
|
||||
#if LOGGING_
|
||||
// Log command count and distance metrics for performance analysis
|
||||
const auto allCommandCount = guessedDescriptors->size();
|
||||
const auto filteredCommandCount = filteredIndices.size();
|
||||
const int currentRound = gameState->current_round();
|
||||
if constexpr (kLogging) {
|
||||
// Log command count and distance metrics for performance analysis
|
||||
const auto allCommandCount = guessedDescriptors->size();
|
||||
const auto filteredCommandCount = filteredIndices.size();
|
||||
const int currentRound = gameState->current_round();
|
||||
|
||||
printf("AI_COMMAND_COUNT: Round %d, Player %d, Defender %d, MinDist %.1f, Commands %zu -> %zu "
|
||||
"(%.1f%% filtered)\n",
|
||||
currentRound,
|
||||
static_cast<int>(pid),
|
||||
isDefender ? 1 : 0,
|
||||
minDistToEnemies,
|
||||
allCommandCount,
|
||||
filteredCommandCount,
|
||||
100.0 * (allCommandCount - filteredCommandCount) / allCommandCount);
|
||||
#endif
|
||||
const auto flags = std::cout.flags();
|
||||
const auto precision = std::cout.precision();
|
||||
std::cout << "AI_COMMAND_COUNT: Round " << currentRound << ", Player "
|
||||
<< static_cast<int>(pid) << ", Defender " << (isDefender ? 1 : 0) << ", MinDist "
|
||||
<< std::fixed << std::setprecision(1) << minDistToEnemies << ", Commands "
|
||||
<< allCommandCount << " -> " << filteredCommandCount << " ("
|
||||
<< 100.0 * (allCommandCount - filteredCommandCount) / allCommandCount
|
||||
<< "% filtered)\n";
|
||||
std::cout.flags(flags);
|
||||
std::cout.precision(precision);
|
||||
}
|
||||
|
||||
const auto commandCount = filteredIndices.size();
|
||||
|
||||
// Structure to hold all command evaluation data
|
||||
struct CommandEvaluation {
|
||||
size_t index;
|
||||
CommandType type;
|
||||
ScoreValue immediateScore;
|
||||
size_t index{};
|
||||
CommandType type{};
|
||||
ScoreValue immediateScore{};
|
||||
bool immediateCompleted = true;
|
||||
std::vector<std::future<EvaluationResult>> lookaheadFutures;
|
||||
};
|
||||
@@ -352,7 +356,7 @@ auto AICommandEvaluator::FindBestCommand(
|
||||
originalIndex,
|
||||
remainingLookahead,
|
||||
maxRepeatCount,
|
||||
_averageGenerator,
|
||||
kAverageGenerator,
|
||||
guessedEngine,
|
||||
attackerStrategy,
|
||||
allCastleCoords,
|
||||
@@ -418,6 +422,7 @@ auto AICommandEvaluator::FindBestCommand(
|
||||
} else {
|
||||
ScoreValue sum = 0.0;
|
||||
const int sampleCount = std::max(1, maxRepeatCount);
|
||||
commandEvaluations[index].lookaheadFutures.reserve(sampleCount);
|
||||
for (int repeatIteration = 0; repeatIteration < sampleCount; repeatIteration++) {
|
||||
// In each iteration, use a double from [0, 1] as the random roll
|
||||
auto sequence =
|
||||
@@ -428,7 +433,7 @@ auto AICommandEvaluator::FindBestCommand(
|
||||
originalIndex,
|
||||
remainingLookahead,
|
||||
maxRepeatCount,
|
||||
std::make_shared<SequenceRandomGenerator>(sequence),
|
||||
std::make_shared<SequenceRandomGenerator>(std::move(sequence)),
|
||||
guessedEngine,
|
||||
attackerStrategy,
|
||||
allCastleCoords,
|
||||
@@ -507,26 +512,30 @@ auto AICommandEvaluator::EvaluateCommand(
|
||||
}
|
||||
|
||||
const auto& guessedDescriptor = guessedDescriptors->at(commandIndex);
|
||||
const auto guessedCommandType = guessedDescriptor->GetCommandType();
|
||||
|
||||
if (const auto guessedCommandType = guessedDescriptor->GetCommandType();
|
||||
guessedCommandType == net::eagle0::shardok::common::END_TURN_COMMAND) {
|
||||
if (guessedCommandType == net::eagle0::shardok::common::END_TURN_COMMAND) {
|
||||
std::promise<EvaluationResult> p;
|
||||
p.set_value(EvaluationResult{.score = currentUtility, .completed = true});
|
||||
return p.get_future();
|
||||
} else if (IsDeterministic(guessedCommandType)) {
|
||||
}
|
||||
|
||||
if (IsDeterministic(guessedCommandType)) {
|
||||
auto evaluation = EvaluateWithRandomness(
|
||||
pid,
|
||||
isDefender,
|
||||
commandIndex,
|
||||
remainingLookahead,
|
||||
maxRepeatCount,
|
||||
_averageGenerator,
|
||||
kAverageGenerator,
|
||||
guessedEngine,
|
||||
attackerStrategy,
|
||||
allCastleCoords,
|
||||
deadline);
|
||||
return std::move(evaluation.lookaheadScore);
|
||||
} else if (guessedDescriptor->HasOdds()) {
|
||||
}
|
||||
|
||||
if (guessedDescriptor->HasOdds()) {
|
||||
const auto successChancePercentile = guessedDescriptor->GetOddsPercentile();
|
||||
const double successChance = static_cast<double>(successChancePercentile) / 100.0;
|
||||
|
||||
@@ -571,47 +580,47 @@ auto AICommandEvaluator::EvaluateCommand(
|
||||
.score = std::lerp(failure.score, success.score, successChance),
|
||||
.completed = true};
|
||||
});
|
||||
} else {
|
||||
// For non-deterministic commands without odds, use multiple attempts
|
||||
std::vector<std::future<EvaluationResult>> lookaheadFutures;
|
||||
const int sampleCount = std::max(1, maxRepeatCount);
|
||||
lookaheadFutures.reserve(sampleCount);
|
||||
|
||||
for (int repeatIteration = 0; repeatIteration < sampleCount; repeatIteration++) {
|
||||
auto sequence = std::vector{RandomnessSampleForRepeat(repeatIteration, sampleCount)};
|
||||
auto evaluation = EvaluateWithRandomness(
|
||||
pid,
|
||||
isDefender,
|
||||
commandIndex,
|
||||
remainingLookahead,
|
||||
maxRepeatCount,
|
||||
std::make_shared<SequenceRandomGenerator>(sequence),
|
||||
guessedEngine,
|
||||
attackerStrategy,
|
||||
allCastleCoords,
|
||||
deadline);
|
||||
|
||||
lookaheadFutures.push_back(std::move(evaluation.lookaheadScore));
|
||||
}
|
||||
|
||||
// Return a future that computes the average when needed
|
||||
return std::async(
|
||||
std::launch::deferred,
|
||||
[lookaheadFutures = std::move(lookaheadFutures),
|
||||
sampleCount]() mutable -> EvaluationResult {
|
||||
ScoreValue total = 0.0;
|
||||
bool completed = true;
|
||||
for (auto& future : lookaheadFutures) {
|
||||
const auto result = future.get();
|
||||
if (!result.completed) { completed = false; }
|
||||
total += result.score;
|
||||
}
|
||||
if (!completed) { return EvaluationResult{.score = 0.0, .completed = false}; }
|
||||
return EvaluationResult{
|
||||
.score = total / static_cast<ScoreValue>(sampleCount),
|
||||
.completed = true};
|
||||
});
|
||||
}
|
||||
|
||||
// For non-deterministic commands without odds, use multiple attempts
|
||||
std::vector<std::future<EvaluationResult>> lookaheadFutures;
|
||||
const int sampleCount = std::max(1, maxRepeatCount);
|
||||
lookaheadFutures.reserve(sampleCount);
|
||||
|
||||
for (int repeatIteration = 0; repeatIteration < sampleCount; repeatIteration++) {
|
||||
auto sequence = std::vector{RandomnessSampleForRepeat(repeatIteration, sampleCount)};
|
||||
auto evaluation = EvaluateWithRandomness(
|
||||
pid,
|
||||
isDefender,
|
||||
commandIndex,
|
||||
remainingLookahead,
|
||||
maxRepeatCount,
|
||||
std::make_shared<SequenceRandomGenerator>(std::move(sequence)),
|
||||
guessedEngine,
|
||||
attackerStrategy,
|
||||
allCastleCoords,
|
||||
deadline);
|
||||
|
||||
lookaheadFutures.push_back(std::move(evaluation.lookaheadScore));
|
||||
}
|
||||
|
||||
// Return a future that computes the average when needed
|
||||
return std::async(
|
||||
std::launch::deferred,
|
||||
[lookaheadFutures = std::move(lookaheadFutures),
|
||||
sampleCount]() mutable -> EvaluationResult {
|
||||
ScoreValue total = 0.0;
|
||||
bool completed = true;
|
||||
for (auto& future : lookaheadFutures) {
|
||||
const auto result = future.get();
|
||||
if (!result.completed) { completed = false; }
|
||||
total += result.score;
|
||||
}
|
||||
if (!completed) { return EvaluationResult{.score = 0.0, .completed = false}; }
|
||||
return EvaluationResult{
|
||||
.score = total / static_cast<ScoreValue>(sampleCount),
|
||||
.completed = true};
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// Separated from AIScoreCalculator to isolate pure state scoring from lookahead logic.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AICOMMANDEVALUATOR_HPP
|
||||
#define EAGLE0_AICOMMANDEVALUATOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_COMMAND_EVALUATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_COMMAND_EVALUATOR_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
@@ -57,11 +57,11 @@ public:
|
||||
|
||||
/// Find the best command among all available commands at the given depth.
|
||||
struct IndexAndScore {
|
||||
size_t index;
|
||||
CommandType type;
|
||||
ScoreValue lookaheadScore;
|
||||
ScoreValue immediateScore;
|
||||
bool completed;
|
||||
size_t index{};
|
||||
CommandType type{};
|
||||
ScoreValue lookaheadScore{};
|
||||
ScoreValue immediateScore{};
|
||||
bool completed{};
|
||||
};
|
||||
|
||||
[[nodiscard]] auto FindBestCommand(
|
||||
@@ -81,8 +81,8 @@ private:
|
||||
BattalionTypeGetter battalionTypeGetter_; // Store by value
|
||||
|
||||
struct ImmediateAndLookaheadScore {
|
||||
ScoreValue immediateScore;
|
||||
bool completed;
|
||||
ScoreValue immediateScore{};
|
||||
bool completed{};
|
||||
std::future<EvaluationResult> lookaheadScore;
|
||||
};
|
||||
|
||||
@@ -114,4 +114,4 @@ private:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AICOMMANDEVALUATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_COMMAND_EVALUATOR_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Filter obviously bad commands to reduce search space for AI
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AICOMMANDFILTER_HPP
|
||||
#define EAGLE0_AICOMMANDFILTER_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_COMMAND_FILTER_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_COMMAND_FILTER_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@@ -116,4 +116,4 @@ private:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AICOMMANDFILTER_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_COMMAND_FILTER_HPP
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// Common type definitions used across AI utility functions
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AICOMMONTYPES_HPP
|
||||
#define EAGLE0_AICOMMONTYPES_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_COMMON_TYPES_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_COMMON_TYPES_HPP
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -20,4 +20,4 @@ using BattalionTypeGetter = std::function<BattalionTypeSPtr(BattalionTypeId)>;
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AICOMMONTYPES_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_COMMON_TYPES_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// AI System Types and Configuration
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AI_CONFIG_HPP
|
||||
#define EAGLE0_AI_CONFIG_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_CONFIG_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_CONFIG_HPP
|
||||
|
||||
namespace shardok {
|
||||
|
||||
@@ -22,4 +22,4 @@ enum class ScoringCalculatorType {
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AI_CONFIG_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_CONFIG_HPP
|
||||
|
||||
@@ -32,6 +32,7 @@ auto AIDefenderStrategySelector::BestDefenderStrategy(
|
||||
bool canFlee = false;
|
||||
|
||||
vector<UnitId> attackerUnitIdsRequiringWaterCrossing{};
|
||||
attackerUnitIdsRequiringWaterCrossing.reserve(gameState->units()->size());
|
||||
for (const auto& player : *gameState->player_infos()) {
|
||||
if (!player->is_defender()) {
|
||||
const auto& unitIdsRequiringWaterCrossing = UnitIdsRequiringWaterCrossing(
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 8/30/23.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIDEFENDERSTRATEGYSELECTOR_HPP
|
||||
#define EAGLE0_AIDEFENDERSTRATEGYSELECTOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_DEFENDER_STRATEGY_SELECTOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_DEFENDER_STRATEGY_SELECTOR_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AICommonTypes.hpp"
|
||||
@@ -26,4 +26,4 @@ public:
|
||||
};
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AIDEFENDERSTRATEGYSELECTOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_DEFENDER_STRATEGY_SELECTOR_HPP
|
||||
|
||||
@@ -55,12 +55,12 @@ auto DefenderDistanceBuf(
|
||||
const bool includeUndead) -> double {
|
||||
const auto &locationsToAttackMe = alCache->CachedLocations(defenderLocation, lateGame);
|
||||
|
||||
struct WithoutAndWith {
|
||||
struct alignas(8) WithoutAndWith {
|
||||
int without;
|
||||
int with;
|
||||
|
||||
WithoutAndWith(const int a, const int b) : without(a), with(b) {}
|
||||
} __attribute__((aligned(8)));
|
||||
};
|
||||
|
||||
vector<WithoutAndWith> pointCosts{};
|
||||
pointCosts.reserve(attackerUnits.size());
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 5/10/21.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIDISTANCEDEBUF_HPP
|
||||
#define EAGLE0_AIDISTANCEDEBUF_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_DISTANCE_DEBUF_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_DISTANCE_DEBUF_HPP
|
||||
|
||||
#include "AIAttackLocations.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AICommonTypes.hpp"
|
||||
@@ -13,14 +13,11 @@
|
||||
|
||||
namespace shardok {
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::vector;
|
||||
|
||||
auto DefenderDistanceBuf(
|
||||
const Coords &defenderLocation,
|
||||
const HexMap *hexMap,
|
||||
const MapId &mapId,
|
||||
const vector<const Unit *> &attackerUnits,
|
||||
const std::vector<const Unit *> &attackerUnits,
|
||||
const APDCache &apdCache,
|
||||
const ALCache &alCache,
|
||||
const BattalionTypeGetter &battalionTypeGetter,
|
||||
@@ -29,4 +26,4 @@ auto DefenderDistanceBuf(
|
||||
bool includeUndead) -> double;
|
||||
|
||||
} // namespace shardok
|
||||
#endif // EAGLE0_AIDISTANCEDEBUF_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_DISTANCE_DEBUF_HPP
|
||||
|
||||
@@ -2,22 +2,34 @@
|
||||
// Temporary AI experiment switches used by benchmark-only EXPERIMENTAL runs.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AI_EXPERIMENT_CONFIG_HPP
|
||||
#define EAGLE0_AI_EXPERIMENT_CONFIG_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_EXPERIMENT_CONFIG_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_EXPERIMENT_CONFIG_HPP
|
||||
|
||||
#include <charconv>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
|
||||
namespace shardok {
|
||||
|
||||
inline auto CurrentAIExperimentId() -> int {
|
||||
const char* rawValue = std::getenv("SHARDOK_SCORING_EXPERIMENT_ID");
|
||||
if (rawValue == nullptr || std::string(rawValue).empty()) { return 0; }
|
||||
return std::atoi(rawValue);
|
||||
if (rawValue == nullptr) { return 0; }
|
||||
|
||||
const std::string_view value(rawValue);
|
||||
if (value.empty()) { return 0; }
|
||||
|
||||
int experimentId = 0;
|
||||
const auto parseResult =
|
||||
std::from_chars(value.data(), value.data() + value.size(), experimentId);
|
||||
if (parseResult.ec != std::errc{} || parseResult.ptr != value.data() + value.size()) {
|
||||
return 0;
|
||||
}
|
||||
return experimentId;
|
||||
}
|
||||
|
||||
inline auto IsAIExperiment(const int id) -> bool { return CurrentAIExperimentId() == id; }
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AI_EXPERIMENT_CONFIG_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_EXPERIMENT_CONFIG_HPP
|
||||
|
||||
@@ -8,12 +8,29 @@
|
||||
|
||||
#include "AIFleeDecisionCalculator.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIScoreUtilities.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIUnitScoreCalculator.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
|
||||
|
||||
namespace shardok {
|
||||
|
||||
namespace {
|
||||
|
||||
void WritePercent(std::ostream& output, const double value) {
|
||||
const auto flags = output.flags();
|
||||
const auto precision = output.precision();
|
||||
|
||||
output << std::fixed << std::setprecision(1) << value;
|
||||
|
||||
output.flags(flags);
|
||||
output.precision(precision);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto AIFleeDecisionCalculator::GetFleeCommandIndex(
|
||||
const CommandList::const_iterator& fleeCommand,
|
||||
const CommandListSPtr& availableCommands) -> size_t {
|
||||
@@ -144,15 +161,14 @@ auto AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
const int fleeSuccessChance = (*fleeCommand)->GetOddsPercentile();
|
||||
|
||||
if (enableDebugLogging) {
|
||||
printf("AI FinalRound: Evaluating flee (odds=%d%%)...\n", fleeSuccessChance);
|
||||
std::cout << "AI FinalRound: Evaluating flee (odds=" << fleeSuccessChance << "%)...\n";
|
||||
}
|
||||
|
||||
// Check if flee odds are good enough to attempt
|
||||
if (fleeSuccessChance >= minimumFleeOddsThreshold) {
|
||||
if (enableDebugLogging) {
|
||||
printf("AI FinalRound: Good flee odds (%d%% >= %d%%), choosing flee\n",
|
||||
fleeSuccessChance,
|
||||
minimumFleeOddsThreshold);
|
||||
std::cout << "AI FinalRound: Good flee odds (" << fleeSuccessChance
|
||||
<< "% >= " << minimumFleeOddsThreshold << "%), choosing flee\n";
|
||||
}
|
||||
return FleeDecision{
|
||||
true,
|
||||
@@ -166,9 +182,9 @@ auto AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
// If combat situation is hopeless, even bad flee odds are better than certain death
|
||||
if (combatWinChance <= 0.05 && fleeSuccessChance >= desperateFleeThreshold) {
|
||||
if (enableDebugLogging) {
|
||||
printf("AI FinalRound: Combat hopeless (%.1f%%), desperate flee attempt (%d%%)\n",
|
||||
combatWinChance * 100,
|
||||
fleeSuccessChance);
|
||||
std::cout << "AI FinalRound: Combat hopeless (";
|
||||
WritePercent(std::cout, combatWinChance * 100);
|
||||
std::cout << "%), desperate flee attempt (" << fleeSuccessChance << "%)\n";
|
||||
}
|
||||
return FleeDecision{
|
||||
true,
|
||||
@@ -188,20 +204,21 @@ auto AIFleeDecisionCalculator::EvaluateFleeVsFight(
|
||||
const double adjustedCombatThreshold = combatWinChance * FLEE_VS_COMBAT_MARGIN;
|
||||
|
||||
if (enableDebugLogging) {
|
||||
printf("AI FinalRound: Flee=%d%%, Combat=%.1f%%, Threshold=%.1f%% -> ",
|
||||
fleeSuccessChance,
|
||||
combatWinChance * 100,
|
||||
adjustedCombatThreshold * 100);
|
||||
std::cout << "AI FinalRound: Flee=" << fleeSuccessChance << "%, Combat=";
|
||||
WritePercent(std::cout, combatWinChance * 100);
|
||||
std::cout << "%, Threshold=";
|
||||
WritePercent(std::cout, adjustedCombatThreshold * 100);
|
||||
std::cout << "% -> ";
|
||||
}
|
||||
|
||||
if (fleeChance > adjustedCombatThreshold) {
|
||||
if (enableDebugLogging) { printf("FLEE (better odds)\n"); }
|
||||
if (enableDebugLogging) { std::cout << "FLEE (better odds)\n"; }
|
||||
return FleeDecision{
|
||||
true,
|
||||
GetFleeCommandIndex(fleeCommand, availableCommands),
|
||||
"Flee has better expected outcome"};
|
||||
} else {
|
||||
if (enableDebugLogging) { printf("FIGHT (better expected outcome)\n"); }
|
||||
if (enableDebugLogging) { std::cout << "FIGHT (better expected outcome)\n"; }
|
||||
// Return 0 to indicate we should use standard command selection
|
||||
return FleeDecision{
|
||||
false,
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// and flee vs fight evaluation for final round scenarios
|
||||
//
|
||||
|
||||
#ifndef AIFleeDecisionCalculator_hpp
|
||||
#define AIFleeDecisionCalculator_hpp
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_FLEE_DECISION_CALCULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_FLEE_DECISION_CALCULATOR_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokCommand.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokEngine.hpp"
|
||||
@@ -63,4 +63,4 @@ private:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif /* AIFleeDecisionCalculator_hpp */
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_FLEE_DECISION_CALCULATOR_HPP
|
||||
@@ -3,8 +3,8 @@
|
||||
// Provides O(1) weights based on command type and context
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AI_HEURISTIC_WEIGHTING_HPP
|
||||
#define EAGLE0_AI_HEURISTIC_WEIGHTING_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_HEURISTIC_WEIGHTING_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_HEURISTIC_WEIGHTING_HPP
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-redundant-constexpr-static-def"
|
||||
@@ -37,4 +37,4 @@ public:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AI_HEURISTIC_WEIGHTING_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_HEURISTIC_WEIGHTING_HPP
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
// Created by Dan Crosby on 8/31/23.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIMINIMUMDISTANCEANDTARGET_HPP
|
||||
#define EAGLE0_AIMINIMUMDISTANCEANDTARGET_HPP
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_MINIMUM_DISTANCE_AND_TARGET_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_MINIMUM_DISTANCE_AND_TARGET_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistances.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"
|
||||
@@ -15,7 +12,6 @@
|
||||
namespace shardok {
|
||||
|
||||
using net::eagle0::shardok::storage::fb::Coords;
|
||||
using std::shared_ptr;
|
||||
|
||||
struct CoordsAndDistance {
|
||||
Coords coords;
|
||||
@@ -34,4 +30,4 @@ auto MinimumDistance(
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AIMINIMUMDISTANCEANDTARGET_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_MINIMUM_DISTANCE_AND_TARGET_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 4/7/21.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AISCOREUTILITIES_HPP
|
||||
#define EAGLE0_AISCOREUTILITIES_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_SCORE_UTILITIES_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_SCORE_UTILITIES_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -16,8 +16,6 @@ namespace shardok {
|
||||
|
||||
using ScoreValue = double;
|
||||
|
||||
using std::vector;
|
||||
|
||||
using GameState = net::eagle0::shardok::storage::fb::GameState;
|
||||
using PlayerInfo = net::eagle0::shardok::storage::fb::PlayerInfo;
|
||||
using Unit = net::eagle0::shardok::storage::fb::Unit;
|
||||
@@ -31,4 +29,4 @@ auto PlayerInfoForPid(const GameStateW &, PlayerId pid) -> const PlayerInfo *;
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AISCOREUTILITIES_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_SCORE_UTILITIES_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 1/10/22.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AISTRATEGY_HPP
|
||||
#define EAGLE0_AISTRATEGY_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_STRATEGY_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_STRATEGY_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackGroups.hpp"
|
||||
|
||||
@@ -34,4 +34,4 @@ auto CrossRiversStrategy(const CoordsSet& targetLocations) -> AIStrategy;
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AISTRATEGY_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_STRATEGY_HPP
|
||||
|
||||
@@ -116,15 +116,6 @@ auto CalculateTimeBudget(
|
||||
const auto clampedBudgetMs = std::clamp(budgetMs, 200.0, maxBudgetMs);
|
||||
const auto remainingBudget = std::chrono::milliseconds(static_cast<int64_t>(clampedBudgetMs));
|
||||
|
||||
// TEMPORARY DEBUG OUTPUT
|
||||
printf("[DEBUG CalculateTimeBudget] numCommands=%zu, msPerCommand=%.2f, budgetMs=%.2f, "
|
||||
"clampedBudgetMs=%.2f, isClose=%d\n",
|
||||
numCommands,
|
||||
msPerCommand,
|
||||
budgetMs,
|
||||
clampedBudgetMs,
|
||||
isClose);
|
||||
|
||||
// Get minimum depth requirement
|
||||
const size_t minDepth = settingsGetter.Backing().min_lookahead_turns();
|
||||
|
||||
@@ -134,4 +125,4 @@ auto CalculateTimeBudget(
|
||||
.isCloseToEnemy = isClose};
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
} // namespace shardok
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 07/04/25.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AITIMEBUDGET_HPP
|
||||
#define EAGLE0_AITIMEBUDGET_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_TIME_BUDGET_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_TIME_BUDGET_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
@@ -25,6 +25,11 @@ class AIEvaluationCounter {
|
||||
public:
|
||||
AIEvaluationCounter();
|
||||
~AIEvaluationCounter();
|
||||
AIEvaluationCounter(const AIEvaluationCounter&) = delete;
|
||||
auto operator=(const AIEvaluationCounter&) -> AIEvaluationCounter& = delete;
|
||||
AIEvaluationCounter(AIEvaluationCounter&&) = delete;
|
||||
auto operator=(AIEvaluationCounter&&) -> AIEvaluationCounter& = delete;
|
||||
|
||||
static int GetCurrentCount();
|
||||
};
|
||||
|
||||
@@ -41,11 +46,11 @@ struct AITimeBudget {
|
||||
// If isAllAiBattle is true, uses allAiBattleTimeBudgetMaximum instead of the normal maximum.
|
||||
auto CalculateTimeBudget(
|
||||
PlayerId playerId,
|
||||
const GameSettingsSPtr &settings,
|
||||
const GameStateW &state,
|
||||
const GameSettingsSPtr& settings,
|
||||
const GameStateW& state,
|
||||
size_t numCommands,
|
||||
bool isAllAiBattle) -> AITimeBudget;
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AITIMEBUDGET_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_TIME_BUDGET_HPP
|
||||
|
||||
@@ -11,10 +11,6 @@
|
||||
|
||||
namespace shardok {
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
using std::shared_ptr;
|
||||
|
||||
constexpr double kProfessionValue = 200;
|
||||
constexpr double kVigorScoreMultiplier = 5.0;
|
||||
constexpr double kCastleMultiplierBonus = 1.0;
|
||||
@@ -280,7 +276,7 @@ auto GetRangedAttackBonus(
|
||||
const int meteorRange,
|
||||
const double minVigorToCast,
|
||||
const ActionPoints archeryActionPointCost) -> double {
|
||||
vector<double> rangedAttackValues{};
|
||||
double bestRangedAttackValue = 0.0;
|
||||
|
||||
const auto &enemyUnits = isAttacker ? defenderUnits : attackerUnits;
|
||||
const auto &friendlyUnits = isAttacker ? attackerUnits : defenderUnits;
|
||||
@@ -296,7 +292,7 @@ auto GetRangedAttackBonus(
|
||||
net::eagle0::shardok::storage::fb::MultiroundMagicState_NONE)) {
|
||||
if (unit->attached_hero().profession_info().meteor_cast_state() ==
|
||||
net::eagle0::shardok::storage::fb::MultiroundMagicState_NONE) {
|
||||
rangedAttackValues.push_back(lightningValue(unit));
|
||||
bestRangedAttackValue = std::max(bestRangedAttackValue, lightningValue(unit));
|
||||
} else {
|
||||
const double mv = meteorValue(
|
||||
unit,
|
||||
@@ -306,14 +302,14 @@ auto GetRangedAttackBonus(
|
||||
friendlyUnits,
|
||||
meteorRange,
|
||||
minVigorToCast);
|
||||
rangedAttackValues.push_back(mv);
|
||||
bestRangedAttackValue = std::max(bestRangedAttackValue, mv);
|
||||
}
|
||||
}
|
||||
|
||||
if (attackLocations.EngineerLocations().Contains(unitLocation) && unit->has_attached_hero() &&
|
||||
unit->attached_hero().profession_info().profession() ==
|
||||
net::eagle0::shardok::storage::fb::Profession_ENGINEER) {
|
||||
rangedAttackValues.push_back(reduceValue(unit, terrain));
|
||||
bestRangedAttackValue = std::max(bestRangedAttackValue, reduceValue(unit, terrain));
|
||||
}
|
||||
|
||||
if (attackLocations.NecromancerLocations().Contains(unitLocation) &&
|
||||
@@ -321,23 +317,24 @@ auto GetRangedAttackBonus(
|
||||
unit->attached_hero().profession_info().profession() ==
|
||||
net::eagle0::shardok::storage::fb::Profession_NECROMANCER &&
|
||||
unit->attached_hero().control_info().controlled_unit_id() != -1) {
|
||||
rangedAttackValues.push_back(fearValue(enemyUnits));
|
||||
bestRangedAttackValue = std::max(bestRangedAttackValue, fearValue(enemyUnits));
|
||||
}
|
||||
|
||||
if (attackLocations.ArcheryLocations().Contains(unitLocation) &&
|
||||
unit->volleys_remaining() > 0) {
|
||||
rangedAttackValues.push_back(archeryValue(enemyUnits, canShootNow));
|
||||
bestRangedAttackValue =
|
||||
std::max(bestRangedAttackValue, archeryValue(enemyUnits, canShootNow));
|
||||
}
|
||||
|
||||
// Longbowmen in castle can archer adjacent
|
||||
if (attackLocations.AdjacentLocations().Contains(unitLocation) &&
|
||||
unit->volleys_remaining() > 0 && terrain->modifier().castle().present() &&
|
||||
unit->battalion().type() == net::eagle0::shardok::storage::fb::BattalionTypeId_LONGBOWMEN) {
|
||||
rangedAttackValues.push_back(archeryValue(enemyUnits, canShootNow));
|
||||
bestRangedAttackValue =
|
||||
std::max(bestRangedAttackValue, archeryValue(enemyUnits, canShootNow));
|
||||
}
|
||||
|
||||
if (rangedAttackValues.empty()) return 0.0;
|
||||
return *std::max_element(begin(rangedAttackValues), end(rangedAttackValues));
|
||||
return bestRangedAttackValue;
|
||||
}
|
||||
|
||||
auto UnitValue(
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 4/7/21.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIUNITSCORECALCULATOR_HPP
|
||||
#define EAGLE0_AIUNITSCORECALCULATOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_UNIT_SCORE_CALCULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_UNIT_SCORE_CALCULATOR_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/action_point_distances/ActionPointDistances.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/map/CoordsSet.hpp"
|
||||
@@ -14,9 +14,6 @@
|
||||
namespace shardok {
|
||||
|
||||
using net::eagle0::shardok::storage::fb::Unit;
|
||||
using std::shared_ptr;
|
||||
using std::vector;
|
||||
|
||||
using ScoreValue = double;
|
||||
class AttackLocations;
|
||||
|
||||
@@ -29,8 +26,8 @@ auto GetRangedAttackBonus(
|
||||
const Terrain *terrain,
|
||||
const AttackLocations &attackLocations,
|
||||
int roundsRemaining,
|
||||
const vector<const Unit *> &attackerUnits,
|
||||
const vector<const Unit *> &defenderUnits,
|
||||
const std::vector<const Unit *> &attackerUnits,
|
||||
const std::vector<const Unit *> &defenderUnits,
|
||||
int meteorRange,
|
||||
double minVigorToCast,
|
||||
ActionPoints archeryActionPointCost = 0) -> double;
|
||||
@@ -38,10 +35,10 @@ auto GetRangedAttackBonus(
|
||||
auto UnitValue(
|
||||
const Unit *unit,
|
||||
bool isAttacker,
|
||||
const vector<const Unit *> &attackerUnits,
|
||||
const std::vector<const Unit *> &attackerUnits,
|
||||
bool attackerWantsCastles,
|
||||
bool includeCastleBonus,
|
||||
const vector<const Unit *> &defenderUnits,
|
||||
const std::vector<const Unit *> &defenderUnits,
|
||||
const HexMap *map,
|
||||
int roundsRemaining,
|
||||
const AttackLocations &locationsThisSideCanAttackFrom,
|
||||
@@ -53,4 +50,4 @@ auto UnitValue(
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AIUNITSCORECALCULATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_UNIT_SCORE_CALCULATOR_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 12/29/21.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIWATERCROSSINGCALCULATOR_HPP
|
||||
#define EAGLE0_AIWATERCROSSINGCALCULATOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_WATER_CROSSING_CALCULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_WATER_CROSSING_CALCULATOR_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AICommonTypes.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
@@ -23,7 +23,8 @@ static inline void AssertValid(const Coords& c, const HexMap* hexMap) {
|
||||
|
||||
if (!valid) {
|
||||
std::stringstream str;
|
||||
str << "Invalid coords " << (int)c.row() << ", " << (int)c.column();
|
||||
str << "Invalid coords " << static_cast<int>(c.row()) << ", "
|
||||
<< static_cast<int>(c.column());
|
||||
|
||||
throw ShardokInternalErrorException(str.str());
|
||||
}
|
||||
@@ -85,4 +86,4 @@ auto WaterCrossingScore(
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AIWATERCROSSINGCALCULATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_WATER_CROSSING_CALCULATOR_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 1/5/22.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIWATERCROSSINGCOMMANDCHOOSER_HPP
|
||||
#define EAGLE0_AIWATERCROSSINGCOMMANDCHOOSER_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_AI_WATER_CROSSING_COMMAND_CHOOSER_HPP
|
||||
#define EAGLE0_SHARDOK_AI_AI_WATER_CROSSING_COMMAND_CHOOSER_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -44,4 +44,4 @@ public:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AIWATERCROSSINGCOMMANDCHOOSER_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_AI_WATER_CROSSING_COMMAND_CHOOSER_HPP
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "IterativeDeepeningAI.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
@@ -19,7 +21,21 @@
|
||||
|
||||
namespace shardok {
|
||||
|
||||
#define DEBUG_ITERATIVE_DEEPENING_TIMINGS 1
|
||||
constexpr bool kDebugIterativeDeepeningTimings = true;
|
||||
|
||||
namespace {
|
||||
|
||||
void WriteFixed(std::ostream& output, const double value, const int precision) {
|
||||
const auto flags = output.flags();
|
||||
const auto originalPrecision = output.precision();
|
||||
|
||||
output << std::fixed << std::setprecision(precision) << value;
|
||||
|
||||
output.flags(flags);
|
||||
output.precision(originalPrecision);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IterativeDeepeningAI::IterativeDeepeningAI(
|
||||
const PlayerId playerId,
|
||||
@@ -54,9 +70,9 @@ auto IterativeDeepeningAI::IterativeSearch(
|
||||
// DEBUG: Clear TT to see if that's causing the suspicious depth reaching
|
||||
// g_transpositionTable.clear(); // Uncomment to test without cross-search caching
|
||||
if (commands->empty()) {
|
||||
#if DEBUG_ITERATIVE_DEEPENING_TIMINGS
|
||||
printf("ID AI: Commands are empty, returning early\n");
|
||||
#endif
|
||||
if (kDebugIterativeDeepeningTimings) {
|
||||
std::cout << "ID AI: Commands are empty, returning early\n";
|
||||
}
|
||||
result.searchCompleted = true;
|
||||
return result;
|
||||
}
|
||||
@@ -168,23 +184,23 @@ auto IterativeDeepeningAI::IterativeSearch(
|
||||
|
||||
// Log if best command changed from previous depth
|
||||
if (currentDepth > 1 && currentBestCommand != previousBestCommand) {
|
||||
#if DEBUG_ITERATIVE_DEEPENING_TIMINGS
|
||||
printf("ID AI: Best command changed at depth %lu:\n", currentDepth);
|
||||
printf(" Depth %lu best: command %zu (score %.2f) - type: %s\n",
|
||||
currentDepth - 1,
|
||||
previousBestCommand,
|
||||
scoresByDepth[previousBestCommand][currentDepth - 1],
|
||||
net::eagle0::shardok::common::CommandType_Name(
|
||||
(*commands)[previousBestCommand]->GetCommandType())
|
||||
.c_str());
|
||||
printf(" Depth %lu best: command %zu (score %.2f) - type: %s\n",
|
||||
currentDepth,
|
||||
currentBestCommand,
|
||||
currentBestScore,
|
||||
net::eagle0::shardok::common::CommandType_Name(
|
||||
(*commands)[currentBestCommand]->GetCommandType())
|
||||
.c_str());
|
||||
#endif
|
||||
if (kDebugIterativeDeepeningTimings) {
|
||||
std::cout << "ID AI: Best command changed at depth " << currentDepth << ":\n"
|
||||
<< " Depth " << currentDepth - 1 << " best: command "
|
||||
<< previousBestCommand << " (score ";
|
||||
WriteFixed(std::cout, scoresByDepth[previousBestCommand][currentDepth - 1], 2);
|
||||
std::cout << ") - type: "
|
||||
<< net::eagle0::shardok::common::CommandType_Name(
|
||||
(*commands)[previousBestCommand]->GetCommandType())
|
||||
<< '\n'
|
||||
<< " Depth " << currentDepth << " best: command "
|
||||
<< currentBestCommand << " (score ";
|
||||
WriteFixed(std::cout, currentBestScore, 2);
|
||||
std::cout << ") - type: "
|
||||
<< net::eagle0::shardok::common::CommandType_Name(
|
||||
(*commands)[currentBestCommand]->GetCommandType())
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
|
||||
previousBestCommand = currentBestCommand;
|
||||
@@ -239,9 +255,9 @@ auto IterativeDeepeningAI::IterativeSearch(
|
||||
static_cast<double>(initialBudgetMs.count());
|
||||
|
||||
if (budgetUsedPercent > 0.5) {
|
||||
printf("ID AI: Stopping after depth %lu - used %.1f%% of time budget\n",
|
||||
currentDepth,
|
||||
budgetUsedPercent * 100);
|
||||
std::cout << "ID AI: Stopping after depth " << currentDepth << " - used ";
|
||||
WriteFixed(std::cout, budgetUsedPercent * 100, 1);
|
||||
std::cout << "% of time budget\n";
|
||||
completionReason = EvaluationCompletionReason::NOT_ENOUGH_TIME_TO_CONTINUE;
|
||||
break;
|
||||
}
|
||||
@@ -271,9 +287,9 @@ auto IterativeDeepeningAI::IterativeSearch(
|
||||
// Validation: if completion reason is RAN_OUT_OF_COMMANDS, evaluation should be 100%
|
||||
if (completionReason == EvaluationCompletionReason::RAN_OUT_OF_COMMANDS &&
|
||||
result.commandCountEvaluated < result.availableCommandCount) {
|
||||
printf("ERROR: Completion reason RAN_OUT_OF_COMMANDS but evaluation %lu/%zu < 100%%\n",
|
||||
result.commandCountEvaluated,
|
||||
result.availableCommandCount);
|
||||
std::cerr << "ERROR: Completion reason RAN_OUT_OF_COMMANDS but evaluation "
|
||||
<< result.commandCountEvaluated << '/' << result.availableCommandCount
|
||||
<< " < 100%\n";
|
||||
}
|
||||
|
||||
// Print TranspositionTable statistics
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 07/04/25.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_ITERATIVEDEEPENINGAI_HPP
|
||||
#define EAGLE0_ITERATIVEDEEPENINGAI_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_ITERATIVE_DEEPENING_AI_HPP
|
||||
#define EAGLE0_SHARDOK_AI_ITERATIVE_DEEPENING_AI_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
@@ -111,4 +111,4 @@ private:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_ITERATIVEDEEPENINGAI_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_ITERATIVE_DEEPENING_AI_HPP
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
#include "ShardokAIClient.hpp"
|
||||
|
||||
#define DEBUG_FLEE_DECISIONS
|
||||
|
||||
// Enable to dump game state and debug tree to /tmp for debugging
|
||||
// #define ENABLE_MCTS_DEBUG_DUMP
|
||||
|
||||
@@ -17,9 +15,12 @@
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -43,6 +44,7 @@ using net::eagle0::shardok::api::ActionResultView;
|
||||
using net::eagle0::shardok::api::GameStateView;
|
||||
|
||||
static constexpr bool kPerformanceLogging = true;
|
||||
static constexpr bool kDebugFleeDecisions = true;
|
||||
|
||||
void ApplyUpdate(GameStateView & /*currentView*/, const ActionResultView & /*update*/) {}
|
||||
|
||||
@@ -175,25 +177,20 @@ auto ShardokAIClient::StandardChooseCommandIndex(
|
||||
|
||||
// Verify that the AI's guessed state produces the same available commands as reality
|
||||
if (commandCount != realAvailableCommands->size()) {
|
||||
fprintf(stderr,
|
||||
"AI command count mismatch: guessed=%zu real=%zu player=%d round=%d\n",
|
||||
commandCount,
|
||||
realAvailableCommands->size(),
|
||||
playerId,
|
||||
guessedState->current_round());
|
||||
std::cerr << "AI command count mismatch: guessed=" << commandCount
|
||||
<< " real=" << realAvailableCommands->size() << " player=" << playerId
|
||||
<< " round=" << guessedState->current_round() << "\n";
|
||||
|
||||
const auto maxDump = std::max(commandCount, realAvailableCommands->size());
|
||||
for (size_t i = 0; i < maxDump; i++) {
|
||||
const auto *realName = i < realAvailableCommands->size()
|
||||
? net::eagle0::shardok::common::CommandType_Name(
|
||||
(*realAvailableCommands)[i]->GetCommandType())
|
||||
.c_str()
|
||||
: "(none)";
|
||||
const auto *guessedName = i < commandCount
|
||||
? net::eagle0::shardok::common::CommandType_Name(
|
||||
(*guessedCommands)[i]->GetCommandType())
|
||||
.c_str()
|
||||
: "(none)";
|
||||
const auto realName = i < realAvailableCommands->size()
|
||||
? net::eagle0::shardok::common::CommandType_Name(
|
||||
(*realAvailableCommands)[i]->GetCommandType())
|
||||
: "(none)";
|
||||
const auto guessedName = i < commandCount
|
||||
? net::eagle0::shardok::common::CommandType_Name(
|
||||
(*guessedCommands)[i]->GetCommandType())
|
||||
: "(none)";
|
||||
const int realActor = i < realAvailableCommands->size()
|
||||
? (*realAvailableCommands)[i]->GetActorUnitId()
|
||||
: -1;
|
||||
@@ -207,17 +204,10 @@ auto ShardokAIClient::StandardChooseCommandIndex(
|
||||
: -1;
|
||||
const int guessedRow = i < commandCount ? (*guessedCommands)[i]->GetTargetRow() : -1;
|
||||
const int guessedCol = i < commandCount ? (*guessedCommands)[i]->GetTargetColumn() : -1;
|
||||
fprintf(stderr,
|
||||
" [%zu] real: %s unit=%d (%d,%d) guessed: %s unit=%d (%d,%d)\n",
|
||||
i,
|
||||
realName,
|
||||
realActor,
|
||||
realRow,
|
||||
realCol,
|
||||
guessedName,
|
||||
guessedActor,
|
||||
guessedRow,
|
||||
guessedCol);
|
||||
std::cerr << " [" << i << "] real: " << realName << " unit=" << realActor << " ("
|
||||
<< realRow << "," << realCol << ") guessed: " << guessedName
|
||||
<< " unit=" << guessedActor << " (" << guessedRow << "," << guessedCol
|
||||
<< ")\n";
|
||||
}
|
||||
|
||||
// Find positions in real commands that are missing from guessed commands
|
||||
@@ -239,45 +229,34 @@ auto ShardokAIClient::StandardChooseCommandIndex(
|
||||
for (const auto &[unitId, positions] : realPositions) {
|
||||
for (const auto &[row, col] : positions) {
|
||||
if (!guessedPositions[unitId].contains({row, col})) {
|
||||
fprintf(stderr,
|
||||
" Missing from guessed: unit=%d pos=(%d,%d)",
|
||||
unitId,
|
||||
row,
|
||||
col);
|
||||
std::cerr << " Missing from guessed: unit=" << unitId << " pos=(" << row << ","
|
||||
<< col << ")";
|
||||
const Coords missingCoords(row, col);
|
||||
const auto *occupant = guessedState.GetOccupant(missingCoords);
|
||||
if (occupant) {
|
||||
fprintf(stderr,
|
||||
" -> guessed occupant: unit_id=%d player=%d status=%d "
|
||||
"loc=(%d,%d) hidden=%d battalion_size=%d\n",
|
||||
occupant->unit_id(),
|
||||
occupant->player_id(),
|
||||
static_cast<int>(occupant->status()),
|
||||
occupant->location().row(),
|
||||
occupant->location().column(),
|
||||
occupant->hidden(),
|
||||
occupant->battalion().size());
|
||||
std::cerr << " -> guessed occupant: unit_id=" << occupant->unit_id()
|
||||
<< " player=" << occupant->player_id()
|
||||
<< " status=" << static_cast<int>(occupant->status()) << " loc=("
|
||||
<< occupant->location().row() << ","
|
||||
<< occupant->location().column()
|
||||
<< ") hidden=" << occupant->hidden()
|
||||
<< " battalion_size=" << occupant->battalion().size() << "\n";
|
||||
} else {
|
||||
fprintf(stderr, " -> no occupant in guessed state\n");
|
||||
std::cerr << " -> no occupant in guessed state\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dump all guessed state units for full picture
|
||||
fprintf(stderr, " Guessed state units (%u total):\n", guessedState->units()->size());
|
||||
std::cerr << " Guessed state units (" << guessedState->units()->size() << " total):\n";
|
||||
for (size_t i = 0; i < guessedState->units()->size(); i++) {
|
||||
const auto *u = guessedState->units()->Get(static_cast<unsigned int>(i));
|
||||
fprintf(stderr,
|
||||
" unit_id=%d player=%d status=%d loc=(%d,%d) hidden=%d "
|
||||
"starting_pos_idx=%d\n",
|
||||
u->unit_id(),
|
||||
u->player_id(),
|
||||
static_cast<int>(u->status()),
|
||||
u->location().row(),
|
||||
u->location().column(),
|
||||
u->hidden(),
|
||||
u->starting_position_index());
|
||||
std::cerr << " unit_id=" << u->unit_id() << " player=" << u->player_id()
|
||||
<< " status=" << static_cast<int>(u->status()) << " loc=("
|
||||
<< u->location().row() << "," << u->location().column()
|
||||
<< ") hidden=" << u->hidden()
|
||||
<< " starting_pos_idx=" << u->starting_position_index() << "\n";
|
||||
}
|
||||
|
||||
throw ShardokInternalErrorException(
|
||||
@@ -357,9 +336,9 @@ auto ShardokAIClient::StandardChooseCommandIndex(
|
||||
|
||||
// Write the flatbuffer game state to file using SaveTo method
|
||||
if (guessedState.SaveTo(statePath)) {
|
||||
printf("Game state dumped to: %s\n", statePath.c_str());
|
||||
std::cout << "Game state dumped to: " << statePath << '\n';
|
||||
} else {
|
||||
printf("Failed to dump game state to: %s\n", statePath.c_str());
|
||||
std::cout << "Failed to dump game state to: " << statePath << '\n';
|
||||
}
|
||||
#endif // ENABLE_MCTS_DEBUG_DUMP
|
||||
|
||||
@@ -397,19 +376,17 @@ auto ShardokAIClient::StandardChooseCommandIndex(
|
||||
|
||||
if constexpr (kPerformanceLogging) {
|
||||
if (result.commandCountEvaluated < result.availableCommandCount) {
|
||||
printf("ID AI: Depth %d - evaluated %lu/%zu commands\n",
|
||||
result.depthAchieved,
|
||||
result.commandCountEvaluated,
|
||||
result.availableCommandCount);
|
||||
std::cout << "ID AI: Depth " << result.depthAchieved << " - evaluated "
|
||||
<< result.commandCountEvaluated << '/' << result.availableCommandCount
|
||||
<< " commands\n";
|
||||
}
|
||||
const auto chosenCommandType =
|
||||
(*realAvailableCommands)[result.chosenIndex]->GetCommandType();
|
||||
printf("ID AI: Search complete - achieved depth %d for best command %zu (%s)\n",
|
||||
result.depthAchieved,
|
||||
result.chosenIndex,
|
||||
net::eagle0::shardok::common::CommandType_Name(chosenCommandType).c_str());
|
||||
std::cout << "ID AI: Search complete - achieved depth " << result.depthAchieved
|
||||
<< " for best command " << result.chosenIndex << " ("
|
||||
<< net::eagle0::shardok::common::CommandType_Name(chosenCommandType) << ")\n";
|
||||
|
||||
fflush(stdout);
|
||||
std::cout << std::flush;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -468,12 +445,7 @@ auto ShardokAIClient::FinalRoundAttackerChooseCommandIndex(
|
||||
maxRounds,
|
||||
minimumFleeOddsThreshold,
|
||||
desperateFleeThreshold,
|
||||
#ifdef DEBUG_FLEE_DECISIONS
|
||||
true // Enable debug logging
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
);
|
||||
kDebugFleeDecisions);
|
||||
|
||||
if (fleeDecision.shouldFlee) {
|
||||
CommandChoiceResults results{};
|
||||
@@ -516,7 +488,7 @@ auto ShardokAIClient::ChooseCommandIndex(
|
||||
totalChoices++;
|
||||
|
||||
if (totalChoices % 1000 == 0) {
|
||||
printf("TYPES CHOSEN:\n");
|
||||
std::cout << "TYPES CHOSEN:\n";
|
||||
vector<std::pair<int, CommandType>> choices{};
|
||||
for (int i = 0; i <= net::eagle0::shardok::common::CommandType_MAX; i++) {
|
||||
if (typeChosenCount[i] != 0) {
|
||||
@@ -527,9 +499,9 @@ auto ShardokAIClient::ChooseCommandIndex(
|
||||
std::ranges::sort(choices);
|
||||
std::ranges::reverse(choices);
|
||||
for (const auto &[index, choice] : choices) {
|
||||
printf("%5d %s\n", index, CommandType_Name(choice).c_str());
|
||||
std::cout << std::setw(5) << index << ' ' << CommandType_Name(choice) << '\n';
|
||||
}
|
||||
printf("\n\n");
|
||||
std::cout << "\n\n";
|
||||
}
|
||||
|
||||
return results;
|
||||
@@ -539,7 +511,7 @@ auto ShardokAIClient::ChooseCommandIndex(const ShardokEngine &engine) const
|
||||
-> CommandChoiceResults {
|
||||
if (const auto &availableCommands = engine.GetAvailableCommandsForAIPlayer(playerId);
|
||||
availableCommands->empty()) {
|
||||
printf("no commands for player %d\n", playerId);
|
||||
std::cout << "no commands for player " << playerId << '\n';
|
||||
throw ShardokInternalErrorException(
|
||||
"Asked to choose a command, but there are none available");
|
||||
} else {
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// Copyright © 2017 none. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ShardokAIClient_hpp
|
||||
#define ShardokAIClient_hpp
|
||||
#ifndef EAGLE0_SHARDOK_AI_SHARDOK_AI_CLIENT_HPP
|
||||
#define EAGLE0_SHARDOK_AI_SHARDOK_AI_CLIENT_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -95,4 +95,4 @@ public:
|
||||
};
|
||||
} // namespace shardok
|
||||
|
||||
#endif /* ShardokAIClient_hpp */
|
||||
#endif // EAGLE0_SHARDOK_AI_SHARDOK_AI_CLIENT_HPP
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "TranspositionTable.hpp"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
namespace shardok {
|
||||
|
||||
@@ -18,7 +18,7 @@ TranspositionTable::TranspositionTable() : table(TABLE_SIZE) {
|
||||
clear();
|
||||
}
|
||||
|
||||
uint64_t TranspositionTable::hashGameState(const GameStateW& state) const {
|
||||
auto TranspositionTable::hashGameState(const GameStateW& state) const -> uint64_t {
|
||||
// The FlatBuffer is contiguous in memory and units are sorted by ID,
|
||||
// so we can just hash the raw bytes for order-independent hashing
|
||||
// Use ComputeFNV1aHash to avoid creating a string copy
|
||||
@@ -100,15 +100,23 @@ void TranspositionTable::clear() {
|
||||
current_age = 0;
|
||||
}
|
||||
|
||||
void TranspositionTable::printStats() const {
|
||||
printf("TranspositionTable Stats:\n");
|
||||
printf(" Probes: %" PRIu64 "\n", stats.probes.load());
|
||||
printf(" Hits: %" PRIu64 " (%.1f%%)\n", stats.hits.load(), stats.hitRate());
|
||||
printf(" Stores: %" PRIu64 "\n", stats.stores.load());
|
||||
printf(" Collisions: %" PRIu64 "\n", stats.collisions.load());
|
||||
printf(" Table size: %zu entries (%.1f MB)\n",
|
||||
TABLE_SIZE,
|
||||
(TABLE_SIZE * sizeof(TTEntry)) / (1024.0 * 1024.0));
|
||||
void TranspositionTable::printStats() const { printStats(std::cout); }
|
||||
|
||||
void TranspositionTable::printStats(std::ostream& output) const {
|
||||
const auto flags = output.flags();
|
||||
const auto precision = output.precision();
|
||||
|
||||
output << "TranspositionTable Stats:\n"
|
||||
<< " Probes: " << stats.probes.load() << '\n'
|
||||
<< " Hits: " << stats.hits.load() << " (" << std::fixed << std::setprecision(1)
|
||||
<< stats.hitRate() << "%)\n"
|
||||
<< " Stores: " << stats.stores.load() << '\n'
|
||||
<< " Collisions: " << stats.collisions.load() << '\n'
|
||||
<< " Table size: " << TABLE_SIZE << " entries ("
|
||||
<< (TABLE_SIZE * sizeof(TTEntry)) / (1024.0 * 1024.0) << " MB)\n";
|
||||
|
||||
output.flags(flags);
|
||||
output.precision(precision);
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
} // namespace shardok
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
// TranspositionTable.hpp - Cache for game state evaluations to avoid redundant calculations
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_TRANSPOSITIONTABLE_HPP
|
||||
#define EAGLE0_TRANSPOSITIONTABLE_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_TRANSPOSITION_TABLE_HPP
|
||||
#define EAGLE0_SHARDOK_AI_TRANSPOSITION_TABLE_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
@@ -27,8 +28,8 @@ public:
|
||||
std::atomic<uint64_t> stores{0};
|
||||
std::atomic<uint64_t> collisions{0};
|
||||
|
||||
double hitRate() const {
|
||||
uint64_t p = probes.load();
|
||||
[[nodiscard]] auto hitRate() const -> double {
|
||||
const uint64_t p = probes.load();
|
||||
return p > 0 ? (100.0 * hits.load() / p) : 0.0;
|
||||
}
|
||||
|
||||
@@ -59,7 +60,7 @@ private:
|
||||
std::atomic<uint16_t> current_age{0};
|
||||
|
||||
// Hash function for FlatBuffer game state
|
||||
uint64_t hashGameState(const GameStateW& state) const;
|
||||
[[nodiscard]] auto hashGameState(const GameStateW& state) const -> uint64_t;
|
||||
|
||||
public:
|
||||
TranspositionTable();
|
||||
@@ -77,10 +78,11 @@ public:
|
||||
void incrementAge() { current_age++; }
|
||||
|
||||
// Get statistics
|
||||
const Stats& getStats() const { return stats; }
|
||||
[[nodiscard]] auto getStats() const -> const Stats& { return stats; }
|
||||
|
||||
// Print statistics to stdout
|
||||
void printStats() const;
|
||||
void printStats(std::ostream& output) const;
|
||||
};
|
||||
|
||||
// Global instance for the AI to use
|
||||
@@ -88,4 +90,4 @@ extern TranspositionTable g_transpositionTable;
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_TRANSPOSITIONTABLE_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_TRANSPOSITION_TABLE_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Shardok-specific MCTS AI that wraps the abstract implementation
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_SHARDOK_MCTSAI_HPP
|
||||
#define EAGLE0_SHARDOK_MCTSAI_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_MCTS_SHARDOK_MCTSAI_HPP
|
||||
#define EAGLE0_SHARDOK_AI_MCTS_SHARDOK_MCTSAI_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@@ -68,4 +68,4 @@ private:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_SHARDOK_MCTSAI_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_MCTS_SHARDOK_MCTSAI_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Shardok-specific action adapter for MCTS
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_SHARDOK_ACTION_HPP
|
||||
#define EAGLE0_SHARDOK_ACTION_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_ACTION_HPP
|
||||
#define EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_ACTION_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -58,4 +58,4 @@ private:
|
||||
|
||||
} // namespace shardok::mcts
|
||||
|
||||
#endif // EAGLE0_SHARDOK_ACTION_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_ACTION_HPP
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
#include "ShardokAction.hpp"
|
||||
@@ -35,6 +37,23 @@ auto LegalActionsCacheKey(const uint64_t stateHash, const bool rootIsDefender) -
|
||||
return rootIsDefender ? stateHash ^ kDefenderRoleHash : stateHash;
|
||||
}
|
||||
|
||||
auto SortActionsByWeight(
|
||||
std::vector<std::unique_ptr<MCTSAction>> actions,
|
||||
const std::vector<double>& weights) -> std::vector<std::unique_ptr<MCTSAction>> {
|
||||
std::vector<size_t> sortedIndices(actions.size());
|
||||
std::iota(sortedIndices.begin(), sortedIndices.end(), 0);
|
||||
|
||||
std::sort(sortedIndices.begin(), sortedIndices.end(), [&weights](size_t a, size_t b) {
|
||||
return weights[a] > weights[b];
|
||||
});
|
||||
|
||||
std::vector<std::unique_ptr<MCTSAction>> sortedActions;
|
||||
sortedActions.reserve(actions.size());
|
||||
for (size_t idx : sortedIndices) { sortedActions.push_back(std::move(actions[idx])); }
|
||||
|
||||
return sortedActions;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Shared cache for legal actions (uses lock-free parallel hash map for thread safety)
|
||||
@@ -280,19 +299,7 @@ std::vector<std::unique_ptr<MCTSAction>> ShardokGameEngine::getLegalActions(
|
||||
|
||||
// Sort actions by weight (descending) to ensure MCTS explores high-value actions first
|
||||
const std::vector<double> weights = getActionWeights(actions, state);
|
||||
|
||||
std::vector<size_t> sortedIndices(actions.size());
|
||||
std::iota(sortedIndices.begin(), sortedIndices.end(), 0);
|
||||
|
||||
std::sort(sortedIndices.begin(), sortedIndices.end(), [&weights](size_t a, size_t b) {
|
||||
return weights[a] > weights[b];
|
||||
});
|
||||
|
||||
std::vector<std::unique_ptr<MCTSAction>> sortedActions;
|
||||
sortedActions.reserve(actions.size());
|
||||
for (size_t idx : sortedIndices) { sortedActions.push_back(std::move(actions[idx])); }
|
||||
|
||||
return sortedActions;
|
||||
return SortActionsByWeight(std::move(actions), weights);
|
||||
}
|
||||
|
||||
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
|
||||
@@ -353,20 +360,7 @@ std::vector<std::unique_ptr<MCTSAction>> ShardokGameEngine::getLegalActions(
|
||||
// get explored deeply. Original indices are preserved in ShardokAction::getIndex()
|
||||
const std::vector<double> weights = getActionWeights(actions, state);
|
||||
|
||||
// Create index vector for sorting
|
||||
std::vector<size_t> sortedIndices(actions.size());
|
||||
std::iota(sortedIndices.begin(), sortedIndices.end(), 0);
|
||||
|
||||
// Sort indices by weight (descending)
|
||||
std::sort(sortedIndices.begin(), sortedIndices.end(), [&weights](size_t a, size_t b) {
|
||||
return weights[a] > weights[b];
|
||||
});
|
||||
|
||||
// Reorder actions according to sorted indices
|
||||
std::vector<std::unique_ptr<MCTSAction>> sortedActions;
|
||||
sortedActions.reserve(actions.size());
|
||||
for (size_t idx : sortedIndices) { sortedActions.push_back(std::move(actions[idx])); }
|
||||
actions = std::move(sortedActions);
|
||||
actions = SortActionsByWeight(std::move(actions), weights);
|
||||
|
||||
const auto actionsEnd = std::chrono::high_resolution_clock::now();
|
||||
timeInLegalActionsComputation_.fetch_add(
|
||||
@@ -509,7 +503,9 @@ size_t ShardokGameEngine::mapFilteredIndexToOriginal(
|
||||
return shardokAction->getIndex();
|
||||
}
|
||||
|
||||
void ShardokGameEngine::reportCacheStatistics() const {
|
||||
void ShardokGameEngine::reportCacheStatistics() const { reportCacheStatistics(std::cout); }
|
||||
|
||||
void ShardokGameEngine::reportCacheStatistics(std::ostream& output) const {
|
||||
const uint64_t hits = cacheHits_.load(std::memory_order_relaxed);
|
||||
const uint64_t misses = cacheMisses_.load(std::memory_order_relaxed);
|
||||
const uint64_t hashTime = timeInHashComputation_.load(std::memory_order_relaxed);
|
||||
@@ -523,27 +519,29 @@ void ShardokGameEngine::reportCacheStatistics() const {
|
||||
const double avgActionsTimeUs =
|
||||
misses > 0 ? static_cast<double>(actionsTime) / static_cast<double>(misses) : 0.0;
|
||||
|
||||
printf("Legal Actions Cache Stats:\n");
|
||||
printf(" Lookups: %llu hits, %llu misses, %.1f%% hit rate, %zu entries\n",
|
||||
static_cast<unsigned long long>(hits),
|
||||
static_cast<unsigned long long>(misses),
|
||||
hitRate * 100.0,
|
||||
legalActionsCache_.size());
|
||||
printf(" Timing: %.2f us avg hash, %.2f us avg actions (on miss)\n",
|
||||
avgHashTimeUs,
|
||||
avgActionsTimeUs);
|
||||
printf(" Total time: %.2f ms in hash, %.2f ms in actions\n",
|
||||
hashTime / 1000.0,
|
||||
actionsTime / 1000.0);
|
||||
|
||||
// Calculate if transposition table is worth it
|
||||
const double timeWithCache = hashTime + actionsTime;
|
||||
const double timeWithoutCache =
|
||||
avgActionsTimeUs * static_cast<double>(totalLookups); // All lookups recompute
|
||||
const double savings = (timeWithoutCache - timeWithCache) / timeWithoutCache * 100.0;
|
||||
printf(" Cache savings: %.1f%% vs. no cache (%.2f ms saved)\n",
|
||||
savings,
|
||||
(timeWithoutCache - timeWithCache) / 1000.0);
|
||||
|
||||
const auto flags = output.flags();
|
||||
const auto precision = output.precision();
|
||||
|
||||
output << "Legal Actions Cache Stats:\n"
|
||||
<< " Lookups: " << hits << " hits, " << misses << " misses, " << std::fixed
|
||||
<< std::setprecision(1) << hitRate * 100.0 << "% hit rate, "
|
||||
<< legalActionsCache_.size() << " entries\n"
|
||||
<< " Timing: " << std::setprecision(2) << avgHashTimeUs << " us avg hash, "
|
||||
<< avgActionsTimeUs << " us avg actions (on miss)\n"
|
||||
<< " Total time: " << hashTime / 1000.0 << " ms in hash, " << actionsTime / 1000.0
|
||||
<< " ms in actions\n"
|
||||
<< " Cache savings: " << std::setprecision(1) << savings << "% vs. no cache ("
|
||||
<< std::setprecision(2) << (timeWithoutCache - timeWithCache) / 1000.0
|
||||
<< " ms saved)\n";
|
||||
|
||||
output.flags(flags);
|
||||
output.precision(precision);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
// Shardok-specific game engine adapter for MCTS
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_SHARDOK_GAME_ENGINE_HPP
|
||||
#define EAGLE0_SHARDOK_GAME_ENGINE_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_GAME_ENGINE_HPP
|
||||
#define EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_GAME_ENGINE_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <gtl/phmap.hpp>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/common/mcts/abstract/MCTSGameEngine.hpp"
|
||||
@@ -92,6 +93,7 @@ public:
|
||||
|
||||
// Report transposition table statistics
|
||||
void reportCacheStatistics() const;
|
||||
void reportCacheStatistics(std::ostream& output) const;
|
||||
|
||||
// Reset cache statistics
|
||||
void resetCacheStatistics();
|
||||
@@ -141,4 +143,4 @@ public:
|
||||
} // namespace mcts
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_SHARDOK_GAME_ENGINE_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_GAME_ENGINE_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Shardok-specific game state adapter for MCTS
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_SHARDOK_GAME_STATE_HPP
|
||||
#define EAGLE0_SHARDOK_GAME_STATE_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_GAME_STATE_HPP
|
||||
#define EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_GAME_STATE_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -80,4 +80,4 @@ private:
|
||||
} // namespace mcts
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_SHARDOK_GAME_STATE_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_GAME_STATE_HPP
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Factory for creating Shardok-specific MCTS components
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_SHARDOK_MCTS_FACTORY_HPP
|
||||
#define EAGLE0_SHARDOK_MCTS_FACTORY_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_MCTS_FACTORY_HPP
|
||||
#define EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_MCTS_FACTORY_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -67,4 +67,4 @@ public:
|
||||
} // namespace mcts
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_SHARDOK_MCTS_FACTORY_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_MCTS_ADAPTERS_SHARDOK_MCTS_FACTORY_HPP
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
// Created by dancrosby on 3/4/20.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AISCORECALCULATOR_HPP
|
||||
#define EAGLE0_AISCORECALCULATOR_HPP
|
||||
|
||||
#include <future>
|
||||
#ifndef EAGLE0_SHARDOK_AI_SCORE_AI_SCORE_CALCULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_SCORE_AI_SCORE_CALCULATOR_HPP
|
||||
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai/AIAttackLocations.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/GameStateW.hpp"
|
||||
@@ -16,8 +14,6 @@
|
||||
namespace shardok {
|
||||
|
||||
using shardok::PlayerId;
|
||||
using std::future;
|
||||
using std::vector;
|
||||
|
||||
using ScoreValue = double;
|
||||
|
||||
@@ -53,4 +49,4 @@ public:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AISCORECALCULATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_SCORE_AI_SCORE_CALCULATOR_HPP
|
||||
|
||||
@@ -160,6 +160,8 @@ auto AttackerHoldsCriticalTilesVictoryScore(
|
||||
ActionPoints braveWaterCost) -> ScoreValue {
|
||||
vector<const Unit*> playerUnits{};
|
||||
vector<const Unit*> claimablePlayerUnits{};
|
||||
playerUnits.reserve(gameState->units()->size());
|
||||
claimablePlayerUnits.reserve(gameState->units()->size());
|
||||
for (const Unit* unit : *gameState->units()) {
|
||||
if (unit->status() == net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT &&
|
||||
unit->player_id() == player->player_id()) {
|
||||
@@ -260,6 +262,8 @@ auto LastPlayerStandingVictoryScore(
|
||||
|
||||
vector<const Unit*> playerUnits{};
|
||||
vector<const Unit*> otherUnits{};
|
||||
playerUnits.reserve(gameState->units()->size());
|
||||
otherUnits.reserve(gameState->units()->size());
|
||||
|
||||
for (const Unit* unit : *gameState->units()) {
|
||||
if (unit->status() != net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) continue;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 4/11/21.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIVICTORYCONDITIONSCORECALCULATOR_HPP
|
||||
#define EAGLE0_AIVICTORYCONDITIONSCORECALCULATOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_SCORE_AI_VICTORY_CONDITION_SCORE_CALCULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_SCORE_AI_VICTORY_CONDITION_SCORE_CALCULATOR_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace shardok {
|
||||
|
||||
using GameState = net::eagle0::shardok::storage::fb::GameState;
|
||||
using net::eagle0::shardok::storage::fb::PlayerInfo;
|
||||
using std::vector;
|
||||
|
||||
using ScoreValue = double;
|
||||
|
||||
@@ -48,4 +47,4 @@ auto LastPlayerStandingVictoryScore(
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AIVICTORYCONDITIONSCORECALCULATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_SCORE_AI_VICTORY_CONDITION_SCORE_CALCULATOR_HPP
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// Uses bounded linear scoring tuned for MCTS exploration/exploitation balance
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_MCTSOPTIMIZEDAISCORECALCULATOR_HPP
|
||||
#define EAGLE0_MCTSOPTIMIZEDAISCORECALCULATOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_SCORE_MCTS_OPTIMIZED_AI_SCORE_CALCULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_SCORE_MCTS_OPTIMIZED_AI_SCORE_CALCULATOR_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -29,4 +29,4 @@ using ALCache = std::unique_ptr<AttackLocationsCache>;
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_MCTSOPTIMIZEDAISCORECALCULATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_SCORE_MCTS_OPTIMIZED_AI_SCORE_CALCULATOR_HPP
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "private/AIScoreCalculatorSharedUtilities.hpp"
|
||||
@@ -41,9 +44,6 @@ class StandardAIScoreCalculator;
|
||||
// Anonymous namespace for helper functions that don't need access to scorer
|
||||
namespace {
|
||||
|
||||
#define LOGGING_ 0
|
||||
#define PERFORMANCE_LOGGING_ 0
|
||||
|
||||
// Performance logging for AttackerScoreForState
|
||||
struct AttackerScorePerformanceLogger {
|
||||
static constexpr int LOG_INTERVAL = 100000;
|
||||
@@ -60,11 +60,12 @@ struct AttackerScorePerformanceLogger {
|
||||
if (callCount.load() % LOG_INTERVAL == 0) {
|
||||
double intervalAvg = intervalTime.load() / LOG_INTERVAL;
|
||||
double overallAvg = totalTime.load() / callCount.load();
|
||||
printf("AttackerScoreForState: %d calls, last %d avg: %.1f µs, overall avg: %.1f µs\n",
|
||||
callCount.load(),
|
||||
LOG_INTERVAL,
|
||||
intervalAvg * 1000000.0,
|
||||
overallAvg * 1000000.0);
|
||||
std::ostringstream message;
|
||||
message << std::fixed << std::setprecision(1)
|
||||
<< "AttackerScoreForState: " << callCount.load() << " calls, last "
|
||||
<< LOG_INTERVAL << " avg: " << intervalAvg * 1000000.0
|
||||
<< " µs, overall avg: " << overallAvg * 1000000.0 << " µs\n";
|
||||
std::cout << message.str();
|
||||
intervalTime.store(0.0); // Reset for next interval
|
||||
}
|
||||
}
|
||||
@@ -143,6 +144,10 @@ private:
|
||||
|
||||
public:
|
||||
AttackerScoreTimer() : startTime(std::chrono::high_resolution_clock::now()) {}
|
||||
AttackerScoreTimer(const AttackerScoreTimer &) = delete;
|
||||
auto operator=(const AttackerScoreTimer &) -> AttackerScoreTimer & = delete;
|
||||
AttackerScoreTimer(AttackerScoreTimer &&) = delete;
|
||||
auto operator=(AttackerScoreTimer &&) -> AttackerScoreTimer & = delete;
|
||||
|
||||
~AttackerScoreTimer() {
|
||||
auto endTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Standard implementation of AIScoreCalculator
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_STANDARDAISCORECALCULATOR_HPP
|
||||
#define EAGLE0_STANDARDAISCORECALCULATOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_SCORE_STANDARD_AI_SCORE_CALCULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_SCORE_STANDARD_AI_SCORE_CALCULATOR_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -34,4 +34,4 @@ using ALCache = std::unique_ptr<AttackLocationsCache>;
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_STANDARDAISCORECALCULATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_SCORE_STANDARD_AI_SCORE_CALCULATOR_HPP
|
||||
|
||||
+3
-3
@@ -3,8 +3,8 @@
|
||||
// This file is private to the ai/score package
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP
|
||||
#define EAGLE0_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_SCORE_PRIVATE_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP
|
||||
#define EAGLE0_SHARDOK_AI_SCORE_PRIVATE_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -92,4 +92,4 @@ auto AttackerMultiplierForTargetDistance(
|
||||
} // namespace score_calculator_internal
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_SCORE_PRIVATE_AI_SCORE_CALCULATOR_SHARED_UTILITIES_HPP
|
||||
|
||||
@@ -47,19 +47,19 @@ auto AbstractAIScoreCalculator::CalculateUnitsScoreComponents(
|
||||
// Memoization cache for EffectiveDistance calls
|
||||
EffectiveDistanceCache distanceCache;
|
||||
|
||||
// Early return for empty game states
|
||||
const size_t estimatedUnitCount = cachedUnits->size();
|
||||
if (estimatedUnitCount == 0) { return UnitsScoreComponents{0.0, 0.0}; }
|
||||
|
||||
std::vector<const Unit *> attackerUnits{};
|
||||
std::vector<const Unit *> defenderUnits{};
|
||||
// Pre-allocate vectors based on estimated unit ratios to avoid reallocations
|
||||
const size_t estimatedUnitCount = cachedUnits->size();
|
||||
attackerUnits.reserve(estimatedUnitCount - 1);
|
||||
defenderUnits.reserve(estimatedUnitCount - 1);
|
||||
attackerUnits.reserve(estimatedUnitCount);
|
||||
defenderUnits.reserve(estimatedUnitCount);
|
||||
|
||||
double attackerUnitsValue = 0;
|
||||
double defenderUnitsValue = 0;
|
||||
|
||||
// Early return for empty game states
|
||||
if (cachedUnits->size() == 0) { return UnitsScoreComponents{0.0, 0.0}; }
|
||||
|
||||
auto occupants = Occupants(*cachedUnits, cachedRowCount, cachedColumnCount);
|
||||
|
||||
for (const Unit *unit : *cachedUnits) {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// This file is private to the ai/score package
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_ABSTRACT_AI_SCORE_CALCULATOR_HPP
|
||||
#define EAGLE0_ABSTRACT_AI_SCORE_CALCULATOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_SCORE_PRIVATE_ABSTRACT_AI_SCORE_CALCULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_SCORE_PRIVATE_ABSTRACT_AI_SCORE_CALCULATOR_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
@@ -178,4 +178,4 @@ private:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_ABSTRACT_AI_SCORE_CALCULATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_SCORE_PRIVATE_ABSTRACT_AI_SCORE_CALCULATOR_HPP
|
||||
|
||||
@@ -21,7 +21,7 @@ std::unique_ptr<BattleConfigProto> AiBattleConfigLoader::LoadFromJsonFile(
|
||||
const std::string& filepath) {
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Failed to open config file: " << filepath << std::endl;
|
||||
std::cerr << "Failed to open config file: " << filepath << '\n';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ std::unique_ptr<BattleConfigProto> AiBattleConfigLoader::LoadFromJsonString(
|
||||
google::protobuf::util::JsonStringToMessage(jsonString, config.get(), options);
|
||||
|
||||
if (!status.ok()) {
|
||||
std::cerr << "Failed to parse JSON config: " << status.message() << std::endl;
|
||||
std::cerr << "Failed to parse JSON config: " << status.message() << '\n';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ std::string AiBattleConfigLoader::ToJsonString(const BattleConfigProto& config)
|
||||
const auto status = google::protobuf::util::MessageToJsonString(config, &jsonString, options);
|
||||
|
||||
if (!status.ok()) {
|
||||
std::cerr << "Failed to convert config to JSON: " << status.message() << std::endl;
|
||||
std::cerr << "Failed to convert config to JSON: " << status.message() << '\n';
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// AI Battle Simulator Configuration Loader
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AI_BATTLE_CONFIG_HPP
|
||||
#define EAGLE0_AI_BATTLE_CONFIG_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_BATTLE_SIMULATOR_AI_BATTLE_CONFIG_HPP
|
||||
#define EAGLE0_SHARDOK_AI_BATTLE_SIMULATOR_AI_BATTLE_CONFIG_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -44,4 +44,4 @@ public:
|
||||
} // namespace ai_battle_simulator
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AI_BATTLE_CONFIG_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_BATTLE_SIMULATOR_AI_BATTLE_CONFIG_HPP
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
|
||||
#include "src/main/cpp/net/eagle0/common/RandomGenerator.hpp"
|
||||
@@ -332,6 +333,7 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
|
||||
|
||||
// Create player info protos
|
||||
std::vector<net::eagle0::shardok::common::PlayerInfo> playerInfoProtos;
|
||||
playerInfoProtos.reserve(2);
|
||||
|
||||
// Attacker info
|
||||
net::eagle0::shardok::common::PlayerInfo attackerInfo;
|
||||
@@ -342,7 +344,7 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
|
||||
net::eagle0::shardok::common::VICTORY_CONDITION_LAST_PLAYER_STANDING);
|
||||
attackerInfo.add_victory_conditions(
|
||||
net::eagle0::shardok::common::VICTORY_CONDITION_HOLDS_CRITICAL_TILES);
|
||||
playerInfoProtos.push_back(attackerInfo);
|
||||
playerInfoProtos.push_back(std::move(attackerInfo));
|
||||
|
||||
// Defender info
|
||||
net::eagle0::shardok::common::PlayerInfo defenderInfo;
|
||||
@@ -353,13 +355,15 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
|
||||
net::eagle0::shardok::common::VICTORY_CONDITION_LAST_PLAYER_STANDING);
|
||||
defenderInfo.add_victory_conditions(
|
||||
net::eagle0::shardok::common::VICTORY_CONDITION_WIN_AFTER_MAX_ROUNDS);
|
||||
playerInfoProtos.push_back(defenderInfo);
|
||||
playerInfoProtos.push_back(std::move(defenderInfo));
|
||||
|
||||
// Create units from config
|
||||
std::vector<net::eagle0::shardok::storage::fb::Unit> units;
|
||||
units.reserve(config_.attacker().units_size() + config_.defender().units_size());
|
||||
|
||||
// Attacker units
|
||||
for (int i = 0; i < config_.attacker().units_size(); ++i) {
|
||||
const int attackerUnitCount = config_.attacker().units_size();
|
||||
for (int i = 0; i < attackerUnitCount; ++i) {
|
||||
const auto& unitConfig = config_.attacker().units(i);
|
||||
|
||||
net::eagle0::shardok::storage::fb::Unit unit{};
|
||||
@@ -449,12 +453,13 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
|
||||
unit.mutable_opponent_knowledge()->Mutate(0, 0);
|
||||
unit.mutable_opponent_knowledge()->Mutate(1, 0);
|
||||
|
||||
units.push_back(unit);
|
||||
units.push_back(std::move(unit));
|
||||
}
|
||||
|
||||
// Defender units
|
||||
int defenderUnitIdStart = config_.attacker().units_size();
|
||||
for (int i = 0; i < config_.defender().units_size(); ++i) {
|
||||
const int defenderUnitIdStart = attackerUnitCount;
|
||||
const int defenderUnitCount = config_.defender().units_size();
|
||||
for (int i = 0; i < defenderUnitCount; ++i) {
|
||||
const auto& unitConfig = config_.defender().units(i);
|
||||
|
||||
net::eagle0::shardok::storage::fb::Unit unit{};
|
||||
@@ -544,7 +549,7 @@ GameStateW AiBattleSimulator::CreateInitialGameState() const {
|
||||
unit.mutable_opponent_knowledge()->Mutate(0, 0);
|
||||
unit.mutable_opponent_knowledge()->Mutate(1, 0);
|
||||
|
||||
units.push_back(unit);
|
||||
units.push_back(std::move(unit));
|
||||
}
|
||||
|
||||
// Create game state
|
||||
@@ -678,8 +683,9 @@ BattleResult AiBattleSimulator::RunConfiguredSetupPhase(ShardokEngine& engine) {
|
||||
playerConfig,
|
||||
int unitIdOffset) {
|
||||
std::vector<UnitPlacementInfo> placements;
|
||||
placements.reserve(playerConfig.units_size());
|
||||
for (int i = 0; i < playerConfig.units_size(); ++i) {
|
||||
const int unitCount = playerConfig.units_size();
|
||||
placements.reserve(unitCount);
|
||||
for (int i = 0; i < unitCount; ++i) {
|
||||
const auto& unitConfig = playerConfig.units(i);
|
||||
if (!unitConfig.has_fixed_position()) {
|
||||
throw std::invalid_argument(
|
||||
@@ -744,7 +750,8 @@ BattleResult AiBattleSimulator::RunBattlePhase(
|
||||
auto availableCommands = engine.GetAvailableCommandProtos(currentPlayer, false);
|
||||
|
||||
if (availableCommands.empty()) {
|
||||
std::cout << "No commands available for player " << (int)currentPlayer << "\n";
|
||||
std::cout << "No commands available for player " << static_cast<int>(currentPlayer)
|
||||
<< "\n";
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -910,6 +917,7 @@ BattleResult AiBattleSimulator::CreateResultFromGameState(
|
||||
// Collect surviving units (for winner, or all units if draw)
|
||||
const auto* units = state->units();
|
||||
if (units) {
|
||||
result.survivingUnits.reserve(units->size());
|
||||
for (const auto* unit : *units) {
|
||||
if (!unit) { continue; }
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// AI Battle Simulator - Runs AI vs AI battles to completion
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AI_BATTLE_SIMULATOR_HPP
|
||||
#define EAGLE0_AI_BATTLE_SIMULATOR_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_BATTLE_SIMULATOR_AI_BATTLE_SIMULATOR_HPP
|
||||
#define EAGLE0_SHARDOK_AI_BATTLE_SIMULATOR_AI_BATTLE_SIMULATOR_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -149,4 +149,4 @@ private:
|
||||
} // namespace ai_battle_simulator
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AI_BATTLE_SIMULATOR_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_BATTLE_SIMULATOR_AI_BATTLE_SIMULATOR_HPP
|
||||
|
||||
@@ -88,8 +88,7 @@ void GenerateConfigFile(const std::string& outputPath) {
|
||||
// Output to file
|
||||
std::ofstream outFile(outputPath);
|
||||
if (!outFile.is_open()) {
|
||||
std::cerr << "Error: Failed to open output file: " << outputPath << "\n";
|
||||
std::exit(1);
|
||||
throw std::runtime_error("Failed to open output file: " + outputPath);
|
||||
}
|
||||
outFile << jsonString << "\n";
|
||||
std::cout << "Sample config written to: " << outputPath << "\n";
|
||||
@@ -295,8 +294,7 @@ BattleResult RunBattle(
|
||||
|
||||
if (!config) {
|
||||
restoreOutput();
|
||||
std::cerr << "Error: Failed to load config file\n";
|
||||
std::exit(1);
|
||||
throw std::runtime_error("Failed to load config file");
|
||||
}
|
||||
|
||||
// Create simulator
|
||||
@@ -328,6 +326,8 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
std::vector<std::string> configPaths;
|
||||
std::vector<std::string> statePaths;
|
||||
configPaths.reserve(argc - 1);
|
||||
statePaths.reserve(argc - 1);
|
||||
std::string outputPath;
|
||||
std::string tracePath;
|
||||
std::string runLabel;
|
||||
|
||||
+2
-3
@@ -2,7 +2,6 @@
|
||||
// Converts saved Eagle ShardokBattle/GameState protobuf blobs into AI battle configs.
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
@@ -146,9 +145,9 @@ int main(int argc, char *argv[]) {
|
||||
} else if (arg.starts_with("--output=")) {
|
||||
outputPath = arg.substr(9);
|
||||
} else if (arg.starts_with("--seed=")) {
|
||||
seed = std::atoi(arg.substr(7).c_str());
|
||||
seed = std::stoi(arg.substr(7));
|
||||
} else if (arg.starts_with("--max-rounds=")) {
|
||||
maxRounds = std::atoi(arg.substr(13).c_str());
|
||||
maxRounds = std::stoi(arg.substr(13));
|
||||
} else if (arg.starts_with("--attacker-scoring=")) {
|
||||
const auto scoring = ParseScoring(arg.substr(19));
|
||||
if (!scoring.has_value()) { throw std::runtime_error("Unknown attacker scoring"); }
|
||||
|
||||
+4
@@ -115,6 +115,7 @@ int main(int argc, char *argv[]) {
|
||||
std::string resultPath;
|
||||
std::string stateFbPath;
|
||||
std::vector<std::string> actionViewPaths;
|
||||
actionViewPaths.reserve(argc - 1);
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const std::string arg = argv[i];
|
||||
@@ -145,6 +146,9 @@ int main(int argc, char *argv[]) {
|
||||
std::vector<int> attackerFactions;
|
||||
std::vector<int> defenderFactions;
|
||||
std::vector<std::string> playerSides;
|
||||
attackerFactions.reserve(battle.players_size());
|
||||
defenderFactions.reserve(battle.players_size());
|
||||
playerSides.reserve(battle.players_size());
|
||||
bool aiVsAi = true;
|
||||
for (const auto &player : battle.players()) {
|
||||
if (player.eagle_fid() == 3 || player.eagle_fid() == 4) { aiVsAi = false; }
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
#include "AIPerformanceRunner.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "PerformanceTestGameStateBuilder.hpp"
|
||||
@@ -18,7 +19,15 @@
|
||||
#include "src/main/cpp/net/eagle0/shardok/library/ShardokEngine.hpp"
|
||||
#include "src/main/protobuf/net/eagle0/shardok/common/command_type.pb.h"
|
||||
|
||||
using namespace shardok;
|
||||
using shardok::AIAlgorithmType;
|
||||
using shardok::AIPerformanceMetrics;
|
||||
using shardok::EvaluationCompletionReason;
|
||||
using shardok::PerformanceTestConfig;
|
||||
using shardok::PerformanceTestGameStateBuilder;
|
||||
using shardok::PlayerId;
|
||||
using shardok::ShardokAIClient;
|
||||
using shardok::ShardokEngine;
|
||||
namespace ai_testing_common = shardok::ai_testing_common;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -39,7 +48,7 @@ auto CompletionReasonToString(EvaluationCompletionReason reason) -> std::string
|
||||
/**
|
||||
* Parse command line arguments into a configuration struct.
|
||||
*/
|
||||
auto ParseCommandLineArgs(int argc, char* argv[]) -> PerformanceTestConfig {
|
||||
auto ParseCommandLineArgs(int argc, char* argv[]) -> std::optional<PerformanceTestConfig> {
|
||||
PerformanceTestConfig config;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
@@ -55,7 +64,7 @@ auto ParseCommandLineArgs(int argc, char* argv[]) -> PerformanceTestConfig {
|
||||
<< " --defender=BOOL AI is defender (default: false)\n"
|
||||
<< " --verbose Enable verbose output\n"
|
||||
<< " --help, -h Show this help message\n";
|
||||
std::exit(0);
|
||||
return std::nullopt;
|
||||
} else if (arg.starts_with("--map=")) {
|
||||
config.mapName = arg.substr(6);
|
||||
} else if (arg.starts_with("--turns=")) {
|
||||
@@ -66,9 +75,8 @@ auto ParseCommandLineArgs(int argc, char* argv[]) -> PerformanceTestConfig {
|
||||
} else if (arg == "--verbose") {
|
||||
config.verbose = true;
|
||||
} else {
|
||||
std::cerr << "Unknown argument: " << arg << "\n";
|
||||
std::cerr << "Use --help for usage information.\n";
|
||||
std::exit(1);
|
||||
throw std::invalid_argument(
|
||||
"Unknown argument: " + arg + "\nUse --help for usage information.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +86,7 @@ auto ParseCommandLineArgs(int argc, char* argv[]) -> PerformanceTestConfig {
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::cout << "Starting AI Performance Runner..." << std::endl;
|
||||
std::cout << "Starting AI Performance Runner..." << '\n';
|
||||
|
||||
// Set exec path so FilesystemUtils can find resource files
|
||||
FilesystemUtils::SetExecPath(argv[0]);
|
||||
@@ -88,7 +96,9 @@ int main(int argc, char* argv[]) {
|
||||
std::cout << "==============================\n";
|
||||
|
||||
// Parse command line arguments
|
||||
auto config = ParseCommandLineArgs(argc, argv);
|
||||
const auto parsedConfig = ParseCommandLineArgs(argc, argv);
|
||||
if (!parsedConfig.has_value()) { return 0; }
|
||||
const auto& config = parsedConfig.value();
|
||||
|
||||
if (config.verbose) {
|
||||
std::cout << "Configuration:\n";
|
||||
@@ -155,6 +165,7 @@ int main(int argc, char* argv[]) {
|
||||
std::cout << "Running AI performance test for " << config.numTurns << " turns...\n";
|
||||
|
||||
std::vector<AIPerformanceMetrics> metrics;
|
||||
if (config.numTurns > 0) { metrics.reserve(config.numTurns); }
|
||||
|
||||
for (int turn = 0; turn < config.numTurns; ++turn) {
|
||||
// Check if AI can make a move
|
||||
@@ -300,4 +311,4 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 2025-01-15.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AIPERFORMANCERUNNER_HPP
|
||||
#define EAGLE0_AIPERFORMANCERUNNER_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_PERFORMANCE_RUNNER_AI_PERFORMANCE_RUNNER_HPP
|
||||
#define EAGLE0_SHARDOK_AI_PERFORMANCE_RUNNER_AI_PERFORMANCE_RUNNER_HPP
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
@@ -52,4 +52,4 @@ struct PerformanceTestConfig {
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AIPERFORMANCERUNNER_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_PERFORMANCE_RUNNER_AI_PERFORMANCE_RUNNER_HPP
|
||||
|
||||
+12
-6
@@ -4,7 +4,9 @@
|
||||
|
||||
#include "PerformanceTestGameStateBuilder.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <utility>
|
||||
|
||||
#include "src/main/cpp/net/eagle0/common/FilesystemUtils.hpp"
|
||||
#include "src/main/cpp/net/eagle0/shardok/ai_testing_common/GameSettingsFactory.hpp"
|
||||
@@ -53,6 +55,7 @@ auto PerformanceTestGameStateBuilder::CreateCustomTestGameState(
|
||||
|
||||
// Create player info protos
|
||||
std::vector<net::eagle0::shardok::common::PlayerInfo> playerInfoProtos;
|
||||
playerInfoProtos.reserve(2);
|
||||
|
||||
// AI player
|
||||
net::eagle0::shardok::common::PlayerInfo aiPlayerInfo;
|
||||
@@ -68,7 +71,7 @@ auto PerformanceTestGameStateBuilder::CreateCustomTestGameState(
|
||||
aiPlayerInfo.add_victory_conditions(
|
||||
net::eagle0::shardok::common::VICTORY_CONDITION_HOLDS_CRITICAL_TILES);
|
||||
}
|
||||
playerInfoProtos.push_back(aiPlayerInfo);
|
||||
playerInfoProtos.push_back(std::move(aiPlayerInfo));
|
||||
|
||||
// Human player
|
||||
net::eagle0::shardok::common::PlayerInfo humanPlayerInfo;
|
||||
@@ -84,13 +87,16 @@ auto PerformanceTestGameStateBuilder::CreateCustomTestGameState(
|
||||
humanPlayerInfo.add_victory_conditions(
|
||||
net::eagle0::shardok::common::VICTORY_CONDITION_HOLDS_CRITICAL_TILES);
|
||||
}
|
||||
playerInfoProtos.push_back(humanPlayerInfo);
|
||||
playerInfoProtos.push_back(std::move(humanPlayerInfo));
|
||||
|
||||
// Create units
|
||||
const int clampedAiUnitCount = std::clamp(aiUnitCount, 0, 6);
|
||||
const int clampedHumanUnitCount = std::clamp(humanUnitCount, 0, 6);
|
||||
std::vector<net::eagle0::shardok::storage::fb::Unit> units;
|
||||
units.reserve(clampedAiUnitCount + clampedHumanUnitCount);
|
||||
|
||||
// Create AI units in reserve (location -1, -1)
|
||||
for (int i = 0; i < aiUnitCount && i < 6; ++i) {
|
||||
for (int i = 0; i < clampedAiUnitCount; ++i) {
|
||||
units.push_back(AddGenericUnit(
|
||||
AI_PLAYER_ID,
|
||||
i, // Unit ID
|
||||
@@ -101,10 +107,10 @@ auto PerformanceTestGameStateBuilder::CreateCustomTestGameState(
|
||||
}
|
||||
|
||||
// Create human units in reserve (location -1, -1)
|
||||
for (int i = 0; i < humanUnitCount && i < 6; ++i) {
|
||||
for (int i = 0; i < clampedHumanUnitCount; ++i) {
|
||||
units.push_back(AddGenericUnit(
|
||||
HUMAN_PLAYER_ID,
|
||||
aiUnitCount + i, // Unit ID starting aiUnitCount
|
||||
clampedAiUnitCount + i, // Unit ID starting after generated AI units
|
||||
net::eagle0::shardok::storage::fb::Coords(-1, -1), // Reserve location
|
||||
NO_PROFESSION,
|
||||
HEAVY_INFANTRY_BATTALION_TYPE,
|
||||
@@ -235,4 +241,4 @@ auto PerformanceTestGameStateBuilder::AddGenericUnit(
|
||||
return unit;
|
||||
}
|
||||
|
||||
} // namespace shardok
|
||||
} // namespace shardok
|
||||
|
||||
+3
-3
@@ -2,8 +2,8 @@
|
||||
// Created by Dan Crosby on 2025-01-15.
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_PERFORMANCETESTGAMESTATEBUILDER_HPP
|
||||
#define EAGLE0_PERFORMANCETESTGAMESTATEBUILDER_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_PERFORMANCE_RUNNER_PERFORMANCE_TEST_GAME_STATE_BUILDER_HPP
|
||||
#define EAGLE0_SHARDOK_AI_PERFORMANCE_RUNNER_PERFORMANCE_TEST_GAME_STATE_BUILDER_HPP
|
||||
|
||||
#include <flatbuffers/flatbuffers.h>
|
||||
|
||||
@@ -85,4 +85,4 @@ private:
|
||||
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_PERFORMANCETESTGAMESTATEBUILDER_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_PERFORMANCE_RUNNER_PERFORMANCE_TEST_GAME_STATE_BUILDER_HPP
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// Used by both AI Performance Runner and AI Battle Simulator
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_AI_CLIENT_FACTORY_HPP
|
||||
#define EAGLE0_AI_CLIENT_FACTORY_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_TESTING_COMMON_AI_CLIENT_FACTORY_HPP
|
||||
#define EAGLE0_SHARDOK_AI_TESTING_COMMON_AI_CLIENT_FACTORY_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -68,4 +68,4 @@ public:
|
||||
} // namespace ai_testing_common
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_AI_CLIENT_FACTORY_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_TESTING_COMMON_AI_CLIENT_FACTORY_HPP
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// Used by both AI Performance Runner and AI Battle Simulator
|
||||
//
|
||||
|
||||
#ifndef EAGLE0_GAME_PHASE_RUNNER_HPP
|
||||
#define EAGLE0_GAME_PHASE_RUNNER_HPP
|
||||
#ifndef EAGLE0_SHARDOK_AI_TESTING_COMMON_GAME_PHASE_RUNNER_HPP
|
||||
#define EAGLE0_SHARDOK_AI_TESTING_COMMON_GAME_PHASE_RUNNER_HPP
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -61,4 +61,4 @@ public:
|
||||
} // namespace ai_testing_common
|
||||
} // namespace shardok
|
||||
|
||||
#endif // EAGLE0_GAME_PHASE_RUNNER_HPP
|
||||
#endif // EAGLE0_SHARDOK_AI_TESTING_COMMON_GAME_PHASE_RUNNER_HPP
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user