top of page
  • Writer's picture1/4" Jack Of All Trades

Creative Coding Week 4: Magic web crawler and a bit more JUCE

Updated: Apr 26, 2021

Continued developing my web crawler from class for the rest of the day: a web-crawler that could traverse the entire collection of Magic: The Gathering cards. So far I achieved one that traverses to the end of a search for creatures cards (if no other search constraints then 11000+) and generates a random one comprising of characteristics obtained from the lists it made as it searched, as well as delivering a summary of the rules text it read along the way for science.

from requests import get
from bs4 import BeautifulSoup
from gensim.summarization import summarize
from random import randrange

 #not all in use
 names = []
 cmcs = []
 manacost = [] #these are images
 types = []
 subtypes = []
 stats = []
 rules =[]
 remove = "\n"

#manually counted number of pages, to do figure out how to do with code

for y in range(117):
    url = "{}&action=advanced&type=+[%22Creature%22]".format(y)
    response = get(url)

    html_soup = BeautifulSoup(response.text, 'html.parser')
    cardNames = html_soup.find_all('tr', class_ = 'cardItem evenItem')
    cardRules = html_soup.find_all('div', class_ = 'rulesText')
    cardTypeLines = html_soup.find_all('span', class_ = 'typeLine')
    cardCMCs = html_soup.find_all('span', class_ = 'convertedManaCost')

    for i in range(len(cardNames)):
        name = cardNames[i].span.text
        name = name.replace(remove, "", 1)
        cmc = cardCMCs[i].text
        typeLine = cardTypeLines[i].text
        #print (typeLine)
        rule = cardRules[i].p.text
        #print (rules)

y = '. '.join(rules)
rand1 = randrange(len(cardNames))
randName = names[rand1]
rand2 = randrange(len(cardNames))
randType = types[rand2]
rand3 = randrange(len(cardNames))
randCMC = cmcs[rand3]
rulesSummary = summarize(y,word_count=200)

summary = summarize(y,word_count=30)
for x in range(len(names)):
    nameRep = names[x]
    summary = summary.replace("{}".format(nameRep),"{}".format(randName))
    if None:


Example output featuring string concatenation to add the selected name to the rules text of the generated card where applicable:

Bioessence Hydra

Creature  — Human Soldier
Sacrifice Bioessence Hydra: Target creature you control gains protection from the color of your choice until end of turn.
Hexproof (This creature can't be the target of spells or abilities your opponents control.).

Artwork for Bioessence Hydra by Mathias Kollros

Post neural network lecture (and AI art in critical studies) I am interested in trying to train a GAN on Magic: The Gathering artwork given the vast amount of it. Could be made interesting by excluding certain colour(s) from amongst the card types to see how that might effect the results.


Developed the object orientated side of the basic synth I have been building alongside a tutorial. Understand the abstraction process and connection between interface and parameters a lot better than previously, learning about the concept of the audio value tree state and using custom components. Current objective now is to find resources on building a granular synthesis engine for this module's project.

Example ADSR Component:

#pragma once

#include <JuceHeader.h>

class ADSR_Component  : public juce::Component

    ADSR_Component(juce::AudioProcessorValueTreeState& apvts, juce::String attackId, juce::String decayId, juce::String sustainId, juce::String releaseId);
    ~ADSR_Component() override;

    void paint (juce::Graphics&) override;
    void resized() override;

    void setSliderParams(juce::Slider& slider);
    using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;

    juce::Slider attackSlider;
    juce::Slider decaySlider;
    juce::Slider sustainSlider;
    juce::Slider releaseSlider;

    juce::Label attackLabel{ "A", "A" };
    juce::Label decayLabel{ "D", "D" };
    juce::Label sustainLabel{ "S", "S" };
    juce::Label releaseLabel{ "R", "R" };

    std::unique_ptr<SliderAttachment> attackAttachment;
    std::unique_ptr<SliderAttachment> decayAttachment;
    std::unique_ptr<SliderAttachment> sustainAttachment;
    std::unique_ptr<SliderAttachment> releaseAttachment;

    static constexpr float fontHeight{ 15.0f };
    static constexpr int textBoxWidth{ 35 };
    static constexpr int textBoxHeight{ 20 };



#include <JuceHeader.h>
#include "OscComponent.h"

OscComponent::OscComponent(juce::AudioProcessorValueTreeState& apvts, juce::String oscId, juce::String gainId, juce::String pitchId, juce::String fmFreqId, juce::String fmDepthId)
    juce::StringArray oscChoices{ "Sine", "Saw", "Square" };
    oscSelector.addItemList(oscChoices, 1);

    oscSelAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(apvts, oscId, oscSelector);

    setSliderParams(gainSlider, gainLabel, gainAttachment, gainId, apvts);
    setSliderParams(pitchSlider, pitchLabel, pitchAttachment, pitchId, apvts);
    setSliderParams(fmFreqSlider, fmFreqLabel, fmFreqAttachment, fmFreqId, apvts);
    setSliderParams(fmDepthSlider, fmDepthLabel, fmDepthAttachment, fmDepthId, apvts);


void OscComponent::paint(juce::Graphics& g)
    auto bounds = getLocalBounds();
    g.drawRoundedRectangle(bounds.toFloat().reduced(10.0f), 5.0f, 2.0f);

    g.drawText(name, 20, 15, 100, 25, juce::Justification::left);

void OscComponent::resized()
    const auto dialSize = 70;
    const auto labelWidth = 70;
    const auto labelHeight = 18;

    oscSelector.setBounds(18, 40, 100, 25);

    gainLabel.setBounds(120, 15, labelWidth, labelHeight);
    gainSlider.setBounds(120, 30, dialSize, dialSize);

    pitchLabel.setBounds(190, 15, labelWidth, labelHeight);
    pitchSlider.setBounds(190, 30, dialSize, dialSize);

    fmFreqLabel.setBounds(260, 15, labelWidth, labelHeight);
    fmFreqSlider.setBounds(260, 30, dialSize, dialSize);

    fmDepthLabel.setBounds(330, 15, labelWidth, labelHeight);
    fmDepthSlider.setBounds(330, 30, dialSize, dialSize);

using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;

void OscComponent::setSliderParams(juce::Slider& slider, juce::Label& label, std::unique_ptr<sliderAttachment>& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts)
    slider.setTextBoxStyle(juce::Slider::TextBoxBelow, true, textBoxWidth, textBoxHeight);


    attachment = std::make_unique<SliderAttachment>(apvts, paramId, slider);

28 views0 comments
bottom of page