xxxxxxxxxx
181
// PRESS THE PLAY BUTTON (TOP LEFT)
/*
Made by Sébastien Raynaud (Twitter: @Chopokopx), the 29 November 2018
Suggestions are welcomed
- This program asks for a JSON with all collectible hearthstone cards
It creates a bucket with all standard cards that can be discovered by paladin
> Paladin cards are 4 times in the bucket, as they are 4 times more likely to be discovered
- Each card is ranked from the best pick (Damaged Stegotron) to the worst pick (Sabretooth Stalker)
> The ranks can be changed at the bottom of the page
- Discover means taking 3 random cards from the bucket
- The program discovers 1 000 000 times and always picks the cards with the best rank
- The percentage of times each card was picked is then displayed on screen
> Note that paladin cards are picked more than better neutral cards
because they are more likely to be discovered
*/
let allCards; // All hearthstone cards
let bucket = []; // Bucket of cards, here 6 drops neutral + 4*paladin for discover
let discover = [];
let n = 1000 * 1000; // Number of times it will discover and select the card with the best rank
// Preload the JSON with all Hearthstone cards
function preload() {
allCards = loadJSON('https://api.hearthstonejson.com/v1/27641/enUS/cards.collectible.json');
}
// SETUP FUNCTION -----------------------------------------------------------------------
// Initialize the card bucket
function setup() {
noCanvas();
// From the allCards JSON, get the six drops discovered by paladin (*4 for paladin class cards)
let nCards = Object.keys(allCards); // Keys to iterate the allCards object
let sets = ['CORE', 'EXPERT1', 'UNGORO', 'ICECROWN', 'LOOTAPALOOZA', 'GILNEAS', 'BOOMSDAY']; // Full: ['TGT', 'BOOMSDAY', 'BRM', 'GANGS', 'CORE', 'EXPERT1', 'HOF', 'NAXX', 'GILNEAS', 'GVG', 'HERO_SKINS', 'ICECROWN', 'KARA', 'LOE', 'LOOTAPALOOZA', 'OG', 'UNGORO']
let classes = ['NEUTRAL', 'PALADIN'];
for (let i of nCards) {
let card = allCards[i]; // Next line checks if the card is a standard 6 mana paladin or neutral minion
if (card.type == 'MINION' && card.cost == '6' && sets.includes(card.set) && classes.includes(card.cardClass)) {
bucket.push(card); // If yes, put the card in the bucket
card.chosen = 0; // Initialize the amount of time the card was picked by the program
checkRating(card); // Checks the rating of the card (c.f. bottom of the page)
if (card.cardClass == 'PALADIN') { // Paladin class cards are 4 times more likely to be discovered
bucket.push(card);
bucket.push(card);
bucket.push(card);
}
}
}
// Add the only 6-drop that can be discovered by paladin in Rastakhan
// When this program was written, Rastakhan cards are not yet in the allCards JSON
let card = {name: 'Mojomaster Zihi'};
bucket.push(card);
checkRating(card);
discoverPaladinSixDrops(); // Chose 3 random cards from the bucket and put them in the discover array
calcStats(); // Discover 1 000 000 times, chose the best and show the cards by rank
}
// MAIN FUNCTION -----------------------------------------------------------------------
// Discover 1 000 000 times, chose the best and show the cards by rank
function calcStats() {
// Reinitialize the amount of times each card was chosen
for (let card of bucket) {
card.chosen = 0;
}
// Discover n times while chosing the card with the best rank
discoverNTimes(n);
// Put the cards in the ranking array
let ranking = [];
for (let i = 0; i < bucket.length; i++) {
let card = {
name: bucket[i].name,
rating: bucket[i].rating,
chosen: floor(bucket[i].chosen / n * 100 * 100) / 100 // To get a xx.xx number in %
};
if (i == 0) {
ranking.push(card);
} else if (bucket[i].name != bucket[i - 1].name) { // Avoid duplicates (paladin cards are 4 times in the bucket)
ranking.push(card);
}
}
ranking.sort((a, b) => b.chosen - a.chosen); // Sort the cards by number of picks
//console.log(ranking);
// Create paragraph elements with the results
for (let i = 0; i < ranking.length; i++) {
let card = ranking[i];
createP(`${i+1}. ${card.name} (${card.chosen}%)`);
}
}
// OTHER FUNCTIONS -----------------------------------------------------------------------
// Discover 3 random 6 drops as paladin
function discoverPaladinSixDrops() {
discover = [];
let count = 0; // Just to avoid inifinite loop if the bucket has less than 3 elements
while (discover.length < 3 && count < 1000) {
// Take a random card from the bucket
let randomSixDrop = random(bucket);
// Check the card is already in the discover array
let alreadyChosen = false;
for (let i = 0; i < discover.length; i++) {
if (randomSixDrop.name == discover[i].name) alreadyChosen = true;
}
// If the card is not already in the discover array, put it
if (!alreadyChosen) {
discover.push(randomSixDrop);
}
count++;
if (count >= 1000) console.log('Error: count >= 1000. Check bucket size and try again')
}
}
// Discover n times and select the cards with highest rank
function discoverNTimes(n) {
for (let i = 0; i < n; i++) {
// Find the index of the card in the discover array that has the best (lowest) rank
let best = 0;
for (let j = 1; j < discover.length; j++) {
if (discover[j].rating < discover[best].rating) best = j;
}
// Increment the number of times the best card was chosen and discover new cards
discover[best].chosen++;
discoverPaladinSixDrops();
}
}
// Assign a rating to each card, from best (1) to worst (37)
// This list of cards initially comes from console.logging the bucket
// Feel free to change the rating of the cards
function checkRating(card) {
switch (card.name) {
case "Damaged Stegotron": card.rating = 1; break;
case "Hungry Ettin": card.rating = 2; break;
case "Cairne Bloodhoof": card.rating = 3; break;
case "Frozen Crusher": card.rating = 4; break;
case "Boulderfist Ogre": card.rating = 5; break;
case "Archmage": card.rating = 6; break;
case "Mechanical Whelp": card.rating = 7; break;
case "Blackguard": card.rating = 8; break;
case "Hogger": card.rating = 9; break;
case "Bone Drake": card.rating = 10; break;
case "Illidan Stormrage": card.rating = 11; break;
case "Furnacefire Colossus": card.rating = 12; break;
case "Hemet, Jungle Hunter": card.rating = 13; break;
case "Skulking Geist": card.rating = 14; break;
case "Genn Greymane": card.rating = 15; break;
case "Lord of the Arena": card.rating = 16; break;
case "Nerubian Unraveler": card.rating = 17; break;
case "Mojomaster Zihi": card.rating = 18; break
case "Crystal Lion": card.rating = 19; break;
case "Frost Elemental": card.rating = 20; break;
case "Sunkeeper Tarim": card.rating = 21; break;
case "Missile Launcher": card.rating = 22; break;
case "Windfury Harpy": card.rating = 23; break;
case "The Black Knight": card.rating = 24; break;
case "Sunwalker": card.rating = 25; break;
case "Mossy Horror": card.rating = 26; break;
case "The Beast": card.rating = 27; break;
case "Priestess of Elune": card.rating = 28; break;
case "Reckless Rocketeer": card.rating = 29; break;
case "Spark Drill": card.rating = 30; break;
case "Argent Commander": card.rating = 31; break;
case "Necrotic Geist": card.rating = 32; break;
case "Spellweaver": card.rating = 33; break;
case "Gadgetzan Auctioneer": card.rating = 34; break;
case "Arcane Dynamo": card.rating = 35; break;
case "Glowstone Technician": card.rating = 36; break;
case "Sabretooth Stalker": card.rating = 37; break;
}
}