Topic: Feature Request - Average CMC

Having the average CMC for a deck would be a useful stat for deckbuilding in EDH. I imagine its a quick add too.

Edit: Current Code:

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.6.1
    // ==/UserScript==

    // Initialize array and hash to hold mana costs
    var manaCosts = {};
    var totalCosts = [];

    // Iterate through rows to get mana cost totals for each card
    var x = document.getElementsByClassName("mtg_mana");

    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[0-9URGBWCX]{1,2}/i;
        //console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        //console.log("Parent element is " + row);
        //console.log("Card element looks like this: " + document.getElementById(row).innerHTML);
        var cardName = document.getElementById(row).innerHTML.match(/(?:a class="simple" href="https:\/\/deckbox\.org\/mtg\/.*" target="_blank">)(.*)(?:<\/a>)/)[1];
        //console.log("Card name is " + cardName);
        var stripped = x[i].outerHTML.match(re).toString();
        //console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.replace("mtg_mana_", "");
        //console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBWC]([URGBWC])?/, "1");
        var converted = converted.replace(/X/, "0");

        if (!manaCosts[row]) {
            //console.log("Adding new item to manaCosts hash for card " + cardName);
            //console.log("Found mana cost " + converted + " for card " + cardName);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
            //console.log("Found mana cost " + converted + " for card " + cardName);
        }
        //console.log("Total mana cost for " + cardName + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    console.log("Here's what we found in the main deck: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            var cardName = document.getElementById(card).innerHTML.match(/(?:a class="simple" href="https:\/\/deckbox\.org\/mtg\/.*" target="_blank">)(.*)(?:<\/a>)/)[1];
            if (document.getElementById(card).innerHTML.indexOf("wishlist") !== -1) {
                //console.log("Deck list has a wishlist.  I think this is YOUR deck.");
                var cardCount = document.getElementById(card).innerHTML.match(/(?:<td class="card_count deck_count.*>(\n)?)([0-9]{1,2})(?:(\n)?<\/td>\n<script>)/);
                cardCount = Number(cardCount[2]);
                //console.log("Raw cardCount is " + cardCount);
                //console.log("I think there are " + cardCount + " copies of " + cardName);
            } else {
                //console.log("Deck list DOESN'T have a wishlist.  I think this is someone elses deck.");
                var cardCount = document.getElementById(card).innerHTML.match(/(?:<td class="card_count">)([0-9]*)(?:<\/td>\n<td class="card_name">)/)[1];
                //console.log("I think there are " + cardCount + " copies of " + cardName);
            }
            if (card.indexOf("main") !== -1) {
                console.log(cardCount + " copies of " + cardName + " (" + manaCosts[card] + ")");
                for(var i = 0; i < cardCount; i++) {
                    console.log("Adding mana cost " + manaCosts[card] + " for card " + cardName + " to the total.");
                    totalCosts.push(manaCosts[card]);
                }
            } else {
                console.log("Card " + cardName + " is not in main deck.  Will NOT use to calculate CMC average.");
            }
        }
    }

    // Get average
    var total = 0;
    console.log("Found " + totalCosts.length + " cards in the main deck.");
    for(var i = 0; i < totalCosts.length; i++) {
        //console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        //console.log("Total is now: " + total);
    }
    console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    console.log("Agerage CMC: " + avg);

    // Create pop up with CMC average
    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Last edited by tjdrake719 (2017-03-01 21:58:20)

Re: Feature Request - Average CMC

We can accomplish this with a greasemonkey script.  My JS skills are garbage but I will work on this a little bit.  Any coders out there that want to help, please do.

Re: Feature Request - Average CMC

Like I said: garbage.  But it's starting to work.

Limitations:  Dies for "X" cards, doesn't ignore sideboard or scratchpad, maybe dies for 0 colorless cards??

    // ==UserScript==
    // @name        deckboxCMC
    // @author       cybrosis
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.1
    // ==/UserScript==

    var manaCosts = {};
    var totalCosts = [];

    var x = document.getElementsByClassName("mtg_mana");
    var i;
    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[1-9URGBW]/i;
        // console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        // console.log("Parent element is " + row);
        var stripped = x[i].outerHTML.match(re).toString();
        // console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.slice(-1);
        // console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBW]/, "1");
        console.log("Found mana cost " + converted + " for row " + row);
        if (!manaCosts[row]) {
            console.log("Adding new item to manaCosts hash for row " + row);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
        }
        console.log("Mana cost for " + row + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    console.log("Here's a hash of mana costs: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            console.log("card is: " + card + ", value is: " + manaCosts[card]);
            totalCosts.push(manaCosts[card]);
        }
    }

    var total = 0;
    console.log("Found " + totalCosts.length + "costs to work with");
    for(var i = 0; i < totalCosts.length; i++) {
        console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        console.log("Total is now: " + total);
    }
    console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    console.log("Here's the agerage CMC (maybe?): " + avg);

    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Last edited by cybrosis (2017-02-10 22:54:35)

Re: Feature Request - Average CMC

Fixed the dies on X mana cost:

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.2
    // ==/UserScript==

    var manaCosts = {};
    var totalCosts = [];

    var x = document.getElementsByClassName("mtg_mana");
    var i;
    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[1-9URGBWX]/i;
        // console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        // console.log("Parent element is " + row);
        var stripped = x[i].outerHTML.match(re).toString();
        // console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.slice(-1);
        console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBW]/, "1");
        var converted = converted.replace(/X/, "0");

        console.log("Found mana cost " + converted + " for row " + row);
        if (!manaCosts[row]) {
            console.log("Adding new item to manaCosts hash for row " + row);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
        }
        console.log("Mana cost for " + row + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    console.log("Here's a hash of mana costs: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            console.log("card is: " + card + ", value is: " + manaCosts[card]);
            totalCosts.push(manaCosts[card]);
        }
    }

    var total = 0;
    console.log("Found " + totalCosts.length + "costs to work with");
    for(var i = 0; i < totalCosts.length; i++) {
        console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        console.log("Total is now: " + total);
    }
    console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    console.log("Here's the agerage CMC (maybe?): " + avg);

    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Last edited by cybrosis (2017-02-10 22:54:19)

Re: Feature Request - Average CMC

Fixed so it only calculates the main portion of the deck and ignores sideboard and scratchpad.  Yes, this could be a lot cleaner, and if people want to use it I will clean it up further and make the value display a little sexier (right now it's just a red text box in the top left; not pretty but it gets the job done.)

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.3
    // ==/UserScript==

    var manaCosts = {};
    var totalCosts = [];

    var x = document.getElementsByClassName("mtg_mana");
    var i;
    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[0-9URGBWX]/i;
        // console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        // console.log("Parent element is " + row);
        var stripped = x[i].outerHTML.match(re).toString();
        // console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.slice(-1);
        //console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBW]/, "1");
        var converted = converted.replace(/X/, "0");

        if (!manaCosts[row]) {
            // console.log("Adding new item to manaCosts hash for row " + row);
            // console.log("Found mana cost " + converted + " for row " + row);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
            // console.log("Found mana cost " + converted + " for row " + row);
        }
        console.log("Total mana cost for " + row + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    console.log("Here's a hash of mana costs: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            if (card.indexOf("main") !== -1) {
                console.log("Card " + card + " with cost " + manaCosts[card] + " is in main deck.  Will use to calculate CMC average.");
                totalCosts.push(manaCosts[card]);
            } else {
                console.log("Card " + card + " with cost " + manaCosts[card] + " is not in main deck.  Will NOT use to calculate CMC average.");
            }
        }
    }

    var total = 0;
    console.log("Found " + totalCosts.length + " costs to work with");
    for(var i = 0; i < totalCosts.length; i++) {
        // console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        // console.log("Total is now: " + total);
    }
    console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    console.log("Agerage CMC: " + avg);

    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Last edited by cybrosis (2017-02-10 22:54:03)

Re: Feature Request - Average CMC

This will work with tampermonkey too I assume? I am reading into it now, its a new extension for me, but the functionality is worth a bit of a learning curve.

Re: Feature Request - Average CMC

Yeah this is working for me with tampermonkey.

https://www.dropbox.com/s/7h5w9c3t6e799 … 5.png?dl=0

Last edited by cybrosis (2017-02-11 19:14:35)

Post's attachments

deckboxCMC.user.js 2.92 kb, file has never been downloaded. 

You don't have the permssions to download the attachments of this post.

Re: Feature Request - Average CMC

cybrosis wrote:

Yeah this is working for me with tampermonkey.

https://www.dropbox.com/s/7h5w9c3t6e799 … 5.png?dl=0

Awesome, thanks.

Re: Feature Request - Average CMC

If you notice it bugging out or not working or seeming to give inaccurate results please let me know and I'll try to fix.  The whole thing is a quick and dirty hack job, but it seems to work for now.

Re: Feature Request - Average CMC

cybrosis wrote:

If you notice it bugging out or not working or seeming to give inaccurate results please let me know and I'll try to fix.  The whole thing is a quick and dirty hack job, but it seems to work for now.

Its better than not having it, so it works for me. Thanks again.

Re: Feature Request - Average CMC

Just a quick update for anyone else looking to use it.

Works perfectly for me so far, no issues.

Thanks again!

Re: Feature Request - Average CMC

cybrosis wrote:

If you notice it bugging out or not working or seeming to give inaccurate results please let me know and I'll try to fix.  The whole thing is a quick and dirty hack job, but it seems to work for now.

Found a bug:

Doesn't work for this deck: https://deckbox.org/sets/1603743

Not sure why, box just doesn't show up.

Re: Feature Request - Average CMC

Ah OK it's almost certainly because of the colorless mana.

I will put a fix up in a few minutes.

Re: Feature Request - Average CMC

Fixed:
1.  Was incorrectly parsing two digit numbers.  The script now does a replace on the mana element name rather than simply stripping to the last character.
2.  Was not set up to handle colorless mana.

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.4
    // ==/UserScript==

    var manaCosts = {};
    var totalCosts = [];

    var x = document.getElementsByClassName("mtg_mana");
    var i;
    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[0-9URGBWCX]{1,2}/i;
        // console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        // console.log("Parent element is " + row);
        var stripped = x[i].outerHTML.match(re).toString();
        // console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.replace("mtg_mana_", "");
        // console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBWC]/, "1");
        var converted = converted.replace(/X/, "0");

        if (!manaCosts[row]) {
            // console.log("Adding new item to manaCosts hash for row " + row);
            // console.log("Found mana cost " + converted + " for row " + row);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
            // console.log("Found mana cost " + converted + " for row " + row);
        }
        // console.log("Total mana cost for " + row + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    // console.log("Here's a hash of mana costs: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            if (card.indexOf("main") !== -1) {
                // console.log("Card " + card + " with cost " + manaCosts[card] + " is in main deck.  Will use to calculate CMC average.");
                totalCosts.push(manaCosts[card]);
            } else {
                // console.log("Card " + card + " with cost " + manaCosts[card] + " is not in main deck.  Will NOT use to calculate CMC average.");
            }
        }
    }

    var total = 0;
    // console.log("Found " + totalCosts.length + " costs to work with");
    for(var i = 0; i < totalCosts.length; i++) {
        // console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        // console.log("Total is now: " + total);
    }
    // console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    // console.log("Agerage CMC: " + avg);

    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Re: Feature Request - Average CMC

cybrosis wrote:

Fixed:
1.  Was incorrectly parsing two digit numbers.  The script now does a replace on the mana element name rather than simply stripping to the last character.
2.  Was not set up to handle colorless mana.

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.4
    // ==/UserScript==

    var manaCosts = {};
    var totalCosts = [];

    var x = document.getElementsByClassName("mtg_mana");
    var i;
    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[0-9URGBWCX]{1,2}/i;
        // console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        // console.log("Parent element is " + row);
        var stripped = x[i].outerHTML.match(re).toString();
        // console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.replace("mtg_mana_", "");
        // console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBWC]/, "1");
        var converted = converted.replace(/X/, "0");

        if (!manaCosts[row]) {
            // console.log("Adding new item to manaCosts hash for row " + row);
            // console.log("Found mana cost " + converted + " for row " + row);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
            // console.log("Found mana cost " + converted + " for row " + row);
        }
        // console.log("Total mana cost for " + row + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    // console.log("Here's a hash of mana costs: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            if (card.indexOf("main") !== -1) {
                // console.log("Card " + card + " with cost " + manaCosts[card] + " is in main deck.  Will use to calculate CMC average.");
                totalCosts.push(manaCosts[card]);
            } else {
                // console.log("Card " + card + " with cost " + manaCosts[card] + " is not in main deck.  Will NOT use to calculate CMC average.");
            }
        }
    }

    var total = 0;
    // console.log("Found " + totalCosts.length + " costs to work with");
    for(var i = 0; i < totalCosts.length; i++) {
        // console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        // console.log("Total is now: " + total);
    }
    // console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    // console.log("Agerage CMC: " + avg);

    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Working well, thanks!

Re: Feature Request - Average CMC

Perhaps you could update your initial post with a copy of the script so people who find this thread can easily get access to it?

I also wonder if I should create a separate post somewhere on this forum to bring it to peoples attention.

Re: Feature Request - Average CMC

cybrosis wrote:

Perhaps you could update your initial post with a copy of the script so people who find this thread can easily get access to it?

I also wonder if I should create a separate post somewhere on this forum to bring it to peoples attention.

A new post would probably be best. It should definitely be advertised, its such a useful feature. I can update my initial post as well, what do you use to inlay the code in the scroll-able box like that?

Re: Feature Request - Average CMC

Another bug: https://deckbox.org/sets/1043553

Returns "NaN".


Possibly from the commander having so many mana symbols? Not sure.


Edit: Same thing for these decks:

https://deckbox.org/sets/847100

https://deckbox.org/sets/1046290

https://deckbox.org/sets/750620

https://deckbox.org/sets/884169

https://deckbox.org/sets/1227193

https://deckbox.org/sets/514210

https://deckbox.org/sets/348273



Unsure what the common denominator is, they all worked before.

Last edited by tjdrake719 (2017-03-01 00:09:31)

Re: Feature Request - Average CMC

tjdrake719 wrote:

Unsure what the common denominator is, they all worked before.

They all have at least one card whose cost contains hybrid mana. Maybe that's the issue?

Re: Feature Request - Average CMC

meldon44 wrote:
tjdrake719 wrote:

Unsure what the common denominator is, they all worked before.

They all have at least one card whose cost contains hybrid mana. Maybe that's the issue?

Yes, this is the issue.  Before, we were simply trimming to the last character to identify a mana cost, but in one of the updates I found that this was broken for double digit mana costs.  To fix that, we started trimming the mana element to a more precise name, which meant that these dual mana costs (RW, UW, RB, etc) weren't being handled correctly.

It is fixed in the update below.

In fixing that bug I think I have found another problem with the script: I believe it only truly works for EDH decks because it doesn't take into account the NUMBER of each card in the deck.  What I mean is that you have more than one of a single card in a deck, the script does not take that into account in calculating the CMC average.  What is displayed is the CMC average for all UNIQUE cards in the deck, meaning that the second, third, and fourth instances of a card will not affect the calculated CMC.  I'm new to magic and unsure if this is desired, or if when calculating CMC average, multiples should be taken into consideration.  Let me know and I will update the script if that shouldn't be the case.

Here is the fixed script to account for the hybrid mana symbols:

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.5.0
    // ==/UserScript==

    // Initialize array and hash to hold mana costs
    var manaCosts = {};
    var totalCosts = [];

    // Iterate through rows to get mana cost totals for each card
    var x = document.getElementsByClassName("mtg_mana");
    var i;
    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[0-9URGBWCX]{1,2}/i;
        console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        console.log("Parent element is " + row);
        var stripped = x[i].outerHTML.match(re).toString();
        console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.replace("mtg_mana_", "");
        console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBWC]([URGBWC])?/, "1");
        var converted = converted.replace(/X/, "0");

        if (!manaCosts[row]) {
            console.log("Adding new item to manaCosts hash for row " + row);
            console.log("Found mana cost " + converted + " for row " + row);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
            console.log("Found mana cost " + converted + " for row " + row);
        }
        console.log("Total mana cost for " + row + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    console.log("Here's a hash of mana costs: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            if (card.indexOf("main") !== -1) {
                console.log("Card " + card + " with cost " + manaCosts[card] + " is in main deck.  Will use to calculate CMC average.");
                totalCosts.push(manaCosts[card]);
            } else {
                console.log("Card " + card + " with cost " + manaCosts[card] + " is not in main deck.  Will NOT use to calculate CMC average.");
            }
        }
    }

    // Get average
    var total = 0;
    console.log("Found " + totalCosts.length + " costs to work with");
    for(var i = 0; i < totalCosts.length; i++) {
        console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        console.log("Total is now: " + total);
    }
    console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    console.log("Agerage CMC: " + avg);

    // Create pop up with CMC average
    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Last edited by cybrosis (2017-03-01 12:02:22)

Re: Feature Request - Average CMC

Ok, 0.5.1 fixes some of the debug logging for the JS console and prepares me to fix the issue mentioned above.  Functionally the same as 0.5.0 but better output in console.

Here's example console output for one of the decks you posted above (https://deckbox.org/sets/847100)

VM8706:50 Here's what we found:
VM8706:56 Accorder Paladin with cost 2 is in main deck.  Will use to calculate CMC average.
VM8706:56 Adriana, Captain of the Guard with cost 5 is in main deck.  Will use to calculate CMC average.
VM8706:56 Alesha, Who Smiles at Death with cost 3 is in main deck.  Will use to calculate CMC average.
VM8706:56 Angel of Glory's Rise with cost 7 is in main deck.  Will use to calculate CMC average.
VM8706:56 Athreos, God of Passage with cost 3 is in main deck.  Will use to calculate CMC average.
VM8706:56 Aurelia, the Warleader with cost 6 is in main deck.  Will use to calculate CMC average.
VM8706:56 Benalish Cavalry with cost 2 is in main deck.  Will use to calculate CMC average.
VM8706:56 Black Knight with cost 2 is in main deck.  Will use to calculate CMC average.
VM8706:56 Blood Knight with cost 2 is in main deck.  Will use to calculate CMC average.
VM8706:56 Burnished Hart with cost 3 is in main deck.  Will use to calculate CMC average.
VM8706:56 Bygone Bishop with cost 3 is in main deck.  Will use to calculate CMC average.
VM8706:56 Crusading Knight with cost 4 is in main deck.  Will use to calculate CMC average.
VM8706:56 Dauthi Mercenary with cost 3 is in main deck.  Will use to calculate CMC average.
VM8706:56 Fallen Askari with cost 2 is in main deck.  Will use to calculate CMC average.
VM8706:56 Hero of Bladehold with cost 4 is in main deck.  Will use to calculate CMC average.
VM8706:56 Hero of Oxid Ridge with cost 4 is in main deck.  Will use to calculate CMC average.
VM8706:56 Kinsbaile Cavalier with cost 4 is in main deck.  Will use to calculate CMC average.
VM8706:56 Knight Exemplar with cost 3 is in main deck.  Will use to calculate CMC average.
...
VM8706:56 Orzhov Signet with cost 2 is in main deck.  Will use to calculate CMC average.
VM8706:56 Rakdos Signet with cost 2 is in main deck.  Will use to calculate CMC average.
VM8706:56 Sol Ring with cost 1 is in main deck.  Will use to calculate CMC average.
VM8706:56 Sword of the Animist with cost 2 is in main deck.  Will use to calculate CMC average.
VM8706:56 Aggravated Assault with cost 3 is in main deck.  Will use to calculate CMC average.
VM8706:56 Berserkers' Onslaught with cost 5 is in main deck.  Will use to calculate CMC average.
VM8706:56 Cathars' Crusade with cost 5 is in main deck.  Will use to calculate CMC average.
VM8706:56 Land Tax with cost 1 is in main deck.  Will use to calculate CMC average.
VM8706:56 True Conviction with cost 6 is in main deck.  Will use to calculate CMC average.
VM8706:56 Gideon, Ally of Zendikar with cost 4 is in main deck.  Will use to calculate CMC average.
VM8706:66 Found 64 costs to work with
VM8706:72 Dividing total mana cost of 211 by 64 total cards to find the average.
VM8706:74 Agerage CMC: 3.296875

This should make it a lot easier to tell if mana is being calculated correctly, and will aide me in accounting for multiple instances of cards within a deck.

Updated script here:

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.5.1
    // ==/UserScript==

    // Initialize array and hash to hold mana costs
    var manaCosts = {};
    var totalCosts = [];

    // Iterate through rows to get mana cost totals for each card
    var x = document.getElementsByClassName("mtg_mana");
    var i;

    //var tableMain = document.getElementsByClassName("main set_cards simple_table with_details");
    //console.log("Main table is " + tableMain);

    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[0-9URGBWCX]{1,2}/i;
        //console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        //console.log("Parent element is " + row);
        var cardName = document.getElementById(row).innerHTML.match(/(?:a class="simple" href="https:\/\/deckbox\.org\/mtg\/.*" target="_blank">)(.*)(?:<\/a>)/)[1];
        //console.log("Card name is " + cardName);
        var stripped = x[i].outerHTML.match(re).toString();
        //console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.replace("mtg_mana_", "");
        //console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBWC]([URGBWC])?/, "1");
        var converted = converted.replace(/X/, "0");

        if (!manaCosts[row]) {
            //console.log("Adding new item to manaCosts hash for card " + cardName);
            //console.log("Found mana cost " + converted + " for card " + cardName);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
            //console.log("Found mana cost " + converted + " for card " + cardName);
        }
        //console.log("Total mana cost for " + cardName + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    console.log("Here's what we found: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            if (card.indexOf("main") !== -1) {
                var cardName = document.getElementById(card).innerHTML.match(/(?:a class="simple" href="https:\/\/deckbox\.org\/mtg\/.*" target="_blank">)(.*)(?:<\/a>)/)[1];
                console.log(cardName + " with cost " + manaCosts[card] + " is in main deck.  Will use to calculate CMC average.");
                totalCosts.push(manaCosts[card]);
            } else {
                console.log("Card " + cardName + " with cost " + manaCosts[card] + " is not in main deck.  Will NOT use to calculate CMC average.");
            }
        }
    }

    // Get average
    var total = 0;
    console.log("Found " + totalCosts.length + " cards in the main deck.");
    for(var i = 0; i < totalCosts.length; i++) {
        //console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        //console.log("Total is now: " + total);
    }
    console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    console.log("Agerage CMC: " + avg);

    // Create pop up with CMC average
    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Last edited by cybrosis (2017-03-01 11:55:16)

Re: Feature Request - Average CMC

I believe this update fixes the problem i mentioned before about non-singleton decks.  Need some outside testing since I'm pretty tired:

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.6.0
    // ==/UserScript==

    // Initialize array and hash to hold mana costs
    var manaCosts = {};
    var totalCosts = [];

    // Iterate through rows to get mana cost totals for each card
    var x = document.getElementsByClassName("mtg_mana");
    var i;

    //var tableMain = document.getElementsByClassName("main set_cards simple_table with_details");
    //console.log("Main table is " + tableMain);

    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[0-9URGBWCX]{1,2}/i;
        //console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        //console.log("Parent element is " + row);
        //console.log("Card element looks like this: " + document.getElementById(row).innerHTML);
        var cardName = document.getElementById(row).innerHTML.match(/(?:a class="simple" href="https:\/\/deckbox\.org\/mtg\/.*" target="_blank">)(.*)(?:<\/a>)/)[1];
        //console.log("Card name is " + cardName);
        var stripped = x[i].outerHTML.match(re).toString();
        //console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.replace("mtg_mana_", "");
        //console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBWC]([URGBWC])?/, "1");
        var converted = converted.replace(/X/, "0");

        if (!manaCosts[row]) {
            //console.log("Adding new item to manaCosts hash for card " + cardName);
            //console.log("Found mana cost " + converted + " for card " + cardName);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
            //console.log("Found mana cost " + converted + " for card " + cardName);
        }
        //console.log("Total mana cost for " + cardName + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    console.log("Here's what we found in the main deck: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            var cardName = document.getElementById(card).innerHTML.match(/(?:a class="simple" href="https:\/\/deckbox\.org\/mtg\/.*" target="_blank">)(.*)(?:<\/a>)/)[1];
            var cardCount = Number(document.getElementById(card).innerHTML.match(/(?:<td class=".*card_count.*>\n)*(?:<td class="card_count.*(\n)?)([1-99])(?:(\n)?<\/td>)/)[2]);
            if (card.indexOf("main") !== -1) {
                console.log(cardCount + " copies of " + cardName + " (" + manaCosts[card] + ")");
                for(var i = 0; i < cardCount; i++) {
                    console.log("Adding mana cost " + manaCosts[card] + " for card " + cardName + " to the total.");
                    totalCosts.push(manaCosts[card]);
                }
            } else {
                console.log("Card " + cardName + " is not in main deck.  Will NOT use to calculate CMC average.");
            }
        }
    }

    // Get average
    var total = 0;
    console.log("Found " + totalCosts.length + " cards in the main deck.");
    for(var i = 0; i < totalCosts.length; i++) {
        //console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        //console.log("Total is now: " + total);
    }
    console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    console.log("Agerage CMC: " + avg);

    // Create pop up with CMC average
    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Last edited by cybrosis (2017-03-01 12:47:19)

Re: Feature Request - Average CMC

cybrosis wrote:

I believe this update fixes the problem i mentioned before about non-singleton decks.  Need some outside testing since I'm pretty tired:


It seems to work for a few of a certain card, but not more. Here is an example: https://deckbox.org/sets/977088

The only time you will see this is for shadowborn or relentless decks where you can play any number of that card.

It worked on all the singleton decks I tested.

Re: Feature Request - Average CMC

tjdrake719 wrote:
cybrosis wrote:

I believe this update fixes the problem i mentioned before about non-singleton decks.  Need some outside testing since I'm pretty tired:


It seems to work for a few of a certain card, but not more. Here is an example: https://deckbox.org/sets/977088

The only time you will see this is for shadowborn or relentless decks where you can play any number of that card.

It worked on all the singleton decks I tested.


Fixed:

  • CMC total only included a maximum of 9 instanes of a single card in a deck

  • CMC not calculated correctly for decks owned by logged in user due to wishlist column + editable

    // ==UserScript==
    // @name        deckboxCMC
    // @match       https://deckbox.org/sets/*
    // @description Calculates CMC in deckbox sets
    // @version     0.6.1
    // ==/UserScript==

    // Initialize array and hash to hold mana costs
    var manaCosts = {};
    var totalCosts = [];

    // Iterate through rows to get mana cost totals for each card
    var x = document.getElementsByClassName("mtg_mana");

    for (i = 0; i < x.length; i++) {
        var re = /mtg_mana_[0-9URGBWCX]{1,2}/i;
        //console.log("This is a mana cost item: " + x[i].outerHTML);
        var row = x[i].parentElement.parentElement.id;
        //console.log("Parent element is " + row);
        //console.log("Card element looks like this: " + document.getElementById(row).innerHTML);
        var cardName = document.getElementById(row).innerHTML.match(/(?:a class="simple" href="https:\/\/deckbox\.org\/mtg\/.*" target="_blank">)(.*)(?:<\/a>)/)[1];
        //console.log("Card name is " + cardName);
        var stripped = x[i].outerHTML.match(re).toString();
        //console.log("This its stripped mana name: " + stripped);
        var strippedMore = stripped.replace("mtg_mana_", "");
        //console.log("Mana cost indicated: " + strippedMore);
        var converted = strippedMore.replace(/[URGBWC]([URGBWC])?/, "1");
        var converted = converted.replace(/X/, "0");

        if (!manaCosts[row]) {
            //console.log("Adding new item to manaCosts hash for card " + cardName);
            //console.log("Found mana cost " + converted + " for card " + cardName);
            manaCosts[row] = Number(converted);
        } else {
            manaCosts[row] += Number(converted);
            //console.log("Found mana cost " + converted + " for card " + cardName);
        }
        //console.log("Total mana cost for " + cardName + " is " + manaCosts[row]);
    }

    // Summary of card costs by table row
    console.log("Here's what we found in the main deck: ");
    for (var card in manaCosts) {
        // use hasOwnProperty to filter out keys from the Object.prototype
        if (manaCosts.hasOwnProperty(card)) {
            var cardName = document.getElementById(card).innerHTML.match(/(?:a class="simple" href="https:\/\/deckbox\.org\/mtg\/.*" target="_blank">)(.*)(?:<\/a>)/)[1];
            if (document.getElementById(card).innerHTML.indexOf("wishlist") !== -1) {
                //console.log("Deck list has a wishlist.  I think this is YOUR deck.");
                var cardCount = document.getElementById(card).innerHTML.match(/(?:<td class="card_count deck_count.*>(\n)?)([0-9]{1,2})(?:(\n)?<\/td>\n<script>)/);
                cardCount = Number(cardCount[2]);
                //console.log("Raw cardCount is " + cardCount);
                //console.log("I think there are " + cardCount + " copies of " + cardName);
            } else {
                //console.log("Deck list DOESN'T have a wishlist.  I think this is someone elses deck.");
                var cardCount = document.getElementById(card).innerHTML.match(/(?:<td class="card_count">)([0-9]*)(?:<\/td>\n<td class="card_name">)/)[1];
                //console.log("I think there are " + cardCount + " copies of " + cardName);
            }
            if (card.indexOf("main") !== -1) {
                console.log(cardCount + " copies of " + cardName + " (" + manaCosts[card] + ")");
                for(var i = 0; i < cardCount; i++) {
                    console.log("Adding mana cost " + manaCosts[card] + " for card " + cardName + " to the total.");
                    totalCosts.push(manaCosts[card]);
                }
            } else {
                console.log("Card " + cardName + " is not in main deck.  Will NOT use to calculate CMC average.");
            }
        }
    }

    // Get average
    var total = 0;
    console.log("Found " + totalCosts.length + " cards in the main deck.");
    for(var i = 0; i < totalCosts.length; i++) {
        //console.log("Adding mana cost number " + i + " to the total.");
        total += totalCosts[i];
        //console.log("Total is now: " + total);
    }
    console.log("Dividing total mana cost of " + total + " by " + totalCosts.length + " total cards to find the average.");
    var avg = total / totalCosts.length;
    console.log("Agerage CMC: " + avg);

    // Create pop up with CMC average
    var box = document.createElement( 'div' );
    box.id = 'myAlertBox';
    box.style.background = 'white';
    box.style.border = '2px solid red';
    box.style.padding = '4px';
    box.style.position = 'absolute';
    box.style.top = '8px';
    box.style.left = '8px';
    box.style.maxWidth = '400px';
    box.textContent = "Converted mana cost: " + avg;
    document.body.appendChild( box );

Re: Feature Request - Average CMC

Works for the 20 or so decks I tested. Awesome work. Sebi should just add this to deckbox's code.