BvS Mahjong

Plays BvS Mahjong according to the "Level Two Strat"

  1. // ==UserScript==
  2. // @name BvS Mahjong
  3. // @namespace taltamir
  4. // @description Plays BvS Mahjong according to the "Level Two Strat"
  5. // @include http*://*animecubed.com/billy/bvs/partyhouse-mahjongplay.html
  6. // @include http*://*animecubedgaming.com/billy/bvs/partyhouse-mahjongplay.html
  7. // @version 2.1
  8. // @history 2.1 New domain - animecubedgaming.com - Channel28
  9. // @history 2.0 Now https compatible (Updated by Channel28)
  10. // @history 1.9 Rewrote dom parsing (by North)
  11. // @history 1.8 Prevents "you are playing too fast" and slowdowns (by taltamir).
  12. // @history 1.7 Chrome compatibility (by taltamir).
  13. // @history 1.06 Works again (McM changed the Dora mouseover); last version by Garyzx
  14. // @history 1.05 Hotkey now works for "Go to Next Round"
  15. // @history 1.04 Add hotkey ('d')
  16. // @history 1.03 Detect winds, dora, reach
  17. // @history 1.02 Added script updater
  18. // @history 1.01 Less fail
  19. // @history 1.00 Initial version by Garyzx
  20. // @grant none
  21. // ==/UserScript==
  22.  
  23. (function () {
  24. var tiles = [], amount = {};
  25. var yWind, tWind, doras;
  26.  
  27. function Tile(node) {
  28. this.id = node.children[0].src.match(/[A-Z0-9]+(?=.gif)/)[0];
  29. this.inputId = node.htmlFor;
  30. this.name = node.textContent;
  31. if (this.name[1] === "-") {
  32. this.value = Number(this.name[0]);
  33. this.suit = this.name.substr(2);
  34. } else {
  35. this.value = this.name;
  36. if (this.value === "Red" || this.value === "White" || this.value === "Green") {
  37. this.suit = "dragon";
  38. } else {
  39. this.suit = "wind";
  40. }
  41. }
  42. this.amount = function (offset) {
  43. if (offset === 0) {
  44. return amount[this.name];
  45. }
  46. if (this.suit === "dragon" || this.suit === "wind") {
  47. return 0;
  48. }
  49. var n = this.value + offset;
  50. if (n < 1 || n > 9) {
  51. return 0;
  52. }
  53. if (!amount[n + "-" + this.suit]) {
  54. amount[n + "-" + this.suit] = 0;
  55. }
  56. return amount[n + "-" + this.suit];
  57. };
  58. }
  59.  
  60. function find(id) {
  61. return document.evaluate("//img[@height='32'][contains(@src,'" + id + ".gif')]", document, null, 7, null).snapshotLength;
  62. }
  63.  
  64. function match(array, arrays) {
  65. var i, j;
  66. for (i = 0; i < arrays.length; i++) {
  67. var good = true;
  68. for (j = 0; j < arrays[i].length; j++) {
  69. if (array[j] !== arrays[i][j] && arrays[i][j] !== -1 && !(arrays[i][j] === 1.5 && array[j] > 0)) {
  70. good = false;
  71. }
  72. }
  73. if (good) {
  74. return true;
  75. }
  76. }
  77. return false;
  78. }
  79.  
  80. function init() {
  81. var i;
  82. var matches = document.evaluate('//label[img]', document, null, 7, null);
  83. if (!matches.snapshotLength) {
  84. return false;
  85. }
  86. for (i = 0; i < matches.snapshotLength; i++) {
  87. tiles[i] = new Tile(matches.snapshotItem(i));
  88. if (amount[tiles[i].name] > 0) {
  89. amount[tiles[i].name]++;
  90. } else {
  91. amount[tiles[i].name] = 1;
  92. }
  93. }
  94. yWind = document.evaluate("//td/b[contains(text(),'You (')]", document, null, 7, null).snapshotItem(0).textContent[5];
  95. try {
  96. tWind = document.evaluate("//td/b/b/i[contains(text(),'Tiles Left')]", document, null, 7, null).snapshotItem(0).parentNode.parentNode.firstChild.textContent[0];
  97. } catch (e) {
  98. tWind = 'E';
  99. }
  100. var dorasTitle = document.evaluate("//td/b/span/b[contains(text(),'Dora Indicators:')]", document, null, 7, null).snapshotItem(0).parentNode.title;
  101. doras = dorasTitle.slice(91, dorasTitle.indexOf('] offsetx') - 1);
  102. doras = doras.split(/\s/);
  103. return true;
  104. }
  105.  
  106. function selectTiles() {
  107. var i, n, neighbors, results = [];
  108. var reaches = document.evaluate("//td/font/b/i", document, null, 7, null);
  109. if (reaches.snapshotLength > 0) {
  110. reaches = reaches.snapshotItem(0).textContent;
  111. reaches = reaches.replace(/\s/g, '').split(',');
  112. for (i = 0; i < tiles.length; i++) {
  113. for (n = 0; n < reaches.length; n++) {
  114. if (tiles[i].name === reaches[n]) {
  115. results.push(tiles[i]);
  116. }
  117. }
  118. }
  119. return results;
  120. }
  121. for (i = 0; i < tiles.length; i++) {
  122. if ((tiles[i].suit === "dragon" || tiles[i].suit === "wind") && tiles[i].amount(0) === 1) {
  123. results.push(tiles[i]);
  124. }
  125. }
  126. if (results.length > 0) {
  127. return results;
  128. }
  129. var pairs = 0;
  130. for (i = 0; i < tiles.length; i++) {
  131. var iso = true;
  132. for (n = -2; n <= 2; n++) {
  133. if (n !== 0 && tiles[i].amount(n) > 0) {
  134. iso = false;
  135. }
  136. }
  137. if (tiles[i].amount(0) > 1) {
  138. iso = false;
  139. }
  140. if (tiles[i].amount(0) === 2) {
  141. pairs++;
  142. }
  143. if (iso) {
  144. results.push(tiles[i]);
  145. }
  146. }
  147. if (results.length > 0) {
  148. return results;
  149. }
  150. pairs /= 2;
  151. for (i = 0; i < tiles.length; i++) {
  152. neighbors = [];
  153. for (n = -4; n <= 4; n++) {
  154. neighbors[n + 4] = tiles[i].amount(n);
  155. }
  156. if (match(neighbors, [ [-1, -1, 0, 0, 1, 0, 1, 1, -1], [-1, 1, 1, 0, 1, 0, 0, -1, -1],
  157. [-1, -1, 0, 0, 2, 1, 1, 0, -1], [-1, 0, 1, 1, 2, 0, 0, -1, -1],
  158. [-1, -1, 0, 1, 2, 1, 0, 0, -1], [-1, 0, 0, 1, 2, 1, 0, -1, -1],
  159. [0, 0, 3, 0, 1, 0, 0, -1, -1], [-1, -1, 0, 0, 1, 0, 3, 0, 0],
  160. [-1, -1, 0, 0, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 0, 0, -1, -1] ])) {
  161. results.push(tiles[i]);
  162. }
  163. }
  164. if (results.length > 0) {
  165. return results;
  166. }
  167. if (pairs >= 3) {
  168. for (i = 0; i < tiles.length; i++) {
  169. if (tiles[i].amount(0) === 2) {
  170. results.push(tiles[i]);
  171. }
  172. }
  173. }
  174. if (pairs === 1) {
  175. for (i = 0; i < tiles.length; i++) {
  176. neighbors = [];
  177. for (n = -2; n <= 2; n++) {
  178. neighbors[n + 2] = tiles[i].amount(n);
  179. }
  180. if (match(neighbors, [ [0, 0, 1, 2, 0], [0, 2, 1, 0, 0] ])) {
  181. results.push(tiles[i]);
  182. }
  183. }
  184. }
  185. if (results.length > 0) {
  186. return results;
  187. }
  188. for (i = 0; i < tiles.length; i++) {
  189. neighbors = [];
  190. for (n = -2; n <= 2; n++) {
  191. neighbors[n + 2] = tiles[i].amount(n);
  192. }
  193. if (match(neighbors, [ [0, 0, 1, 0, 1.5], [1.5, 0, 1, 0, 0] ])) {
  194. results.push(tiles[i]);
  195. } else if (tiles[i].value === 1 && match(neighbors, [[0, 0, 1, 1, 0]])) {
  196. results.push(tiles[i]);
  197. } else if (tiles[i].value === 9 && match(neighbors, [[0, 1, 1, 0, 0]])) {
  198. results.push(tiles[i]);
  199. }
  200. }
  201. if (results.length > 0) {
  202. return results;
  203. }
  204. for (i = 0; i < tiles.length; i++) {
  205. neighbors = [];
  206. for (n = -2; n <= 2; n++) {
  207. neighbors[n + 2] = tiles[i].amount(n);
  208. }
  209. if (!match(neighbors, [ [1.5, 1.5, 1.5, -1, -1], [-1, 1.5, 1.5, 1.5, -1], [-1, -1, 1.5, 1.5, 1.5], [-1, -1, 3, -1, -1], [-1, -1, 4, -1, -1] ])) {
  210. results.push(tiles[i]);
  211. }
  212. }
  213. if (results.length > 0) {
  214. return results;
  215. }
  216. return tiles;
  217. }
  218.  
  219. function key_press(event) {
  220. if (event.keyCode==68) {
  221. if(document.forms.namedItem("tsumo")) {
  222. window.removeEventListener("keyup", key_press, false); //Remove key listener
  223. location.assign('javascript:tsumo.submit()');
  224. } else if(document.forms.namedItem("takeron")) {
  225. window.removeEventListener("keyup", key_press, false); //Remove key listener
  226. location.assign('javascript:takeron.submit()');
  227. } else if(document.forms.namedItem("startmj")) {
  228. window.removeEventListener("keyup", key_press, false); //Remove key listener
  229. location.assign('javascript:startmj.submit()');
  230. } else if(document.forms.namedItem("reshuffle")) {
  231. window.removeEventListener("keyup", key_press, false); //Remove key listener
  232. location.assign('javascript:reshuffle.submit()');
  233. } else if(document.forms.namedItem("discard")) {
  234. window.removeEventListener("keyup", key_press, false); //Remove key listener
  235. location.assign('javascript:discard.submit()');
  236. } else if(document.forms.namedItem("passontake")) {
  237. window.removeEventListener("keyup", key_press, false); //Remove key listener
  238. location.assign('javascript:passontake.submit()');
  239. } else if(document.forms.namedItem("backtohome2")) {
  240. window.removeEventListener("keyup", key_press, false); //Remove key listener
  241. location.assign('javascript:backtohome2.submit()');
  242. }
  243. }
  244. }
  245.  
  246. if (init()) {
  247. tiles = selectTiles();
  248. var maxScore = -9001, bestTile, log = "";
  249. var i, n;
  250. for (n = 0; n < tiles.length; n++) {
  251. log += tiles[n].name + ", ";
  252. var id = tiles[n].id;
  253. var score = find(id);
  254. if (tiles[n].value > 1) {
  255. score += find(id[0] + (Number(id.substr(1)) - 1)) / 2;
  256. }
  257. if (tiles[n].value < 9) {
  258. score += find(id[0] + (Number(id.substr(1)) + 1)) / 2;
  259. }
  260. if (tiles[n].name === tWind) {
  261. score -= 0.6;
  262. }
  263. if (tiles[n].name === yWind) {
  264. score -= 0.6;
  265. }
  266. if (tiles[n].suit === "dragon") {
  267. score -= 0.6;
  268. }
  269. for (i = 0; i < doras.length; i++) {
  270. if (tiles[n].name === doras[i]) {
  271. score -= 0.6;
  272. }
  273. }
  274. if (score > maxScore) {
  275. maxScore = score;
  276. bestTile = tiles[n];
  277. }
  278. }
  279. log = log.substr(0, log.length - 2);
  280. console.log(log);
  281. var input = document.getElementById(bestTile && bestTile.inputId);
  282. if (input && !input.disabled) {
  283. input.click();
  284. }
  285. input = document.getElementById("reachbutton");
  286. if (input) {
  287. input.click();
  288. }
  289. }
  290.  
  291. window.addEventListener("keyup", key_press, false);
  292. }) ();