BvS WK Sync

Attempts to solve World Kaiju sync.

  1. // ==UserScript==
  2. // @name BvS WK Sync
  3. // @namespace BvS
  4. // @version 1.3
  5. // @history 1.3 New domain - animecubedgaming.com - Channel28
  6. // @history 1.2 Now https compatible (Updated by Channel28)
  7. // @history 1.1 Added grant permissions (Updated by Channel28)
  8. // @history 1.0 Initial Release
  9. // @description Attempts to solve World Kaiju sync.
  10. // @include http*://*animecubed.com/billy/bvs/worldkaiju-group.html
  11. // @include http*://*animecubedgaming.com/billy/bvs/worldkaiju-group.html
  12. // @licence MIT; http://www.opensource.org/licenses/mit-license.php
  13. // @copyright 2010, Daniel Karlsson
  14. // @grant GM_log
  15. // ==/UserScript==
  16.  
  17. const TIMELIMIT = 5000; // ms
  18.  
  19. var options = document.evaluate("//form[@name='groupcheck']/select[@name='c1']/option",
  20. document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  21.  
  22. var colours = [];
  23. for (var i = 0; i < options.snapshotLength; i++) {
  24. var value = parseInt(options.snapshotItem(i).value);
  25. var colour = options.snapshotItem(i).textContent;
  26. colours[value] = colour;
  27. }
  28.  
  29. function find(val, arr)
  30. {
  31. for (var i in arr)
  32. if (arr[i] == val)
  33. return i;
  34. return -1;
  35. }
  36. function print(arr)
  37. {
  38. var xarr = [];
  39. for (var i in arr)
  40. xarr.push(colours[arr[i]]);
  41. return xarr.join(",");
  42. }
  43. function Mastermind(pegs, colours)
  44. {
  45. var my = this;
  46. my.colours = colours;
  47. my.pegs = pegs;
  48. my.prevGuesses = [];
  49. // Create array of all possible combinations
  50. my.combinations = [];
  51. for (var i = 0; i < Math.pow(my.colours, my.pegs); i++) {
  52. var n = i;
  53. var comb = [];
  54. for (var p = 0; p < my.pegs; p++) {
  55. comb.push(n % my.colours);
  56. n = Math.floor(n / my.colours);
  57. }
  58. my.combinations.push(comb);
  59. }
  60. my.appendGuess = function(g, s) {
  61. my.prevGuesses.push({guess: g, score: s});
  62. }
  63. // Determine score as [<correct colour, correct position>, <correct colour, wrong position>]
  64. my.score = function(guess, board) {
  65. var boardColours = [];
  66. var guessColours = [];
  67. for (var i = 0; i < my.colours; i++) {
  68. boardColours.push(0);
  69. guessColours.push(0);
  70. }
  71.  
  72. var correctPosition = 0;
  73. for (var i = 0; i < my.pegs; i++) {
  74. if (board[i] == guess[i])
  75. correctPosition++;
  76. else {
  77. boardColours[board[i]]++;
  78. guessColours[guess[i]]++;
  79. }
  80. }
  81.  
  82. var correctColour = 0;
  83. for (var c = 0; c < my.colours; c++)
  84. correctColour += Math.min(guessColours[c], boardColours[c]);
  85.  
  86. return [correctPosition, correctColour];
  87. }
  88. my.possibleScores = [];
  89. for (var c = 0; c <= my.pegs; c++)
  90. for (var p = 0; p <= my.pegs - c; p++)
  91. my.possibleScores.push([p, c]);
  92.  
  93. // Remove combinations based on guess and score
  94. my.eliminateCombinations = function(guess, score, splice) {
  95. var keepers = [];
  96. var removals = 0;
  97. for (var c in my.combinations) {
  98. var s = my.score(guess, my.combinations[c]);
  99. if (s[0] == score[0] && s[1] == score[1]) {
  100. if (splice)
  101. keepers.push(my.combinations[c]);
  102. } else {
  103. removals++;
  104. }
  105. }
  106. if (splice)
  107. my.combinations = keepers;
  108. return removals;
  109. }
  110. // Calculate minimum number of eliminations
  111. my.guessScore = function(guess) {
  112. var score = my.combinations.length + 1;
  113. for (var s in my.possibleScores) {
  114. var removals = my.eliminateCombinations(guess, my.possibleScores[s], false);
  115. score = Math.min(removals, score);
  116. }
  117. return score;
  118. }
  119. // Estimate time of bsetGuess brute force method
  120. my.estimateTime = function() {
  121. var t1 = new Date();
  122. var score = my.guessScore(my.combinations[0]);
  123. var t2 = new Date();
  124. var t = t2.getTime() - t1.getTime();
  125. return t * my.combinations.length;
  126. }
  127. // Brute force search of all possibilities
  128. my.bestGuess = function() {
  129. var bestGuess;
  130. var bestScore = -1;
  131. for (var guess in my.combinations) {
  132. var score = my.guessScore(my.combinations[guess]);
  133. if (score > bestScore) {
  134. bestScore = score;
  135. bestGuess = my.combinations[guess];
  136. }
  137. }
  138. my.bestScore = bestScore;
  139. my.bestGuess = bestGuess;
  140. return bestGuess;
  141. }
  142.  
  143. // Try random guesses until we run out of time
  144. my.fastGuess = function(limit) {
  145. var t1 = new Date();
  146. t1 = t1.getTime();
  147. var bestGuess;
  148. var bestScore = -1;
  149. var time = 0;
  150. var tried = [];
  151. do {
  152. var guess;
  153. guess = Math.floor(Math.random() * my.combinations.length);
  154. while (find(guess, tried) >= 0)
  155. guess = (guess + 1) % my.combinations.length;
  156. tried.push(guess);
  157. var score = my.guessScore(my.combinations[guess]);
  158. if (score > bestScore) {
  159. bestScore = score;
  160. bestGuess = my.combinations[guess];
  161. }
  162. var t2 = new Date();
  163. time = t2.getTime() - t1;
  164.  
  165. if (tried.length >= my.combinations.length)
  166. break;
  167. } while (time < limit)
  168. my.bestScore = bestScore;
  169. my.bestGuess = bestGuess;
  170. return bestGuess;
  171. }
  172. }
  173.  
  174. var form = document.evaluate("//form[@name='groupcheck']",
  175. document, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null);
  176. if (form) {
  177. form = form.singleNodeValue;
  178. var guesses = document.evaluate("//table/tbody/tr[count(descendant::td)=5]",
  179. document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  180. var prevGuesses = 0;
  181. var board = new Mastermind(4, colours.length);
  182.  
  183. // Legacy hint
  184. if (/You have a strong feeling that the first Chakra isn.t (\w+)/.test(form.textContent)) {
  185. GM_log("Legacy: " + RegExp.lastParen);
  186. var chakra = find(RegExp.lastParen, colours);
  187. if (chakra >= 0) {
  188. keepers = [];
  189. for (var i in board.combinations)
  190. if (board.combinations[i][0] != chakra)
  191. keepers.push(board.combinations[i]);
  192. GM_log("Kept " + keepers.length + " / " + board.combinations.length);
  193. board.combinations = keepers;
  194. }
  195. }
  196.  
  197. for (var g = 0; g < guesses.snapshotLength; g++) {
  198. var tds = guesses.snapshotItem(g).getElementsByTagName("td");
  199. var guess = [];
  200. var res = []
  201. for (var i = 0; i < 4; i++) {
  202. var col = tds[i].textContent;
  203. if (find(col, colours))
  204. guess.push(find(col, colours));
  205. else
  206. continue;
  207. }
  208. var match = tds[4].textContent.match(/(\d+)[^\d]+(\d+)/);
  209. if (match && guess.length == 4) {
  210. res = [parseInt(match[1]), parseInt(match[2])];
  211. board.appendGuess(guess, res);
  212. board.eliminateCombinations(guess, res, true);
  213. prevGuesses++;
  214. }
  215. }
  216. var div = document.createElement("div");
  217. form.parentNode.insertBefore(div, form.nextSibling);
  218. div.textContent = "Searching...";
  219. setTimeout(function() {
  220. var limited = false;
  221. var bestGuess;
  222. if (board.estimateTime() > TIMELIMIT) {
  223. bestGuess = board.fastGuess(TIMELIMIT);
  224. limited = true;
  225. } else
  226. bestGuess = board.bestGuess();
  227.  
  228. if (board.combinations.length > 0) {
  229. if (limited)
  230. div.textContent = "Guess (" + Math.round(TIMELIMIT / 1000) + " s search): " + print(bestGuess);
  231. else
  232. div.textContent = "Best guess: " + print(bestGuess);
  233. }
  234. }, 100);
  235. }