lib_number_one

A Library Script used for Number One on BvS

目前为 2018-03-17 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/39671/259484/lib_number_one.js

  1. /** Functions to handle game state **/
  2.  
  3. /** objects we handle:
  4. probability object: map from bullet use number to probability we should pick it
  5. like { "-1":0.25, "0":0.25, "1":0.5 }
  6. player object: essentially
  7. struct player {
  8. char hp; // ranges from -1 to 5
  9. unsigned char bullets; // ranges from 0 to 6
  10. unsigned char fatigue; // ranges from 0 to 3
  11. enum player_type type; // ranges from 0 to 3
  12. };
  13. game state object:
  14. struct game_state {
  15. struct player p1;
  16. struct player p2;
  17. unsigned char round; // ranges from 1 to 16
  18. };
  19. strategy object:
  20. member p1Move for p1's first action
  21. + map from p2's action to p1's doubletime action
  22. **/
  23.  
  24.  
  25. // deep copy plain data object
  26. // why is there no builtin equivalent to this
  27. // javascript pls
  28. function copy(obj) {
  29. return JSON.parse(JSON.stringify(obj));
  30. }
  31.  
  32. var PREPARATION = 0;
  33. var CATCH = 1;
  34. var PIERCE = 2;
  35. var AURA = 3;
  36.  
  37. function player_rep(p) {
  38. var ret = p.hp+2;
  39. ret = ret | (p.bullets << 3);
  40. ret = ret | (p.fatigue << 6);
  41. // prep and aura both act only at the game start so states have same action probabilities
  42. ret = ret | ((p.type == AURA ? PREPARATION : p.type) << 8);
  43. return ret;
  44. }
  45.  
  46. function game_state_rep(x) {
  47. return ((player_rep(x.p1) << 14) ^
  48. (player_rep(x.p2) << 4) ^
  49. (x.round - 1));
  50. }
  51.  
  52. function act(s, act1, act2) {
  53. var new_state = copy(s);
  54.  
  55. // Resolve round change and HP damage due to round 10+
  56. if (new_state.round >= 10) {
  57. new_state.p1.hp--;
  58. new_state.p2.hp--;
  59. }
  60. new_state.round += 1;
  61.  
  62. // Resolve bullet count change
  63. new_state.p1.bullets -= act1;
  64. new_state.p2.bullets -= act2;
  65. // should add some asserts here really
  66.  
  67. // Resolve fatigue and effects thereof
  68. if (act1 || (act2 > 0)) {
  69. // player 1 did not block, or blocked a shot
  70. new_state.p1.fatigue = 0;
  71. } else if (act2 <= 0) {
  72. switch (new_state.p1.fatigue) {
  73. case 0:
  74. new_state.p1.fatigue = 1;
  75. break;
  76. case 1:
  77. new_state.p1.fatigue = 2;
  78. break;
  79. case 2:
  80. new_state.p1.fatigue = 3;
  81. // fall through
  82. case 3:
  83. new_state.p1.hp--;
  84. break;
  85. }
  86. }
  87.  
  88. if (act2 || (act1 > 0)) {
  89. // player 2 did not block, or blocked a shot
  90. new_state.p2.fatigue = 0;
  91. } else if (act1 <= 0) {
  92. switch (new_state.p2.fatigue) {
  93. case 0:
  94. new_state.p2.fatigue = 1;
  95. break;
  96. case 1:
  97. new_state.p2.fatigue = 2;
  98. break;
  99. case 2:
  100. new_state.p2.fatigue = 3;
  101. // fall through
  102. case 3:
  103. new_state.p2.hp--;
  104. break;
  105. }
  106. }
  107.  
  108. // Resolve results of shooting
  109. if (act1 > 0) {
  110. if (act2 > 0) {
  111. // both players shot
  112. if (act1 > act2) {
  113. // p2 loses
  114. new_state.p2.hp = -10;
  115. } else if (act1 < act2 ||
  116. (new_state.p2.type == PIERCE &&
  117. new_state.p1.type != PIERCE)) {
  118. // p1 loses
  119. new_state.p1.hp = -10;
  120. } else if (new_state.p1.type == PIERCE &&
  121. new_state.p2.type != PIERCE) {
  122. // p2 loses
  123. new_state.p2.hp = -10;
  124. }
  125. } else if (act2) {
  126. // p2 reloaded and therefore loses
  127. new_state.p2.hp = -10;
  128. } else {
  129. // p2 blocked
  130. new_state.p2.hp -= (act1 - 1);
  131. if (new_state.p2.type == CATCH) {
  132. // bullet was caught
  133. new_state.p2.bullets++;
  134. }
  135. }
  136. } else if (act2 > 0) {
  137. if (act1) {
  138. // p1 reloaded and loses
  139. new_state.p1.hp = -10;
  140. } else {
  141. // p1 blocked
  142. new_state.p1.hp -= (act2 - 1);
  143. if (new_state.p1.type == CATCH) {
  144. new_state.p1.bullets++;
  145. }
  146. }
  147. }
  148. // cap bullet count
  149. if (new_state.p1.bullets > 6) {
  150. new_state.p1.bullets = 6;
  151. }
  152. if (new_state.p2.bullets > 6) {
  153. new_state.p2.bullets = 6;
  154. }
  155. // cap hp count
  156. if (new_state.p1.hp < 0) {
  157. new_state.p1.hp = -1;
  158. }
  159. if (new_state.p2.hp < 0) {
  160. new_state.p2.hp = -1;
  161. }
  162. return new_state;
  163. }
  164.  
  165. function pick_action(probabilities, bullets) {
  166. if (probabilities == undefined || probabilities == null) {
  167. // trivial/unreachable state, reload
  168. return -1;
  169. }
  170.  
  171. var accu = Math.random();
  172.  
  173. for(var act = -1; act <= bullets; act++) {
  174. var p = probabilities[act];
  175.  
  176. if(p == undefined || p == null) {
  177. // something weird happened but whatever
  178. return Math.max(-1, act-1);
  179. }
  180.  
  181. if (p >= accu) {
  182. return act;
  183. }
  184.  
  185. accu -= p;
  186. }
  187.  
  188. // rounding error might have the sum of probabilities be less than accu
  189. // in which case
  190. return bullets;
  191. }
  192.  
  193. // from text representation of the states, get the state object for
  194. // the state s
  195. // we avoid JSON parsing because we only want 1 state among millions
  196. function get_state(states, s) {
  197. var rep = game_state_rep(s);
  198. // essentially
  199. // "$rep": { .* }
  200. var reg = new RegExp ('"' + rep + '"[^:]*:[^{]*({[^}]*})', "m");
  201.  
  202. //console.log("getting state " + JSON.stringify(s) + " rep " + rep);
  203. var matches = states.match(reg);
  204. if (matches == null || matches.length < 2)
  205. return null;
  206.  
  207. var res = matches[1];
  208. //console.log("results in " + res);
  209. return JSON.parse(res);
  210. }
  211.  
  212. // states: map from state ids to probabilities
  213. function get_doubletime_strat(states, s) {
  214. var strat = {};
  215. var p1Move = pick_action(get_state(states, s), s.p1.bullets);
  216. strat["p1Move"] = p1Move;
  217.  
  218. for(var p2Move = -1; p2Move <= s.p2.bullets; p2Move++) {
  219. var s2 = act(s, p1Move, p2Move);
  220. if (s2.p1.bullets == 0 && s2.p2.bullets == 0)
  221. s2 = act(s2, -1, -1);
  222.  
  223. strat[p2Move] = pick_action(get_state(states,s2), s2.p1.bullets);
  224. }
  225. return strat;
  226. }
  227.  
  228. function get_winchance(wins, state) {
  229. var rep = game_state_rep(state);
  230. var reg = new RegExp ("" + rep + ":(.*)");
  231.  
  232. var matches = wins.match(reg);
  233. if (matches == null || matches.length < 2) {
  234. console.log("N1 win chances: unknown state " + JSON.stringify(state) + " rep " + rep);
  235. return "-1";
  236. }
  237. return matches[1];
  238. }
  239.  
  240. // parsing used to be based on https://greasyfork.org/en/scripts/18136-n1bot-inputs
  241.  
  242. /* A game is displayed in a big <td> whose text goes
  243. Tier X Event
  244. Battle Y
  245. You (win-loss)
  246. HP: $HP1
  247. Ammo: $bullets1
  248. Fatigue: $fatigue1
  249. Power: Prep|Catch|Pierce|Aura
  250. <hr>
  251. P2name
  252. $P2block (same as P1)
  253. <hr>
  254. Round $round
  255. <hr>
  256. then some stuff about previous actions,
  257. then <select> elements to input actions, going
  258. P1 first move - if P2 reloads - if P2 blocks - if P2 fires 1 to P2 bullets
  259. The values are according to action_map below.
  260. We need to parse the 2 player blocks + the round count, send the
  261. resulting game state to the background script then use the response
  262. to fill in the selects.
  263. */
  264. var action_map = {
  265. "-1": "1^0",
  266. "0": "2^0",
  267. "1": "3^1",
  268. "2": "3^2",
  269. "3": "3^3",
  270. "4": "3^4",
  271. "5": "3^5",
  272. "6": "3^6"
  273. };
  274.  
  275. var power_map = {
  276. "Prep": 0,
  277. "Catch": 1,
  278. "Pierce": 2,
  279. "Aura": 3
  280. }
  281.  
  282. function parseGame(elmt) {
  283. var txt = elmt.textContent;
  284.  
  285. var state = {p1:{}, p2:{}};
  286.  
  287. var hp_patt = /HP: (\d)/g;
  288. state.p1.hp = parseInt(hp_patt.exec(txt)[1]);
  289. state.p2.hp = parseInt(hp_patt.exec(txt)[1]);
  290.  
  291. var bullet_patt = /Ammo: (\d)/g;
  292. state.p1.bullets = parseInt(bullet_patt.exec(txt)[1]);
  293. state.p2.bullets = parseInt(bullet_patt.exec(txt)[1]);
  294.  
  295. var fatigue_patt = /Fatigue: (\d)/g;
  296. state.p1.fatigue = parseInt(fatigue_patt.exec(txt)[1]);
  297. state.p2.fatigue = parseInt(fatigue_patt.exec(txt)[1]);
  298.  
  299. var pow_patt = /Power: (\w+)/g;
  300. state.p1.type = power_map[pow_patt.exec(txt)[1]];
  301. state.p2.type = power_map[pow_patt.exec(txt)[1]];
  302.  
  303. var round_patt = /Round (\d+)/;
  304. state.round = parseInt(round_patt.exec(txt)[1]);
  305.  
  306. return state;
  307. }