// ==UserScript== // @name High-Low // @namespace http://tampermonkey.net/ // @version 0.1.10 // @description try to take over the world! // @author You // @match https://www.torn.com/loader.php?sid=high*ow // @require https://cdn.jsdelivr.net/npm/vue/dist/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 // @resource pure https://unpkg.com/purecss@1.0.1/build/pure-min.css // @resource rocker https://relentless.rocks/rocker.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); let pure = GM_getResourceText('pure'); GM_addStyle(pure); let rocker_css = GM_getResourceText('rocker'); GM_addStyle(rocker_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) { this.number = parseInt(number, 10); let card = this.getCardFromNumber(this.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; } } /* global */ let action = {}; let formula = {}; let won = 0; let lost = -1; let ratio = '0.000000'; /* Initialize deck */ let deck = GM_getValue('deck', []); let createDeck = (deck) => { for (let i = 2; i < 54; i++) { deck.push(i); } GM_setValue('deck', deck); console.warn('new deck.length:', deck.length); // console.log(deck); action.cards = deck.length; 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('deck', deck); action.cards = deck.length; // console.log('deck.length:', deck.length); } //******** // Controller //******** let reset = () => { // Reset // See https://openuserjs.org/install/DeKleineKobini/TORN_HighLow_Helper.user.js $(".actions-wrap")[0].style = "display: block"; $(".actions")[0].appendChild($(".startGame")[0]) $(".startGame")[0].style = "display:inline-block"; $(".low")[0].style = "display: none"; $(".high")[0].style = "display: none"; $(".continue")[0].style = "display: none"; } 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); } } console.log("Multiplier:", 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(); }); reset(); }; 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.warn('dealerCard.number:', dealerCard.number); removeCard(deck, dealerCard.number); if ('result' in db.currentGame[0]) { console.log('LOST'); removeCard(deck, parseInt(db.currentGame[0].playerCard, 10)); } 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'); $(".startGame")[0].style = "display: none"; if (lower >= higher) { $(".high")[0].style = "display: none"; $(".low")[0].style = "display: inline-block"; } else if (higher > lower) { $(".low")[0].style = "display: none"; $(".high")[0].style = "display: inline-block"; } }; 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); 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= 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)); myVue.jsonobject = JSON.stringify(db.currentGame, null, 4); // hljs.initHighlighting.called = false; // hljs.initHighlighting(); /* Vue.nextTick(function() { hljs.initHighlighting.called = false; hljs.initHighlighting(); }); */ console.log("db:", db); if ("currentGame" in db && "result" in db.currentGame[0] && db.currentGame[0].result === "Incorrect") { db.status = "Incorrect"; } let status = db.status; console.log("db.status:", db.status); myVue.header = status; if ('DB' in db) { if ('formulaStats' in db.DB) { console.warn('Changing formulaStats'); console.log(db.DB.formulaStats[0]); myVue.won = parseInt(db.DB.formulaStats[0].moneyWon); myVue.lost = parseInt(db.DB.formulaStats[0].moneyLost); won = parseInt(db.DB.formulaStats[0].moneyWon); lost = parseInt(db.DB.formulaStats[0].moneyLost); myVue.ratio = (won/lost).toFixed(6); let formatNumber = new Intl.NumberFormat('en-US') db.DB.formulaStats[0].moneyWon = formatNumber.format(db.DB.formulaStats[0].moneyWon); db.DB.formulaStats[0].moneyLost = formatNumber.format(db.DB.formulaStats[0].moneyLost); myVue.formula = db.DB.formulaStats[0]; } } Vue.nextTick(function() { hljs.initHighlighting.called = false; hljs.initHighlighting(); }); // Moved this to 'main' because it missed a few before if ('deckShuffled' in db.DB) { console.warn('db.DB.deckShuffled:', db.DB.deckShuffled); if (db.DB.deckShuffled === true) { deck = []; deck = createDeck(deck); } } if (status === "startGame") { action.lower = 0; action.higher = 0; processStartGame(db); } if (status === "gameStarted") { processGameStarted(db); } if (status === "makeChoice") { action.lower = 0; action.higher = 0; processMakeChoice(db); } if (status === "Incorrect") { reset(); } //console.log("db.currentGame:", db.currentGame); } }); //******** // VIEW //******** let boxTitle = "Let's play High-Low"; let boxHTML = `
` + boxTitle + `

Most recent formulaStats


`; $('.highlow-main-wrap').after(boxHTML); // Vue.js template //creating component var actionHeader = Vue.extend({ template: '

{{ header }}

', data: function() { return { // action: "Current Action", actionStyle: { marginTop: "0px" } }; }, props: ['header'] }); var vueObject = Vue.extend({ template: '
', data: function() { return { vueObjectStyle: { marginTop: "0px" } }; }, props: ['jsonobject'] }); let templateAction = `
Odds Table
ActionPlacesOdds
Lower{{ action.lower }}{{ action.lower | calculatePercentage }} %
Higher{{ action.higher }}{{ action.higher | calculatePercentage }} %
Deck{{ action.cards }}100 %
`; var actionTable = Vue.extend({ template: templateAction, filters: { calculatePercentage: (places) => { return Math.round(places * 100 / deck.length); } }, props: ['action'] }); let templateRatio = "

Ratio

"; /* let ratioCalculation = Vue.extend({ template: templateRatio, props: ['won', 'lost'], data: function () { return { wwon: won, llost: this.lost } }, computed: { ratio: function() { console.log('[Ratio] won:', this.wwon); console.log('[Ratio] lost:', this.llost); if (this.lost === 0) { return '0.000000'; } else { console.log('[Ratio] ratio:', (this.won/this.lost).toFixed(6)); return (this.won/this.lost).toFixed(6); } } } }); */ let ratioCalculation = Vue.extend({ template: templateRatio, props: ['ratio'] }); let templateStats = '
'; let formulaStats = Vue.extend({ template: templateStats, props: ['formula'] }); //registering component Vue.component('action-header', actionHeader); Vue.component('vue-object', vueObject); Vue.component('action-table', actionTable); Vue.component('formula-stats', formulaStats); Vue.component('ratio-calculation', ratioCalculation); //initializing the Vue application action.cards = deck.length; action.lower = 0; action.higher = 0; let header = "Current Action"; let myVue = new Vue({ el: "#app", data: function() { return { jsonobject: "Start", header: header, action: action, formula: formula, ratio: ratio }; } }); //******** // 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(deck) { // Create new parsedDeck from 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); parsedDeck[rank]++; } if (deckLength === 0) { // Shuffle -> create new deck for (let i = 2; i < 15; i++) { parsedDeck[i] = 4; } } return parsedDeck; }; let calculateOdds = function(dealerCard, deck) { let parsedDeck = parseDeck(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.assert(deck.length === numberOfCards, 'deck.length: '+deck.length+' <> numberOfCards: '+numberOfCards); // console.log("[calculteOdds] parsedDeck;", parsedDeck); action.lower = lowerDoesNotLose; action.higher = higherDoesNotLose; console.log("[calculateOdds] lowerDoesNotLose:", lowerDoesNotLose); console.log("[calculateOdds] higherDoesNotLose:", higherDoesNotLose); console.log("[calculateodds] numberOfCards:", numberOfCards); return [lowerDoesNotLose, higherDoesNotLose, numberOfCards]; }; })();