Compare commits

...

5 Commits

371 changed files with 605 additions and 232 deletions

View File

@ -18,7 +18,7 @@ I am a PhD researcher at the Faculty of Engineering Technology, KU Leuven. My ac
### ... Programmer
I'm an experienced software engineer and took on various roles from agile coaching to technical lead. I hold a Master of Computer Science. I also dabble in open source through my [<svg class='icon'><use xlink:href='#github'></use></svg>Github](https://github.com/wgroeneveld) account. Im a big Test Driven Development advocate. I teach software engineering techniques in both industry and academia.
I'm an experienced software engineer and took on various roles from agile coaching to technical lead. I hold a Master of Computer Science. I also dabble in open source, have a peek at https://git.brainbaking.com/. Im a big Test Driven Development advocate. I teach software engineering techniques in both industry and academia.
### ... Writer

View File

@ -10,7 +10,7 @@ Inspired by Leo Babauta's ["uncopyright and the minimalist mindset"](https://mnm
I've always detested the _'this is mine!'_-mindset, especially when it comes to intellectual property. Everyone benefits if everything is open and everyone can build upon each other's work. A possible financial loss is not an excuse. Leo has found copyrights not to be particularly helpful, so he simply got rid of them. He sells thousands of ebooks monthly. You have the right to share them with friends. He would rather have you buy them, but this way his work reaches a broader audience.
The source of this website is available at my Brain Baking [<svg class='icon'><use xlink:href='#github'></use></svg>Github repository](https://github.com/wgroeneveld/brainbaking). Feel free to do with it whatever you want: copy excerpts, republish images, use code partials, rip the theme, ... There's a reason the repository is **public**.
The source of this website is available at my Brain Baking [Git repository](https://git.brainbaking.com/wgroeneveld/brainbaking). Feel free to do with it whatever you want: copy excerpts, republish images, use code partials, rip the theme, ... There's a reason the repository is **public**.
> Love only grows by sharing. You can only have more for yourself by giving it away to others. <span>Brian Tracy</span>

View File

@ -3,6 +3,6 @@ date: 2022-05-11T17:44:06+02:00
context: "https://minutestomidnight.co.uk/blog/automation-for-my-blog-publishing-workflow/"
---
I wonder how you set your `date` frontmatter, which isn't mentioned clearly? If I make a new note, I fire up a stupid JS script that creates the file accordingly---something akin to your method---but also formats the date (and pries out the active Firefox tab I want to "respond" to using Webmentions, see https://github.com/wgroeneveld/brainbaking/blob/master/hooks/prenote.js )
I wonder how you set your `date` frontmatter, which isn't mentioned clearly? If I make a new note, I fire up a stupid JS script that creates the file accordingly---something akin to your method---but also formats the date (and pries out the active Firefox tab I want to "respond" to using Webmentions, see https://git.brainbaking.com/wgroeneveld/brainbaking/src/branch/master/hooks/prenote.js )
For me, I don't want that many prompts and find those annoying, I just want to automatically make the file in the right location with as much metadata filled in as possible. It then opens the file in Sublime. Title, tags, and content is added by hand.

View File

@ -10,7 +10,7 @@ categories:
When switching over to a new editor and new language, I can sometimes get frustrated by missing features I got (very) attached to. This excludes the obvious difference in shortcut keys.
### Shortcuts and refactoring tools ###
### Shortcuts and refactoring tools ###
One plugin to rule them all: [ReSharpner](http://www.jetbrains.com/resharper/). This productivity tool brings back the incredible development speed to the Visual Studio platform. You can almost map the eclipse (or IntelliJ, since they guys from JetBrains developed it) keys to the ReSharpner keys. If you're used to quickly refactor out variables, introduce classes from parameters or create test classes, you'll be in heaven.
@ -46,11 +46,11 @@ Other interesting links:
- [Default keymap PDF overview](http://www.jetbrains.com/resharper/docs/ReSharper70DefaultKeymap_IDEA_scheme.pdf)
- [IntelliJ keymap PDF overview](http://www.jetbrains.com/resharper/docs/ReSharper70DefaultKeymap_IDEA_scheme.pdf)
### Comparing files with each other ###
### Comparing files with each other ###
Simply comparing two files within the editor can be a pain - the easiest way to do it in Eclipse is just select both files, rightclick and select "compare". No such option here. You can compare a file with a previous version from TFS, but not two physically different files, weird. Install [VSCommands](http://vscommands.squaredinfinity.com/) and that problem is also solved:
![compare files in vstudio](../compare_files_vstudio2012.png)
![compare files in vstudio](../compare_files_vstudio2012.jpg)
It uses the built-in VS2012 comparison window, which is quite nice.

View File

@ -92,7 +92,7 @@ After that, you can start writing tests. Looking at the examples, the test flow
});
![](../siesta.png "The Siesta view test in action")
![](../siesta.jpg "The Siesta view test in action")
Siesta also comes with it's downsides though.

View File

@ -22,7 +22,7 @@ Public Sub MyTestMethod_WithSomeArg_ShouldReturn45
End Sub
```
![simply vb unit screenshot](../simplyvbunit.png)
![simply vb unit screenshot](../simplyvbunit.jpg)
The test code is very readable thanks to the [NUnit](https://nunit.org/index.php?p=documentation) influence on SimplyVBUnit. The package is very easy to install, but there are a few gotcha's.
You need to create a separate VBP file (Visual Basic Project) which acts as your UnitTest project with a reference to the SimplyVBUnit package. That's easy enough, but it's a project. That means it can't reference other projects! Our software is basically one large project with heaps of muddy code. Compiling the EXE and referencing that one is not an option for us. That leaves us with a few alternatives:

View File

@ -28,7 +28,7 @@ But let's return to option two for now.
I forgot about the discussion and moved on.
But taking mental notes is like async AJAX calls: they do return, but you don't know when. That's when it hit me: why don't I email him some details on how to quickly start a blog? Would that be appropriate or not? In any case he's free to delete the email - no harm done. But if he decides to do something with it, all the better. The step from no blog and no experience in Hugo is smaller when a friendly push in the right direction has been given.
![giving](/img/sharing.png)
![giving](../sharing.jpg)
One could say "what have you to gain? Why do you care?" And I don't - I'm not a game developer, but I do care - a bit - about the subject. I do care - a lot - about my friend and his personal growth. [Show your work](/post/teaching-kids-how-to-program/), remember?
I love to inspire others on things that I'm passionate about myself. And sharing is caring. I would love to receive an email with loose ends like this from someone I talked to.

View File

@ -22,7 +22,7 @@ Let's start with different stages of learning. Musashi writes that only after he
This looks like an interpretation of "[Shu - Ha - Ri](https://en.wikipedia.org/wiki/Shuhari)".
![ShuHaRi](/img/ShuHaRi.png)
![ShuHaRi](../ShuHaRi.jpg)
In martial arts, there are 3 stages to learning mastery:

View File

@ -36,7 +36,7 @@ I made something really simple for dinner: tortilla's. That would require someth
Well, not really. I added a fermented shallot to create a sour tang and - of course - some salt. Because it had to be spreadable and because I happen to love olive oil, that was also added.
![YAGNI](/img/yagni1.png "Mozarella and beetroot in a blender.")
![YAGNI](../yagni1.jpg "Mozarella and beetroot in a blender.")
Three ingredients. (Salt and oil doesn't count as an ingredient in my kitchen)
That's it.

View File

@ -24,7 +24,7 @@ That is called a **creative scar** and can be very deadly. If someone ever says
The problem gets worse if you are not aware that you have in facts such a scar. If you're not aware, then how on earth are you going to do something about it? The answer is you won't. You're stuck in the "Unconsciously incompetent" stage of competency. A diagram from [Wikipedia](https://en.wikipedia.org/wiki/Four_stages_of_competence) might help to sketch the situation a bit:
![stages of competence](/img/competences.png)
![stages of competence](../competences.jpg)
> Unconsciously incompetent: The individual does not understand or know how to do something and does not necessarily recognize the deficit.
@ -45,7 +45,7 @@ Somehow, thanks to lots and lots and lots of writing, I started to become more a
Of course concepts like **journaling** aren't new at all: Roman emperor and philosopher Marcus Aurelius wrote down how he should behave centuries ago. Great minds like Wittgenstein, John Locke and Seneca also loved writing. The medium was and is a great fit for rapid note taking - whether it's ideas or feelings, that doesn't really matter. As long as you do something with it afterwards. Or not - that's okay too.
![Aurelius](/img/aurelius.jpg)
![Aurelius](../aurelius.jpg)
So I did not try to reinvent the wheel: I merely looked and copied what worked for me. That very same thing might not work for you at all - you might be one of those digital whizz kids that like to use Evernote or Google Keep. I'm more of an analog guy: it enables me to sketch, write, make mind maps and paste pictures. But what should you do with your piece of work?
@ -76,7 +76,7 @@ Those notes in my journal set me on track to identify what I wanted to do the mo
3. I decided that I should finally [learn to draw](/post/post/teaching-yourself-to-draw/).
4. After falling in love with [sourdough bread](http://www.redzuurdesem.be), I decided to follow a three year long night class to become a professional baker, and did an intensive internship, combined with my full-time job.
![Aurelius](/img/notes.png)
![Aurelius](../notes.jpg)
These examples might sound trivial to you, but mean the world to me. Everything wasn't a simple "okay let's do that" thought but came up organically by tracking what I was thinking on a given moment. When I notice I complain a lot about my bad drawing skills, I might - finally - do something about that.
<br/>Before journaling, I _simply had no idea_!
@ -108,7 +108,7 @@ That's exactly what it feels like.
### In practice
![full journals](/img/boekjes.jpg)
![full journals](../boekjes.jpg)
There are a lot of techniques involved in efficient journaling I like to save for a future blog post to be linked here. (Update: [it's here!](/post/journaling-in-practice/)) But in practice, it all boils down to just starting to write. Do what works for you. If you decide to buy a journal, consider scanning in pages after it's full. I archive everything in Evernote but that requires tagging manually and soaks op a lot of hours. GTD is a nice way to keep track of things to do - if you're the kind of person who likes TODOs. I also like pasting pictures, doodling and sketching and even scrap-booking. Whatever that works.

View File

@ -50,8 +50,7 @@ The **made by hand** part is crucial here. The recent resurgence of "things made
The fancy word "craftsmanship" is sadly often abused thinking it might net you a nice job with a high wage if you spray it around your CV.
![craftsman at work in the Middle Ages](/img/craftsman.jpg)
Craftsman at work in the [Middle Ages](http://www.thomasmorecollege.edu/student-life/catholic-guilds/)
![craftsman at work in the Middle Ages](../craftsman.jpg "The craftsman at work in the Middle Ages.")
Creating something physical has been replaced by something digital: bolts and pieces by lines of code. You're still creating, but that feeling of the material disappeared. Apprenticeship also changed: instead of working and living for years at the bidding of your master, you now hopefully pick up some tips and tricks from a colleague before moving on to the next job. It's become much more implicit and that is definitely for the worse. Googling "the lost art of mentoring" nets me an astonishing amount of recent articles on cool websites like "refresh leadership".

View File

@ -16,7 +16,7 @@ Also worth reading: [Reverse engineering a curriculum](/post/reverse-engineering
I happened to come across a very interesting study path for "game programmers" published at [https://github.com/miloyip/game-programmer](https://github.com/miloyip/game-programmer). It's a nice visual representation of books that help you become a better (game) programmer, starting from zero (game programming for kids) and ending at advanced game physics and Artificial Intelligence. I am not keen on becoming a game programmer but the reason this is interesting is that the author has done a great job trying to categorize the different requirements - what it takes to be a programmer in general. Let's take a closer look at that.
![](../miloyip.png "A cut-out part of the study path, copyright Milo Yip")
![](../miloyip.jpg "A cut-out part of the study path, copyright Milo Yip")
## Study paths for any programmer

View File

@ -68,7 +68,7 @@ Een kanttekening: er zijn reeds eerder brainstorm sessies gevoerd rond de cultuu
### 1. Tevredenheid is de oogst van Hart-werk.
![ethiek img](/img/pratoway/1.png)
![ethiek img](../pratoway/1.jpg)
_**Hart** werken is zeker en vast **hard** werk. Samenwerken en communiceren met respect voor je collega en klant. Tevredenheid ligt ons nauw aan het **hart** omdat we het **hart** op de juiste plaats hebben.<br/>
Een tevreden klant is een gelukkige klant. Een tevreden collega is een gelukkige collega. <br/>
@ -76,14 +76,14 @@ Een goede oogst vol Hart-werk dus!_
### 2. De beste intenties voorkomen klanten interventies.
![ethiek img](/img/pratoway/2.png)
![ethiek img](../pratoway/2.jpg)
_Wij kijken positief naar het leven en nemen aan dat iedereen met de beste intenties handelt, zowel klant als collega. Loopt er al eens iets mis, dan stropen we gewoon de mouwen op en gaan we ertegenaan. <br/>
Anderen helpen is ook jezelf helpen!_
### 3. Echte comPAINons delen meer dan alleen maar brood.
![ethiek img](/img/pratoway/3.png)
![ethiek img](../pratoway/3.jpg)
_Er wordt gezegd dat planten beter groeien als je ze dagelijks complimentjes toefluistert. Dat kunnen we niet hard maken, maar dat collegas daarvan opfleuren is een feit! Het “Je bent me dr eendje” initiatief heeft dit zeker bewezen. Deel daarom meer dan enkel kennis en breek ook eens brood bij een lach en een traan.<br/>
Zet elkaars talenten in de verf. Vier ook kleine successen!_
@ -91,7 +91,7 @@ Zet elkaars talenten in de verf. Vier ook kleine successen!_
### 4. Een gedeelde voedingsbodem kweekt inventieve ideeën.
![ethiek img](/img/pratoway/4.png)
![ethiek img](../pratoway/4.jpg)
_Het magische woord **serendipity**, de “toevallige” samenkomst van ideeën onder de juiste condities, is voor ons al lang geen geheim meer. Die condities zijn heel simpel: een gedeelde voedingsbodem! Wees betrokken in alle initiatieven en deel uw mening om verder te bouwen aan een beter Prato.<br/>
Kruisbestuiving van ideeën zorgt voor vruchtbare concepten._
@ -99,7 +99,7 @@ Kruisbestuiving van ideeën zorgt voor vruchtbare concepten._
### 5. Te midden van de moeilijkheid ligt jouw mogelijkheid.
![ethiek img](/img/pratoway/5.png)
![ethiek img](../pratoway/5.jpg)
_Jouw mogelijkheid staat voor dingen zelf in beweging kunnen zetten. Wij zijn allemaal een uniek radertje in het Prato tandwiel en kunnen allemaal even betrokken zijn - als we dat willen. De pure kracht van verantwoordelijkheid zit in ieder van ons. <br/>
Obstakels zijn er om gezamenlijk uit de weg te ruimen!_
@ -107,7 +107,7 @@ Obstakels zijn er om gezamenlijk uit de weg te ruimen!_
### 6. Goede dingen gebeuren als je openstaat voor verandering.
![ethiek img](/img/pratoway/6.png)
![ethiek img](../pratoway/6.jpg)
_Het buigzame twijgje blijft groeien na een storm, terwijl de grote onbuigzame eik splijt. Durf zelf op zoek te gaan naar verandering en durf mee te gaan met voorstellen van collegas die niet meteen aansluiten met je eigen visie. <br/>
Goede dingen gebeuren als je onderweg af en toe vragen stelt over het pad dat je hebt afgelegd en nog gaat afleggen. Durf dingen in vraag te stellen bij jezelf, je team en Prato._

View File

@ -27,7 +27,7 @@ Als software ingenieur met meer dan een [decennium ervaring](/about) heb ik een
Het traditioneel coachen en lesgeven brengt echter niet altijd even veel op. Dat kan natuurlijk aan mij liggen (de manier waarop), aan het de stof die ik wil overbrengen (het onderwerp) of aan de interesse van het doelpubliek (de ontvanger). Bij veel teams waar ik de afgelopen jaren tijd in heb doorgebracht is het moeilijk om iedereen op dezelfde golflengte te krijgen.
![sw engineering probleem](../sweng_prob.png)
![sw engineering probleem](../sweng_prob.jpg)
Ik begon mij af te vragen hoe ik het probleem zou kunnen identificeren en hier iets concreet rond doen. De vraag werd een **meta-vraag**: in plaats van te vragen wat te leren begon ik te vragen hoe het leren te leren. Dus: _wat zien we over het hoofd wanneer we toegeven dat software schrijven niet altijd gesmeerd loopt?_
@ -128,7 +128,7 @@ Dit concept wil ik graag doortrekken in dit doctoraatsvoorstel. In plaats van ee
Dit sluit beter aan met de _agile proces_ dat ook bij Prato gebruikt wordt om sneller feedback te kunnen verzamelen en ook sneller een meerwaarde te kunnen bieden.
![het agile proces](../agile.png)
![het agile proces](../agile.jpg)
## De toepasbaarheid

View File

@ -19,16 +19,15 @@ Om kwaliteit te produceren moet je soms tot tien jaar geduld hebben. Die tijd wo
Een wijnmaker draait zijn hand niet om voor een jaar meer of minder. Wij in de moderne bedrijfswereld (in context van IT) durven zelfs binnen het jaar meedere keren van werkgever te veranderen. Als je nu zegt dat je 5 jaar voor dezelfde firma hebt gewerkt krijg je gelukwensen en de term _"ancien"_ opgeplakt. 10 jaar voor kwaliteit. 5 jaar voor een label.
<center>
<img src="/img/wine.jpg" class="bordered" />
Onze gids te [Maison Autréau-Lasnot](http://www.champagne-autreau-lasnot.com/maison/)
</center>
![](../wine.jpg "Onze gids te Maison Autréau-Lasnot.")
Druivenvelden zijn in de streek erg duur: de concurrentie aast constant op lapjes grond. Voor grote champagnehuizen in Reims is die kostprijs verwaarloosbaard ten opzichte van een familiebedrijf als Autréau. Ze zijn begonnen in 1932 en van generatie op generatie werd de grond binnen de familie gehouden. Dat is dan ook de enige manier om het te doen: het is zo duur dat het voor kleine bedrijven 50 jaar kost om rendabel te zijn.
Kunnen wij ons dat nog inbeelden? Zo'n grote onkosten maken over generaties heen? Zo in het onzekere durven stappen? Vijf jaar wachten om te weten of je product écht goed is, of maar middelmatige brol geworden is? Om dan daarna ook de ballen te hebben om alles gewoon weg te kappen en opnieuw te beginnen?
Ik denk het niet. <br/>Het is tegenwoordig al een hel om vijf jaar hetzelfde te moeten doen. Als _[polymath](https://en.wikipedia.org/wiki/Polymath)_ is het onmogelijk om zo'n lange periode voor hetzelfde te reserveren. Het [Dreyfus model](https://en.wikipedia.org/wiki/Dreyfus_model_of_skill_acquisition) schrijft een decennium voor als de tijd die nodig is om exceptionaliteit te kweken - daarna wordt het tijd voor iets anders. De geïncarneerde Jezus in [The Man from Earth](https://www.imdb.com/title/tt0756683/) doet ook elke 10 jaar iets anders - wat moet een mens anders als hij eeuwig leeft?
Ik denk het niet.
Het is tegenwoordig al een hel om vijf jaar hetzelfde te moeten doen. Als _[polymath](https://en.wikipedia.org/wiki/Polymath)_ is het onmogelijk om zo'n lange periode voor hetzelfde te reserveren. Het [Dreyfus model](https://en.wikipedia.org/wiki/Dreyfus_model_of_skill_acquisition) schrijft een decennium voor als de tijd die nodig is om exceptionaliteit te kweken - daarna wordt het tijd voor iets anders. De geïncarneerde Jezus in [The Man from Earth](https://www.imdb.com/title/tt0756683/) doet ook elke 10 jaar iets anders - wat moet een mens anders als hij eeuwig leeft?
Champagne maken bijvoorbeeld. Ook al betekenen jaren niets voor onze gids, toch weet hij bij het proeven van champagne zich exact te herinneren welke blend dat was, hoe de oogst dat jaar was en in welke ton het sap aan het gisten was. De ontmoeting leert mij veel gematigder te zijn als het gaat over het spenderen van tijd. Een doctoraat van zes jaar (enkelvoud) doorploeteren hoeft helemaal niets met ploeteren te maken hebben. Die zes jaren (meervoud) zullen zo ook wel voorbij vliegen.
@ -42,4 +41,4 @@ Om af te sluiten met onderzoek: [What happened to the Time? The Relationship of
> There is a connected relationship between time and occupation.
Dus toch.
Dus toch.

View File

@ -79,7 +79,7 @@ That's simply a matter of configuring your `.bashrc` or `.bash_profile` files -
OSX's Finder makes it easy to create shortcuts for directories that have been heavily used, like my `~/development` dir where all repositories live. Luckily, Ubuntu's file manger also has this feature:
![linux files](../files_linux.png)
![linux files](../files_linux.jpg)
##### Taking screenshots of areas

View File

@ -38,7 +38,7 @@ To fail means to get feedback. To get feedback means to **publish**. That's righ
For example, Katy identified daily stand ups used in agile teams as a useful way to identify what was blocking her progress by doing a stand-up with... yourself! That way she got feedback from herself. A PhD can be a lonely process indeed, but it doesn't have to be that way. You can change that yourself by explicitly asking feedback to your peers, colleagues and friends.
My way to get feedback is to follow in Jake's footsteps and to simply [publish **everything** on Github](https://wgroeneveld.github.io/phd). Add a big "feedback" button that redirects to the Github Issues page to invite people who come across your work to leave valuable insights. Why only publishing a paper, if you can publish everything related to your work, including papers? It contains reports of meetings, brainstorms, concept items I'm working on, definitions, bibliography, ... Literally everything. **Document everything** - a useful tip!
My way to get feedback is to follow in Jake's footsteps and to simply publish **everything** on GitHub. Add a big "feedback" button that redirects to the Github Issues page to invite people who come across your work to leave valuable insights. Why only publishing a paper, if you can publish everything related to your work, including papers? It contains reports of meetings, brainstorms, concept items I'm working on, definitions, bibliography, ... Literally everything. **Document everything** - a useful tip!
Another way to get feedback if you're developing is using your trusty Unit Tests and Continuous Integration system. As I'm not coding for my PhD, this might not apply, you might think. But actually, I can still take those concepts and try to apply them to my writing:
@ -99,8 +99,8 @@ Writing requires a... typewriter? Text editor? Sublime Text. Pushing to a github
Sublime works well in combination with Git and Markdown; especially with plugins like WordCount, MarkdownEditing, GitGutter, BracketHighlighter and Compare Side-By-Side. My choice for using Markdown instead of LaTeX might sound strange for academics, but Hugo's publishing skills are simply unmatched if you want feedback, fast. Things like Pandoc and AcademicMarkdown can be used to convert `.md` files to academic `.pdf` papers, like [Eric J. Ma](http://www.ericmjl.com/blog/2016/6/22/tooling-up-for-plain-text-academic-writing-in-markdown/) did. More on that later when I'm on the verge of actually publishing something in an academic journal.
### Anything else?
### Anything else?
I'm sure my methods of bootstrapping my research can be further enhanced by people who've been through the whole process and are also agile advocates. If you think my work might benefit from some other tool or practice not mentioned here, please let me know. Every remark is greatly appreciated!
I'm sure my methods of bootstrapping my research can be further enhanced by people who've been through the whole process and are also agile advocates. If you think my work might benefit from some other tool or practice not mentioned here, please let me know by [adding an issue in the Github repo](https://github.com/wgroeneveld/phd/issues) as feedback. Every remark is greatly appreciated!
In the meantime, take a second to skim through [my work so far](https://wgroeneveld.github.io/phd/). Thank you!

View File

@ -32,7 +32,7 @@ Mijn vrouw heeft van haar jeugd nog een originele SNES console die we ook aanges
Dat digitale aan de SNES Mini stoort me al vanaf het eerste uur dat ik er mee speel: het keuzemenu dat een vrolijk liedje speelt terwijl jij je spel kiest. De vraag is: welk spel? Niet genoeg keuze. Tools als Hackchi2 installeren bevrijdt je Mini van het ronde getal 20 en laat je toe om eender welke ROM die je in je bezit hebt er bij op te proppen. Nog meer keuze. Welk spel? De Hackchi tool heeft addons waarbij emulators van andere consoles geïnstalleerd kunnen worden. Ik kan plots een Playstation 1, DOS(Box) of SEGA MegaDrive spel spelen op mijn Nintendo SNES... Nog meer keuze. Welk spel?
![](/img/snesmini.jpg "SNES Origineel en SNES Classic Mini")
![](../snesmini.jpg "SNES Origineel en SNES Classic Mini")
Hoe meer _luie keuze_ ik heb (een druk op de knop, een selectie uit een menu), hoe minder ik toegewijd ben om een spel van begin tot einde écht te _spelen_. Dit vind ik heel vreemd. Ik ben bijvoorbeeld ook opgegroeid met een Gameboy en tijdens beursbezoekjes schuim ik dan standjes af om herinneringen terug tot leven te laten komen. Die dure heraankopen maken mij blijkbaar wel erg toegewijd. Alle GBA Castlevania's zijn volledig herspeeld. De actie van een fysieke casette in een console te steken en die aan te zetten geeft mij veel meer goesting om er iets langdurig mee te doen dan simpelweg iets te kiezen uit een hoop dingen die reeds beschikbaar zijn.

View File

@ -12,7 +12,7 @@ tags: [ 'unit testing', 'assembly', 'picoblaze']
To continue our [unit testing tradition](/tags/unit-testing/), each time I land on a new language or piece of technology, I carefully assess whether it's possible to write tests first. Unsurprisingly, even in Assembly it's possible. My recent foray into the digital electronics world has let me to write instructions for the [Xilinx PicoBlaze](https://www.xilinx.com/products/intellectual-property/picoblaze.html) 6 FPGA microcontroller. This Assembly dialect, written in "Psm(4)" files, is destined for a different architecture. That means linking and leaning on Google Test using C++ isn't possible.
As any good software developer does, I decided to reinvent the wheel and create an [Open PicoBlaze Assembler Unit Test package](https://github.com/wgroeneveld/opbtest) myself, written in Python 3. It uses the open `opbasm` and `opbsim` cross-platform assembler and simulator to compile your written Assembly, and then leverages Python's `unittest` package to execute assertions. Take a look at the [github repository](https://github.com/wgroeneveld/opbtest) README file an in-depth technical background.
As any good software developer does, I decided to reinvent the wheel and create an [Open PicoBlaze Assembler Unit Test package](https://git.brainbaking.com/wgroeneveld/opbtest) myself, written in Python 3. It uses the open `opbasm` and `opbsim` cross-platform assembler and simulator to compile your written Assembly, and then leverages Python's `unittest` package to execute assertions. Take a look at the [github repository](https://git.brainbaking.com/wgroeneveld/opbtest) README file an in-depth technical background.
Say, I write a procedure that adds two registers and stores it in a third. This procedure lives in a `.psm4` file, together with a bunch of other procedures. What if I want to test only that "method"?
@ -70,7 +70,7 @@ As you can see, I've been inspired by other well-known C#/Javascript frameworks
Assertions like `reg`, `scratchpad`, `port` verify the correct end state of your Assembly file. Seeing a red or green test in your favorite IDE or in the console is a _huge_ improvement over this:
![](../picoblaze_sim.png "Simulating hardware in a testbench")
![](../picoblaze_sim.jpg "Simulating hardware in a testbench")
Inspecting a wave form after simulating your hardware configuration in tools like Vivado is a huge pain compared to writing separate test cases in software. I know the Test Bench tooling can be used to write test for your synthesis, but when uploading a bitstream into an FPGA, you never know whether it's the HDL that's incorrect, or the Assembly loaded into it.
@ -80,4 +80,4 @@ For instance, if you use a generated PRNG using the M4 macro, somewhere a `call
Another advantage of using Python is, surprise, readability - at least compared to your Assembly to test! It's also non-intrusive: you don't need to modify your source code or include other files. And it's easily integrated into your CI build using `python -m unittest`.
If you have a feature request, want to contribute, or report a bug, feel free to [open up a new issue](https://github.com/wgroeneveld/opbtest/issues) on Github! As we'll continue to use the package, I'll be sure to make changes here and there.
If you have a feature request, want to contribute, or report a bug, feel free to [open up a new issue](https://git.brainbaking.com/wgroeneveld/opbtest/issues)! As we'll continue to use the package, I'll be sure to make changes here and there.

View File

@ -35,7 +35,7 @@ I'm actually really proud of what they achieved. Developing something for the GB
All those things are extras, the main point of the course is to learn object-oriented development, close to the hardware. Most students had difficulties enough with the `C++` syntax. They did complete another programming course, software design in Java, but that seemed to be long lost and forgotten, as is usually the case with hard working students.
To ease their pain, I created a concept framework in `C++`, called '[<i class='fa fa-github'></i>&nbsp;gba-sprite-engine](https://github.com/wgroeneveld/gba-sprite-engine/)' (available on GitHub through that link), which students had to fork, compile, and use for their projects. It comes equipped with a few demo projects, and throughout the different labs, we worked our way through the GBA's conceptual hardware model, using the following outlines as a guideline:
To ease their pain, I created a concept framework in `C++`, called '[gba-sprite-engine](https://git.brainbaking.com/wgroeneveld/gba-sprite-engine/)' (available on GitHub through that link), which students had to fork, compile, and use for their projects. It comes equipped with a few demo projects, and throughout the different labs, we worked our way through the GBA's conceptual hardware model, using the following outlines as a guideline:
1. [Introduction in C](https://kuleuven-diepenbeek.github.io/cpp-course/c/labo-1/)
2. [Pointers in C and C++](https://kuleuven-diepenbeek.github.io/cpp-course/c/labo-2/)
@ -55,11 +55,11 @@ Especially the live Castlevania demo's were quite appealing to students, showcas
God I love that game. I'll gladly take every opportunity I have to look and/or play it.
The sprite engine does the heavy lifting in terms of image memory allocation and storage, and provides some abstract concepts for sprites/backgrounds/music/scenes. But not before we've seen these in the labs ourselves (lab 1-4). Take a look at the [<i class='fa fa-github'></i>&nbsp;GitHub documentation of the engine](https://github.com/wgroeneveld/gba-sprite-engine/) to get a better picture on the included features. (For the most part) Unit tested and all. It is cross-platform compatible, as the GBA is actually an ARM machine, you'll be needing a cross-compiler from the [DevKitPro toolchain](https://devkitpro.org/wiki/Getting_Started).
The sprite engine does the heavy lifting in terms of image memory allocation and storage, and provides some abstract concepts for sprites/backgrounds/music/scenes. But not before we've seen these in the labs ourselves (lab 1-4). Take a look at the [source documentation of the engine](https://git.brainbaking.com/wgroeneveld/gba-sprite-engine/) to get a better picture on the included features. (For the most part) Unit tested and all. It is cross-platform compatible, as the GBA is actually an ARM machine, you'll be needing a cross-compiler from the [DevKitPro toolchain](https://devkitpro.org/wiki/Getting_Started).
The emphasis lies on the _object-oriented_ part of the course title, that is why the design of the game, and game engine, was very important to me. If you are creating a platformer like Mario, and you can jump, grab coins, and squash enemies, then I want to see that reflected in your design. Where is the statement `mario.jump()`, where are the `class Coin` and `class Goomba : public Enemy` definitions? You can extend from the following engine parts:
![gba-sprite-engine design](https://github.com/wgroeneveld/gba-sprite-engine/raw/master/img/design.png?raw=true)
![gba-sprite-engine design](https://git.brainbaking.com/wgroeneveld/gba-sprite-engine/raw/branch/master/img/design.png)
After the oral defense of their game, students completed a short survey that helped me assess what to do with the course during the next academic year. Most students were very enthusiastic regarding the inclusion of the GBA, compared to another dull set of assignments. They also responded positively to the question whether the Game Boy could be used in other courses as well, such as hardware architecture design, or (advanced) chip design (using an FPGA).

View File

@ -26,7 +26,7 @@ Na een zware werkdag, die ook nog eens eiste dat je de frustrerende files moest
Onmiddellijke voldoening heeft ook te maken met de onmiddellijke aanwezigheid van alles, dankzij de hyper verbonden wereld en de 150gr zware apparaatjes die precies een extensie van onze hand geworden zijn. Als we nu op vakantie zijn, kunnen we onmiddellijk de familie kiekjes opsturen, waarvan we uiteraard verwachten dat ze onmiddellijk bekeken worden. Wanneer mijn ouders op vakantie zijn, word ik gebombardeerd met foto's van idyllische landschappen, goedkoop bier, en veel te veel 'selfies'. Daar wordt dan onmiddellijk op gereageerd, liefst met zaken als 'leuk! (hartje) (hartje)', of 'amuseer u, hier is het nog steeds kak!'.
Vijfentwintig jaar geleden reden mijn ouders mijn zussen en mij voorbij de Spaanse grens, om een dag later het dorpscentrum in te rijden en peseta's in een telefooncel te steken, hopend op een verbonden lijn die de grootouders geruststellen dat we allemaal nog leven. Vakantiefoto's werden uiteraard pas ontwikkeld na de terugreis.
![](/img/mediamarkt.jpg "Meer wachten dan nodig? Ik ben toch niet gek!")
![](../mediamarkt.jpg "Meer wachten dan nodig? Ik ben toch niet gek!")
Dat klinkt allemaal redelijk pessimistisch, verlangend naar een vervlogen tijdperk van telefoonkaarten in een pre-Euro zone. In feite wil ik dit zeggen: waarom is het nodig om nutteloze foto's van nietszeggende dingen door te sturen op het moment zelf, in plaats van ter plekke te genieten en het verhaal achteraf te vertellen? Want wat valt er de familie te vertellen bij de thuiskomst, als we heel de rit al veel te intens hebben meegemaakt? 'Oh ja, dat wist ik al, ik zag het op foto x'. Iedereen is al op de hoogte van elkaars reilen en zeilen voordat we mekaar effectief zien. Dat betekent dat er op het moment van weerzien maar bitter weinig te vertellen valt. En dan zijn we verwonderd dat sociale capaciteiten toch niet de grote sterktes van tegenwoordig zijn.

View File

@ -71,7 +71,7 @@ The result is not great. Instead of utilizing URLs such as `/page/2/`, as the de
#### The Blog Detail page
!["Invalid brizy content"](../brizy_invalid.png "Did I do something wrong?")
!["Invalid brizy content"](../brizy_invalid.jpg "Did I do something wrong?")
A lot of shortcodes are needed here. Blog title, blog contents, blog metadata (the detail footer), blog comment forms, showing a list of categories, a list of most popular or recent posts in the sidebar, ... The problem for my wife is that while designing this page, there are no placeholders available, so it's a bit of a guess how it will turn out to be.

View File

@ -21,7 +21,7 @@ And it did not happen overnight. I started working on the idea in 2017, but was
To focus on the writing itself, and not the layout and whatnot, I decided to write in Markdown. In a non-fiction book, such as the one I was planning to write, it is important not to overburden readers with flashy layouting - so Markdown's minimalistic approach helped a lot here: underscores to _emphasize_ things is mostly enough.
![subl](../makingof-schrijven.png)
![subl](../makingof-schrijven.jpg)
### The toolchain
@ -64,7 +64,7 @@ The first three pages of the book are the cover pages, and they are set:
An optional fourth page, which I included, contains the copyright, edition information, and so forth. Everything is part of `\frontmatter`, and written in LaTex itself. I got inspired by some examples provided by the memoir fellows:
![subl](../book-coversheet.png)
![subl](../book-coversheet.jpg)
The style is called 'Gentle Madness'. I had no intention of pouring hours into making my own original version of something that was already good enough: it's part of the cover pages... You know, the things you flip through, in search for the actual content?
@ -72,7 +72,7 @@ The style is called 'Gentle Madness'. I had no intention of pouring hours into m
As said before, memoir comes with 'batteries included' - and that is definitely the case for chapter styles. I opted for a minimalistic chapter number and a title - that's it:
![subl](../book-chapter.png)
![subl](../book-chapter.jpg)
The tex needed to do that:
@ -122,7 +122,7 @@ Enter the `perpage` package, where footnote numbering can be tampered with, with
The end result looks like this:
![footnotes](../book-footnotes.png)
![footnotes](../book-footnotes.jpg)
For the header and footer, I wanted to maximize the available space on a page. That means only one of both, so no header. It is important to distinguish footer text from chapter text, so I opted to reduce the harshness of the text by using gray and another font type. It can be configured as a separate chapter style, that should distinctively **not** be applied to 'part x' pages:
@ -138,7 +138,7 @@ For the header and footer, I wanted to maximize the available space on a page. T
For citing work, the rough draft employed a dusty academic way (the default way) of doing that: author and year between brackets: 'in research blabla bla (Surname, 2019).' It gave me a headache: it does _not_ read fluently at all. the '\[10\]' thing at the end of a sentence was not that great either. It is non-fiction, but it should not be a boring piece of academic work! In the end, I opted for superscript with a reduced font size:
![cite](../book-cite.png)
![cite](../book-cite.jpg)
I found that citation style in [zotero.org/styles](https://www.zotero.org/styles) and [https://citationstyles.org/authors/](https://citationstyles.org/authors/), it's called `the-open-university-numeric-superscript.csl` and added as a parameter in the metadata YML file for pandoc to parse. As you can see from the pandoc command, the `pandoc-citeproc` filter was used - it worked well enough and meant not manually fiddling with bibliography tools.
@ -147,7 +147,7 @@ I found that citation style in [zotero.org/styles](https://www.zotero.org/styles
The problem with using LaTeX and the memoir style is that most of the default settings feel a bit too rigid - too academic. I tried shaving off that word 'academic' as much as possible. For lists, this meant reducing it's complexity to simply a pointer to the page instead of numbering them individually:
![figure](../book-figures.png)
![figure](../book-figures.jpg)
After a lot of stackoverflow hints, I managed to nail it down to:
@ -189,7 +189,7 @@ For 'default' inline figures, that works. However, some figures I wanted spread
The end result:
![img](../book-image.png)
![img](../book-image.jpg)
Sadly, to make use of the new LaTeX command, I had to interleave it with the rest of the text in my Markdown chapter file. The above screenshot is the result of this:
@ -254,7 +254,7 @@ I found the solution to the problem on another blog and can't remember which one
The end result, at the bottom of the page:
![starbreak](../book-starbreak.png)
![starbreak](../book-starbreak.jpg)
### The verdict

View File

@ -8,7 +8,7 @@ categories:
- programming
---
When I started programming the [gba-sprite-engine](https://github.com/wgroeneveld/gba-sprite-engine/) two years ago, I knew I would be getting myself into trouble. The Game Boy Advance only has 16Mhz and it's whole software library is written in low-level C using DMA (Direct Memory Access) and memory-mapped IO. Translation: pointers! `**` - Yay!
When I started programming the [gba-sprite-engine](https://git.brainbaking.com/wgroeneveld/gba-sprite-engine/) two years ago, I knew I would be getting myself into trouble. The Game Boy Advance only has 16Mhz and it's whole software library is written in low-level C using DMA (Direct Memory Access) and memory-mapped IO. Translation: pointers! `**` - Yay!
In the end, switching to `C++11` while trying to unit test and stub out BIOS code as much as possible did help soften the pain. I'm glad I got my hands dirty again, and it writing "closer to the metal" was a welcome change from the usual high-level stuff I produce.
@ -21,9 +21,9 @@ But GBA MODE 0-1-2 is not the only possibility to write a GBA game. There's also
How do you render things in 3D without hardware acceleration, and without an FPU on the circuit board that handles `float` digits, taken into account the (mostly) 16-BIT bus rate and 16Mhz CPU? Well... It does not exactly produce 30+ FPS:
![](https://github.com/wgroeneveld/gba-bitmap-engine/raw/master/img/monkey.gif?raw=true "Wireframing 507 vertices and 968 faces")
![](https://git.brainbaking.com/wgroeneveld/gba-bitmap-engine/raw/branch/master/img/monkey.gif "Wireframing 507 vertices and 968 faces")
![octahedron](https://github.com/wgroeneveld/gba-bitmap-engine/raw/master/img/raster.gif?raw=true "Trying to rasterize the same thing")
![octahedron](https://git.brainbaking.com/wgroeneveld/gba-bitmap-engine/raw/branch/master/img/raster.gif "Trying to rasterize the same thing")
Drawing a lot of lines is not exactly something the GBA loves to do. And I did use [tonclib's optimized routines](https://www.coranac.com/tonc/text/toc.htm) after a failed attempt to implement Bresenham myself. MODE4 has weird byte-write requirements and you can optimize DMA writing of horizontal lines.
@ -33,9 +33,9 @@ I intended to design the engine again as high-level as possible taking advantage
Reverting to a simple box sped up the FPS:
![](https://github.com/wgroeneveld/gba-bitmap-engine/raw/master/img/wired2.gif?raw=true "A BabylonJS-exported Box. (including a bug)")
![](https://git.brainbaking.com/wgroeneveld/gba-bitmap-engine/raw/branch/master/img/wired2.gif "A BabylonJS-exported Box. (including a bug)")
![](https://github.com/wgroeneveld/gba-bitmap-engine/raw/master/img/octa.gif?raw=true "A rasterized octahedron, with back-face culling.")
![](https://git.brainbaking.com/wgroeneveld/gba-bitmap-engine/raw/branch/master/img/octa.gif "A rasterized octahedron, with back-face culling.")
Even calculating the frames per second is a pain. What's a "second"? Okay, so we need a hardware timer interrupt. When does this thing overflow? How many cycles does the CPU take before that happens? Are you seriously using the divide operator instead of `fxdiv()`?
@ -43,7 +43,7 @@ Also, I could not remember most of the math needed to project 3D vertices into a
Future work: texturizing - I'm curious to see at what rate we could get a simple box textured with a mario "?" block. I won't even try to attempt portal rendering like the 007 Nightfire devs.
Check out the source code here: https://github.com/wgroeneveld/gba-bitmap-engine/
Check out the source code here: https://git.brainbaking.com/wgroeneveld/gba-bitmap-engine/
### Unit testing GBA BIOS functions
@ -81,5 +81,5 @@ Sometimes, that does not suffice. In `tonc_math.h`, some forward-declared functi
#endif
```
Since I did not want to change the tonc files itself, constructs like the above sometimes appear in the engine header files when referencing tonc files. Interested readers can always plow through the C++ files in the Github repository.
Since I did not want to change the tonc files itself, constructs like the above sometimes appear in the engine header files when referencing tonc files. Interested readers can always plow through the C++ files in the Git repository.

View File

@ -12,7 +12,7 @@ After the previous months' [reviving of a 80486 PC](/post/2020/09/reviving-a-804
The 486 PC is able to run games from the early eighties to 1995. My Windows XP machine is a late WinXP era PC that is able to play games up to 2011. I needed something that sits comfortably in between these two timelines. The original Pentium CPU wasn't on my mind since a fast 486 (DX4) is able to beat it. As a kid, in the year 2000 I was a proud owner of a newly released AMD Athlon Thunderbird `1GHz`, upgrading from a Pentium II. It was the year of the Gigahertz barrier breach:
![](../ClockSpeed.png "Stock CPU clock speed history. Source: maximumpc.com")
![](../ClockSpeed.jpg "Stock CPU clock speed history. Source: maximumpc.com")
As clearly visible in [the graph](https://web.archive.org/web/20150418074002/http://www.maximumpc.com:80/article/home/history_dream_how_ultimate_pc_has_evolved_15_years), 2000 was a big turning point for CPU speed. The Thunderbird was one of the first, that was also easily overclockable. AMD's K7 Athlon XP breached `2GHz` only two years later. So, the quest became clear and my mind was set: chasing nostalgic values again. I even managed to find my original [AOpen HQ45 mid tower](https://www.cnet.com/products/aopen-hq45-mid-tower-atx-series/) again!

View File

@ -116,7 +116,7 @@ Until then, I'll compile and debug cmdline. **CMake** works flawlessly, using th
### Game Boy Advance
Cross-compiling **GBA stuff** using [pacman](https://github.com/devkitPro/pacman/releases/latest) worked flawlessly, obviously in Rosetta mode. I doubt it will ever be released natively. Cross-compiling the whole [gba-sprite-library](https://github.com/wgroeneveld/gba-sprite-engine), including four demo projects, took `15343`ms. I was surprised that this worked without any problems, and a Rosetta-enabled mGBA happily plays my binaries! On the 2012 laptop, it takes more than twice that long: `32950`ms.
Cross-compiling **GBA stuff** using [pacman](https://github.com/devkitPro/pacman/releases/latest) worked flawlessly, obviously in Rosetta mode. I doubt it will ever be released natively. Cross-compiling the whole [gba-sprite-library](https://git.brainbaking.com/wgroeneveld/gba-sprite-engine), including four demo projects, took `15343`ms. I was surprised that this worked without any problems, and a Rosetta-enabled mGBA happily plays my binaries! On the 2012 laptop, it takes more than twice that long: `32950`ms.
### Arduino

View File

@ -25,7 +25,7 @@ A quote from the book that resonated with me:
### 1. Acceleration
![](../icon-iterm.png#right)
![](../icon-iterm.jpg#right)
_Don't type the same commands again and again_. Find something to do the work for you. Neal suggests to use things that _remember history_: clipboard extenders, history keepers that can be automatically recalled, command prompt plug-ins, and so forth. These things seem so mundane and obvious nowadays. Yet, who knows every [obscure feature](https://iterm2.com/features.html) of their [Zsh](https://www.zsh.org/)-powered [iTerm2](https://iterm2.com/)? A few I just learned yesterday:
@ -35,7 +35,7 @@ _Don't type the same commands again and again_. Find something to do the work fo
Things like a built-in password manager, badges, image integration and clipboard managing are less useful for me, since I use other tools that work outside of a shell. I'm still discovering new features and it's impossible to list them all here.
![](../icon-alfred.png#right)
![](../icon-alfred.jpg#right)
Another saying: _search trumps navigation_. Don't use Finder to click through all directories if you know what you're looking for: let the tools do the work for you. Remember Google's clean and simple search UI? There's one single input box: type and thou shall find. I cannot recount the number of times I've pressed `CTRL+SHIFT+T` in Eclipse in my life, or `⌘+O` in IntelliJ. Why limiting yourself to only use these search tools inside a specific code editor, when you can have it across your Mac?
@ -73,7 +73,7 @@ _"Can I script that?"_ should be the first thing that springs into your mind whe
Another thing Neal was advocating for: use a _real_ programming language when scripting. You never know when that _jig_ will turn out to be a permanent part of your development cycle. If that is the case, it will get expanded. If that is the case, it better damn well be easily unit-testable!
![](../icon-firefox.png#right)
![](../icon-firefox.jpg#right)
In 2010, Selenium and WebDriver was thé tool to automate your browser, and to write acceptance tests with. Nowadays, we have [Cypress](https://www.cypress.io/) and others that are gaining popularity. One thing that struck me in that Devoxx presentation was, why limit the usage of these tools to your workday? They can also be useful to automate mundane things such as form completions - outside of the enterprise application you're working on. [Tampermonkey](https://www.tampermonkey.net/scripts.php) and [MonkeyScripts](https://monkeyscripts.org/) also fall into this category.
@ -93,7 +93,7 @@ This is the single best reason to completely ditch (manually adding) Javadoc. Us
_Learn to get to know your tools._ Do not just "use" them - understand them, click through all menus, write down the shortcuts, and try to learn one (of an action you of course actually use) very day. This isn't limited to just your (code) editor!
![](../icon-sublime.png#right)
![](../icon-sublime.jpg#right)
Talking about editors: pick one and dive deep - it's as simple as that. Editors come and go, but Vi and Emacs will probably stay forever, so both are a solid choice - if you can muster chewing through thick guides and a _very_ steep learning curve. In 2012, I gave up on Vi (sorry, I'm a softie) and bought [Sublime Text](https://www.sublimetext.com/) together with my first MacBook - probably the best decisions I've made that year. Sublime is available on any platform. Funnily enough, eight years later, I'm still learning new Sublime tricks. Admittedly, it takes a lot of effort to deliberately learn new things when actually you want to concentrate at the task at hand. A few recent things I've learned:

View File

@ -11,7 +11,7 @@ categories:
Web 2.0 is a monstrosity that never should have been evolved the way it did. It is now officially impossible to develop a new web browser, and Google's [dangerously massively-used](https://gs.statcounter.com/browser-market-share) Chrome browser keeps on kicking people in the nuts by [removing privacy-focused extensions](https://www.bleepingcomputer.com/news/security/google-removes-privacy-focused-clearurls-chrome-extension/) from the Chrome Web Store - because it's "not in line with their business model". Did you know the W3C specification set is [114 million words long](https://drewdevault.com/2020/03/18/Reckless-limitless-scope.html)? That's simply insane.
Lately, I've been implementing the [Webmention](/post/2021/03/the-indieweb-mixed-bag/) and Pingback W3C protocols in [my jamstack-augmented microservice](https://github.com/wgroeneveld/serve-my-jams), and I've come to realize that, although Webmention is the post-modernistic hailed and IndieWeb-backed successor to Pingbacks, they technically both suffer form the same problems. I honestly do _not_ see any real difference between `POST`-ing an encoded form or an XML-formatted message. The specs are full of ambiguities that leave the door wide open to (1) interpretation and (2) a proliferation of wildly diverging implementations.
Lately, I've been implementing the [Webmention](/post/2021/03/the-indieweb-mixed-bag/) and Pingback W3C protocols in [my jamstack-augmented microservice](https://git.brainbaking.com/wgroeneveld/go-jamming), and I've come to realize that, although Webmention is the post-modernistic hailed and IndieWeb-backed successor to Pingbacks, they technically both suffer form the same problems. I honestly do _not_ see any real difference between `POST`-ing an encoded form or an XML-formatted message. The specs are full of ambiguities that leave the door wide open to (1) interpretation and (2) a proliferation of wildly diverging implementations.
Don't get me wrong, I love JavaScript. I used to be that guy that constantly yells "check it out, `const()={await...]`!" But the more I tried implementing something inherently _simple_, the more I realized how many different ways the same problem can be tackled in just one language. And then I used `.replaceAll()` in an older version of Node. Many programming languages evolve by borrowing ideas from each other, but really, why would JS need classes? [As Rob Pike argues](https://www.youtube.com/watch?v=rFejpH_tAHM&t=91s), keeping things simple in a language can be quite complex. I hope Go will keep it together.

View File

@ -50,8 +50,8 @@ All these headaches made people not just leave the IndieWeb standards behind: th
Sleeves up, code out:
- microservice endpoint: check, https://github.com/wgroeneveld/serve-my-jams
- javascript tools: check, https://github.com/wgroeneveld/jam-my-stack
- microservice endpoint: check, https://git.brainbaking.com/wgroeneveld/go-jamming
- javascript tools: check, https://git.brainbaking.com/wgroeneveld/jam-my-stack
Okay, so now, I can answer the _why_. Not because it will be heavily used, or the IndieWeb stuff feels like the future. The low adoption rate says it all... No, simply because I was bored and wanted to learn about modern JS frameworks. I had an excuse to code again. Yay!

View File

@ -13,7 +13,7 @@ categories:
When I wrote [Teaching students about coding trends](/2021/03/teaching-students-how-to-follow-development-trends/), I formed a plan to treat myself by exploring new and perhaps exciting programming languages. As a polyglot, I love learning new things like that. But instead of simply writing a hello world application and calling it a day after compiling it, I wanted to get my hands dirty. Really, _really_ dirty. I had my eye on Go for a few years now, and this was the perfect opportunity to pretend to attend a virtual GopherCon to watch experts talk about the language. They convinced me to buy 2 books and gave me permission to dive deep (see, now it looks like it's their fault, not mine!).
Anyway, the [serve-my-jams](https://github.com/wgroeneveld/serve-my-jams) Node Webmention microservice [I talked about](/post/2021/03/the-indieweb-mixed-bag/) seemed like the perfect candidate for a little port project. Go excels at handling web requests robustly, it's partly conceived at Google because of that. After a week of fiddling, tinkering and testing, I came up with [go-jamming](https://github.com/wgroeneveld/go-jamming), a native compiled alternative to serve-my-jams, written in Go. I knew nothing about the language, and in the process, I learned about unit testing, channels and parallel programming, http serving, struct embedding, slices, and so forth. It was well worth the journey. I'd like to take this opportunity to glance back and clarify why I think Go is both **amazing** and **boring**.
Anyway, the `serve-my-jams` Node Webmention microservice [I talked about](/post/2021/03/the-indieweb-mixed-bag/) seemed like the perfect candidate for a little port project. Go excels at handling web requests robustly, it's partly conceived at Google because of that. After a week of fiddling, tinkering and testing, I came up with [go-jamming](https://git.brainbaking.com/wgroeneveld/go-jamming), a native compiled alternative to serve-my-jams, written in Go. I knew nothing about the language, and in the process, I learned about unit testing, channels and parallel programming, http serving, struct embedding, slices, and so forth. It was well worth the journey. I'd like to take this opportunity to glance back and clarify why I think Go is both **amazing** and **boring**.
## Go fixed Java/C#
@ -110,7 +110,7 @@ func (e *Elephant) toot() error {
}
```
This is **good** and **bad**. It's good, because it forces the programmer to think carefully about what can go wrong, and implementing graceful degradation is very easy in Go. It's also bad, because Go code is usually littered with constructs such as the above one. You can return the error up the chain, or wrap it with `fmt.Errorf("whoops: %w", err)`. Take a look at any Go source code at GitHub and you'll find hundreds of examples. This makes code _longer_, but more _clear_ about what should be done when something goes wrong. Most Go code is structured to lean to the left of the gutter as much as possible: first check what can go wrong, exit early, and then go on to do your business. In Java/C#, most code is filled with intricate nested `{ }` stuff. Extracting stuff in methods helps, but does not solve the core problem.
This is **good** and **bad**. It's good, because it forces the programmer to think carefully about what can go wrong, and implementing graceful degradation is very easy in Go. It's also bad, because Go code is usually littered with constructs such as the above one. You can return the error up the chain, or wrap it with `fmt.Errorf("whoops: %w", err)`. Take a look at any Go source code and you'll find hundreds of examples. This makes code _longer_, but more _clear_ about what should be done when something goes wrong. Most Go code is structured to lean to the left of the gutter as much as possible: first check what can go wrong, exit early, and then go on to do your business. In Java/C#, most code is filled with intricate nested `{ }` stuff. Extracting stuff in methods helps, but does not solve the core problem.
I love the simplicity philosophy of Go, although I must admit that some things could be viewed as weird decisions. For example, I've been lying, there's a way to recover from "panics" (crashes such as nil references), but it involves using `defer` and `recover()`, which is decisively NOT simple. Furthermore, the absence of something simple like a set implementation is baffling. I had to roll my own, as recommended in many books, but if it's that common, why not include it in the standard library? Many utility one-liner functions such as `errors.New()` are there, why not a stupid set based on a map?

View File

@ -184,4 +184,4 @@ Written by Wouter Groeneveld on {{ .Lastmod.Format (.Site.Params.dateFormat | de
=> https://brainbaking.com{{ replace (replace .RelPermalink "/gemini" "" 1) "index.gmi" "" }} View this article on the WWW
```
For more information, feel free to contact me or to [plod around in the GitHub repo](https://github.com/wgroeneveld/brainbaking/tree/26cfbac7d554bc385fe0a24d313fde8dbb8585e9) tree at that point in time.
For more information, feel free to contact me or to [plod around in the GitHub repo](https://git.brainbaking.com/wgroeneveld/brainbaking/src/commit/d0d5513e8f89a78a90448ac748ba1869d2b7c6cf) tree at that point in time.

View File

@ -18,11 +18,11 @@ So, is there a decent alternative? No. But their are alternatives which allow yo
## Enabling Webmentions on Hugo
Another sore point seemed to be the lack of decent guides for people to follow when trying to enable Webmentions - beyond the WM.io "standard" - on their static sites. Since I wrote a Jamstack/webmention microservice called [go-jamming](https://github.com/wgroeneveld/go-jamming) which is used on my blogs, I'd like to take this opportunity to show you how easy it is to do it yourself.
Another sore point seemed to be the lack of decent guides for people to follow when trying to enable Webmentions - beyond the WM.io "standard" - on their static sites. Since I wrote a Jamstack/webmention microservice called [go-jamming](https://git.brainbaking.com/wgroeneveld/go-jamming) which is used on my blogs, I'd like to take this opportunity to show you how easy it is to do it yourself.
### Step 1: Running go-jamming yourself
The service is a single binary that is super easy to run anywhere. An amd64 version binary is available in the [GitHub releases page](https://github.com/wgroeneveld/go-jamming/releases). Follow the [INSTALL.md instructions](https://github.com/wgroeneveld/go-jamming/blob/master/INSTALL.md) if you'd like to install it as a Linux service or set up a reverse proxy through Nginx. If all goes according to plan, you'll end up with a `https://jam.yourdomain.com/webmention` and pingback endpiont others can POST to. There!
The service is a single binary that is super easy to run anywhere. An amd64 version binary is available in the [GitHub releases page](https://git.brainbaking.com/wgroeneveld/go-jamming/releases). Follow the [INSTALL.md instructions](https://git.brainbaking.com/wgroeneveld/go-jamming/src/branch/master/INSTALL.md) if you'd like to install it as a Linux service or set up a reverse proxy through Nginx. If all goes according to plan, you'll end up with a `https://jam.yourdomain.com/webmention` and pingback endpiont others can POST to. There!
Go-jamming _should_ be an easy drop-in replacement for Webmention.io. However, since I wrote it with my mainly own needs taken into consideration, and nobody else as far as I know is running it, it could be that you require a missing feature or so. Ping me, fork the code, ... and we'll work it out.
@ -30,7 +30,7 @@ Go-jamming _should_ be an easy drop-in replacement for Webmention.io. However, s
There are simple GET endpoints available that allow you to fetch mentions from the go-jamming service. I store these under `data/webmentions.json`, enabling the Hugo template engine to access this data. We'll get to that in a minute. How to retrieve? Issue a simple GET request.
The question is, how to fit this into a typical Hugo/Jamstack build? I have a single JS file in the root of my website that fetches this data using [jam-my-stack](https://github.com/wgroeneveld/jam-my-stack), a few simple wrappers around services such as Lunr and go-jamming:
The question is, how to fit this into a typical Hugo/Jamstack build? I have a single JS file in the root of my website that fetches this data using [jam-my-stack](https://git.brainbaking.com/wgroeneveld/jam-my-stack), a few simple wrappers around services such as Lunr and go-jamming:
```js
const mentions = await webmention.getWebmentions("brainbaking.com")
@ -38,9 +38,9 @@ const json = JSON.stringify(mentions, null, 4)
await fsp.writeFile(`${__dirname}/data/webmentions.json`, json, 'utf-8')
```
There is zero magic involved in `getWebmentions()`: see [webmention.get.js source](https://github.com/wgroeneveld/jam-my-stack/blob/main/src/webmention/get.js), if you do not want to depend on jam-my-stack.
There is zero magic involved in `getWebmentions()`: see `webmention.get.js` source, if you do not want to depend on jam-my-stack.
What triggers this JS script? A [GitHub actions workflow](https://github.com/wgroeneveld/brainbaking/blob/master/.github/workflows/main.yml). It also automatically checks in any changes! This is a great way to get notified of new mentions. `git pull` tells me if new entries are added into the JSON file. This saves me from implementing a stupid mail function in the service.
What triggers this JS script? A GitHub actions workflow (Addendum 07/2022: I no longer rely on GitHub's workflows). It also automatically checks in any changes! This is a great way to get notified of new mentions. `git pull` tells me if new entries are added into the JSON file. This saves me from implementing a stupid mail function in the service.
### Step 3: Integrate webmentions in your Hugo template
@ -56,7 +56,7 @@ Now that we have a local JSON file, things are dead simple:
{{ end }}
```
See [my full template file](https://github.com/wgroeneveld/brainbaking/blob/master/themes/brainbaking-minimal/layouts/partials/single-webmentions.html) for a complete example. How do you know which properties to use? Well, a go-jamming mention looks like this:
See [my full template file](https://git.brainbaking.com/wgroeneveld/brainbaking/src/branch/master/themes/brainbaking-minimal/layouts/partials/single-webmentions.html) for a complete example. How do you know which properties to use? Well, a go-jamming mention looks like this:
```js
{
@ -75,7 +75,7 @@ See [my full template file](https://github.com/wgroeneveld/brainbaking/blob/mast
},
```
See the [go-jamming documentation](https://github.com/wgroeneveld/go-jamming) for more information.
See the [go-jamming documentation](https://git.brainbaking.com/wgroeneveld/go-jamming) for more information.
### Step 4: Sending out webmentions
@ -88,7 +88,7 @@ Go-jamming does all the rest, such as:
3. Checking if the links have a webmention or pingback endpoint;
4. Sending out the mentions accordingly.
It remembers the last processed post as not to overwhelm other endpoints, so you can call `send()` as many times as you'd like. If you do not like jam-my-stack as a dependency, just write your own: it's simply [a PUT request](https://github.com/wgroeneveld/jam-my-stack/blob/main/src/webmention/send.js)!
It remembers the last processed post as not to overwhelm other endpoints, so you can call `send()` as many times as you'd like. If you do not like jam-my-stack as a dependency, just write your own: it's simply a PUT request (see `send.js` in jam-my-stack)!
## Does this solve the issue?

View File

@ -31,7 +31,7 @@ A selection of community smells identified by his team, from interviewing many p
These are only a few highlights: there are many more available in [Damian's papers](https://scholar.google.com/citations?user=l7BGAq8AAAAJ). I love these analogies, because it suddenly makes it much easier to _talk about them_! Jumping from code smell to community smell isn't hard, and everybody knows communities don't build themselves. Jessica Kerr goes as far as saying [great teams are "symmathesized"](https://jessitron.com/2018/04/15/the-origins-of-opera-and-the-future-of-programming/).
![](/img/pratoway/1.png "Community work equals a good harvest.")
![](/post/2018/06/pratoway/1.jpg "Community work equals a good harvest.")
My own findings from [a decade in the industry](/post/2018/10/a-decade-in-the-industry/) and [creating a software company culture](/post/2018/06/over-bedrijfsethiek/) aren't far off: work with people, not technology. That means that when programming in teams, the above community smells should receive more attention than "mere" code smells! Nowadays, most software-centered conferences include a social issues track. There certainly is some attention for social and psychological aspects of software engineering, but not nearly enough. That is the most important reason why I chose to [investigate non-technical skills](/tags/phd) in software engineering education.

View File

@ -25,12 +25,12 @@ Domains that should not be there, excluding the subdomains:
- youtube-nocookie.com (it's worse by default!)
- ytimg.com
Wow. Time for some coding action. I created another JS module in [jam-my-stack on Github](https://github.com/wgroeneveld/jam-my-stack), the Node utilities project I use on my Jamstack sites where [I've written about before](/post/2021/05/beyond-webmention-io/). I basically converted Ruben's ideas into a JS file that automatically gets called and checked in via a Github Action. In sum, this does more or less the following:
Wow. Time for some coding action. I created another JS module in [jam-my-stack](https://git.brainbaking.com/wgroeneveld/jam-my-stack), the Node utilities project I use on my Jamstack sites where [I've written about before](/post/2021/05/beyond-webmention-io/). I basically converted Ruben's ideas into a JS file that automatically gets called via a Git commit hook. In sum, this does more or less the following:
1. Collect YouTube embed IDs: scan a dir for `.md` files, and scan those for Hugo YouTube shortcode embeds. Pry out the single argument.
2. For each id, if it was not done already, download the thumbnail, and merge it with a transparent `.png` that represents the play button using ImageMagick.
Inspect the [thumbify.js source](https://github.com/wgroeneveld/jam-my-stack/blob/main/src/youtube/thumbify.js) for more details. Since the JS script only downloads thumbnails into a designated folder, I let a custom Hugo shortcode overwrite the default behavior of embedding the video to showing the thumbnail instead:
Inspect the [thumbify.js source](https://git.brainbaking.com/wgroeneveld/jam-my-stack/src/branch/main/src/youtube/thumbify.js) for more details. Since the JS script only downloads thumbnails into a designated folder, I let a custom Hugo shortcode overwrite the default behavior of embedding the video to showing the thumbnail instead:
```
<figure>

View File

@ -51,7 +51,7 @@ Over at the sister blog [Jefklak's Retro Codex](https://jefklakscodex.com), the
- How long to beat it
- The box art image
This felt like driving around with your foot firmly placed on the break pedal. Genres were inconsistent and visitors could not click through to see other related games. But fetching the box art was the worst: going to DuckDuckGo/Google Images, deciding on a "good one", converting, yaddayadda... I have written before on how I leverage the Npm package [howlongtobeat](https://www.npmjs.com/package/howlongtobeat) to automatically grab the hours and howlong-ID. This metadata gets injected into the Frontmatter of the relevant posts: see [jam-my-stack source](https://github.com/wgroeneveld/jam-my-stack) on GitHub. I simply extended that: it apparently came with an URL pointing to the box art! So, this was easy:
This felt like driving around with your foot firmly placed on the break pedal. Genres were inconsistent and visitors could not click through to see other related games. But fetching the box art was the worst: going to DuckDuckGo/Google Images, deciding on a "good one", converting, yaddayadda... I have written before on how I leverage the Npm package [howlongtobeat](https://www.npmjs.com/package/howlongtobeat) to automatically grab the hours and howlong-ID. This metadata gets injected into the Frontmatter of the relevant posts: see [jam-my-stack source](https://git.brainbaking.com/wgroeneveld/jam-my-stack). I simply extended that: it apparently came with an URL pointing to the box art! So, this was easy:
```javascript
async function downloadThumbnail(url, id, dir) {

View File

@ -16,7 +16,7 @@ Sure, the Grey Brick is very old. Sure, it was based on what Nintendo's Gunpei Y
Take a look at the average battery life of Nintendo's handheld systems over the years:
![](../batterychart.png "Average battery life of devices.")
![](../batterychart.jpg "Average battery life of devices.")
Scrambling together these numbers required a lot of guesswork as my own playthroughs and reported numbers from various sources differed quite a bit. [Nerdly Pleasures](https://nerdlypleasures.blogspot.com/2014/10/battery-life-in-8-bit-game-boy-line.html) explains battery life in the 8-bit Game Boy line more thoroughly. [Wired](https://www.wired.com/2012/09/battery-test/) and [IGN](https://www.ign.com/articles/2019/07/17/nintendo-switch-how-the-new-models-battery-life-compares-to-other-handhelds) also conducted bigger tests.

View File

@ -15,7 +15,7 @@ I hate pretending to be a salesmen. Their stereotypical sleezy ways of turning p
I've been attracting a small following of bread baking enthusiasts on Facebook since I started uploading pictures and stories onto it in 2012. Thus, it only seemed natural to announce the release of the book on Facebook. That's exactly what I did in May 2020, with this sale graph as a consequence:
![](../booksalegraph.png)
![](../booksalegraph.jpg)
More than 100 people bought the book the first few months, and I'm very grateful for that. After the initial excitement of a newly released bread adventure book wore off, the monthly sales were still doing all right. Mind you, I wasn't investing any time to promote it, besides putting it on my website [redzuurdesem.be](https://redzuurdesem.be). The total amount of physical books sold since then is 494. That is still awfully low. There are a couple of reasons for that:

View File

@ -24,11 +24,11 @@ If I read a text, and the contents appeals to me, I might go ahead and read a fo
[The Rise of Yeast](https://www.goodreads.com/book/show/34757361) is such a book. I love its contents---I'm a big yeast nut. I wish I read it before writing my own book [about the science of sourdough bread](/post/2021/08/on-selling-self-published-books/). It certainly is a geeky book. One of those books where I love to read the footnotes. But I can't. They could be references! It's a tarp! A missed opportunity.
![](..//theriseofyeast.png "A mish-mash of 'footnotes' in The Rise Of Yeast.")
![](..//theriseofyeast.jpg "A mish-mash of 'footnotes' in The Rise Of Yeast.")
While thinking about the layout for my own book, I ended up making a clear distinction between footnote and reference by using symbols for footnotes---that appear at the end of the very same page---and numbers for references. The used symbols "reset" for every page, as there is space for a new block of footnotes for every page. The numbers do not: each reference is assigned a unique number throughout the whole book.
![](/post/2020/05/book-footnotes.png "Footnotes in my own book.")
![](/post/2020/05/book-footnotes.jpg "Footnotes in my own book.")
Since I self-published my work, I had to decide everything: from font (size, style, spacing) to margins, and yes, footnote styling and placing. Perhaps many authors who work with publishers do not have a saying in this process. Another missed opportunity.

View File

@ -56,7 +56,7 @@ public function subscribe($email, $tag) {
Listmonk uses numbered IDs in the `lists` array, which kind of sucks, so associative array `$tags` "solves" this. Lastly, since we don't want to collect a name, but the API still requires one, I simply submit "anonymous".
![](../listmonk.png "The Listmonk Campaign page.")
![](../listmonk.jpg "The Listmonk Campaign page.")
So far, we're happy with the switch. A few things to take into account:

View File

@ -14,7 +14,7 @@ If you'd like to chime in, you can submit your monthly quiz at http://www.retrog
The November 2021 quiz was themed **Metroidvania**. As a fanboy, I thought I could easily handle it. Yeah. It seems that there's a big difference between guessing and correctly guessing. Here is the quiz picture:
![](../retroguess.png "The November 2021 Quiz.")
![](../retroguess.jpg "The November 2021 Quiz.")
Looks simple enough, right? Samus, Soma, ... Uhh, what's the third one? Oh man! Give it another look (and thought).
@ -48,6 +48,6 @@ Below are all twenty-five answers:
Last month indeed resembled a typical [game meme grid](/post/2021/09/favorite-game-meme/), except this time you have to guess its content based on only a portion of a screenshot instead of the whole box art. Sometimes, the quiz is a fake screenshot consisting of assets of multiple games, such as the one below where The Duke's shotgun appears to blast a Stormtrooper, a Rise of the Triad eye is flying by, a dead imp from DOOM decorates the floor, and the stat bar is taken from Shadow Warrior. Cool! What else can you spot?
![](../retroguess2.png "The Fake Screenshot Quiz.")
![](../retroguess2.jpg "The Fake Screenshot Quiz.")
Most past quizzes are still online, so in case you're bored, taking a peek is highly recommended. Stay tuned for the December one. Thanks again Mr_Horizon, we nerds do love our retro gaming quizzes!

View File

@ -13,7 +13,7 @@ Last December, our energy provider, the [Vlaamse Energieleverancier](https://www
I decided to take a look at the history of charged prices (indicated in red, solid line) in relation to our actual usage (green, dashed line, multiply by `1000`) and the mean temperatures for each quarter in the past five years (blue, dotted line, multiply by `10`). Here's the result:
![](../gasprices.png)
![](../gasprices.jpg)
The final bill isn't in yet so I can't plot the 2021 actual usage part. This only accompanies for central heating (natural gas) prices. We have solar panels installed and I wasn't interested in electricity prices.

View File

@ -7,7 +7,7 @@ tags:
January 2022 is no more. This has been a very productive work month, but a boring personal one. I managed to score a contract with tech book publisher [Manning](https://manning.com/)! I'm writing a book on creativity for programmers, and the first rough draft is more or less done. We've made the first steps toward setting up an editing process. I hope I can learn a lot about writing (not to be confused with [writing specifically in and for tech](https://blog.pragmaticengineer.com/becoming-a-better-writer-in-tech/)) from these experienced editors.
Other than that, the last few exams of the first semester are finally behind us---until next semester, that is. I'm looking forward to the teaching of a new Android-based course that'll be done in Kotlin---something new for both our students and the teaching staff. It was decided that the C++ course, where I use [GBA programming](github.com/wgroeneveld/gba-sprite-engine), is getting retired. A bit mixed feelings: it has been great fun to use the Game Boy to motivate students the past four years.
Other than that, the last few exams of the first semester are finally behind us---until next semester, that is. I'm looking forward to the teaching of a new Android-based course that'll be done in Kotlin---something new for both our students and the teaching staff. It was decided that the C++ course, where I use [GBA programming](https://git.brainbaking.com/wgroeneveld/gba-sprite-engine), is getting retired. A bit mixed feelings: it has been great fun to use the Game Boy to motivate students the past four years.
Previous month in review: [December 2021](/post/2022/01/december-2021)

View File

@ -12,7 +12,7 @@ After plotting the [natural gas prices of each quarter the last five years](/pos
Let's first take a look at another graph (blue, dotted: actual usage in cubic meters divided by ten; green, slashed: absolute total amount to pay yearly, divided by hundred; red, solid: unit price of water consumption on the bill):
![](../waterprice.png)
![](../waterprice.jpg)
I lack the proper numbers for year 2018, which explains the flat line between 2017 and 2018. It is not too clear from the graph, but the raw unit price of water in 2015 was `€1.2066`, and in 2020 `€1.8364`. That's an increase of more than `51%` over five years, which is by itself quite stunning!

View File

@ -12,7 +12,7 @@ tags:
Eight years ago, when our Golden Retriever, Miel, was still a puppy, we wondered what he was up to when we were out working all day. I felt bad having to leave him home alone, with all that energy and social craving. A part of puppy training included bench training, but was he all right with that? Did he try to break out a lot? Was he barking all the time? We didn't know.
Until I cooked up a tiny Node application that periodically takes snapshots using the embedded webcam of my MacBook, streamed to a URL via a websocket. The project [websocket-webcam](https://github.com/wgroeneveld/websocket-webcam) meant I could check up on Miel while at work.
Until I cooked up a tiny Node application that periodically takes snapshots using the embedded webcam of my MacBook, streamed to a URL via a websocket. The project [websocket-webcam](https://git.brainbaking.com/wgroeneveld/websocket-webcam) meant I could check up on Miel while at work.
The result was both a relief and a bit of a disappointment. Miel did nothing but sleep---even our cat joined in and settled for the blankets on top of Miel's bench! It's too bad I lost those captures.

View File

@ -11,7 +11,7 @@ categories:
My websites use both the modern Webmention and conventional Pingback systems to communicate with other websites. I've written about [implementing your own Webmention server](/post/2021/05/beyond-webmention-io/) and [my first IndieWeb experience](/post/2021/03/the-indieweb-mixed-bag/) before (hey, that was about a year ago!), so I won't cover the basics here. I still stand by my decision to support Pingbacks, which is _much_ more popular (Wordpress owns half of the internet).
And herein lies the pickle. More popular obviously also means more prone to spam. IndieWeb enthusiasts are quick to dismiss a working concept and invent their own (instead of posting an XML, you're posting a form. Great progress guys!), also "because Pingback comes with a lot of spam". Unfortunately, I'm here to report that Webmention endpoints also receive their share of shit. That's why I spent all weekend redesigning my [go-jamming Jamstack server](https://github.com/wgroeneveld/go-jamming) to better combat spam.
And herein lies the pickle. More popular obviously also means more prone to spam. IndieWeb enthusiasts are quick to dismiss a working concept and invent their own (instead of posting an XML, you're posting a form. Great progress guys!), also "because Pingback comes with a lot of spam". Unfortunately, I'm here to report that Webmention endpoints also receive their share of shit. That's why I spent all weekend redesigning my [go-jamming Jamstack server](https://git.brainbaking.com/wgroeneveld/go-jamming) to better combat spam.
Go-Jamming converts a Pingback to a Webmention, so internally, they're the same and adhere to the same rules. Once the Pingback XML manages to get decoded, that is---most of the spam I see coming my way comes in the form of malformed XML! Hilarious. Here's the log output of a failing one:
@ -46,4 +46,4 @@ There is one big downside in this system: I'm still confronted by the spam. A me
"So are you ready to remove that stupid `<link rel="pingback"` in your header now, Wouter?" To which I reply: no, I am not. I like the fact that my Go-Jamming server can send and receive Pingbacks, and I've had a few genuinely interesting interactions that way. I suppose micro-bloggers that regularly post links to (Wordpress) sites will also appreciate it.
If you are interested in joining the IndieWeb community or enabling Webmentions (and Pingbacks!), but don't want to either be dependent on [Webmention.io](https://webmention.io/) or implement your own server, please [give Go-Jamming a "go"](https://github.com/wgroeneveld/go-jamming) (ha!). It passes all the [Webmention Rocks!](https://webmention.rocks/) tests and I've put in a lot of effort to make running it effortless, there's a `README` and `INSTALL` instruction on GitHub, and if you're stuck, please let me know!
If you are interested in joining the IndieWeb community or enabling Webmentions (and Pingbacks!), but don't want to either be dependent on [Webmention.io](https://webmention.io/) or implement your own server, please [give Go-Jamming a "go"](https://git.brainbaking.com/wgroeneveld/go-jamming) (ha!). It passes all the [Webmention Rocks!](https://webmention.rocks/) tests and I've put in a lot of effort to make running it effortless, there's a `README` and `INSTALL` instruction on there, and if you're stuck, please let me know!

View File

@ -27,7 +27,7 @@ After a bit of trail and error, I found the last 8 bytes of the password being s
**Problem 3**: if we know where the password we _entered_ is stored, can we also figure out the passwords the game will compare it with? That's where GDB's memory access breakpoints come in. Set it to `$DC00` in read-only mode and voila: after pressing start in the password select screen, the debugger breaks at `$7456` in ROM bank 2 with instruction: `ld a, (de)`. Register `de` contains our password pointer.
![](../bgb.png "The password check routine found using the BGB debugger.")
![](../bgb.jpg "The password check routine found using the BGB debugger.")
After breaking my head on the cryptic routines there, I realized I had to scroll up a bit to `$7444` where this specific routine actually starts. You can see that it loads both `$DBFC` (just before where we enter the password in) into `de` and `498A5` into `hl`. Curious! After a lot of `inc` and `ldi` instructions (these increase both pointers, meaning byte per bytes gets read, see the [Game Boy Opcode summary](http://www.devrs.com/gb/files/opcodes.html)), we encounter a couple of `jr nz, x` instructions that form a loop (the check itself). Debugging is annoying because the breakpoint is hit four times per drawn frame.

View File

@ -13,7 +13,7 @@ I don't like running with a smartphone (or music). Even keys can get irritating.
After recharging, my watch still works. Sort of. The GPS locks in quickly (thanks TomTom) and the live stats are still visible. However, as soon as I want to view other data on a computer, I'm out of luck: Nike pulled the plug on the SportWatch ecosystem. Gone are the servers and the syncing software. I wasn't keen on uploading my run data to their server anyway, but the problem is even more severe. The Nike+ Connect software _requires_ a server connection in order to upload GPS and metadata. It's _never_ stored locally!
![](../sportwatch.png)
![](../sportwatch.jpg)
The popularity spike of Strava birthed all sorts of small projects that [automatically export from Nike+ to Strava](https://kodyvajjha.wordpress.com/2016/06/18/exporting-nike-run-data-to-strava-and-as-a-csv-file/). Even if I wanted to use Strava, these are useless nowadays: this exports from the `https://developer.nike.com/services` API, which is defunct. I only found a single GitHub project that attempts to directly pry out the data from the watch itself. Thanks to the closed-source secrets of the Nike software, Dutch university students Leendert van Duijn & Hristo Dimitrov had to listen in on the communication between OS, USB, and Nike server, using BurpSuite, TcpDump, Wireshark, pycap and pyusb. The details can be found in their paper, stored in the [SportWatch hacking GitHub repository](https://github.com/Jurph/sportwatch).

View File

@ -29,13 +29,13 @@ You have basically two options here. Option one: find templates someone else alr
The second option then: create your own custom Game Boy cassette covers that look like this:
![](../cassette-template-gb.png "The Game Boy cassette cover template.")
![](../cassette-template-gb.jpg "The Game Boy cassette cover template.")
You can download the three templates here:
- [For Game Boy](../cassette-template-gb.png);
- [For Game Boy Color](../cassette-template-gbc.png);
- [For Game Boy Advance](../cassette-template-gba.png).
- [For Game Boy](../cassette-template-gb.jpg);
- [For Game Boy Color](../cassette-template-gbc.jpg);
- [For Game Boy Advance](../cassette-template-gba.jpg).
To fill the void, [The Cover Project](http://thecoverproject.net/) is an indispensable resource---most game covers have been scanned in using a high resolution. If all else fails, use a search engine. For the smaller side in-between front and back, I usually paste the logo and fill the color with a primary or secondary color of the front game cover to seamlessly match every side. The shorter flap, the back, matters less to me: as long as the front and middle is okay, I'm happy.

View File

@ -5,6 +5,7 @@ categories:
- software
tags:
- open source
- self-hosted
---
_Give Up GitHub!_---a clear message that the Software Freedom Conservancy started spreading to open source software (FOSS) developers on 29 June via https://giveupgithub.org. Why? The whole story is available on their website, but the gist is that recently, GitHub yet again proved that they don't really care about FOSS, or about the privacy or licensing choices of their users. Their newest product called [GitHub Copilot](https://github.com/features/copilot) is "trained on billions of lines of code, GitHub Copilot turns natural language prompts into coding suggestions across dozens of languages.", as the landing page suggests. That is, they used _your_ code, with or without your consent, and while it used to be FOSS, now it's converted into a closed-source product you have to pay for. If you have a repository hosted on GitHub, I urge you to read what the Conservancy has to say about this.
@ -37,7 +38,9 @@ What are the alternatives? There are a couple of viable options, but none of the
I have two Gitea instances running: one on our internal NAS, and one on our external VPS. I initially didn't want to install a second one but the VPS needs `git` access to my sites as part of the build workflow. The VPS is essentially a web server, not a source code or integration server, and mixing responsibilities is never a great idea. Yet it still had ample juice left and since my projects are neither rocket science nor very popular, I figured I'd spin up a second Gitea instance. It currently holds about `820 MB` on data, which is still manageable to backup. A tarball is pulled weekly from the VPS into the NAS---that's good enough, as most important repositories are checked out anyway on different computers here.
The biggest downside of giving up GitHub---and especially self-hosting---is that you're essentially leaving (yet another) social media. How are potential contributors going to find you? GitHub is explored, scoured, and rummaged through daily. Source Hut and Codeberg are doing their best to build a community. Federated Gitea systems do not exist, and by that I don't mean the `git` protocol itself.
The biggest downside of giving up GitHub---and especially self-hosting---is that you're essentially leaving (yet another) social media. How are potential contributors going to find you? GitHub is explored, scoured, and rummaged through daily. Source Hut and Codeberg are doing their best to build a community. Federated Gitea systems do not exist[^nex], and by that I don't mean the `git` protocol itself.
[^nex]: Apparently, [it's being worked at](https://github.com/go-gitea/gitea/issues/18240), nice! I'm really happy to see that many cool things forthcoming. More support for importing repository data [from an exported GitHub data blob](https://github.com/go-gitea/gitea/pull/18165) is coming as well, making it possible to host the Gitea source code on... Gitea!
Suppose you want to create a new issue on my Go-Jamming repository, at https://git.brainbaking.com/wgroeneveld/go-jamming/issues. You press "New Issue" and are redirected to the sign-in page. But I don't want you to create an account, and you're likely to pass up on that too. After a bit of config file fiddling, I managed to enable OAuth2 and "Sign In With GitHub". That way, you can simply use your existing account to interact with my source repositories, even if those aren't hosted on GitHub anymore. Behind the screens, Gitea automatically creates a user in its local DB and links your GitHub profile to it.
@ -51,6 +54,13 @@ DISABLE_REGISTRATION = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = true
ENABLE_CAPTCHA = false
REQUIRE_SIGNIN_VIEW = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = false
[admin]
DISABLE_REGULAR_ORG_CREATION = true
[repository]
MAX_CREATION_LIMIT = 0
[oauth2_client]
REGISTER_EMAIL_CONFIRM = false
@ -79,3 +89,5 @@ Migrating the repositories themselves was dead easy: `git remote set-url origin
I guess it'll be up to me now to promote my own coding work, instead of letting GitHub/Microsoft do that. Oh noes, all those lost stars and followers! What shall I do?
Yo check it out: https://git.brainbaking.com/. Contributions welcome!
Addendum `21:26`: [Mykal Machon](https://mykal.codes/) hacked my Gitea instance! I seemed to have forgotten to disable repository/organization creation for new users (that are created via the OAuth sign-in). What a nasty surprise, thanks so much Mykal for reaching out! The solution was found in this [Can new users be restricted to issues only?](https://discourse.gitea.io/t/can-new-users-be-restricted-to-issues-only/1045/3) forum post.

View File

@ -9,7 +9,7 @@ tags:
In my [The Modern QR Code Life](/post/2022/06/the-modern-qr-code-life) rant, I briefly mentioned _Farm Girl_ in Notting Hill, London, as a hipster place we ended up in four Saturday mornings ago, in pursuit of a good bowl of porridge. It turned out that _Good_ was an understatement: it was superbly splendidly amazing. The image below is lifted from the QR-code powered online menu [at arch2order.com](https://o-farmgirl.arch2order.com/menu/notting-hill):
![](../porridge.png "Description: Cherry Ripe Porridge (GF, VG) gluten free oats, coconut milk, desiccated coconut, sour cherry jam, cacao nibs & coconut yoghurt. Allergens: Contains soya and tree nuts.")
![](../porridge.jpg "Description: Cherry Ripe Porridge (GF, VG) gluten free oats, coconut milk, desiccated coconut, sour cherry jam, cacao nibs & coconut yoghurt. Allergens: Contains soya and tree nuts.")
To get to the more nourishing items on the menu, one of course has to scroll past the fancy coffees, lattes, teas, and pastries. As I tried grabbing the URL of the image to include here, I couldn't believe what the Network Inspector reported: by then, your browser has downloaded an eye-watering `52 MB` of content. Fifty-Two. Mega. Bytes. What. The. Fuck? Each beautifully arranged photograph costs a sloppy `5+ MB`, while the images themselves are contained within a `100x100 px` `<div/>`. This is sickening, worrying, and makes me angry at so many levels. The same is true in mobile mode---so when I was dutifully using my international mobile plan in their bar, looking at something to eat, my smartphone already gobbled up fifty-plus megabytes of crap.

View File

@ -2,7 +2,7 @@
title: Go-jamming
---
This is a [Webmention Rocks!](https://webmention.rocks/) test page for the project [Go-jamming](/go-jamming/), which is a Go-based microservice that helps your Jamstack in doing IndieWeb stuff and more. For more information, visit the [GitHub source page](https://github.com/wgroeneveld/go-jamming).
This is a [Webmention Rocks!](https://webmention.rocks/) test page for the project [Go-jamming](/go-jamming/), which is a Go-based microservice that helps your Jamstack in doing IndieWeb stuff and more. For more information, visit the [go-jamming source page](https://git.brainbaking.com/wgroeneveld/go-jamming).
## Endpoint discovery tests

View File

@ -1,111 +0,0 @@
---
title: amchart renderer
draft: true
---
this is a chart:
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<div id="chartdiv" style="width: 100%; height: 800px"></div>
done. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac neque ac justo bibendum condimentum. Ut ac erat non urna elementum cursus. Phasellus vestibulum elementum diam eu accumsan. In justo lectus, lobortis ut mattis ut, iaculis vel massa. Ut dapibus, arcu ut dapibus bibendum, augue massa fringilla velit, nec lobortis felis elit et velit. Aliquam erat volutpat. Mauris non arcu velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Donec odio purus, feugiat sit amet tristique in, tristique sit amet odio. In nisi quam, mollis quis mollis mattis, mattis eu enim. Pellentesque pellentesque eros ut eleifend ultrices. Suspendisse maximus magna vel ipsum consequat porta. Integer mattis sem ut semper condimentum.
Proin gravida vitae tortor eu posuere. Nunc quis pellentesque mauris, ac ultrices turpis. Nulla iaculis, risus nec posuere euismod, risus est efficitur purus, in rutrum nulla est id sapien. Nam venenatis ipsum gravida, feugiat tortor suscipit, tincidunt metus. Phasellus purus nisl, pulvinar tristique tellus id, eleifend fringilla dui. Cras porta pharetra ligula quis tincidunt. Praesent condimentum auctor dolor, nec aliquam enim accumsan non. Nullam congue gravida libero sit amet hendrerit.
<script>
am4core.ready(function() {
am4core.useTheme(am4themes_animated);
function createChart(divid, data) {
var chart = am4core.create(divid, am4charts.XYChart);
chart.exporting.menu = new am4core.ExportMenu();
chart.data = data;
chart.padding(40, 40, 40, 40);
var categoryAxis = chart.yAxes.push(new am4charts.CategoryAxis());
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.dataFields.category = "config";
categoryAxis.renderer.inversed = true;
categoryAxis.renderer.grid.template.disabled = true;
//categoryAxis.renderer.minGridDistance = 100;
categoryAxis.renderer.minWidth = 120;
var valueAxis = chart.xAxes.push(new am4charts.ValueAxis());
valueAxis.min = 0;
var series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.categoryY = "config";
series.dataFields.valueX = "val";
series.tooltipText = "{valueX.value}"
series.columns.template.strokeOpacity = 0;
series.columns.template.column.cornerRadiusBottomRight = 5;
series.columns.template.column.cornerRadiusTopRight = 5;
var labelBullet = series.bullets.push(new am4charts.LabelBullet())
labelBullet.label.horizontalCenter = "left";
labelBullet.fontSize = 20;
labelBullet.label.dx = 5;
labelBullet.label.fill = am4core.color("white");
labelBullet.label.text = "{values.valueX.workingValue}";
labelBullet.locationX = 1;
categoryAxis.sortBySeries = series;
var columnTemplate = series.columns.template;
columnTemplate.adapter.add("fill", function(fill, target) {
return am4core.color("#018660")
})
}
createChart("chartdiv", [{
"config": "Game Boy 1989",
"val": 25
}, {
"config": "Game Boy Pocket 1996",
"val": 10
}, {
"config": "Game Boy Color 1998",
"val": 10
}, {
"config": "Game Boy Advance 2001",
"val": 15
}, {
"config": "Game Boy Advance SP 2003",
"val": 8
}, {
"config": "Game Boy Micro 2005",
"val": 6
}, {
"config": "Nintendo DS 2004",
"val": 11
}, {
"config": "Nintendo DS Lite 2006",
"val": 14
}, {
"config": "Nintendo 3DS 2011",
"val": 5
}, {
"config": "New 3DS XL 2012",
"val": 6
}, {
"config": "Nintendo Switch 2017",
"val": 5
}, {
"config": "Switch Lite 2019",
"val": 7
}, {
"config": "Evercade 2020",
"val": 4
}]
);
}); // end am4core.ready()
</script>

View File

@ -1,10 +0,0 @@
<html>
<head>
<meta http-equiv="Refresh" content="0; url=https://brainbaking.com/teaching/cpp/sigcse2020/" />
</head>
<body>
Redirecting to <a href="https://brainbaking.com/teaching/cpp/sigcse2020/">https://brainbaking.com/teaching/cpp/sigcse2020/</a>...
</body>
</html>

View File

@ -1,4 +1,18 @@
[
{
"author": {
"name": "https://jacky.wtf/about",
"picture": "/pictures/jacky.wtf"
},
"name": "",
"content": "Hand-waving is a bit gracious, no? There's an extension that allows for a flexible form of filtering (one could employ WoT for example) via https://indieweb.org/Vouch. There's also solutions people can use: https://brainbaking.com/post/2022/04/fighti...",
"published": "2022-07-09T19:36:57+00:00",
"url": "https://jacky.wtf/2022/7/vZJk",
"type": "mention",
"source": "https://jacky.wtf/2022/7/vZJk",
"target": "https://brainbaking.com/post/2022/04/fighting-webmention-and-pingback-spam/",
"relativeTarget": "/post/2022/04/fighting-webmention-and-pingback-spam/"
},
{
"author": {
"name": "Wouter Groeneveld",

View File

@ -0,0 +1,10 @@
<html>
<head>
<meta http-equiv="Refresh" content="0; url=https://people.cs.kuleuven.be/~wouter.groeneveld/brainstorm/" />
</head>
<body>
Redirecting to <a href="https://people.cs.kuleuven.be/~wouter.groeneveld/brainstorm/">https://people.cs.kuleuven.be/~wouter.groeneveld/brainstorm/</a>...
</body>
</html>

112
static/cppst/data.json Normal file
View File

@ -0,0 +1,112 @@
{
"attributes": [
"Curiosity",
"Creative State of Mind",
"Creative Techniques",
"Technical Knowledge",
"Communication",
"Constraints",
"Critical Thinking"
],
"questions": [{
"q": "During the project, I really came outside my comfort zone",
"v": [ { "weight": 1, "attribute": "Curiosity" }]
}, {
"q": "Many parts of the project peaked my curiosity",
"v": [ { "weight": 1, "attribute": "Curiosity" }]
}, {
"q": "I liked to really delve into some aspects of the project",
"v": [ { "weight": 1, "attribute": "Curiosity" }]
}, {
"q": "I did not feel the urge to create things",
"v": [ { "weight": 1, "attribute": "Curiosity" }],
"invert": true
},
{
"q": "I remained concentrated for a long time on a part of the project",
"v": [ { "weight": 1, "attribute": "Creative State of Mind" }]
}, {
"q": "I used productivity tools to better concentrate on the essence of the problem (e.g. shortcuts, cmdline tools, ...)",
"v": [ { "weight": 1, "attribute": "Creative State of Mind" }]
}, {
"q": "I did not find the experience very rewarding",
"v": [ { "weight": 1, "attribute": "Creative State of Mind" }],
"invert": true
}, {
"q": "Time seemed to fly by while working",
"v": [ { "weight": 1, "attribute": "Creative State of Mind" }]
},
{
"q": "I approached problems in multiple ways",
"v": [ { "weight": 1, "attribute": "Creative Techniques" }]
}, {
"q": "I did not use knowledge from another domain to solve something",
"v": [ { "weight": 1, "attribute": "Creative Techniques" }],
"invert": true
}, {
"q": "I combined different ideas to solve a problem",
"v": [ { "weight": 1, "attribute": "Creative Techniques" }]
}, {
"q": "I occasionally took deliberate breaks to let things settle in",
"v": [ { "weight": 1, "attribute": "Creative Techniques" }]
},
{
"q": "I gained little knowledge as the project progressed",
"v": [ { "weight": 1, "attribute": "Technical Knowledge" }],
"invert": true
}, {
"q": "I learned new practical techniques and also applied them",
"v": [ { "weight": 1, "attribute": "Technical Knowledge" }]
}, {
"q": "I have gained insight into the problem domain",
"v": [ { "weight": 1, "attribute": "Technical Knowledge" }]
},
{
"q": "I barely asked for feedback from my fellow students",
"v": [ { "weight": 1, "attribute": "Communication" }],
"invert": true
}, {
"q": "I visualized the problem on a white-board or on paper",
"v": [ { "weight": 1, "attribute": "Communication" }]
}, {
"q": "I regularly asked for feedback from my teachers",
"v": [ { "weight": 1, "attribute": "Communication" }]
}, {
"q": "I regularly shared my ideas with others",
"v": [ { "weight": 1, "attribute": "Communication" }]
},
{
"q": "The limitations of the assignment kept me alert",
"v": [ { "weight": 1, "attribute": "Constraints" }]
}, {
"q": "I regularly thought about the correctness of my solution",
"v": [ { "weight": 1, "attribute": "Constraints" }]
}, {
"q": "Due to the time pressure I performed worse",
"v": [ { "weight": 1, "attribute": "Constraints" }],
"invert": true
}, {
"q": "I tried to make my program efficient as possible",
"v": [ { "weight": 1, "attribute": "Constraints" }]
},
{
"q": "I barely questioned the choices made and did not come up with alternatives",
"v": [ { "weight": 1, "attribute": "Critical Thinking" }],
"invert": true
}, {
"q": "I often took multiple options into careful consideration",
"v": [ { "weight": 1, "attribute": "Critical Thinking" }]
}, {
"q": "I often re-evaluated my approach in order to learn from it",
"v": [ { "weight": 1, "attribute": "Critical Thinking" }]
}, {
"q": "I used multiple sources to find out information myself",
"v": [ { "weight": 1, "attribute": "Critical Thinking" }]
}]
}

187
static/cppst/index.html Normal file
View File

@ -0,0 +1,187 @@
<html>
<head>
<script type='text/javascript' src='https://www.amcharts.com/lib/4/core.js?ver=20190614-01'></script>
<script type='text/javascript' src='https://www.amcharts.com/lib/4/charts.js?ver=20190614-01'></script>
<script type='text/javascript' src='https://www.amcharts.com/lib/4/themes/animated.js?ver=20190614-01'></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
h1, h2 {
margin: 2rem;
}
h3 {
font-weight: normal;
font-size: 1.5rem;
}
.question {
padding: 1em;
}
.input label {
display: block;
}
#chartdiv {
width: 100%;
height: 500px;
}
.hide {
display: none;
}
</style>
<title>Creative Programing Problem Solving Test (CPPST)</title>
</head>
<body>
<div class="container">
<h1>Creative Programming Problem Solving Test (CPPST)</h1>
<hr/>
<div id="testmeta">
<p>
Here are a few statements that may or may not be applicable, <em>in context of your project</em>. There are no right or wrong answers, so please just answer honestly. In the end, you'll receive an overview of all CPPST attributes.
</p>
<div id="test">
</div>
<p>
<button type="button" id="calculate" class="btn btn-primary">📈 Calculate Results</button>
<button type="button" id="random" class="btn btn-primary">🍀 I'm Feeling Lucky</button>
</p>
</div>
<div id="chartmeta" class="hide">
<p>
Below you see the results divided in seven unique attributes of the CPPST model, each with a score from 1 to 5.
</p>
<div id='chartdiv'></div>
<h2>Results in Detail</h2>
<p>
Please notice that these results are always <strong>in context of your project</strong>! A low score on an attribute does not mean you are not good at that attribute. It merely suggests that in context of the project, you were less creative. <br/>
</p>
<p>
<h3>1. Curiosity</h3>
Your score: <span class="result" id="result_Curiosity"></span>/5. <br/><br/>
A <em>curious</em> someone is a person that always shows interest in new, unknown things, that likes to come out of his or hers comfort zone and that likes to delve into a subject or to create things. Someone who does not need external motivation.
<br/>
<strong>Curiosity</strong> is the most important factor.
<ul>
<li>Low score: did not learn new things or no will/desire to learn new things</li>
<li>High score: A "hungry" mind. Motivated to look for new things, enriches own world</li>
</ul>
<h3>2. Creative State of Mind</h3>
Your score: <span class="result" id="result_Creative-State-of-Mind"></span>/5. <br/><br/>
Someone with a <em>creative state of mind</em> is a person that faces work with the right attitude such that creativity can flow optimally and time seems to fly by.
<br/><strong>Focus</strong> is the most important factor.
<ul>
<li>Low score: Stays at the surface of problems. Gets interrupted frequently.</li>
<li>High score: Feels freedom and a "flow" during work, goes deep sometimes, has illuminating "aha" moments</li>
</ul>
<h3>3. Creative Techniques</h3>
Your score: <span class="result" id="result_Creative-Techniques"></span>/5. <br/><br/>
Someone who uses <em>creative techniques</em> is someone who knows how exactly to approach problems, and which techniques are involved to get to the best possible solution.
<br/><strong>Technique</strong> is the most important factor.
<ul>
<li>Low score: no idea how to take hurdles. Halted when stuck.</li>
<li>High score: Ideas on how to move forward, especially when stuck.</li>
</ul>
<h3>4. Technical Knowledge</h3>
Your score: <span class="result" id="result_Technical-Knowledge"></span>/5. <br/><br/>
Someone with <em>technical knowledge</em> is someone who has the know-how to tackle the problem at hand: knowledge of programming languages, techniques, willing to continuously improve, and so forth.
<br/><strong>Knowledge</strong> is the most important factor.
<ul>
<li>Low score: no new knowledge gathered or too little knowledge to solve bigger programming problems</li>
<li>High score: builds upon newly acquired knowledge to take on problems easily.</li>
</ul>
<h3>5. Communication</h3>
Your score: <span class="result" id="result_Communication"></span>/5. <br/><br/>
A <em>communicative</em> someone is a person driven by internal or external feedback, that way striving to be able to offer the best solution possible. Someone who actively shares.
<br/><strong>Feedback</strong> is the most important factor.
<ul>
<li>Low score: does not ask for feedback, resulting in suboptimal solutions.</li>
<li>High score: uses fast and regular feedback of others to improve the solution. Shares own ideas, open to serendipity.</li>
</ul>
<h3>6. Constraints</h3>
Your score: <span class="result" id="result_Constraints"></span>/5. <br/><br/>
Someone who can handle <em>constraints</em> is someone who gets the best out of themselves given the limitations that are applied because of internal or external circumstances.
<br/><strong>Context</strong> is the most important factor.
<ul>
<li>Low score: cannot deal with the constraints of the assignment and therefore cannot arrive at a relevant solution.</li>
<li>High score: the context of the assignment can be turned into focus and thus better performance.</li>
</ul>
<h3>7. Critical Thinking</h3>
Your score: <span class="result" id="result_Critical-Thinking"></span>/5. <br/><br/>
A <em>critical</em> someone is a questioner who does not only doubt things, but also comes up with alternatives, compares, considers, and implements. Critical thinking means making well-informed decisions in context of the problem at hand.
<br/><strong>Introspection</strong> is the most important factor.
<ul>
<li>Low score: takes everything for granted without questioning the facts. Loses time to irrelevant things.</li>
<li>High score: Questions requirements and comes up with alternatives. Is willing to change things half-way through.</li>
</ul>
</p>
<p>
<button type="button" id="download" class="btn btn-primary">💾 Download Results</button>
<button type="button" id="restart" class="btn btn-primary">🔄 Restart test</button>
<a id="downloadAnchorElem" style="display:none"></a>
</p>
</div>
<hr/>
<h2>The Paper</h2>
<p>
<a href="https://arxiv.org/pdf/2203.13565.pdf"/>Self-Assessing Creative Problem Solving for Aspiring Software Developers: A Pilot Study</a>.<br/>
The seven attributes are identified in the paper <a href="https://people.cs.kuleuven.be/~wouter.groeneveld/creafocus/">Exploring the Role of Creativity in Software Engineering</a>.
</p>
<hr/>
<br/>
</div>
<script>
window.radios = [ "Completely disagree", "Disagree", "Neither agree nor disagree", "Agree", "Completely agree" ];
</script>
<script src="test.js"></script>
<footer class="page-footer bg-light font-small pt-4" style="border-top: 1px solid lightgray;">
<div class="container">
<div class="row">
<div class="footer-copyright text-center py-3 col-sm">
Wouter Groeneveld, Lynn Van den Broeck, Joost Vennekens, Kris Aerts<br>
<pre>{firstname dot lastname} at kuleuven dot be</pre>
</div>
</div>
</div>
</footer>
</body>

164
static/cppst/test.js Normal file
View File

@ -0,0 +1,164 @@
Object.defineProperty(Array.prototype, 'shuffle', {
value: function() {
for (let i = this.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this[i], this[j]] = [this[j], this[i]];
}
return this;
}
});
(() => {
const unit = 5
const testmeta = document.querySelector('#testmeta')
const chartmeta = document.querySelector('#chartmeta')
let questionData = undefined
const hide = (el) => {
el.style.display = "none"
return el
}
const show = (el) => {
el.style.display = "block"
return el
}
const questionTemplate = (tpl) => {
return `
<div class="question">
<h3>${tpl.i + 1}. ${tpl.q}</h3>
<div class="input" data-q="${tpl.q}">
<label><input type="radio" name="q-${tpl.i}" value="5">&nbsp;&nbsp;${window.radios[4]}</label>
<label><input type="radio" name="q-${tpl.i}" value="4">&nbsp;&nbsp;${window.radios[3]}</label>
<label><input type="radio" name="q-${tpl.i}" value="3">&nbsp;&nbsp;${window.radios[2]}</label>
<label><input type="radio" name="q-${tpl.i}" value="2">&nbsp;&nbsp;${window.radios[1]}</label>
<label><input type="radio" name="q-${tpl.i}" value="1">&nbsp;&nbsp;${window.radios[0]}</label>
</div>
</div>`
}
const downloadResults = () => {
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(window.results))
var dlAnchorElem = document.querySelector('#downloadAnchorElem')
dlAnchorElem.setAttribute("href", dataStr)
dlAnchorElem.setAttribute("download", "results.json")
dlAnchorElem.click()
}
const fillInRandomResults = () => {
document.querySelectorAll('.input').forEach(d => { const r = Math.round(Math.random() * 4) + 1; d.querySelector(`input[value="${r}"]`).click(); })
calculateResults()
}
const calculateResults = () => {
const addResultsInHtml = (data) => {
data.forEach(d => {
const attr = d.attribute.replace(/ /g, '-')
document.querySelector(`#result_${attr}`).innerHTML = d.score;
})
}
const generateAmChart = (data) => {
// expected data: [{attribute: "Curiosity", score: 5}]
am4core.useTheme(am4themes_animated)
const chart = am4core.create("chartdiv", am4charts.RadarChart)
chart.data = data
const categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis())
categoryAxis.dataFields.category = "attribute"
categoryAxis.cursorTooltipEnabled = false
const valueAxis = chart.yAxes.push(new am4charts.ValueAxis())
valueAxis.renderer.axisFills.template.fill = chart.colors.getIndex(2)
valueAxis.renderer.axisFills.template.fillOpacity = 0.05
valueAxis.min = 0
valueAxis.max = unit
const series = chart.series.push(new am4charts.RadarSeries())
series.dataFields.valueY = "score"
series.tooltipText = "[bold]{score}[/]"
//series.fill = am4core.color('black')
series.dataFields.categoryX = "attribute"
series.strokeWidth = 3
chart.cursor = new am4charts.RadarCursor()
chart.cursor.lineY.disabled = true
}
const vals = [0, 1, 2, 3, 4, 5]
const valsInverted = [0, 5, 4, 3, 2, 1]
const convertInputResultsToChartData = () => {
return questionData.attributes.map((attribute, i) => {
const calculatedQs = questionData.questions
.filter(q => q.v.find(v => v.attribute === attribute))
.map(q => {
const checked = document.querySelector(`.input[data-q="${q.q}"] input:checked`)
const value = checked ? parseInt(checked.value) : 0
const weight = q.v.find(v => v.attribute === attribute).weight
return {
weight,
question: q.q,
value: q.invert ? valsInverted[value] : vals[value]
}
})
const totalWeights = calculatedQs
.map(calc => calc.weight * unit)
.reduce((one, two) => one + two, 0)
const totalScore = calculatedQs
.map(calc => calc.weight * calc.value)
.reduce((one, two) => one + two, 0)
const theScore = (totalScore / (totalWeights == 0 ? 1 : totalWeights)) * unit
return {
attribute,
questions: calculatedQs,
score: (theScore === 0 ? 1 : theScore).toFixed(2)
}
})
}
if(document.querySelectorAll('input:checked').length < questionData.questions.length) {
alert("Plese answer all questions first!")
return
}
hide(testmeta)
show(chartmeta)
window.results = convertInputResultsToChartData()
generateAmChart(results)
addResultsInHtml(results)
window.scrollTo(0, 0)
}
const generateQuestions = (data) => {
questionData = data
/* data format:
attributes
questions { q, v [ weight, attrIndex ] } -> totals to 100 parts
*/
let html = ''
data.questions.slice().shuffle().forEach((q, i) => {
html += questionTemplate({
q: q.q,
i: i
})
})
document.querySelector('#test').innerHTML = html;
}
document.querySelector('#download').addEventListener('click', downloadResults)
document.querySelector('#random').addEventListener('click', fillInRandomResults)
document.querySelector('#calculate').addEventListener('click', calculateResults)
document.querySelector('#restart').addEventListener('click', () => {
location.reload()
})
fetch('data.json').then((res) => {
return res.json();
}).then(generateQuestions);
})()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 321 KiB

After

Width:  |  Height:  |  Size: 321 KiB

View File

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Some files were not shown because too many files have changed in this diff Show More