|
|
@@ -0,0 +1,372 @@
|
|
|
+// ==UserScript==
|
|
|
+// @name High-Low
|
|
|
+// @namespace http://tampermonkey.net/
|
|
|
+// @version 0.1.2
|
|
|
+// @description try to take over the world!
|
|
|
+// @author You
|
|
|
+// @match https://www.torn.com/loader.php?sid=high*ow
|
|
|
+// @require https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js
|
|
|
+// @resource sourceCodePro https://fonts.googleapis.com/css?family=Source+Code+Pro
|
|
|
+// @require https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/highlight.min.js
|
|
|
+// @resource json-formatter_css https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/styles/default.min.css
|
|
|
+// @grant GM_getResourceText
|
|
|
+// @grant GM_addStyle
|
|
|
+// @grant GM_setValue
|
|
|
+// @grant GM_getValue
|
|
|
+// ==/UserScript==
|
|
|
+
|
|
|
+/* jshint -W097 */
|
|
|
+/*global
|
|
|
+ $, console, Raven, GM_addStyle, GM_getResourceText, Vue, hljs
|
|
|
+*/
|
|
|
+
|
|
|
+(function() {
|
|
|
+ 'use strict';
|
|
|
+ var sourceCodePro = GM_getResourceText("codeSourcePro");
|
|
|
+ GM_addStyle(sourceCodePro);
|
|
|
+ GM_addStyle("pre,code {font-family: 'Source Code Pro', monospace; font-size: 14px;}");
|
|
|
+ var js_css = GM_getResourceText("json-formatter_css");
|
|
|
+ GM_addStyle(js_css);
|
|
|
+ //var bootstrap_css = GM_getResourceText("bootstrap_css");
|
|
|
+ //GM_addStyle(bootstrap_css);
|
|
|
+
|
|
|
+ GM_addStyle(".hljs-number { color: blue; }");
|
|
|
+
|
|
|
+ var formatterConfig = {
|
|
|
+ hoverPreviewEnabled: false,
|
|
|
+ hoverPreviewArrayCount: 100,
|
|
|
+ hoverPreviewFieldCount: 5,
|
|
|
+ theme: 'dark',
|
|
|
+ animateOpen: true,
|
|
|
+ animateClose: true,
|
|
|
+ open: 20
|
|
|
+ };
|
|
|
+
|
|
|
+ var changeTimeout = function() {
|
|
|
+ var oldTimeout = setTimeout;
|
|
|
+ /*global setTimeout:true */
|
|
|
+ setTimeout = function(a, b) {
|
|
|
+ return oldTimeout(a, b/3);
|
|
|
+ };
|
|
|
+ console.log("setTimout: changed");
|
|
|
+ };
|
|
|
+ let script = document.createElement('script');
|
|
|
+ script.appendChild(document.createTextNode('('+ changeTimeout +')();'));
|
|
|
+ (document.head || document.body || document.documentElement).appendChild(script);
|
|
|
+
|
|
|
+ class Card {
|
|
|
+ constructor(number) {
|
|
|
+ let card = this.getCardFromNumber(number);
|
|
|
+ this.color = card[0];
|
|
|
+ this.rank = card[1];
|
|
|
+ }
|
|
|
+
|
|
|
+ getCardFromNumber(number) {
|
|
|
+ let mod = (number-1) % 4;
|
|
|
+ let color = "";
|
|
|
+ if (mod === 0) {
|
|
|
+ color = "spades";
|
|
|
+ }
|
|
|
+ if (mod === 1) {
|
|
|
+ color = "diamonds";
|
|
|
+ }
|
|
|
+ if (mod === 2) {
|
|
|
+ color = "hearts";
|
|
|
+ }
|
|
|
+ if (mod === 3) {
|
|
|
+ color = "clubs";
|
|
|
+ }
|
|
|
+ let offset = 0;
|
|
|
+ offset += 4; // Counter for 2 being first card, not 1
|
|
|
+ offset += 3; // Counter for using floor
|
|
|
+ let rank = Math.floor((parseInt(number, 10)+offset)/4);
|
|
|
+ return [color, rank];
|
|
|
+ }
|
|
|
+
|
|
|
+ toString() {
|
|
|
+ let rank = this.rank.toString();
|
|
|
+ console.log("rank:", rank);
|
|
|
+ /* if (rank === "10") {
|
|
|
+ rank = "T";
|
|
|
+ } */
|
|
|
+ if (rank === "11") {
|
|
|
+ rank = "J";
|
|
|
+ }
|
|
|
+ if (rank === "12") {
|
|
|
+ rank = "Q";
|
|
|
+ }
|
|
|
+ if (rank === "13") {
|
|
|
+ rank = "K";
|
|
|
+ }
|
|
|
+ if (rank === "14") {
|
|
|
+ rank = "A";
|
|
|
+ }
|
|
|
+ return this.color + "-" + rank;
|
|
|
+ }
|
|
|
+
|
|
|
+ toValue() {
|
|
|
+ return this.rank;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Initialize deck */
|
|
|
+ let deck = GM_getValue('deck', []);
|
|
|
+ let createDeck = (deck) => {
|
|
|
+ for (let i = 1; i <= 52; i++) {
|
|
|
+ deck.push(i);
|
|
|
+ }
|
|
|
+ GM_setValue('[createDeck] deck', deck);
|
|
|
+ // console.log(deck);
|
|
|
+ return deck;
|
|
|
+ }
|
|
|
+ if (deck.length === 0) {
|
|
|
+ deck = createDeck(deck);
|
|
|
+ }
|
|
|
+
|
|
|
+ let removeCard = (deck, card) => {
|
|
|
+ /*
|
|
|
+ console.log('[removeCard] card:', card);
|
|
|
+ console.log('typeof card:', typeof card);
|
|
|
+ console.log('typeof deck[5]:', typeof deck[5]);
|
|
|
+ */
|
|
|
+ for( var i = 0; i < deck.length; i++) {
|
|
|
+ if ( deck[i] === card) {
|
|
|
+ deck.splice(i, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ GM_setValue('[removeCard] deck', deck);
|
|
|
+ // console.log(deck);
|
|
|
+ }
|
|
|
+
|
|
|
+ //********
|
|
|
+ // Controller
|
|
|
+ //********
|
|
|
+
|
|
|
+ let processStartGame = function(db) {
|
|
|
+ let formulaStats = db.DB.formulaStats[0];
|
|
|
+ let previousMultiplier = 0;
|
|
|
+ let currentMultiplier = 0;
|
|
|
+ if (formulaStats) {
|
|
|
+ currentMultiplier = formulaStats.currentRatio;
|
|
|
+ previousMultiplier = formulaStats.previousRatio;
|
|
|
+ if (previousMultiplier !== currentMultiplier) {
|
|
|
+ console.log("Multiplier changed!. Old: " + previousMultiplier + " => New: " + currentMultiplier);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //$("pre code.json").empty().append(JSON.stringify(db, null, 4));
|
|
|
+ console.log('myVue:', myVue);
|
|
|
+ myVue.jsonobject = JSON.stringify(db, null, 4);
|
|
|
+ // myVue.jsonobject = "Test";
|
|
|
+ // action.header = "Test";
|
|
|
+ Vue.nextTick(function() {
|
|
|
+ hljs.initHighlighting.called = false;
|
|
|
+ hljs.initHighlighting();
|
|
|
+ });
|
|
|
+ console.log("Multiplier:", currentMultiplier);
|
|
|
+ };
|
|
|
+
|
|
|
+ let processGameStarted = function(db) {
|
|
|
+ // dealerCard is revealed; player picks High or Low
|
|
|
+ // Note: Player also has the option to cash in but that is never considered
|
|
|
+ console.log("dealerCard:", db.currentGame[0].dealerCard);
|
|
|
+ let dealerCard = new Card(db.currentGame[0].dealerCard);
|
|
|
+
|
|
|
+ console.assert(dealerCard.toString() === db.currentGame[0].dealerCardInfo.classCode,
|
|
|
+ "Mismatch dealerCard " + dealerCard + " <-> " + db.currentGame[0].dealerCardInfo.classCode);
|
|
|
+ console.log("dealerCard:", dealerCard.toString());
|
|
|
+ let arrayWithOdds = calculateOdds(dealerCard, deck);
|
|
|
+ let lower = arrayWithOdds[0];
|
|
|
+ let higher = arrayWithOdds[1];
|
|
|
+ let numberOfCards = arrayWithOdds[2];
|
|
|
+ // testing
|
|
|
+ //$('div.action-c.action-btn-wrap.active.low').addClass('iconPulseExpiring');
|
|
|
+ //$('div.action-r.action-btn-wrap.active.high').removeClass('active');
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ let processMakeChoice = function(db) {
|
|
|
+ // The deal has ended. The choice is cash in or play on.
|
|
|
+ if (db.currentGame[0].dealerCard !== db.currentGame[0].lastDealerCard) {
|
|
|
+ // Reveal dealerCard of next deal
|
|
|
+ // Page has been refreshed
|
|
|
+ // There is no playerCard (there is a lastPlayerCard)
|
|
|
+ db.currentGame[0].playerCard = db.currentGame[0].lastPlayerCard;
|
|
|
+ db.currentGame[0].playerCardInfo = db.currentGame[0].lastPlayerCardInfo;
|
|
|
+ // dealerCard assert will fail!
|
|
|
+ }
|
|
|
+ console.log("step:", db.currentGame[0].step);
|
|
|
+ let dealerCard = new Card(db.currentGame[0].dealerCard);
|
|
|
+ console.assert(dealerCard.toString() === db.currentGame[0].dealerCardInfo.classCode,
|
|
|
+ "Mismatch dealerCard " + dealerCard + " <-> " + db.currentGame[0].dealerCardInfo.classCode);
|
|
|
+ console.log("dealerCard:", dealerCard.toString());
|
|
|
+ removeCard(deck, parseInt(db.currentGame[0].dealerCard, 10));
|
|
|
+ let playerCard = new Card(db.currentGame[0].playerCard);
|
|
|
+ console.assert(playerCard.toString() === db.currentGame[0].playerCardInfo.classCode,
|
|
|
+ "Mismatch playerCard " + playerCard + " <-> " + db.currentGame[0].playerCardInfo.classCode);
|
|
|
+ console.log("playerCard:", playerCard.toString());
|
|
|
+ removeCard(deck, parseInt(db.currentGame[0].playerCard, 10));
|
|
|
+ if (db.DB.deckShuffled) {
|
|
|
+ console.log('[processMakeChoice] deckShuffled', db.DB.deckShuffled);
|
|
|
+ // New deck
|
|
|
+ deck = [];
|
|
|
+ deck = createDeck(deck);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ $(document).ajaxSuccess(
|
|
|
+ // On every detected Ajax communication
|
|
|
+ function(event, jqxhr, settings, data) {
|
|
|
+ // Make sure it was to or from /loader.php?sid=hiloJson&rfcv=<rfcv>
|
|
|
+ if (settings.url.indexOf("loader.php?sid=hiloJson") != -1) {
|
|
|
+ //console.log("event:", event);
|
|
|
+ //console.log("jqxhr:", jqxhr);
|
|
|
+ //console.log("settings:", settings);
|
|
|
+ let db = JSON.parse(data);
|
|
|
+ $("pre code.json").empty().append(JSON.stringify(db.currentGame, null, 4));
|
|
|
+ hljs.initHighlighting.called = false;
|
|
|
+ hljs.initHighlighting();
|
|
|
+ console.log("db:", db);
|
|
|
+ let status = db.status;
|
|
|
+ console.log("db.status:", db.status);
|
|
|
+ if (status === "startGame") {
|
|
|
+ processStartGame(db);
|
|
|
+ }
|
|
|
+ if (status === "gameStarted") {
|
|
|
+ processGameStarted(db);
|
|
|
+ }
|
|
|
+ if (status === "makeChoice") {
|
|
|
+ processMakeChoice(db);
|
|
|
+ }
|
|
|
+ //console.log("db.currentGame:", db.currentGame);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ //********
|
|
|
+ // VIEW
|
|
|
+ //********
|
|
|
+ let boxTitle = "Let's play High-Low";
|
|
|
+ let boxHTML = "<div class=\"tutorial-cont m-top10\">" +
|
|
|
+ "<div class=\"title-gray top-round\" role=\"heading\" aria-level=\"5\">" +
|
|
|
+ "<i class=\"tutorial-icon\"></i>" +
|
|
|
+ "<span style=\"padding-left: 6px\">" + boxTitle + "</span>" +
|
|
|
+ "</div>" +
|
|
|
+ "<div class=\"bottom-round cont-gray p10\">"+
|
|
|
+ "<div id=\"app\" style=\"min-height: 450px;\">" +
|
|
|
+ "<div style=\"float: left; width: 50%;\">" +
|
|
|
+ "<current-action v-bind:action=\"action\"></current-action>" +
|
|
|
+ "</div><div id=\"right\" style=\"float: right; width: 50%;\">"+
|
|
|
+ //"<pre><code class=\"json\"></code></pre>" +
|
|
|
+ "<vue-object v-bind:jsonobject=\"jsonobject\"></vue-object>" +
|
|
|
+ "</div></div><div class=\"clear\"></div>" +
|
|
|
+ "<hr class=\"page-head-delimiter m-top10\">" +
|
|
|
+ "</div>";
|
|
|
+ $('.highlow-main-wrap').after(boxHTML);
|
|
|
+
|
|
|
+ // Vue.js template
|
|
|
+ //creating component
|
|
|
+ var currentAction = Vue.extend({
|
|
|
+ template: '<h1 v-bind:style="actionStyle">{{ action.header }}</h1>',
|
|
|
+ data: function() {
|
|
|
+ return {
|
|
|
+ // action: "Current Action",
|
|
|
+ actionStyle: {
|
|
|
+ marginTop: "0px"
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ props: ['action']
|
|
|
+ });
|
|
|
+ var vueObject = Vue.extend({
|
|
|
+ template: '<div v-bind:style="vueObjectStyle"><pre><code class="json" v-html="jsonobject"></code></pre></div>',
|
|
|
+ data: function() {
|
|
|
+ return {
|
|
|
+ vueObjectStyle: {
|
|
|
+ marginTop: "0px"
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ props: ['jsonobject']
|
|
|
+ });
|
|
|
+ //registering component
|
|
|
+ Vue.component('current-action', currentAction);
|
|
|
+ Vue.component('vue-object', vueObject);
|
|
|
+ //initializing the Vue application
|
|
|
+ let action = {};
|
|
|
+ action.header = "Current Action";
|
|
|
+ let myVue = new Vue({
|
|
|
+ el: "#app",
|
|
|
+ data: function() {
|
|
|
+ return {
|
|
|
+ jsonobject: "Start",
|
|
|
+ action: action
|
|
|
+ };
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ //********
|
|
|
+ // MODEL
|
|
|
+ //********
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param {array} deck, cards by number
|
|
|
+ * @return {array} number of cards by rank where key is the rank \
|
|
|
+ * and val is the number of that rank in the deck
|
|
|
+ */
|
|
|
+ let parseDeck = function(dealerCard, deck) {
|
|
|
+ let parsedDeck = [];
|
|
|
+ // parsedDeck runs from 0 to 14 where 0 and 1 are always 0.
|
|
|
+ // Real deck values count from 2 to 14 (ace)
|
|
|
+ for (let i = 0; i < 15; i++) {
|
|
|
+ parsedDeck[i] = 0;
|
|
|
+ }
|
|
|
+ let deckLength = deck.length;
|
|
|
+ for (let i = 0; i < deckLength; i++) {
|
|
|
+ /*
|
|
|
+ if (i < 10) {
|
|
|
+ console.log('first cards:', deck[i]);
|
|
|
+ }
|
|
|
+ */
|
|
|
+ let offset = 0;
|
|
|
+ offset += 4; // Counter for 2 being first card, not 1
|
|
|
+ offset += 3; // Counter for using floor
|
|
|
+ let rank = Math.floor((parseInt(deck[i], 10)+offset)/4);
|
|
|
+ // console.log("[parseDeck] rank:", rank);
|
|
|
+ if (!(rank in parsedDeck)) {
|
|
|
+ parsedDeck[rank] = 0;
|
|
|
+ }
|
|
|
+ parsedDeck[rank]++;
|
|
|
+ }
|
|
|
+ if (deckLength === 0) {
|
|
|
+ // Shuffle -> create new deck
|
|
|
+ for (let i = 2; i < 15; i++) {
|
|
|
+ parsedDeck[i] = 4;
|
|
|
+ }
|
|
|
+ // Remove dealerCard
|
|
|
+ parseDeck[dealerCard.rank]--;
|
|
|
+ }
|
|
|
+ // console.table(parsedDeck);
|
|
|
+ return parsedDeck;
|
|
|
+ };
|
|
|
+
|
|
|
+ let calculateOdds = function(dealerCard, deck) {
|
|
|
+ let parsedDeck = parseDeck(dealerCard, deck);
|
|
|
+ let deckLength = parsedDeck.length;
|
|
|
+ let numberOfCards = 0;
|
|
|
+ let lowerDoesNotLose = 0; // Equals count as 'not lose'
|
|
|
+ let higherDoesNotLose = 0; // Equals count as 'not lose'
|
|
|
+ for (let i = 0; i < deckLength; i++) {
|
|
|
+ if (i <= dealerCard.rank) {
|
|
|
+ lowerDoesNotLose += parsedDeck[i];
|
|
|
+ }
|
|
|
+ if (i >= dealerCard.rank) {
|
|
|
+ higherDoesNotLose += parsedDeck[i];
|
|
|
+ }
|
|
|
+ numberOfCards += parsedDeck[i];
|
|
|
+ }
|
|
|
+ // console.log("[calculteOdds] parsedDeck;", parsedDeck);
|
|
|
+ console.log("[calculateOdds] lowerDoesNotLose:", lowerDoesNotLose);
|
|
|
+ console.log("[calculateOdds] higherDoesNotLose:", higherDoesNotLose);
|
|
|
+ console.log("[calculateodds] numberOfCards:", numberOfCards);
|
|
|
+ return [lowerDoesNotLose, higherDoesNotLose, numberOfCards];
|
|
|
+ };
|
|
|
+})();
|