Published
Edited
Aug 31, 2021
Insert cell
# FaB Deck Builder Tool
Insert cell
cards = fetch("https://api.fabdb.net/cards/ARC000")
.then(response => {
return response.json();
})
Insert cell
function getDecklist(decklist_slug)
{
let decklist = fetch("https://api.fabdb.net/decks/" + decklist_slug)
.then(response => {
if (response.redirected === false)
{
return response.json();
}
})
return decklist
}
Insert cell
function create_colors(){return {"blue":0, "yellow":0, "red":0}}
Insert cell
function getCards(deckListOBJ)
{
for (const [key, value] of Object.entries(deckListOBJ)) {
if (key === "cards")
{
return value;
}
}
}
Insert cell
function colorCount(cards, side)
{
let colors = create_colors()
for (let i = 0; i < cards.length; i++)
{
let pitch = parseInt(cards[i].stats["resource"])
let count;
if (side)
{
count = cards[i].total
}
else
{
count = cards[i].total - cards[i].totalSideboard
}
if (pitch === 1)
{
colors.red = colors.red + count
}
else if (pitch === 2)
{
colors.yellow = colors.yellow + count
}
else if (pitch === 3)
{
colors.blue = colors.blue + count
}
}
return colors
}
Insert cell
function choosei(n,k){
var result = 1;
for(var i=1; i <= k; i++){
result *= (n+1-i)/i;
}
return result;
}
Insert cell
function to_OBJ(count, prob)
{
var obj = {};
obj[count] = prob
return obj
}
Insert cell
function frequencyColor(counts)
{
let N = counts.blue + counts.yellow + counts.red
let freq = {"red":[], "yellow":[], "blue":[]}
let colors = ["red", "yellow", "blue"]
for (let i = 0; i < colors.length; i++)
{
for (let k = 0; k <= 4; k++)
{
let K = counts[colors[i]]
let prob = (choosei(K, k) * choosei(N - K, 4 - k))/choosei(N, 4)
freq[colors[i]].push(to_OBJ(k, prob))
}
}
return freq
}
Insert cell
function getAverage(frequency)
{
let total = 0;
let resource = 0;
for (const color in frequency) {
resource++
for (const number in frequency[color]) {
total = total + (frequency[color][number][number] * resource * number)
}
}
return total
}
Insert cell
viewof slug = Inputs.text({label: "Deck:", placeholder: "What’s the slug for the deck?"})
Insert cell
viewof sideboard = Inputs.toggle({label: "Include Sideboard:"})
Insert cell
sideboard
Insert cell
//Earyxqdr
Insert cell
getDecklist(slug)
Insert cell
list = getDecklist(slug)
Insert cell
cardl = getCards(list)
Insert cell
colors = colorCount(cardl, sideboard)
Insert cell
frequency = frequencyColor(colors)
Insert cell
Insert cell
officialTracker = {
let probScale = d3.scaleLinear()
.domain([0, 1])
.range([0, 200]);

let drawScale = d3.scaleLinear()
.domain([1, 0])
.range([0, 200]);

let svg = d3.create('svg').style("background-color", 'white').attr("preserveAspectRatio", "none").style("background-color", 'white').attr("viewBox", "0 0 700 340");

if (list !== undefined)
{
let cardl = getCards(list)
let colors = colorCount(cardl, sideboard)
let frequency = frequencyColor(colors)
let average = getAverage(frequency)
svg.append("text")
.attr("class", "title")
.attr("text-anchor", "left")
.attr("y", 6)
.attr("x", 0)
.attr("dy", ".75em")
.text(list.name)
.call(addWebFont, 'Bebas Neue', 'https://fonts.gstatic.com/s/bebasneue/v2/JTUSjIg69CK48gW7PXoo9WlhyyTh89Y.woff2')
.attr('style', "font-size: 50px; font-family: 'Bebas Neue';");
svg.append("text")
.attr("class", "title")
.attr("text-anchor", "end")
.attr("y", 6)
.attr("x", 700)
.attr("dy", ".75em")
.text("Tool by Sam Rosenberg")
.attr('style', "font-size: 15px; font-family: 'Bebas Neue';");

svg.append("text")
.attr("class", "title")
.attr("text-anchor", "middle")
.attr("y", 55)
.attr("x", 310)
.attr("dy", ".75em")
.text("Probability of Colors per Hand")
.attr('style', "font-size: 20px; font-family: 'Bebas Neue';");
svg.append('rect')
.attr("x", 1)
.attr("y", 50)
.attr("width", 650).attr("fill", "transparent")
.attr("height", 280).attr('stroke-width', '1').attr("stroke", "black");

svg.append("text")
.attr("class", "title")
.attr("text-anchor", "end")
.attr("y", 55)
.attr("x", 648)
.attr("dy", ".75em")
.text("Average Resources per Hand: " + average.toFixed(2).toString() )
.attr('style', "font-size: 10px; font-family: 'Bebas Neue';");

svg.selectAll("redBars")
.data(frequency.red)
.join('rect')
.attr("x", function(d, i) {return 31 + 40*i})
.attr("y", function(d, i) {return 300 - probScale(d[i])})
.attr("width", 38)
.attr("height", function(d, i) {return probScale(d[i])})
.attr("fill", "#ad191c").attr('stroke-width', '1').attr("stroke", "black")
.on('mouseover', function(event, d, i) {
d3.selectAll("#highlightTitle").text("The probability of " + Object.keys(d) +" Red(s) is "+ Object.values(d)[0].toFixed(2));
})
.on('mouseout', function(event, d) {
d3.selectAll("#highlightTitle").text("");
});

svg.selectAll("redlabel")
.data(frequency.red)
.join('text')
.attr("x", function(d, i) {return 45 + 40*i})
.attr("y", function(d, i) {return 314})
.text(function(d,i){return i});

svg.selectAll("yellowBars")
.data(frequency.yellow)
.join('rect')
.attr("x", function(d, i) {return 241 + 40*i})
.attr("y", function(d, i) {return 300 - probScale(d[i])})
.attr("width", 38)
.attr("height", function(d, i) {return probScale(d[i])})
.attr("fill", "#fff300").attr('stroke-width', '1').attr("stroke", "black")
.on('mouseover', function(event, d, i) {
d3.selectAll("#highlightTitle").text("The probability of " + Object.keys(d) +" Yellow(s) is "+ Object.values(d)[0].toFixed(2));
})
.on('mouseout', function(event, d) {
d3.selectAll("#highlightTitle").text("");
});


svg.selectAll("yellowlabel")
.data(frequency.yellow)
.join('text')
.attr("x", function(d, i) {return 255 + 40*i})
.attr("y", function(d, i) {return 314})
.text(function(d,i){return i});


svg.append("svg:text")
.attr("id", "highlightTitle")
.attr("text-anchor", "middle")
.attr("y", 75)
.attr("x", 310)
.attr("dy", ".75em")
.attr('style', "font-size: 25px; font-family: 'Bebas Neue';");

svg.selectAll("blueBars")
.data(frequency.blue)
.join('rect')
.attr("x", function(d, i) {return 451 + 40*i})
.attr("y", function(d, i) {return 300 - probScale(d[i])})
.attr("width", 38)
.attr("height", function(d, i) {return probScale(d[i])})
.attr("fill", "#139bcc").attr('stroke-width', '1').attr("stroke", "black")
.on('mouseover', function(event, d, i) {
d3.selectAll("#highlightTitle").text("The probability of " + Object.keys(d) +" Blue(s) is "+ Object.values(d)[0].toFixed(2));
})
.on('mouseout', function(event, d) {
d3.selectAll("#highlightTitle").text("");
});

svg.selectAll("bluelabel")
.data(frequency.blue)
.join('text')
.attr("x", function(d, i) {return 464 + 40*i})
.attr("y", function(d, i) {return 314})
.text(function(d,i){return i});
svg.append('g')
.attr('transform', `translate(28, 100)`)
.call(d3.axisLeft(drawScale));

}

else
{
svg.append("text")
.attr("class", "title")
.attr("text-anchor", "middle")
.attr("y", 150)
.attr("x", 310)
.attr("dy", ".75em")
.text("Enter deck slug above to view hand probabilities")
.attr('style', "font-size: 30px; font-family: 'Bebas Neue';").call(addWebFont, 'Bebas Neue', 'https://fonts.gstatic.com/s/bebasneue/v2/JTUSjIg69CK48gW7PXoo9WlhyyTh89Y.woff2');
}
return svg.node();
}
Insert cell
async function addWebFont(selection, fontName, fontURL, fontType = 'woff2') {
const fontData = await toDataURL(fontURL);
return selection.append('style').text(`
@font-face {
font-family: '${fontName}';
src: url(${fontData}) format('${fontType}');
}
`);
};
Insert cell
async function toDataURL(url) {
return new Promise(async(resolve, reject) => {
const res = await fetch(url);
if(!res.ok) return reject(`Error: ${res.status} ${res.statusText}`);
const blob = await res.blob();
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => resolve(reader.result);
});
}
Insert cell
Object.entries(list)
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more