encoder.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /*
  2. * encoder.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "schema/v13/constants.h"
  12. #include "schema/v13/types.h"
  13. #include "BAI/v13/encoder.h"
  14. #include "common.h"
  15. namespace MMAI::BAI::V13
  16. {
  17. namespace S13 = Schema::V13;
  18. using Encoding = Schema::V13::Encoding;
  19. using BS = Schema::BattlefieldState;
  20. using clock = std::chrono::system_clock;
  21. #define ADD_ZEROS_AND_RETURN(n, out) \
  22. out.insert((out).end(), n, 0); \
  23. return
  24. #define MAYBE_ADD_ZEROS_AND_RETURN(v, n, out) \
  25. if((v) <= 0) \
  26. { \
  27. ADD_ZEROS_AND_RETURN(n, out); \
  28. }
  29. #define MAYBE_ADD_MASKED_AND_RETURN(v, n, out) \
  30. if((v) == S13::NULL_VALUE_UNENCODED) \
  31. { \
  32. (out).insert((out).end(), n, S13::NULL_VALUE_ENCODED); \
  33. return; \
  34. }
  35. #define MAYBE_THROW_STRICT_ERROR(v) \
  36. if((v) == S13::NULL_VALUE_UNENCODED) \
  37. throw std::runtime_error("NULL values are not allowed for strict encoding")
  38. void Encoder::Encode(const EncoderInput & in, BS & out)
  39. {
  40. if(in.e == Encoding::RAW)
  41. {
  42. out.push_back(in.v);
  43. return;
  44. }
  45. auto v = in.v;
  46. if(in.v > in.vmax)
  47. {
  48. // THROW_FORMAT("Cannot encode value: %d (vmax=%d, a=%d, n=%d, e=%d)", v % vmax % EI(a) % n % EI(e));
  49. // Can happen (e.g. DMG_*_ACC_REL0 > 1 if there were resurrected stacks)
  50. // Warn at most once every 600s
  51. auto now = clock::now();
  52. static thread_local std::map<std::string, std::map<int, clock::time_point>> warns;
  53. auto & warned_at = warns[std::string(in.attrname)][EI(in.a)];
  54. if(std::chrono::duration_cast<std::chrono::seconds>(now - warned_at) > std::chrono::seconds(600))
  55. {
  56. // This is not critical; the value will be capped to vmax (should not occur often)
  57. logAi->info(
  58. "MMAI: Attribute value out of bounds: v=%d (vmax=%d, a=%d, e=%d, n=%d, attrname=%s)\n", in.v, in.vmax, EI(in.a), EI(in.e), in.n, in.attrname
  59. );
  60. warns[std::string(in.attrname)][EI(in.a)] = now;
  61. }
  62. v = in.vmax;
  63. }
  64. switch(in.e)
  65. {
  66. case Encoding::BINARY_EXPLICIT_NULL:
  67. EncodeBinaryExplicitNull(v, in.n, out);
  68. break;
  69. case Encoding::BINARY_MASKING_NULL:
  70. EncodeBinaryMaskingNull(v, in.n, out);
  71. break;
  72. case Encoding::BINARY_STRICT_NULL:
  73. EncodeBinaryStrictNull(v, in.n, out);
  74. break;
  75. case Encoding::BINARY_ZERO_NULL:
  76. EncodeBinaryZeroNull(v, in.n, out);
  77. break;
  78. case Encoding::EXPNORM_EXPLICIT_NULL:
  79. EncodeExpnormExplicitNull(v, in.vmax, in.p, out);
  80. break;
  81. case Encoding::EXPNORM_MASKING_NULL:
  82. EncodeExpnormMaskingNull(v, in.vmax, in.p, out);
  83. break;
  84. case Encoding::EXPNORM_STRICT_NULL:
  85. EncodeExpnormStrictNull(v, in.vmax, in.p, out);
  86. break;
  87. case Encoding::EXPNORM_ZERO_NULL:
  88. EncodeExpnormZeroNull(v, in.vmax, in.p, out);
  89. break;
  90. case Encoding::LINNORM_EXPLICIT_NULL:
  91. EncodeLinnormExplicitNull(v, in.vmax, out);
  92. break;
  93. case Encoding::LINNORM_MASKING_NULL:
  94. EncodeLinnormMaskingNull(v, in.vmax, out);
  95. break;
  96. case Encoding::LINNORM_STRICT_NULL:
  97. EncodeLinnormStrictNull(v, in.vmax, out);
  98. break;
  99. case Encoding::LINNORM_ZERO_NULL:
  100. EncodeLinnormZeroNull(v, in.vmax, out);
  101. break;
  102. case Encoding::CATEGORICAL_EXPLICIT_NULL:
  103. EncodeCategoricalExplicitNull(v, in.n, out);
  104. break;
  105. case Encoding::CATEGORICAL_IMPLICIT_NULL:
  106. EncodeCategoricalImplicitNull(v, in.n, out);
  107. break;
  108. case Encoding::CATEGORICAL_MASKING_NULL:
  109. EncodeCategoricalMaskingNull(v, in.n, out);
  110. break;
  111. case Encoding::CATEGORICAL_STRICT_NULL:
  112. EncodeCategoricalStrictNull(v, in.n, out);
  113. break;
  114. case Encoding::CATEGORICAL_ZERO_NULL:
  115. EncodeCategoricalZeroNull(v, in.n, out);
  116. break;
  117. case Encoding::ACCUMULATING_EXPLICIT_NULL:
  118. EncodeAccumulatingExplicitNull(v, in.n, out);
  119. break;
  120. case Encoding::ACCUMULATING_IMPLICIT_NULL:
  121. EncodeAccumulatingImplicitNull(v, in.n, out);
  122. break;
  123. case Encoding::ACCUMULATING_MASKING_NULL:
  124. EncodeAccumulatingMaskingNull(v, in.n, out);
  125. break;
  126. case Encoding::ACCUMULATING_STRICT_NULL:
  127. EncodeAccumulatingStrictNull(v, in.n, out);
  128. break;
  129. case Encoding::ACCUMULATING_ZERO_NULL:
  130. EncodeAccumulatingZeroNull(v, in.n, out);
  131. break;
  132. default:
  133. THROW_FORMAT("Unexpected Encoding: %d", EI(in.e));
  134. }
  135. }
  136. void Encoder::Encode(const S13::HexAttribute a, int v, BS & out)
  137. {
  138. const auto & [_, e, n, vmax, p] = S13::HEX_ENCODING.at(EI(a));
  139. Encode(EncoderInput{.attrname = "HexAttribute", .a = EI(a), .e = e, .n = n, .vmax = vmax, .p = p, .v = v}, out);
  140. }
  141. void Encoder::Encode(const S13::PlayerAttribute a, int v, BS & out)
  142. {
  143. const auto & [_, e, n, vmax, p] = S13::PLAYER_ENCODING.at(EI(a));
  144. Encode(EncoderInput{.attrname = "PlayerAttribute", .a = EI(a), .e = e, .n = n, .vmax = vmax, .p = p, .v = v}, out);
  145. }
  146. void Encoder::Encode(const S13::GlobalAttribute a, int v, BS & out)
  147. {
  148. const auto & [_, e, n, vmax, p] = S13::GLOBAL_ENCODING.at(EI(a));
  149. Encode(EncoderInput{.attrname = "GlobalAttribute", .a = EI(a), .e = e, .n = n, .vmax = vmax, .p = p, .v = v}, out);
  150. }
  151. //
  152. // ACCUMULATING
  153. //
  154. void Encoder::EncodeAccumulatingExplicitNull(int v, int n, BS & out)
  155. {
  156. if(v == S13::NULL_VALUE_UNENCODED)
  157. {
  158. out.push_back(1);
  159. ADD_ZEROS_AND_RETURN(n - 1, out);
  160. }
  161. out.push_back(0);
  162. EncodeAccumulating(v, n - 1, out);
  163. }
  164. void Encoder::EncodeAccumulatingImplicitNull(int v, int n, BS & out)
  165. {
  166. if(v == S13::NULL_VALUE_UNENCODED)
  167. {
  168. ADD_ZEROS_AND_RETURN(n, out);
  169. }
  170. EncodeAccumulating(v, n, out);
  171. }
  172. void Encoder::EncodeAccumulatingMaskingNull(int v, int n, BS & out)
  173. {
  174. MAYBE_ADD_MASKED_AND_RETURN(v, n, out);
  175. EncodeAccumulating(v, n, out);
  176. }
  177. void Encoder::EncodeAccumulatingStrictNull(int v, int n, BS & out)
  178. {
  179. MAYBE_THROW_STRICT_ERROR(v);
  180. EncodeAccumulating(v, n, out);
  181. }
  182. void Encoder::EncodeAccumulatingZeroNull(int v, int n, BS & out)
  183. {
  184. if(v <= 0)
  185. {
  186. out.push_back(1);
  187. ADD_ZEROS_AND_RETURN(n - 1, out);
  188. }
  189. EncodeAccumulating(v, n, out);
  190. }
  191. void Encoder::EncodeAccumulating(int v, int n, BS & out)
  192. {
  193. out.insert(out.end(), v + 1, 1);
  194. out.insert(out.end(), n - v - 1, 0);
  195. }
  196. //
  197. // BINARY
  198. //
  199. void Encoder::EncodeBinaryExplicitNull(int v, int n, BS & out)
  200. {
  201. out.push_back(v == S13::NULL_VALUE_UNENCODED);
  202. EncodeBinary(v, n - 1, out);
  203. }
  204. void Encoder::EncodeBinaryMaskingNull(int v, int n, BS & out)
  205. {
  206. MAYBE_ADD_MASKED_AND_RETURN(v, n, out);
  207. EncodeBinary(v, n, out);
  208. }
  209. void Encoder::EncodeBinaryStrictNull(int v, int n, BS & out)
  210. {
  211. MAYBE_THROW_STRICT_ERROR(v);
  212. EncodeBinary(v, n, out);
  213. }
  214. void Encoder::EncodeBinaryZeroNull(int v, int n, BS & out)
  215. {
  216. EncodeBinary(v, n, out);
  217. }
  218. void Encoder::EncodeBinary(int v, int n, BS & out)
  219. {
  220. MAYBE_ADD_ZEROS_AND_RETURN(v, n, out);
  221. int vtmp = v;
  222. for(int i = 0; i < n; ++i)
  223. {
  224. out.push_back(vtmp % 2);
  225. vtmp /= 2;
  226. }
  227. }
  228. //
  229. // CATEGORICAL
  230. //
  231. void Encoder::EncodeCategoricalExplicitNull(int v, int n, BS & out)
  232. {
  233. if(v == S13::NULL_VALUE_UNENCODED)
  234. {
  235. out.push_back(v == S13::NULL_VALUE_UNENCODED);
  236. ADD_ZEROS_AND_RETURN(n - 1, out);
  237. }
  238. out.push_back(0);
  239. EncodeCategorical(v, n - 1, out);
  240. }
  241. void Encoder::EncodeCategoricalImplicitNull(int v, int n, BS & out)
  242. {
  243. if(v == S13::NULL_VALUE_UNENCODED)
  244. {
  245. ADD_ZEROS_AND_RETURN(n, out);
  246. }
  247. EncodeCategorical(v, n, out);
  248. }
  249. void Encoder::EncodeCategoricalMaskingNull(int v, int n, BS & out)
  250. {
  251. MAYBE_ADD_MASKED_AND_RETURN(v, n, out);
  252. EncodeCategorical(v, n, out);
  253. }
  254. void Encoder::EncodeCategoricalStrictNull(int v, int n, BS & out)
  255. {
  256. MAYBE_THROW_STRICT_ERROR(v);
  257. EncodeCategorical(v, n, out);
  258. }
  259. void Encoder::EncodeCategoricalZeroNull(int v, int n, BS & out)
  260. {
  261. EncodeCategorical(v, n, out);
  262. }
  263. void Encoder::EncodeCategorical(int v, int n, BS & out)
  264. {
  265. if(v <= 0)
  266. {
  267. out.push_back(1);
  268. ADD_ZEROS_AND_RETURN(n - 1, out);
  269. }
  270. for(int i = 0; i < n; ++i)
  271. {
  272. if(i == v)
  273. {
  274. out.push_back(1);
  275. ADD_ZEROS_AND_RETURN(n - i - 1, out);
  276. }
  277. else
  278. {
  279. out.push_back(0);
  280. }
  281. }
  282. }
  283. //
  284. // EXPNORM
  285. //
  286. void Encoder::EncodeExpnormExplicitNull(int v, int vmax, double slope, BS & out)
  287. {
  288. out.push_back(v == S13::NULL_VALUE_UNENCODED);
  289. EncodeExpnorm(v, vmax, slope, out);
  290. }
  291. void Encoder::EncodeExpnormMaskingNull(int v, int vmax, double slope, BS & out)
  292. {
  293. if(v == S13::NULL_VALUE_UNENCODED)
  294. {
  295. out.push_back(S13::NULL_VALUE_ENCODED);
  296. return;
  297. }
  298. EncodeExpnorm(v, vmax, slope, out);
  299. }
  300. void Encoder::EncodeExpnormStrictNull(int v, int vmax, double slope, BS & out)
  301. {
  302. MAYBE_THROW_STRICT_ERROR(v);
  303. EncodeExpnorm(v, vmax, slope, out);
  304. }
  305. void Encoder::EncodeExpnormZeroNull(int v, int vmax, double slope, BS & out)
  306. {
  307. EncodeExpnorm(v, vmax, slope, out);
  308. }
  309. void Encoder::EncodeExpnorm(int v, int vmax, double slope, BS & out)
  310. {
  311. if(v <= 0)
  312. {
  313. out.push_back(0);
  314. return;
  315. }
  316. out.push_back(CalcExpnorm(v, vmax, slope));
  317. }
  318. // Visualise on https://www.desmos.com/calculator:
  319. // ln(1 + (x/M) * (exp(S)-1))/S
  320. // Add slider "S" (slope) and "M" (vmax).
  321. // Play with the sliders to see the nonlinearity (use M=1 for best view)
  322. // XXX: slope cannot be 0
  323. float Encoder::CalcExpnorm(int v, int vmax, double slope)
  324. {
  325. auto ratio = static_cast<double>(v) / vmax;
  326. return std::log1p(ratio * (std::exp(slope) - 1.0)) / (slope + 1e-6);
  327. }
  328. //
  329. // LINNORM
  330. //
  331. void Encoder::EncodeLinnormExplicitNull(int v, int vmax, BS & out)
  332. {
  333. out.push_back(v == S13::NULL_VALUE_UNENCODED);
  334. EncodeLinnorm(v, vmax, out);
  335. }
  336. void Encoder::EncodeLinnormMaskingNull(int v, int vmax, BS & out)
  337. {
  338. if(v == S13::NULL_VALUE_UNENCODED)
  339. {
  340. out.push_back(S13::NULL_VALUE_ENCODED);
  341. return;
  342. }
  343. EncodeLinnorm(v, vmax, out);
  344. }
  345. void Encoder::EncodeLinnormStrictNull(int v, int vmax, BS & out)
  346. {
  347. MAYBE_THROW_STRICT_ERROR(v);
  348. EncodeLinnorm(v, vmax, out);
  349. }
  350. void Encoder::EncodeLinnormZeroNull(int v, int vmax, BS & out)
  351. {
  352. EncodeLinnorm(v, vmax, out);
  353. }
  354. void Encoder::EncodeLinnorm(int v, int vmax, BS & out)
  355. {
  356. if(v <= 0)
  357. {
  358. out.push_back(0);
  359. return;
  360. }
  361. // XXX: this is a simplified version for 0..1 norm
  362. out.push_back(CalcLinnorm(v, vmax));
  363. }
  364. float Encoder::CalcLinnorm(int v, int vmax)
  365. {
  366. return static_cast<float>(v) / static_cast<float>(vmax);
  367. }
  368. }