From 397eb9d7c2004c2b65fb3392ff2bfcf2e3fb2931 Mon Sep 17 00:00:00 2001 From: Adrien Fabre Date: Mon, 13 May 2024 18:29:05 +0200 Subject: [PATCH] Prettier on markdown files --- README.md | 14 +- assets/pdf/electronics/readme.md | 14 +- src/coolcovers/all_along_the_watchtower.md | 4 +- src/coolcovers/ca_plane_pour_moi.md | 2 +- src/coolcovers/california_dreaming.md | 1 - src/cv_entries/content/interests.md | 6 +- src/cv_entries/content/introduction.md | 1 - .../education/2014_marne_la_valee.md | 4 +- src/cv_entries/work/2015_enablon.md | 10 +- src/cv_entries/work/2016_nexworld.md | 28 ++-- src/cv_entries/work/2019_dashlane.md | 40 ++--- src/guitar/index.md | 41 ++--- src/music/index.md | 15 +- src/pages/about.md | 14 +- src/pages/apps.md | 7 +- src/pages/ring.md | 30 ++-- src/pedals/index.md | 55 +++--- src/posts/2017/02/GOD_vim.md | 45 ++--- src/posts/2017/09/vim_colorscheme_changer.md | 10 +- src/posts/2019/01/p5-pool.md | 16 +- src/posts/2020/01/p5-genetic-algorithms.md | 36 ++-- src/posts/2020/03/p5-maze.md | 13 +- src/posts/2020/05/p5-cellular-automaton.md | 17 +- src/posts/2020/06/p5-circle-packing.md | 27 ++- src/posts/2020/07/comments.md | 123 +++++++------- .../2020/07/vim_flash_yanked_text/index.md | 51 +++--- src/posts/2020/10/asteroids/asteroids.md | 4 +- src/posts/2020/10/breath/index.md | 8 +- src/posts/2020/11/ants/index.md | 22 +-- src/posts/2021/01/reversi/index.md | 41 ++--- src/posts/2021/01/triomino/index.md | 4 +- .../2021/02/stepping-down-vi-se/index.md | 5 +- .../2021/03/alter_ansible_debugging_output.md | 6 +- .../03/breaking_habits_floating_window.md | 34 ++-- src/posts/2021/03/travis-to-githubactions.md | 54 +++--- src/posts/2021/04/color_picker_shaders.md | 116 +++++++------ .../2021/04/firefox_autohide_bookmarks.md | 63 +++---- .../2021/05/javascript_golf_tips/index.md | 158 +++++++++--------- src/posts/2021/10/race_generator.md | 42 +++-- src/posts/2021/11/eleventy_search_bar.md | 29 ++-- src/posts/2021/11/eleventy_secrets.md | 44 +++-- src/posts/2022/03/dockerizing_dev_env.md | 6 +- src/posts/2022/04/updates_04_22.md | 22 ++- .../04/api_logging_elasticsearch/index.md | 38 ++--- .../04/github_dependabot_auto_merge/index.md | 57 +++---- src/posts/posts.md | 5 +- src/todo/circular_jump.md | 11 +- src/todo/homemade_monitoring.md | 6 +- src/todo/todo_page.md | 6 +- src/todo/weather_station.md | 8 +- src/user_manuals/index.md | 48 +++--- 51 files changed, 730 insertions(+), 731 deletions(-) diff --git a/README.md b/README.md index 351c92230..a8d16405e 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ For now the rest of this readme will probably be my cheatsheet/todo list. # Tools used -- The [newcss](https://newcss.net/) css framework. -- The [eleventy](https://www.11ty.dev/) static site generator. +- The [newcss](https://newcss.net/) css framework. +- The [eleventy](https://www.11ty.dev/) static site generator. # Commands @@ -28,9 +28,9 @@ Github pages serves the `gh-pages` branch. The deployment is automated with Gith In [`.deploy.yml`](https://github.com/statox/blog/blob/master/.github/workflows/deploy.yml) I use the github pages provider which does the following: - - Install everything - - Use `npm run build` to generate the site in the `docs/` directory - - Checkout the result on the `gh-pages` branch +- Install everything +- Use `npm run build` to generate the site in the `docs/` directory +- Checkout the result on the `gh-pages` branch ## Analytics @@ -45,5 +45,5 @@ The template syntax is [nunjucks](https://www.11ty.dev/docs/languages/nunjucks/) ## References -- A blog made with the same tools https://github.com/11ty/eleventy-base-blog -- Eleventy docs https://www.11ty.dev/docs/ +- A blog made with the same tools https://github.com/11ty/eleventy-base-blog +- Eleventy docs https://www.11ty.dev/docs/ diff --git a/assets/pdf/electronics/readme.md b/assets/pdf/electronics/readme.md index ed3a22e8f..a2b50ff7c 100644 --- a/assets/pdf/electronics/readme.md +++ b/assets/pdf/electronics/readme.md @@ -1,12 +1,12 @@ All the files in this directory were found online -- practical_electronic_for_inventors.pdf - - http://instrumentacion.qi.fcen.uba.ar/libro/Scherz.pdf -- practical_electronics_handbook_sixth_edition.pdf - - https://electronicsforfun.weebly.com/uploads/1/7/6/5/17652675/practical_electronics_handbook_sixth_edition.pdf -- a_beginners_guide_to_effect_pedals_components.pdf - - https://guitarpcb.com/wp-content/uploads/2018/07/A-Beginners-Guide-to-Components.pdf +- practical_electronic_for_inventors.pdf + - http://instrumentacion.qi.fcen.uba.ar/libro/Scherz.pdf +- practical_electronics_handbook_sixth_edition.pdf + - https://electronicsforfun.weebly.com/uploads/1/7/6/5/17652675/practical_electronics_handbook_sixth_edition.pdf +- a_beginners_guide_to_effect_pedals_components.pdf + - https://guitarpcb.com/wp-content/uploads/2018/07/A-Beginners-Guide-to-Components.pdf Online resources: -- https://www.electronics-tutorials.ws/ +- https://www.electronics-tutorials.ws/ diff --git a/src/coolcovers/all_along_the_watchtower.md b/src/coolcovers/all_along_the_watchtower.md index 53ce485a0..89c1bd500 100644 --- a/src/coolcovers/all_along_the_watchtower.md +++ b/src/coolcovers/all_along_the_watchtower.md @@ -1,7 +1,7 @@ --- layout: layouts/coolcover.njk tags: ['coolcovers'] -title: "All along the watchtower - Barbara Keith vs Jimi Hendrix vs Bob Dylan" +title: 'All along the watchtower - Barbara Keith vs Jimi Hendrix vs Bob Dylan' --- All along the watchtower is a song written and recorded in 1967 by Bob Dylan when he was spending 18 months recovering from a motorcycle accident. The song has been covered numerous times and the most memorable of these covers is probably the one by the Jimi Hendrix Experience in 68. @@ -10,7 +10,7 @@ This is a simple 3 verses song narrating a very brief slice of life. A joker and While Dylan's version is very straightforward with a simple guitar and an harmonica break between the second and third verse, Hendrix version is much more explicit. The guitar breaks and the increasingly excited drums take a more violent take on the unexplained event which is coming. The quick short beats of the drums illustrate a battle or a revolution of some kind. -Barbara Keith's version is closer to Hendrix's one because it borrows more to the codes of the psychedelic rock from the 60s than to the folk songs of Dylan. I love how the very regular and calm drums of the first two verses illustrate the peaceful walk of our characters heading towards the castle, one can almost picture them on horses calmly walking to an agitated destiny. At the beginning of the third verse the tension grows in a psychedelic explosion worth of the best 60s songs and picturing the agitation in the castle. +Barbara Keith's version is closer to Hendrix's one because it borrows more to the codes of the psychedelic rock from the 60s than to the folk songs of Dylan. I love how the very regular and calm drums of the first two verses illustrate the peaceful walk of our characters heading towards the castle, one can almost picture them on horses calmly walking to an agitated destiny. At the beginning of the third verse the tension grows in a psychedelic explosion worth of the best 60s songs and picturing the agitation in the castle. In the third verse the lyrics says "Outside in the distance a wildcat did growl" and a guitar with a wahwah pedal mimics this growl, this is the kind of usage of this pedal which I really like. After this cool third verse a beautiful guitar solo reminds the end of the Hendrix version and the song could have ended right there leaving us with a beautiful rock cover of a classical folk song... But Barbara Keith decided to repeat the third verse again. Was it necessary for the song? No. But that let us appreciate her charming voice for a bit longer and we can't blame her for that. diff --git a/src/coolcovers/ca_plane_pour_moi.md b/src/coolcovers/ca_plane_pour_moi.md index 8762facaa..2ba085e95 100644 --- a/src/coolcovers/ca_plane_pour_moi.md +++ b/src/coolcovers/ca_plane_pour_moi.md @@ -1,7 +1,7 @@ --- layout: layouts/coolcover.njk tags: ['coolcovers'] -title: "Ca plane pour moi - The Presidents Of The United States Of America vs. Plastic Bertrand" +title: 'Ca plane pour moi - The Presidents Of The United States Of America vs. Plastic Bertrand' --- When an American punk band from the 90s make a cover of an eccentric Belgian punk from the 70s the result is dumb, messy and a ton of fun! diff --git a/src/coolcovers/california_dreaming.md b/src/coolcovers/california_dreaming.md index aa5c91b25..175b7f6cc 100644 --- a/src/coolcovers/california_dreaming.md +++ b/src/coolcovers/california_dreaming.md @@ -20,4 +20,3 @@ The introduction really translate the melancholic side of the song until the bra California Dreamin' is one of the songs with the most occurrences in my playlist but I think this version is by far my favorite. - diff --git a/src/cv_entries/content/interests.md b/src/cv_entries/content/interests.md index b896b297d..d5a6852aa 100644 --- a/src/cv_entries/content/interests.md +++ b/src/cv_entries/content/interests.md @@ -3,7 +3,7 @@ tags: custom title: Interests --- -- Pratique de la guitare seul et en groupe depuis 15 ans. -- Pratique reguliere du Yoga Vinyasa. +- Pratique de la guitare seul et en groupe depuis 15 ans. +- Pratique reguliere du Yoga Vinyasa. -- TODO: Add side projects +- TODO: Add side projects diff --git a/src/cv_entries/content/introduction.md b/src/cv_entries/content/introduction.md index b6e90a852..a1692a60d 100644 --- a/src/cv_entries/content/introduction.md +++ b/src/cv_entries/content/introduction.md @@ -4,4 +4,3 @@ title: About Me --- Ingénieur logiciel a Paris. Orienté développement serveur et gestion de l'infrastructure selon la philosophie DevOps. - diff --git a/src/cv_entries/education/2014_marne_la_valee.md b/src/cv_entries/education/2014_marne_la_valee.md index 08c0dd5fa..1e44ef968 100644 --- a/src/cv_entries/education/2014_marne_la_valee.md +++ b/src/cv_entries/education/2014_marne_la_valee.md @@ -1,6 +1,6 @@ --- -title: "Licence sciences et technologies : mathématiques et informatique" -organization: "Partenariat Efrei/Université Paris-Est Marne-La-Vallée" +title: 'Licence sciences et technologies : mathématiques et informatique' +organization: 'Partenariat Efrei/Université Paris-Est Marne-La-Vallée' end: 2014-07-01 --- diff --git a/src/cv_entries/work/2015_enablon.md b/src/cv_entries/work/2015_enablon.md index dee369745..0461b1ae5 100644 --- a/src/cv_entries/work/2015_enablon.md +++ b/src/cv_entries/work/2015_enablon.md @@ -1,12 +1,12 @@ --- -title: "Ingénieur Qualité Junior" +title: 'Ingénieur Qualité Junior' organization: Enablon organizationUrl: https://www.linkedin.com/company/enablon/ start: 2015-04-01 end: 2015-09-01 --- -- Développement de tests d’API et de tests d’intégration au sein des applications -- Etude sur le Framework de test intégré aux applications -- Analyse et production de code en langage propriétaire Nabsic -- Echange avec l’équipe de d’Assurance Qualité à Chicago +- Développement de tests d’API et de tests d’intégration au sein des applications +- Etude sur le Framework de test intégré aux applications +- Analyse et production de code en langage propriétaire Nabsic +- Echange avec l’équipe de d’Assurance Qualité à Chicago diff --git a/src/cv_entries/work/2016_nexworld.md b/src/cv_entries/work/2016_nexworld.md index 3e4b3b048..3564cfe46 100644 --- a/src/cv_entries/work/2016_nexworld.md +++ b/src/cv_entries/work/2016_nexworld.md @@ -1,5 +1,5 @@ --- -title: "Consultant" +title: 'Consultant' organization: Nexworld organizationUrl: https://nexworld.fr/ start: 2016-02-01 @@ -8,19 +8,19 @@ end: 2019-02-01 Développeur Full Stack, Elis, 2 ans -- Réalisation et maintenance du système de création de devis et de rémunération des commerciaux Elis. -- Développement backend Java (Spring/Hibernate/Maven) -- Développement mobile et frontend (Cordova/AngularJS) -- Conception et développement d’une application desktop (NW.js/AngularJS) -- Conception et recette des flux ESB liés aux backends -- Accompagnement et formation des nouveaux développeurs dans l’équipe -- Configuration et utilisation d'outils d’intégration continue (Git/Jenkins/Nexus/Sonarr) -- Administration système (CentOS/tomcat/postgres/docker) +- Réalisation et maintenance du système de création de devis et de rémunération des commerciaux Elis. +- Développement backend Java (Spring/Hibernate/Maven) +- Développement mobile et frontend (Cordova/AngularJS) +- Conception et développement d’une application desktop (NW.js/AngularJS) +- Conception et recette des flux ESB liés aux backends +- Accompagnement et formation des nouveaux développeurs dans l’équipe +- Configuration et utilisation d'outils d’intégration continue (Git/Jenkins/Nexus/Sonarr) +- Administration système (CentOS/tomcat/postgres/docker) Consultant API Management, Sanofi, Engie, Caisse d'épargne, 1 an -- Installation de CA API Management Gateway et Portail Développeur -- Développements des expositions de webservices sur des plateformes d’API Management -- Développements des policies de sécurité et scripts de migration API Management -- Rédaction des documents d’architectures et d’exploitation (Anglais et Français) -- Rédaction de guides de bonnes pratiques d'administration et de développement (Anglais et Français) +- Installation de CA API Management Gateway et Portail Développeur +- Développements des expositions de webservices sur des plateformes d’API Management +- Développements des policies de sécurité et scripts de migration API Management +- Rédaction des documents d’architectures et d’exploitation (Anglais et Français) +- Rédaction de guides de bonnes pratiques d'administration et de développement (Anglais et Français) diff --git a/src/cv_entries/work/2019_dashlane.md b/src/cv_entries/work/2019_dashlane.md index 0f95b6f7a..469b125fc 100644 --- a/src/cv_entries/work/2019_dashlane.md +++ b/src/cv_entries/work/2019_dashlane.md @@ -1,5 +1,5 @@ --- -title: "Senior Software Engineer" +title: 'Senior Software Engineer' organization: Dashlane organizationUrl: https://www.dashlane.com/ start: 2019-02-04 @@ -7,25 +7,25 @@ start: 2019-02-04 Server Squad - Développeur et scrum master -- Migration de notre cluster de monitoring Elasticsearch 5 vers Elasticsearch 7 - - Création des scripts Terraform et Ansible pour créer et déployer les machines - - Configuration du nouveau cluster Elasticsearch - - Configuration de l'ingestion des logs vers differents index via logstash - - Configuration de Kibana pour les différents utilisateurs - - Gestion de la phase de migration entre les deux clusters -- Migration de notre BDD MySQL entre deux comptes AWS - - Création de la nouvelle instance MySQL et de ses security groups via Terraform - - Création et test de la réplication entre les instances MySQL - - Test du bon fonctionnement des applications avec la nouvelle BDD - - Réalisation de la migration, temps d'indisponibilité < 5mn -- En tant que Scrum Master - - Présentation de la review aux stackholders a chaque sprint - - Mise en place de cérémonies régulières (Rétrospectives, refinement, planning) - - Gestion des priorités des requêtes externes (support utilisateurs, équipes clients, équipe sécurité, ...) +- Migration de notre cluster de monitoring Elasticsearch 5 vers Elasticsearch 7 + - Création des scripts Terraform et Ansible pour créer et déployer les machines + - Configuration du nouveau cluster Elasticsearch + - Configuration de l'ingestion des logs vers differents index via logstash + - Configuration de Kibana pour les différents utilisateurs + - Gestion de la phase de migration entre les deux clusters +- Migration de notre BDD MySQL entre deux comptes AWS + - Création de la nouvelle instance MySQL et de ses security groups via Terraform + - Création et test de la réplication entre les instances MySQL + - Test du bon fonctionnement des applications avec la nouvelle BDD + - Réalisation de la migration, temps d'indisponibilité < 5mn +- En tant que Scrum Master + - Présentation de la review aux stackholders a chaque sprint + - Mise en place de cérémonies régulières (Rétrospectives, refinement, planning) + - Gestion des priorités des requêtes externes (support utilisateurs, équipes clients, équipe sécurité, ...) Mission team "Synchronisation" - Développeur -- Participation aux spécifications et développement de nouveaux flots d'authentification -- Analyse de l'architecture existante de la synchronisation et des problèmes de performances -- Résolution des principaux bugs de la synchronisation pour en améliorer la fiabilité -- Échanges constants avec les équipes de développeurs clients +- Participation aux spécifications et développement de nouveaux flots d'authentification +- Analyse de l'architecture existante de la synchronisation et des problèmes de performances +- Résolution des principaux bugs de la synchronisation pour en améliorer la fiabilité +- Échanges constants avec les équipes de développeurs clients diff --git a/src/guitar/index.md b/src/guitar/index.md index 04facbdee..e0a6972a7 100644 --- a/src/guitar/index.md +++ b/src/guitar/index.md @@ -1,33 +1,32 @@ --- layout: layouts/base.njk eleventyNavigation: - key: Guitar - parent: Music + key: Guitar + parent: Music --- ## Guitar I'm using this page to gather resources that I've found useful while learning music. Maybe one day I will organize that properly to make it easier to read for anyone who is not me. - ### Chords and staffs - - https://www.hooktheory.com/theorytab +- https://www.hooktheory.com/theorytab ### Music theory - Scales, Chords, Fretboard...
Scales and chords visualization tools - - [JGuitar scales, chords, harmonizers](https://jguitar.com/) - - [Guitar scales - Online Guitar Book](https://onlineguitarbooks.com/guitar-scales/) - - [The Guitar Grimoire - Adam Kadmon](http://mikesimm.djlemonk.com/bblog/Scales-and-Modes.pdf) - - [FretFlip](https://fretflip.com/) - - [Fretbo.ar](https://fretbo.ar/) - - [muted.io](https://muted.io/) - - [Triads visualizations](https://triadmapper.herokuapp.com/) - - [All chords diagrams](https://images.template.net/wp-content/uploads/2016/05/05072432/PDF-Document-for-Suspended-Guitar-Chord-Templates.pdf) - - [Keyboard scales](https://www.pianoscales.org/) +- [JGuitar scales, chords, harmonizers](https://jguitar.com/) +- [Guitar scales - Online Guitar Book](https://onlineguitarbooks.com/guitar-scales/) +- [The Guitar Grimoire - Adam Kadmon](http://mikesimm.djlemonk.com/bblog/Scales-and-Modes.pdf) +- [FretFlip](https://fretflip.com/) +- [Fretbo.ar](https://fretbo.ar/) +- [muted.io](https://muted.io/) +- [Triads visualizations](https://triadmapper.herokuapp.com/) +- [All chords diagrams](https://images.template.net/wp-content/uploads/2016/05/05072432/PDF-Document-for-Suspended-Guitar-Chord-Templates.pdf) +- [Keyboard scales](https://www.pianoscales.org/)
@@ -35,6 +34,7 @@ I'm using this page to gather resources that I've found useful while learning mu ![Notes sur le manche](/images/guitar/C_majeur_Entier.png) ![Guitar fingerboard chart](https://i.redd.it/hs2ewp5vznd81.jpg) +
@@ -46,14 +46,15 @@ Position III ![Pentatonique mineur en La - Position III](/images/guitar/gamme-bl Position IV-1 ![Pentatonique mineur en La - Position IV 1](/images/guitar/gamme-blues-de-LA-position-4.png) Position IV-2 ![Pentatonique mineur en La - Position IV 2](/images/guitar/gamme-blues-de-LA-position-4_2.png) Position V ![Pentatonique mineur en La - Position V](/images/guitar/gamme-blues-de-LA-position-5.png) +
CAGED system [Pick up music - The CAGED system explained](https://www.youtube.com/watch?v=-nphFK6HFjY) -
+
Learning to read a staff @@ -63,6 +64,7 @@ Position V ![Pentatonique mineur en La - Position V](/images/guitar/gamme-blues- ![Points de reperes](/images/guitar/lire-les-notes-points-de-repere-clef-de-sol.webp) Mnemonic in French: +
-
iOS applications - - [Solfa](https://apps.apple.com/us/app/solfa-learn-musical-notes/id1436741307) Learning to read notes - - [Hearing](https://apps.apple.com/us/app/hearing-ear-training-piano/id1474241582) Learning to identify notes by hear +- [Solfa](https://apps.apple.com/us/app/solfa-learn-musical-notes/id1436741307) Learning to read notes +- [Hearing](https://apps.apple.com/us/app/hearing-ear-training-piano/id1474241582) Learning to identify notes by hear
- - ### Chords progressions and licks #### Common chord progressions +
Common chords progressions
@@ -101,6 +101,7 @@ G13 - 3x344x Cmin7 - x3534x Cmin9 - x3133x ``` +
@@ -125,6 +126,7 @@ Gmin9 - x 10 8 10 10 x Ebmaj7 - x 6 8 7 8 6 Ebmin7 - x 6 8 6 7 6 ``` +
@@ -135,6 +137,7 @@ C#min7 - x46454 G#min7 - 4x444x F#min7 - x24222 ``` +
#### Blues diff --git a/src/music/index.md b/src/music/index.md index d98d3af46..b58fff5af 100644 --- a/src/music/index.md +++ b/src/music/index.md @@ -1,8 +1,8 @@ --- layout: layouts/base.njk eleventyNavigation: - key: Music - order: 5 + key: Music + order: 5 CSSFile: 'css/pages/music.css' --- @@ -14,11 +14,11 @@ When I'm coding I listen to music and when I'm not coding I try to play some mus A list of useful apps I created for my music playing: -- [Song Book](https://apps.statox.fr/songbook) A list of song chords I took from the internet or made by myself and like to play -- [Metronome](https://apps.statox.fr/metronome) A metrone which works just as I want -- [Tap Tempo](https://apps.statox.fr/taptempo) A Tap Tempo app -- [Chord wheel](https://apps.statox.fr/chordwheel) The online implementation of a book by [Jim Flesser](https://chordwheel.com/) -- [Scales visualizer and chords progressions](https://apps.statox.fr/scales) A tool I made to learn scale degrees +- [Song Book](https://apps.statox.fr/songbook) A list of song chords I took from the internet or made by myself and like to play +- [Metronome](https://apps.statox.fr/metronome) A metrone which works just as I want +- [Tap Tempo](https://apps.statox.fr/taptempo) A Tap Tempo app +- [Chord wheel](https://apps.statox.fr/chordwheel) The online implementation of a book by [Jim Flesser](https://chordwheel.com/) +- [Scales visualizer and chords progressions](https://apps.statox.fr/scales) A tool I made to learn scale degrees ### [Production](https://soundcloud.com/statox/tracks) @@ -43,4 +43,3 @@ The user manuals for my gear as well as some PDF documents about music and elect One of my weird obsession is cover songs. I try to write a bit about my discoveries. [Cool covers page](/coolcovers/) - diff --git a/src/pages/about.md b/src/pages/about.md index bfa126c61..c4ca9aa4b 100644 --- a/src/pages/about.md +++ b/src/pages/about.md @@ -1,11 +1,11 @@ --- layout: layouts/base.njk title: About -permalink: "/about/" -CSSFile: "css/pages/about.css" +permalink: '/about/' +CSSFile: 'css/pages/about.css' eleventyNavigation: - key: About - order: 6 + key: About + order: 6 --- ### Hello @@ -20,9 +20,9 @@ This website is just a place for me to talk a bit about my side projects and kee You can email me at me AT statox DOT fr or reach out to me on any of these social platforms: -- [Github](https://github.com/statox/) - Where I host my side projects and experiments. -- [vi.stackexchange](https://vi.stackexchange.com/users/1841/statox) - Where I've been a moderator for a few years and try to be helpful to other Vim users. -- [Linked in](https://www.linkedin.com/in/adrien-fabre-7a30994b/) - Where you can contact me if you want to. +- [Github](https://github.com/statox/) - Where I host my side projects and experiments. +- [vi.stackexchange](https://vi.stackexchange.com/users/1841/statox) - Where I've been a moderator for a few years and try to be helpful to other Vim users. +- [Linked in](https://www.linkedin.com/in/adrien-fabre-7a30994b/) - Where you can contact me if you want to. ### Technology on this site diff --git a/src/pages/apps.md b/src/pages/apps.md index 675525ba3..63b84dbbd 100644 --- a/src/pages/apps.md +++ b/src/pages/apps.md @@ -1,10 +1,10 @@ --- layout: layouts/base.njk title: Apps -permalink: "/apps/" +permalink: '/apps/' eleventyNavigation: - key: Apps - order: 4 + key: Apps + order: 4 --- ## Stuff I use @@ -14,4 +14,3 @@ At one point I got limited by the fact that this blog is statically generated an So I setup [apps.statox.fr](https://apps.statox.fr) which is a collection of web app powered by [SvelteJS](https://svelte.dev/) I made for my own use. They are probably not useful to any one else than me but have a look you might find something interesting. For example this blog used to have a `/notes` page which I used to take notes of various commands and code snippets I found convenient to have available, this page now lives [here](https://apps.statox.fr/notes). I also moved [my songbook](https://apps.statox.fr/songbook) over there and I keep this collection of apps growing. - diff --git a/src/pages/ring.md b/src/pages/ring.md index 459253b25..a802ba869 100644 --- a/src/pages/ring.md +++ b/src/pages/ring.md @@ -1,25 +1,25 @@ --- layout: layouts/base.njk title: Ring -permalink: "/ring/" +permalink: '/ring/' eleventyNavigation: - key: Ring - order: 7 + key: Ring + order: 7 --- ### Stuff to share Some blogs and personal websites I like to read regularly (or just every once in a while) and sometimes use as an inspiration for some features on this site: -- https://25.wf -- https://arp242.net -- https://benknoble.github.io -- https://www.galaxykate.com/ -- https://jvns.ca -- https://kevq.uk/ -- https://nicolas-hoizey.com -- http://rachelbythebay.com/w/ -- https://sericaia.me -- https://tonycodes.com -- https://tonsky.me/ -- https://uglyduck.ca/ +- https://25.wf +- https://arp242.net +- https://benknoble.github.io +- https://www.galaxykate.com/ +- https://jvns.ca +- https://kevq.uk/ +- https://nicolas-hoizey.com +- http://rachelbythebay.com/w/ +- https://sericaia.me +- https://tonycodes.com +- https://tonsky.me/ +- https://uglyduck.ca/ diff --git a/src/pedals/index.md b/src/pedals/index.md index 4a6236c4d..de8aba9d2 100644 --- a/src/pedals/index.md +++ b/src/pedals/index.md @@ -1,8 +1,8 @@ --- layout: layouts/base.njk eleventyNavigation: - key: Pedals - parent: Music + key: Pedals + parent: Music --- ## Pedals @@ -11,43 +11,43 @@ A work in progress list of resources for the effect pedals I use in my pedalboar ![My pedalboard (front) in Jan. 2024](/images/pedals/pedalboard_02_2024_front.jpg) ![My pedalboard (back) in Jan. 2024](/images/pedals/pedalboard_02_2024_back.jpg) +
A picture of my pedalboard as of January 2024.
Some stats: -- 20 Jack SquarePlug SP400 Mono hand-soldered by me -- ~2m50 of patch cable -- ~4m of velcro tape -- ~5m of pre-made black electric cable -- ~1m50 of transparent electric cable hand-soldered by me to power the OC-3, BO100, Dark Mouse and Instant Lofi Junky in parallel from one 100mA output +- 20 Jack SquarePlug SP400 Mono hand-soldered by me +- ~2m50 of patch cable +- ~4m of velcro tape +- ~5m of pre-made black electric cable +- ~1m50 of transparent electric cable hand-soldered by me to power the OC-3, BO100, Dark Mouse and Instant Lofi Junky in parallel from one 100mA output The power supply is a [Donner DP-2](https://fr.audiofanzine.com/alimentation-pedale/donner/dp-2-power-supply/) I can't find a lot of references to it online but I'm quite happy with it: It is very quiet and just does the job. - Signal chain: -| Brand | Model | Description | Manual -|-------|-------|-------------|------- -| Peterson | Strobostomp HD | Tuner and mute button | [Peterson](https://www.petersontuners.com/media/pdf/StroboStomp_HD_Manual_v1.1_EN.pdf) (_[mirror](/pdf/pedal_user_manuals/StroboStomp_HD_Manual_v1.1_EN.pdf)_) -| Boss | OC-3 | Octaver. Bass and low synth sounds | [Roland](https://static.roland.com/assets/media/pdf/OC-3_e01_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_oc3_user_manual.pdf)_) -| Behringer | BO100 | Overdrive. "not a clone" of the Boss BD-2. Overall light overdrive to add some sustain and space. | [Behringer](https://mediadl.musictribe.com/media/sys_master/hed/h3f/8850084069406.pdf) (_[mirror](/pdf/pedal_user_manuals/behringer_bo100_user_manual.pdf)_) -| Donner | Dark Mouse | Op. Amp distortion. Two modes from overdrive to heavy distortion | [Donner](https://cdn.shopify.com/s/files/1/1384/9629/files/EC1178_Dark_Mouse__Manual_V02_190809.pdf?v=1597289663) (_[mirror](/pdf/pedal_user_manuals/donner_dark_mouse_manual.pdf)_) -| ZVEX | Instant Lofi Junky | Vibrato+Compressor. Lofi vibe mixing vibrato, chorus-y sounds with a compressor. | [ZVEX](https://static1.squarespace.com/static/555e332ce4b0577e788c3a16/t/559efae5e4b0da93269a9ffb/1436482277079/ZVEX+Instant+Lo-Fi+Junky+Instructions.pdf) (_[mirror](/pdf/pedal_user_manuals/zvex_instant_lofi_junky_user_manual.pdf)_) -| Boss | CH-1 | Chorus. I haven't found a good way to incorporate it to my sound yet but it's fun to use once in a while | [Roland](https://static.roland.com/assets/media/pdf/CH-1_M_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_ch1_user_manual.pdf)_) -| Electro-Harmonix | Canyon | Delay. A very complete delay pedal from classic delay, to tape and modulation delays. The shimmer mode give a nice wide reverb and the octave mode plays amazingly with the EQD Afterneath | [EHX](https://www.ehx.com/wp-content/uploads/2021/01/canyon-manual.pdf) (_[mirror](/pdf/pedal_user_manuals/ehx_canyon_manual.pdf)_) -| EarthQuaker Devices | Afterneath | A crazy reverb pedal. It's quite hard to describe but creates crazy ambient sounds which reverberate to the infinity. The user manual is worth a read, it approaches the pedal as a role playing game | [EQD](https://static1.squarespace.com/static/57cebe2c03596e075fca5f24/t/656fc46102d950061c2b9d06/1701823585949/EQD-Afterneath-Manual-R2-WEB.pdf) (_[mirror](/pdf/pedal_user_manuals/eqd_afterneath_manual.pdf)_) -| ToneCity | Tiny Spring | A discreet reverb. Always on to add some space to the sound | [device.report](https://device.report/m/2a2f8a0f7cf833579a89c3817b61a29cd74144e0d5414496ffd212cabed59c0c_optim.pdf) (_[mirror](/pdf/pedal_user_manuals/tonecity_tinyspring_manual.jpg)_) -| Boss | RC-3 | Loop station with "start record on play", tap tempo and a drum machine | [Roland](https://static.roland.com/assets/media/pdf/RC-3_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc3_user_manual.pdf)_) -| Boss | RC-10r | Loop station with two parts loop and larger drums than the RC-3 | [Owner's manual](https://static.roland.com/assets/media/pdf/RC-10R_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc10r_user_manual.pdf)_) / [Parameter guide](https://static.roland.com/assets/media/pdf/RC-10R_parameter_eng01_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc10r_parameter_guide.pdf)_) +| Brand | Model | Description | Manual | +| ------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Peterson | Strobostomp HD | Tuner and mute button | [Peterson](https://www.petersontuners.com/media/pdf/StroboStomp_HD_Manual_v1.1_EN.pdf) (_[mirror](/pdf/pedal_user_manuals/StroboStomp_HD_Manual_v1.1_EN.pdf)_) | +| Boss | OC-3 | Octaver. Bass and low synth sounds | [Roland](https://static.roland.com/assets/media/pdf/OC-3_e01_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_oc3_user_manual.pdf)_) | +| Behringer | BO100 | Overdrive. "not a clone" of the Boss BD-2. Overall light overdrive to add some sustain and space. | [Behringer](https://mediadl.musictribe.com/media/sys_master/hed/h3f/8850084069406.pdf) (_[mirror](/pdf/pedal_user_manuals/behringer_bo100_user_manual.pdf)_) | +| Donner | Dark Mouse | Op. Amp distortion. Two modes from overdrive to heavy distortion | [Donner](https://cdn.shopify.com/s/files/1/1384/9629/files/EC1178_Dark_Mouse__Manual_V02_190809.pdf?v=1597289663) (_[mirror](/pdf/pedal_user_manuals/donner_dark_mouse_manual.pdf)_) | +| ZVEX | Instant Lofi Junky | Vibrato+Compressor. Lofi vibe mixing vibrato, chorus-y sounds with a compressor. | [ZVEX](https://static1.squarespace.com/static/555e332ce4b0577e788c3a16/t/559efae5e4b0da93269a9ffb/1436482277079/ZVEX+Instant+Lo-Fi+Junky+Instructions.pdf) (_[mirror](/pdf/pedal_user_manuals/zvex_instant_lofi_junky_user_manual.pdf)_) | +| Boss | CH-1 | Chorus. I haven't found a good way to incorporate it to my sound yet but it's fun to use once in a while | [Roland](https://static.roland.com/assets/media/pdf/CH-1_M_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_ch1_user_manual.pdf)_) | +| Electro-Harmonix | Canyon | Delay. A very complete delay pedal from classic delay, to tape and modulation delays. The shimmer mode give a nice wide reverb and the octave mode plays amazingly with the EQD Afterneath | [EHX](https://www.ehx.com/wp-content/uploads/2021/01/canyon-manual.pdf) (_[mirror](/pdf/pedal_user_manuals/ehx_canyon_manual.pdf)_) | +| EarthQuaker Devices | Afterneath | A crazy reverb pedal. It's quite hard to describe but creates crazy ambient sounds which reverberate to the infinity. The user manual is worth a read, it approaches the pedal as a role playing game | [EQD](https://static1.squarespace.com/static/57cebe2c03596e075fca5f24/t/656fc46102d950061c2b9d06/1701823585949/EQD-Afterneath-Manual-R2-WEB.pdf) (_[mirror](/pdf/pedal_user_manuals/eqd_afterneath_manual.pdf)_) | +| ToneCity | Tiny Spring | A discreet reverb. Always on to add some space to the sound | [device.report](https://device.report/m/2a2f8a0f7cf833579a89c3817b61a29cd74144e0d5414496ffd212cabed59c0c_optim.pdf) (_[mirror](/pdf/pedal_user_manuals/tonecity_tinyspring_manual.jpg)_) | +| Boss | RC-3 | Loop station with "start record on play", tap tempo and a drum machine | [Roland](https://static.roland.com/assets/media/pdf/RC-3_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc3_user_manual.pdf)_) | +| Boss | RC-10r | Loop station with two parts loop and larger drums than the RC-3 | [Owner's manual](https://static.roland.com/assets/media/pdf/RC-10R_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc10r_user_manual.pdf)_) / [Parameter guide](https://static.roland.com/assets/media/pdf/RC-10R_parameter_eng01_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc10r_parameter_guide.pdf)_) | ![My pedalboard in Feb. 2022](/images/pedals/pedalboard_02_2022.jpg) +
A picture of my pedalboard as of February 2022.
- ### Sample settings #### Boss BD-2 (similar to Behringer BO100) @@ -70,15 +70,14 @@ _TODO: Turn that into images and come up with my own sample settings. Current se **Brighter Chorus with commpressed signal** -- Volume: 60% -- Speed: 60% -- Comp/Lofi: 40% -- Tone: 100% -- Depth: 60% +- Volume: 60% +- Speed: 60% +- Comp/Lofi: 40% +- Tone: 100% +- Depth: 60% Important part here is the `Tone` pot. Up: Let high cuts through. Down: Mellow things out. - ### Users manuals See my [user manuals](/user_manuals/) page. diff --git a/src/posts/2017/02/GOD_vim.md b/src/posts/2017/02/GOD_vim.md index 832157a80..d8b144915 100644 --- a/src/posts/2017/02/GOD_vim.md +++ b/src/posts/2017/02/GOD_vim.md @@ -14,40 +14,43 @@ When I write an answer I always try to think about how I can not only help the u Indeed Vim has an amazing built-in help system accessible directly from the editor itself using the command `:h`. So whenever I write my answers I do my best to add the relevant help topics. And I also like to add a link to one of the several online version of the help - - http://vimdoc.sourceforge.net/ That I used at first but the anchoring of the topics were not always deterministic so it was to practical to use. - - https://vimhelp.org/ Which I currently use. - - And even https://neovim.io/doc for neovim users. +- http://vimdoc.sourceforge.net/ That I used at first but the anchoring of the topics were not always deterministic so it was to practical to use. +- https://vimhelp.org/ Which I currently use. +- And even https://neovim.io/doc for neovim users. ### But it's painful + For a long time I just wrote the help command like `:h autocmd-events` in my answers but this was not convenient: - - First I have to do my own research in the help system, like any vimmer would do. - - Then I have to copy the help topic to my clipboard - - In the web interface I have to redundantly type the string `:h` and add the help topic. - - Then because I think new users are more inclined to read the doc if it is available right under their hand I look for the topic on an online version of the help - - Finally I can update my markdown with the right link +- First I have to do my own research in the help system, like any vimmer would do. +- Then I have to copy the help topic to my clipboard +- In the web interface I have to redundantly type the string `:h` and add the help topic. +- Then because I think new users are more inclined to read the doc if it is available right under their hand I look for the topic on an online version of the help +- Finally I can update my markdown with the right link After doing that for a few months and feeling the pain I started creating a short hack in my `.vimrc` to improve this workflow, until the day I read [How can I quickly convert a Vim help tag to a vimhelp.appspot.com link?](https://vi.stackexchange.com/q/4346/1841). This question confirmed that this action was a pain point for other people and that pushed me to create a proper plugin. ### So let's create a plugin! + So I created [GOD.vim](https://github.com/statox/GOD.vim) this is a very simple plugin which goal is to easily get a markdown expression describing a help topic and linking to its online version. The plugin does the following: - - Create a new command `:GOD` which takes the exact same parameter as `:h`. The autcomplete behavior is completely duplicated thanks to [`:h :command-nargs`](http://vimhelp.appspot.com/map.txt.html#%3Acommand-nargs) and [`:h :command-complete`](http://vimhelp.appspot.com/map.txt.html#%3Acommand-complete), see [how I create the command](https://github.com/statox/GOD.vim/blob/bed2a6fe9458284760d6fb5f08495e6579ce69dd/plugin/GOD.vim#L16): - ``` vim - command! -nargs=+ -complete=help GOD call GOD#GetOnlineDoc('vimhelp', ) - ``` - - Open the corresponding help page, parse the help topics under the cursor and get the first one. - - Use this string to build a URL to the relevant help website. - - Use this URL to generate a markdown template of the form `[:h help-topic](https://url-of-the-help.com/escaped-help-topic)` - - Copy this template to the clipboard. +- Create a new command `:GOD` which takes the exact same parameter as `:h`. The autcomplete behavior is completely duplicated thanks to [`:h :command-nargs`](http://vimhelp.appspot.com/map.txt.html#%3Acommand-nargs) and [`:h :command-complete`](http://vimhelp.appspot.com/map.txt.html#%3Acommand-complete), see [how I create the command](https://github.com/statox/GOD.vim/blob/bed2a6fe9458284760d6fb5f08495e6579ce69dd/plugin/GOD.vim#L16): + ```vim + command! -nargs=+ -complete=help GOD call GOD#GetOnlineDoc('vimhelp', ) + ``` +- Open the corresponding help page, parse the help topics under the cursor and get the first one. +- Use this string to build a URL to the relevant help website. +- Use this URL to generate a markdown template of the form `[:h help-topic](https://url-of-the-help.com/escaped-help-topic)` +- Copy this template to the clipboard. I also added some additional features: - - Being able to link either to the vim help or the neovim help using respectively `:GOD` and `:NGOD` - - Being able to have a nicely formatted markdown list if several help topics are given as parameters to the command. + +- Being able to link either to the vim help or the neovim help using respectively `:GOD` and `:NGOD` +- Being able to have a nicely formatted markdown list if several help topics are given as parameters to the command. I think the trickiest part about writing this plugin was to create a pure vimscript encoding function to handle the URLs. Indeed a lot of help topics have some characters which need to be URL encoded. The first version of my encoding function didn't work very well but fortunately I got the help of great developers from the [vi.stackexchange](https://vi.stackexchange.com) community, namely [Luc Hermitte](https://luchermitte.github.io/) and [Martin Tournoij](https://www.arp242.net/) who helped me with different parts of this plugin and in particular who helped me coming up with this function: -``` vim +```vim " Encode url function! s:URLEncode(str) abort " Replace each non hex character of the string by its hex representation @@ -59,8 +62,8 @@ It takes an help topic as a parameter like `/\@<=`, iterates on each characters Here `printf()` is used with the following format: `%%%02x`: - - `%%` is used to insert a litteral `%` character (which is used in URL encoding to specify encoded characters) - - `%02x` is used to get the hex value of the character. `02` is the number of `0` to use for the padding. +- `%%` is used to insert a litteral `%` character (which is used in URL encoding to specify encoded characters) +- `%02x` is used to get the hex value of the character. `02` is the number of `0` to use for the padding. With our example topic `/\@<=` the function returns `%2f%5c%40%3c%3d`. diff --git a/src/posts/2017/09/vim_colorscheme_changer.md b/src/posts/2017/09/vim_colorscheme_changer.md index 699986484..532e1a3d9 100644 --- a/src/posts/2017/09/vim_colorscheme_changer.md +++ b/src/posts/2017/09/vim_colorscheme_changer.md @@ -14,8 +14,8 @@ In a world where Apple has a tool by default on most of its devices to change th To add this feature I made a very simple plugin which takes for variables as its configuration: -- `g:dayTime` and `g:nightTime` which are two arrays used to describe when in the plugin should switch the colorscheme in the morning and in the evening. -- `g:dayColorscheme` and `g:nightColorscheme` which are the names of the colorschemes to use for each time of the day. +- `g:dayTime` and `g:nightTime` which are two arrays used to describe when in the plugin should switch the colorscheme in the morning and in the evening. +- `g:dayColorscheme` and `g:nightColorscheme` which are the names of the colorschemes to use for each time of the day. The source code is on [Github](https://github.com/statox/colorscheme-changer.vim). @@ -27,7 +27,7 @@ This was interesting to create this plugin because I didn't have an opportunity I noticed that my main pain point related to my colorscheme was when I switch vim to the diff mode using [`:h :diffthis`](http://vimhelp.appspot.com/diff.txt.html#%3Adiffthis). I realized that my current colorscheme didn't do as well as the previous one in diff mode. So I came up with a pretty simple trick in my `.vimrc`: -``` vim +```vim " Color configuration try " Define the default colorscheme and the one used in diff mode @@ -47,7 +47,7 @@ I noticed that my main pain point related to my colorscheme was when I switch vi The idea is very simple: Declare two variables holding the names of the colorschemes I want to use in normal mode and in diff mode and define some wrapper commands around diffmode: - - `:DT` (For `DiffThis`) will change the colorscheme and then switch every window to diffmode - - `:DO` (For `DiffOff`) will do the opposite: Switch back to default colorscheme and stop diff mode. +- `:DT` (For `DiffThis`) will change the colorscheme and then switch every window to diffmode +- `:DO` (For `DiffOff`) will do the opposite: Switch back to default colorscheme and stop diff mode. These lines having been [in my `.vimrc`](https://github.com/statox/dotfiles/blob/7fd57caab6f7e610529b976ec45728c650a1322b/vimrc#L381-L403) for a few time now and so far they fit my needs. Maybe in the future I'll also explain with more details how I handle the diff mode in Vim. diff --git a/src/posts/2019/01/p5-pool.md b/src/posts/2019/01/p5-pool.md index 5e7f847f8..a92cb2d43 100644 --- a/src/posts/2019/01/p5-pool.md +++ b/src/posts/2019/01/p5-pool.md @@ -24,13 +24,13 @@ What do I need to do that? First of all I need a table and some balls rolling on it. This is pretty straightforward: a p5.js canvas will be my table, my balls are simple javascript objects with some coordinates, the ability to apply a force on it (i.e. incrementing the coordinates), a friction force to keep the ball from rolling forever and some tests to prevent the balls from falling out of the table. -``` js +```js function Ball(x, y, id, color) { this.pos = new p5.Vector(x, y); this.vel = new p5.Vector(0, 0); this.r = 10; - this.move = function() { + this.move = function () { // Add friction var coefficientOfFriction = 0.99; this.vel.mult(coefficientOfFriction); @@ -41,12 +41,12 @@ function Ball(x, y, id, color) { // Check the limits if (this.pos.x - this.r < 0 || this.pos.x + this.r > W - this.r) { - this.vel.x *= - coefficientOfFriction; + this.vel.x *= -coefficientOfFriction; } if (this.pos.y - this.r < 0 || this.pos.y + this.r > L - this.r) { - this.vel.y *= - coefficientOfFriction; + this.vel.y *= -coefficientOfFriction; } - } + }; } ``` @@ -54,8 +54,8 @@ Easy, right? With that I can apply a force to a ball (as it's velocity) and way Let's add to that a simple function to know if a ball is colliding with another one. To do that we can simply check the distance between two balls is larger than the sum of the balls radius. I came up with something quick and dirty, but mostly dirty. Here I knew I wanted to tag all the balls part of a collision: -``` js -this.isColliding = function(otherBall) { +```js +this.isColliding = function (otherBall) { // Get the distance between the center of the two balls var dx = otherBall.pos.x - this.pos.x; var dy = otherBall.pos.y - this.pos.y; @@ -69,7 +69,7 @@ this.isColliding = function(otherBall) { return true; } return false; -} +}; ``` Having this code I was able to create my pool, put balls on it, apply a force on them and change their color when they hit each other. That is cool... But now what? diff --git a/src/posts/2020/01/p5-genetic-algorithms.md b/src/posts/2020/01/p5-genetic-algorithms.md index df5d64e1f..8033360cf 100644 --- a/src/posts/2020/01/p5-genetic-algorithms.md +++ b/src/posts/2020/01/p5-genetic-algorithms.md @@ -12,7 +12,7 @@ And [the code](https://github.com/statox/p5-genetics) is on Github, it's not bea ### Teaching a machine to do stuff -Once of the funniest things to do with a computer or a piece of hardware and is to make it smart. This has been humankind obsession for many decades now and it's not going to stop anytime soon. Of course there are tons of way to make a piece of silicon smart, but for this project I wanted to explore a very simple category of machine learning algorithms: *The genetic algorithms*. +Once of the funniest things to do with a computer or a piece of hardware and is to make it smart. This has been humankind obsession for many decades now and it's not going to stop anytime soon. Of course there are tons of way to make a piece of silicon smart, but for this project I wanted to explore a very simple category of machine learning algorithms: _The genetic algorithms_. Basically the way genetic algorithms works is the following: You take a bunch of things that you want to make smart, you make them accomplish a task in a random way and you score each of them depending on how well they succeeded at the task. Once they are all done you eliminate the worst ones and you keep the ones which did best. You will then slightly change the remaining ones and make them run the task again. The theory says that your new generation of things should do a bit better than the previous generation. Rinse, repeat and boom after a few generation you have a bunch of things pretty good at doing this one thing you asked them to do. @@ -20,7 +20,7 @@ I love the idea of these algorithms because it is quite simple to understand and ### I want to make a smart robot -So first thing first, I need to create a robot... Here I will make the simplest robot ever: it will be *a square*! All it needs is a position (i.e. a 2 dimensional vector) and a way to change this position. Here is the time to introduce the concept of genes: +So first thing first, I need to create a robot... Here I will make the simplest robot ever: it will be _a square_! All it needs is a position (i.e. a 2 dimensional vector) and a way to change this position. Here is the time to introduce the concept of genes: I am creating a genetic algorithm, so it makes sense that at some point some genes are involved, right? Here the genes of a robot will be the pattern it will follow: This is actually a succession of order saying "Go up" or "Go left" or basically "Move of one position in one of the two dimensions of your plan". Every robot will be created with an array of genes and its life will be devoted to following these instructions. @@ -28,7 +28,7 @@ My robot will also use some additional properties. I will store its initial posi So here is my simple robot: -``` js +```js function Robot(x, y, r, lifespan) { this.initX = x; this.initY = y; @@ -40,16 +40,16 @@ function Robot(x, y, r, lifespan) { this.genes = new Genes(this.lifespan); } -Robot.prototype.show = function() { +Robot.prototype.show = function () { if (this.crashed) { - fill(200, 0, 0) + fill(200, 0, 0); } else if (this.foundTarget) { - fill(0, 200, 0) + fill(0, 200, 0); } else { - fill(0, 0, 200) + fill(0, 0, 200); } rect(this.pos.x, this.pos.y, this.r, this.r); -} +}; ``` Let's notice that it changes its color depending on if it crashed (i.e touched the border of the frame) or if it reached the target. These calculations are made when the robot moves. @@ -58,13 +58,13 @@ Let's notice that it changes its color depending on if it crashed (i.e touched t So having a robot and making it move is good but to create a genetic algorithm I need to use more bots. So I will need to create a crowd of robots all starting at the same position but all with different genes (and that makes each robot unique and beautiful, just like you 🤗): -``` js +```js function Crowd(size) { this.robots = []; this.matingPool = []; this.size = size; - for (var i=0; i In geometry, circle packing is the study of the arrangement of circles (of equal or varying sizes) on a given surface such that no overlapping occurs and so that no circle can be enlarged without creating an overlap. +> In geometry, circle packing is the study of the arrangement of circles (of equal or varying sizes) on a given surface such that no overlapping occurs and so that no circle can be enlarged without creating an overlap. And to quote [the same source](https://en.wikipedia.org/wiki/Kitten) @@ -26,17 +26,12 @@ And to quote [the same source](https://en.wikipedia.org/wiki/Kitten) So for this project I wanted to use some really cute pictures and duplicate them with a bunch of non overlapping circles. - ### Loading the pictures Before packing circles on these cuties we first need to load the images in our p5.js sketch. To do so I created a `reset()` function which will be used each time I need a new image. It's goal is to get the color of each pixels on the image so that we can use the color later on: -``` js -const IMAGES = [ - 'data/kitten1.png', - 'data/kitten2.jpg', - 'data/kitten3.jpg' -]; +```js +const IMAGES = ['data/kitten1.png', 'data/kitten2.jpg', 'data/kitten3.jpg']; function reset() { // Stop calling draw() while we load the picture otherwise we break everything @@ -57,13 +52,13 @@ function reset() { img.loadPixels(); let d = img.pixels.length; - for (let i = 0; i < d; i+=4) { + for (let i = 0; i < d; i += 4) { r = img.pixels[i]; - g = img.pixels[i+1]; - b = img.pixels[i+2]; - a = img.pixels[i+3]; + g = img.pixels[i + 1]; + b = img.pixels[i + 2]; + a = img.pixels[i + 3]; - imgColors.push(color(r, g, b, a)) + imgColors.push(color(r, g, b, a)); } // Start the packing again! @@ -82,13 +77,13 @@ Once we looped through all these values we have an array `imgColors` containing Before we pack the image with circles we need to generate one of them. Here our goal is the following: Return a new circle which does not overlap the others or return nothing (we will handle the failed generations later). So far the algorithm is not very complex: We have a list of existing circles `circles` (Empty at the beginning), we generate a random position `(x, y)` and a radius `r`, we then iterate on the list of existing circles and test if its distance to the newly generated one is larger than the sum of their radius (i.e. they don't overlap). -``` js +```js function newCircle() { let x = random(img.width); let y = random(img.height); let r = random(MAX_INITIAL_SIZE); - const intersection = circles.findIndex(other => { + const intersection = circles.findIndex((other) => { if (dist(x, y, other.x, other.y) < other.r + r) { return true; } @@ -106,7 +101,7 @@ function newCircle() { Now that we can generate one circle let's make a function which tries to generate a given amount of circles so that one iteration will see several ones created: -``` js +```js function newCircles() { let totalNewCircles = NEW_CIRCLES_BY_ITERATION; let remainingAttemps = NEW_CIRCLES_ATTEMPTS; diff --git a/src/posts/2020/07/comments.md b/src/posts/2020/07/comments.md index 3e61bd95e..16ab66840 100644 --- a/src/posts/2020/07/comments.md +++ b/src/posts/2020/07/comments.md @@ -16,12 +16,12 @@ The full source of the script is included in the site repository [here](https:// Here are the important steps to make all of this working: - - Create a dedicated repo [blog-comments](https://github.com/statox/blog-comments/issues) which will contain the issues used to host comments; - - Have the CI run the script each time a new article is published; - - Give the script the ability to create new issues in the blog-comments repo; - - Give each post a unique number representing the issue ID on Github. [Eleventy](https://www.11ty.dev/docs/data-cascade/) provides a simple way to do that; - - Make the script parsing all the published posts, listing the posts without an associated issue and creating the issues; - - Change the posts source code to inject the comments. +- Create a dedicated repo [blog-comments](https://github.com/statox/blog-comments/issues) which will contain the issues used to host comments; +- Have the CI run the script each time a new article is published; +- Give the script the ability to create new issues in the blog-comments repo; +- Give each post a unique number representing the issue ID on Github. [Eleventy](https://www.11ty.dev/docs/data-cascade/) provides a simple way to do that; +- Make the script parsing all the published posts, listing the posts without an associated issue and creating the issues; +- Change the posts source code to inject the comments. ### A quick update @@ -37,16 +37,16 @@ _February 2022 -_ I just found out about [utteranc.es](https://utteranc.es/) whi Running a script at build time with travis-ci is fairly straight forward. I added a new step in the `script` job looking like this: -``` yaml +```yaml script: - - npm run create-issues -- $BASIC_AUTH_HEADER + - npm run create-issues -- $BASIC_AUTH_HEADER ``` `$BASIC_AUTH_HEADER` is a variable which contains the string `github_username:github_password` encoded in base64. It will be used by the script to authenticate its calls to the Github API [using Basic Authentication](https://developer.github.com/v3/#authentication). Travis has [a simple interface](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings) to define this kind of variable. The double dash `--` is used to give a parameter to a npm script defined in `package.json`. In `package.json` I added a new script like this: -``` json +```json "scripts": { "create-issues": "node tools/createIssues.js" } @@ -58,7 +58,7 @@ Committing a simple nodejs script at `tools/createIssues.js` with only a `consol Using the [axios](https://github.com/axios/axios) library I can list the existing issues in the blog-comment repo: -``` javascript +```javascript const Axios = require('axios'); const BASIC_AUTH_HEADER = process.argv[2]; // [0] is "node", [1] is scriptname @@ -70,7 +70,7 @@ const axios = Axios.create({ baseURL: 'https://api.github.com/', headers: { 'User-Agent': 'statox', - 'Authorization': `basic ${BASIC_AUTH_HEADER}` + Authorization: `basic ${BASIC_AUTH_HEADER}` } }); @@ -78,20 +78,22 @@ const axios = Axios.create({ * Get all of the open issues in a github repo */ function getIssues(cb) { - return axios.get( - `repos/${REPO_NAME}/issues` - ).then((response) => { - return cb(null, response.data) - }).catch((error) => { - return cb(error); - }); + return axios + .get(`repos/${REPO_NAME}/issues`) + .then((response) => { + return cb(null, response.data); + }) + .catch((error) => { + return cb(error); + }); } ``` The other items to list are the posts. Here I needed to create a function to recursively list the files in my `src/posts` directory. These files have a header section delimited by `---` strings which are used by eleventy (the static site generator I used for this site). I created a function to parse these header and return a javascript object, that was a quick way to get things done without digging the doc but I'm sure there is a more efficient way to get this data out of eleventy. _(For example I could write this data directly in JS in the posts files)_ With this short function I list all my posts, get their title and the ID of the issue I associated to it and exclude the posts I have not published yet. -``` javascript + +```javascript /* * Iterate through all the files found in the post folder * Read them to get their data section @@ -100,50 +102,59 @@ With this short function I list all my posts, get their title and the ID of the function getPosts(cb) { const files = walkSync('src/posts/'); - async.map(files, (file, cb) => { - return fs.readFile(file, {encoding: 'utf-8'}, (error, content) => { + async.map( + files, + (file, cb) => { + return fs.readFile(file, { encoding: 'utf-8' }, (error, content) => { + if (error) { + return cb(error); + } + + // Only keep the part between the two '---' lines + const postHeader = content.split('---')[1].split('\n'); + return cb(null, convertPostHeader(postHeader)); + }); + }, + (error, results) => { if (error) { return cb(error); } - // Only keep the part between the two '---' lines - const postHeader = content.split('---')[1].split('\n') - return cb(null, convertPostHeader(postHeader)); - }); - }, - (error, results) => { - if (error) { - return cb(error); + // Only keep the published posts + return cb( + null, + results.filter((p) => p.eleventyExcludeFromCollections !== true && p.title) + ); } - - // Only keep the published posts - return cb(null, results.filter(p => p.eleventyExcludeFromCollections !== true && p.title)); - }); + ); } ``` A bit more logic to detect the issues which need to be created and an additional call to the Github API and here is our working script: -``` javascript +```javascript /* * Post a new issue on github */ function createIssue(issue, cb) { if (DRY_RUN) { - console.log('DRY RUN: creating issue', {issue}); + console.log('DRY RUN: creating issue', { issue }); return cb(); } - return axios.post( - `repos/${REPO_NAME}/issues`, - JSON.stringify({ - title: issue.title, + return axios + .post( + `repos/${REPO_NAME}/issues`, + JSON.stringify({ + title: issue.title + }) + ) + .then((response) => { + return cb(null, response.data); }) - ).then((response) => { - return cb(null, response.data) - }).catch((error) => { - return cb(error); - }); + .catch((error) => { + return cb(error); + }); } ``` @@ -153,7 +164,7 @@ I added a `DRY_RUN` variable which comes from how I call the script for testing Using the `commentIssueId` of each post and the following script allows to inject the comments in the comment section: -``` javascript +```javascript // Script to inject comments based on github issues // Shamelessly taken from https://25.wf/posts/2020-06-21-comments.html function domReady(fn) { @@ -168,7 +179,7 @@ async function getComments(url = '') { method: 'GET', mode: 'cors', cache: 'no-cache', - headers: { Accept: 'application/vnd.github.v3.html+json' }, + headers: { Accept: 'application/vnd.github.v3.html+json' } }); return response.json(); } @@ -185,11 +196,11 @@ domReady(() => { commentSection.insertAdjacentHTML( 'beforeend', '
' + - '• ' + comment.user.login + '' + - ' on' + - ' ' + new Date(comment.created_at).toUTCString() + '' + - comment.body_html + - '
', + `• ${comment.user.login}` + + ' on' + + ` ${new Date(comment.created_at).toUTCString()}` + + comment.body_html + + '' ); }); }; @@ -199,15 +210,15 @@ domReady(() => { ### So much time saved! -And that's how I came up with a system which makes me save about one minute every time I publish a new post (which doesn't happen more than a few time a month at best)! It took me about 4 hours to get the whole thing working so according to this famous XKCD... that might have been a bit of a waste of time, but it was fun to do! :sweat_smile: +And that's how I came up with a system which makes me save about one minute every time I publish a new post (which doesn't happen more than a few time a month at best)! It took me about 4 hours to get the whole thing working so according to this famous XKCD... that might have been a bit of a waste of time, but it was fun to do! :sweat_smile: ![XKCD is it worth the time](https://imgs.xkcd.com/comics/is_it_worth_the_time.png) I still have a few more things I want to implement: - - Improving how I handle the data coming from the posts to avoid parsing it myself - - Adding a check to make sure my posts all have unique and sequential issues ID - - Adding a mechanism to update the title of the issues if the title of the post change - - Adding a content to the OP of the issue to have a link to the article +- Improving how I handle the data coming from the posts to avoid parsing it myself +- Adding a check to make sure my posts all have unique and sequential issues ID +- Adding a mechanism to update the title of the issues if the title of the post change +- Adding a content to the OP of the issue to have a link to the article But given the previous XKCD graph I'll see when I have time for that and if I really have a need for it too. diff --git a/src/posts/2020/07/vim_flash_yanked_text/index.md b/src/posts/2020/07/vim_flash_yanked_text/index.md index db1961910..9989c9242 100644 --- a/src/posts/2020/07/vim_flash_yanked_text/index.md +++ b/src/posts/2020/07/vim_flash_yanked_text/index.md @@ -8,7 +8,7 @@ title: Highlighting yanked text with pure vimscript A few days ago I saw [a blog post](https://blog.kdheepak.com/three-built-in-neovim-features.html#highlight-yanked-text) showing a built-in way to highlight yanked text on neovim. The author uses neovim's lua integration combined with the [`:h TextYankPost`](https://neovim.io/doc/user/autocmd.html#TextYankPost) autocommand event: -``` vim +```vim augroup highlightYankedText autocmd! autocmd TextYankPost * silent! lua require'vim.highlight'.on_yank() @@ -29,23 +29,23 @@ In this article I will to relate the main steps I followed to get this feature w First a bit of Vim terminology about highlighting: - - A `pattern` is basically a regular expression which can be used to search for some text. +- A `pattern` is basically a regular expression which can be used to search for some text. - Patterns can be as simple as plain text (e.g. `/TODO` ) or complex regexes with a lot of items as described in [`:h pattern`](http://vimhelp.appspot.com/pattern.txt.html#pattern). + Patterns can be as simple as plain text (e.g. `/TODO` ) or complex regexes with a lot of items as described in [`:h pattern`](http://vimhelp.appspot.com/pattern.txt.html#pattern). - - A `highlighting group` is a named group of highlighting instructions. +- A `highlighting group` is a named group of highlighting instructions. - The [`:highlight`](http://vimhelp.appspot.com/syntax.txt.html#%3Ahighlight) command allows to list the existing groups when given no arguments. It also allows to create new groups or get details about the existing ones. By default both Vim and Neovim have an `IncSearch` highlighting group which we will reuse to highlight our text. You can see what it looks like on your system with the command `:highlight IncSearch` + The [`:highlight`](http://vimhelp.appspot.com/syntax.txt.html#%3Ahighlight) command allows to list the existing groups when given no arguments. It also allows to create new groups or get details about the existing ones. By default both Vim and Neovim have an `IncSearch` highlighting group which we will reuse to highlight our text. You can see what it looks like on your system with the command `:highlight IncSearch` - - Finally a `match` is a way to tell Vim to highlight a specific pattern using a specific highlighting group. +- Finally a `match` is a way to tell Vim to highlight a specific pattern using a specific highlighting group. - A match can be created with [`matchadd()`](http://vimhelp.appspot.com/eval.txt.html#matchadd%28%29). The first argument is the name of an highlighting group as shown in the result of `:highlight` and the second argument is a pattern: + A match can be created with [`matchadd()`](http://vimhelp.appspot.com/eval.txt.html#matchadd%28%29). The first argument is the name of an highlighting group as shown in the result of `:highlight` and the second argument is a pattern: - ```vim - let id = matchadd('IncSearch', 'TODO') - ``` + ```vim + let id = matchadd('IncSearch', 'TODO') + ``` - To delete a match simply use the ID returned by the previous command. Note that this command only works in the window where the match was created, this will be important later on. + To delete a match simply use the ID returned by the previous command. Note that this command only works in the window where the match was created, this will be important later on. ```vim call matchdelete(id) @@ -63,10 +63,10 @@ let g:idTemporaryHighlight = matchadd('IncSearch', "\\%'\\[.*\\%']") The main items of the pattern are the following: - - `'[` is the mark I mentioned before but `[` being a special character (used in [`:h /[]`](http://vimhelp.appspot.com/pattern.txt.html#%2f%5b%5d)) it needs to be escaped hence `'\\[`. Note that each `\` needs to be escaped to be used in the command. - - Given this previous point, `\\%'\\[` is the way to use [`:h /\%'m`](http://vimhelp.appspot.com/pattern.txt.html#%2f%5c%25%27m) with the `'[` mark, matching the beginning of the previously yanked text. - - `.*` allows to match any characters any number of time. - - `\\%']` is the equivalent of the first item with the `']` mark. Note that here `]` doesn't need to be escaped since there is no risk of confusion with [`:h /[]`](http://vimhelp.appspot.com/pattern.txt.html#%2f%5b%5d). +- `'[` is the mark I mentioned before but `[` being a special character (used in [`:h /[]`](http://vimhelp.appspot.com/pattern.txt.html#%2f%5b%5d)) it needs to be escaped hence `'\\[`. Note that each `\` needs to be escaped to be used in the command. +- Given this previous point, `\\%'\\[` is the way to use [`:h /\%'m`](http://vimhelp.appspot.com/pattern.txt.html#%2f%5c%25%27m) with the `'[` mark, matching the beginning of the previously yanked text. +- `.*` allows to match any characters any number of time. +- `\\%']` is the equivalent of the first item with the `']` mark. Note that here `]` doesn't need to be escaped since there is no risk of confusion with [`:h /[]`](http://vimhelp.appspot.com/pattern.txt.html#%2f%5b%5d). This is a great first attempt which kind of works on some simple cases but fails when yanking text on several lines. This is because the [`.`](http://vimhelp.appspot.com/pattern.txt.html#%2f.) atom doesn't match end of lines characters, so we need to use [`\_.`](http://vimhelp.appspot.com/pattern.txt.html#%2f%5c_.) instead: @@ -76,11 +76,11 @@ let g:idTemporaryHighlight = matchadd('IncSearch', "\\%'\\[\\_.*\\%']") That is better but still not completely working, for example the first and last characters of the yanked text are not highlighted. That's the moment where we turn to the doc and read a bit more what [`:h /\%'m`](http://vimhelp.appspot.com/pattern.txt.html#%2f%5c%25%27m) has to say, particularly this: - Example, to highlight the text from mark 's to 'e: - /.\%>'s.*\%<'e.. - Note that two dots are required to include mark 'e in the match. That - is because "\%<'e" matches at the character before the 'e mark, and - since it's a |/zero-width| match it doesn't include that character. + Example, to highlight the text from mark 's to 'e: + /.\%>'s.*\%<'e.. + Note that two dots are required to include mark 'e in the match. That + is because "\%<'e" matches at the character before the 'e mark, and + since it's a |/zero-width| match it doesn't include that character. Easy peasy, let's reuse the same thing but with our marks `'[` and `']`: @@ -88,7 +88,7 @@ Easy peasy, let's reuse the same thing but with our marks `'[` and `']`: let g:idTemporaryHighlight = matchadd('IncSearch', ".\\%>'\\[\\_.*\\%<']..") ``` -*Important note*: Some of this escaping could be greatly simplified using a different magic mode but it's not my point here. For more information on magic in Vim see [`:h /magic`](http://vimhelp.appspot.com/pattern.txt.html#%2fmagic) +_Important note_: Some of this escaping could be greatly simplified using a different magic mode but it's not my point here. For more information on magic in Vim see [`:h /magic`](http://vimhelp.appspot.com/pattern.txt.html#%2fmagic) So after a few tests yanking some random text, using `matchadd` to highlight it and `matchdelete` to remove the highlighting I am satisfied with the result, it is then time to automatically highlight our text. @@ -96,7 +96,7 @@ So after a few tests yanking some random text, using `matchadd` to highlight it Vim provides since it [patch 8.0.1394](https://github.com/vim/vim/commit/7e1652c63c96585b9e2235c195a3c322b1f11595) the [`:h TextYankPost`](http://vimhelp.appspot.com/autocmd.txt.html#TextYankPost) autocommand event which triggers just after a yank or deleting command. So our first step is to create a function triggered by this event: -``` vim +```vim augroup highlightYankedText autocmd! autocmd TextYankPost * call FlashYankedText() @@ -111,8 +111,7 @@ As you can see I put my autocommand in an [`augroup`](http://vimhelp.appspot.com That is great! Each time we yank some text it gets highlighted... but then it remains highlighted indefinitely. So let's simply use a timer to delete the match we just created. Note that the function puts the id of the newly created match in a global variable which is kind of ugly but pretty pratical to access it in the `DeleteTemporaryMatch()` function. - -``` vim +```vim function! FlashYankedText() let g:idTemporaryHighlight = matchadd('IncSearch', ".\\%>'\\[\\_.*\\%<']..") call timer_start(500, 'DeleteTemporaryMatch') @@ -127,8 +126,8 @@ endfunction The previous code kind of works but some edge cases are problematic: -- When I yank two different texts too quickly sometimes the `DeleteTemporaryMatch()` function doesn't have the time to delete the previous match. -- More importantly, when I switch to another window just after yanking some text, `deletematches()` fails because the matches id are local to a window. +- When I yank two different texts too quickly sometimes the `DeleteTemporaryMatch()` function doesn't have the time to delete the previous match. +- More importantly, when I switch to another window just after yanking some text, `deletematches()` fails because the matches id are local to a window. So let's put the ids in a list, with the window id where they were created: diff --git a/src/posts/2020/10/asteroids/asteroids.md b/src/posts/2020/10/asteroids/asteroids.md index 1252fb40f..076426ce0 100644 --- a/src/posts/2020/10/asteroids/asteroids.md +++ b/src/posts/2020/10/asteroids/asteroids.md @@ -6,7 +6,7 @@ commentIssueId: 15 title: Asteroids... with real pew pews --- -Asteroids! The game is as old as the world (well almost, it was [released in 1979](https://en.wikipedia.org/wiki/Asteroids_(video_game))) and is an iconic figure of the golden era of the video game. +Asteroids! The game is as old as the world (well almost, it was [released in 1979]()) and is an iconic figure of the golden era of the video game. ![Original asteroids screenshot](https://upload.wikimedia.org/wikipedia/en/1/13/Asteroi1.png) @@ -18,7 +18,7 @@ So after a few hours of code and some really crappy recording on my phone [here No technological innovation or mind blowing creativity here but the result still amuses me more than I would be willing to admit. I feel like I am re-discovering the early 2000's flash games and that's fun! -To make it a complete game I still implemented some bonuses, like the autopews and the triple pew which allow respectively to continuously shoot at the asteroids and to shoot in 3 directions, as well as some maluses like slowing down the spaceship rotation speed or locking its engine on to force the player to get good at slaloming between the rocks at full speed. I also used my own noise function (actually a wrapper around p5js's `noise()` function) to generate random rocks which *kinda* look like asteroids. +To make it a complete game I still implemented some bonuses, like the autopews and the triple pew which allow respectively to continuously shoot at the asteroids and to shoot in 3 directions, as well as some maluses like slowing down the spaceship rotation speed or locking its engine on to force the player to get good at slaloming between the rocks at full speed. I also used my own noise function (actually a wrapper around p5js's `noise()` function) to generate random rocks which _kinda_ look like asteroids. But let's be real despite all these amazing features the main interest of the game is hearing my dumb "pew pew" and "boum" for five minutes before it gets annoying. diff --git a/src/posts/2020/10/breath/index.md b/src/posts/2020/10/breath/index.md index 0d06a8197..5ca7b8230 100644 --- a/src/posts/2020/10/breath/index.md +++ b/src/posts/2020/10/breath/index.md @@ -26,8 +26,8 @@ The first step was to create a [codepen](https://codepen.io/statox/pen/abNVYZZ) That resulted in some pretty rough animations and a not very well organized code, but at least it allowed me to validate what are the two main components I would need to implement in my project: -- Some kind of state processing to handle the transitions between the "breath in" and "breath out" phases (as well as two additional "pause" phases, which I find more comfortable to add); -- A "pluggable architecture" where I can write independent functions for different animations and only have to plug them in the existing canvas. +- Some kind of state processing to handle the transitions between the "breath in" and "breath out" phases (as well as two additional "pause" phases, which I find more comfortable to add); +- A "pluggable architecture" where I can write independent functions for different animations and only have to plug them in the existing canvas.

See the Pen @@ -46,7 +46,7 @@ The first module I needed to implement was my `StateComputer` which is used to k To do so it has an array of state representing the different steps and how long they should last: -``` javascript +```javascript this.currentStateIndex; // keep track of the current state this.states = [ // durations are in milliseconds @@ -94,7 +94,6 @@ this.updateState = () => { And finally a function call for each frame indicate where we are in percentage of the current step: - ```javascript this.getCurrentPercentage = () => { if (!this.playing) { @@ -182,6 +181,7 @@ function SimpleCircleAlpha() { ``` Here we draw a circle whose radius and color change depending on the progress of the state. + ### Meditative coding Once the skeleton of the app was made (i.e. adding some buttons, a UI based on Vue.js to choose the duration of the session, playing with a "fullscreen" button, some CSS media queries and all the other boring details I didn't include here) all that is left for me to do is to add more animations. And, weirdly, spending time just creating some pleasing visuals on the rythm of a slow breathing really has something close to meditation. diff --git a/src/posts/2020/11/ants/index.md b/src/posts/2020/11/ants/index.md index c9438d2f0..55e82200f 100644 --- a/src/posts/2020/11/ants/index.md +++ b/src/posts/2020/11/ants/index.md @@ -12,8 +12,8 @@ So I came up with [this web page](https://statox.github.io/ants-colony/) which w This kind of posts is probably not very useful nor interesting for anyone other than me, but I see it as some kind of journal that I'll probably be happy to look at one day. - ![Visualization of several food sources](./ants.gif) +

This visualization shows the green pheromone trail being updated for a few different food sources. The ants are not shown. @@ -25,26 +25,26 @@ The idea here is to have a colony of autonomous ants all starting on the same po During its trip an ant can either: - - Find a spot with some food and stop moving; - - Get stuck because it surrounded itself with cells it already walked on; - - Never get stuck and not find food, in this case it will walk until its time to live (the preset duration of the trip) is reached. +- Find a spot with some food and stop moving; +- Get stuck because it surrounded itself with cells it already walked on; +- Never get stuck and not find food, in this case it will walk until its time to live (the preset duration of the trip) is reached. In the case where an ant finds some food, it will go back to the anthill and lay down some amount of pheromones on all the cells it visited during this trip. The amount of pheromones an ant lays down is inversely proportional to the length of its path, meaning that the ant finding the shortest path to food will leave the strongest trail. When it walks an ant will follow the pheromones: Each surrounding case is evaluated and attributed a probability to be chosen depending on its amount of pheromones and whether or not it contains food. The ant will then "throw a dice" and choose a cell following the probabilities and the result of the dice. -For the first few iterations the ants just walk randomly on the grid until one finds some food and start creating a trail. Then for the next iterations the ants will generally follow this trails more or less closely, allowing some ants to find a better solution and gradually optimizing their way. That's what we can observe in the following gif. +For the first few iterations the ants just walk randomly on the grid until one finds some food and start creating a trail. Then for the next iterations the ants will generally follow this trails more or less closely, allowing some ants to find a better solution and gradually optimizing their way. That's what we can observe in the following gif. The cells outlined in a greyish color indicate the cells where some ants walked but didn't found any food, on the first iterations there are a lot of these cells and they get fewer once ants find the food source. In this example the convergence could happen sooner if the time to live of the ants and their attraction to pheromones were tweaked. -![Visualization of ants walking](./ants_walk.gif) *This visualization shows the ants (blue dots) refining the pheromone trail for one food source* +![Visualization of ants walking](./ants_walk.gif) _This visualization shows the ants (blue dots) refining the pheromone trail for one food source_ ### Improvement points There are a few things I want to change in the app: -- Obstacles: For now I am able to say that a cell is an obstacle, and I created a function to create some random obstacles on the grid. That works quite well for now, but I would like to improve that to create actual mazes [as I did in another project](https://www.statox.fr/posts/p5/p5-maze/). This could be done but that would mean changing how I handle the grid and the ants moving. -- Perception radius: The main issue with the current state of the project is that ants have a defined perception radius representing the cells they can see around them. However due to the way I implemented my neighbors search and my obstacles this doesn't work well with radius > 2. +- Obstacles: For now I am able to say that a cell is an obstacle, and I created a function to create some random obstacles on the grid. That works quite well for now, but I would like to improve that to create actual mazes [as I did in another project](https://www.statox.fr/posts/p5/p5-maze/). This could be done but that would mean changing how I handle the grid and the ants moving. +- Perception radius: The main issue with the current state of the project is that ants have a defined perception radius representing the cells they can see around them. However due to the way I implemented my neighbors search and my obstacles this doesn't work well with radius > 2. ### Maybe a V2 @@ -52,8 +52,8 @@ Maybe I will do a v2 of this project. If I do I'll think from the beginning of h I would also like to add some feature which shouldn't be too hard to implement: -- Automatic TTL: The ants could increase their TTL automatically if they don't find anything for a few iterations allowing a more efficient exploration of the map. -- Ant selection: Instead of having all the ants which find food to leave a trail, I could use some min-max algorithm or another way to select the best ant to have only one trail. -- Bees battle: When ants leave a trail when they go back to the anthill, bees remember their position and communicate it to the other insects. This would create a different approach to the path finding algorithm and it would be interesting to see both types of insect running for the same food source and battling to get more than the others. +- Automatic TTL: The ants could increase their TTL automatically if they don't find anything for a few iterations allowing a more efficient exploration of the map. +- Ant selection: Instead of having all the ants which find food to leave a trail, I could use some min-max algorithm or another way to select the best ant to have only one trail. +- Bees battle: When ants leave a trail when they go back to the anthill, bees remember their position and communicate it to the other insects. This would create a different approach to the path finding algorithm and it would be interesting to see both types of insect running for the same food source and battling to get more than the others. In the meantime I still have fun looking at my little insects finding their way in this small virtual and meaningless world :ant: diff --git a/src/posts/2021/01/reversi/index.md b/src/posts/2021/01/reversi/index.md index eeaf86f7a..fb2e8214f 100644 --- a/src/posts/2021/01/reversi/index.md +++ b/src/posts/2021/01/reversi/index.md @@ -11,6 +11,7 @@ commentIssueId: 18 After not finishing my [triomino project](../triomino/) I started working on an implementation of the [Reversi game](https://en.wikipedia.org/wiki/Reversi). According to Wikipedia my implementation is technically an Othello game but this name was patented in Japan in 1971 and I decided to play it safe and calling it Reversi (no doubt that the owner of the name would have felt greatly threatened by my _amazing_ implementation of his game). ![Screenshot of my Reversi implementation](./reversi.png) +
My patent infrigement material
@@ -43,32 +44,32 @@ It turns out that for a game of Othello there are a lot of different possibiliti While I was implementing my different AIs I needed to compare them together to make sure I was heading in the right direction, so in addition of the GUI I also developed a testing program which runs thousands of games and collects some simple statistics. Here are some results: -|Random vs. Random | win percentages | nb of games won| -|:-----------------|----------------:|---------------:| -|Random | 49.11% | 4911 | -|Random | 50.89% | 5089 | +| Random vs. Random | win percentages | nb of games won | +| :---------------- | --------------: | --------------: | +| Random | 49.11% | 4911 | +| Random | 50.89% | 5089 | -|Random vs. Most disks | win percentages | nb of games won| -|:---------------------|----------------:|---------------:| -|Random | 42.82% | 4282 | -|Most disks | 57.18% | 5718 | +| Random vs. Most disks | win percentages | nb of games won | +| :-------------------- | --------------: | --------------: | +| Random | 42.82% | 4282 | +| Most disks | 57.18% | 5718 | -|Random vs. MinMax (depth 3) | win percentages | nb of games won| avg. nodes evaluated by turn| -|:---------------------------|----------------:|---------------:|----------------------------:| -|Random | 26.55% | 478 | - | -|MinMax | 73.44% | 1322 | 12 179 319 | +| Random vs. MinMax (depth 3) | win percentages | nb of games won | avg. nodes evaluated by turn | +| :-------------------------- | --------------: | --------------: | ---------------------------: | +| Random | 26.55% | 478 | - | +| MinMax | 73.44% | 1322 | 12 179 319 | -|Random vs. AlphaBeta (depth 3) | win percentages | nb of games won| avg. nodes evaluated by turn| -|:------------------------------|----------------:|---------------:|----------------------------:| -|Random | 35.33% | 1590 | - | -|AlphaBeta | 64.66% | 2910 | 2 785 672 | +| Random vs. AlphaBeta (depth 3) | win percentages | nb of games won | avg. nodes evaluated by turn | +| :----------------------------- | --------------: | --------------: | ---------------------------: | +| Random | 35.33% | 1590 | - | +| AlphaBeta | 64.66% | 2910 | 2 785 672 | Nothing really surprising here but at least the results seem to be pretty coherent with what I was expecting: -- When two player play randomly they roughly have a 50% rate of victory, at least `Math.random()` seems to be working good enough for this use case -- Trying to always flip as many disks as possible is a bit more efficient than playing randomly, at least if your opponent plays randomly. -- My MinMax implementation is pretty effective against a random user, increasing the maximal depth would make it pretty good. However given the time it takes to run I couldn't run as many games as for the other AIs. -- My AlphaBeta pruning without a good heuristic is just a semi-broken MinMax: it's better than a random player but it prunes some valid nodes which makes it not as efficient as it could be. +- When two player play randomly they roughly have a 50% rate of victory, at least `Math.random()` seems to be working good enough for this use case +- Trying to always flip as many disks as possible is a bit more efficient than playing randomly, at least if your opponent plays randomly. +- My MinMax implementation is pretty effective against a random user, increasing the maximal depth would make it pretty good. However given the time it takes to run I couldn't run as many games as for the other AIs. +- My AlphaBeta pruning without a good heuristic is just a semi-broken MinMax: it's better than a random player but it prunes some valid nodes which makes it not as efficient as it could be. #### Demo diff --git a/src/posts/2021/01/triomino/index.md b/src/posts/2021/01/triomino/index.md index 08c9df5cd..b4c384151 100644 --- a/src/posts/2021/01/triomino/index.md +++ b/src/posts/2021/01/triomino/index.md @@ -21,6 +21,7 @@ As the name of the project suggest I was aiming for an IA mastering the Triomino When I started thinking about this project I had in mind a [great article on redblobgame](http://www-cs-students.stanford.edu/~amitp/game-programming/grids/) about different kind of grids and how to implement them I had read several months ago and thought it would be useful. But being impatient to start coding I went straight in and didn't read it again, probably making some mistakes which I could have avoided. ![Screenshot of my Triomino implementation](./triomino.png) +
Another obvious proof of my UX/UI designer genius
@@ -68,10 +69,9 @@ Things got messy when I finished displaying the triangles and started thinking a One of the main issue was to keep the `Sprite` position synced with its parent object position (mainly because I handled the `Triomino` moves terribly). After some tweaks it ended up working. - #### AI -At first I wanted to go with a clever AI which would use some kind of A* algorithm to find the best possible move to do. However my first AI version was much simpler: Looking for all the possible moves and placing the triomino with the highest score. +At first I wanted to go with a clever AI which would use some kind of A\* algorithm to find the best possible move to do. However my first AI version was much simpler: Looking for all the possible moves and placing the triomino with the highest score. To have a working AI I needed to implement the [complete set of scoring rules](https://www.pressmantoy.com/wp-content/uploads/2018/01/Tri-Ominos.pdf), but I only implemented the basic rule of "when the player places a tile her score is incremented by the sum of the digits on the tile" and the bonus points at the beginning of the game. I didn't implemented the bonus points granted when a shape is completed. diff --git a/src/posts/2021/02/stepping-down-vi-se/index.md b/src/posts/2021/02/stepping-down-vi-se/index.md index 9cb016df3..de94e5da9 100644 --- a/src/posts/2021/02/stepping-down-vi-se/index.md +++ b/src/posts/2021/02/stepping-down-vi-se/index.md @@ -100,7 +100,6 @@ As we can see the community has grown quite a lot, today it drives about 7000 vi As we can see the stats are flirting with the out-of-beta thresholds, even thought I don't think they will reach these thresholds any time soon. As it is the case for a lot of smaller websites dedicated to niche topics on stackexchange, [vi.se](https://vim.stackexchange.com) might very well stay in beta all of its life. And that's great! A smaller community is friendlier and usually easier to moderate which is a nice way to generate some interesting content. - ### Moderator actions Due to the relatively small size of the site, the community is quite easy to moderate. In my 3 years of moderation duty I have encountered only one or two problematic users who required the moderation team to take an action against them. @@ -131,8 +130,8 @@ What about my actual posts? Amongst the 60 questions and 500+ answers I wrote, some of them are more important to me than the others.Firstly, I pride myself in having two of my questions in the Top ~~10~~ 11 [most frequently visited questions](https://vi.stackexchange.com/questions?tab=Frequent): -- [How to debug a mapping](https://vi.stackexchange.com/q/7722/1841) which is a question I decided to create when I saw how common were questions about mappings misunderstandings. I also self answered this question with a completely methodology which is my highest voted answer with 117 votes and around 18k views. -- [What is the difference between the vim snippets plugins?](https://vi.stackexchange.com/q/7466/1841) which was an actual question I had when I tried different snippets plugins (which I ditched from my `.vimrc` since). +- [How to debug a mapping](https://vi.stackexchange.com/q/7722/1841) which is a question I decided to create when I saw how common were questions about mappings misunderstandings. I also self answered this question with a completely methodology which is my highest voted answer with 117 votes and around 18k views. +- [What is the difference between the vim snippets plugins?](https://vi.stackexchange.com/q/7466/1841) which was an actual question I had when I tried different snippets plugins (which I ditched from my `.vimrc` since). My most upvoted question is [What is the Vim8 package feature and how should I use it?](https://vi.stackexchange.com/q/9522/1841) which is another question I self answered. When Vim8 was released the new package feature which is a new way to install your plugins was not entirely trivial to understand, so once I had spent a bit of time reading the doc and testing the feature by myself I thought it would be useful for other people too. My 41k page views seems to say I was right 😎 (Yes, this is me openly bragging). diff --git a/src/posts/2021/03/alter_ansible_debugging_output.md b/src/posts/2021/03/alter_ansible_debugging_output.md index 762d55388..fe4b22930 100644 --- a/src/posts/2021/03/alter_ansible_debugging_output.md +++ b/src/posts/2021/03/alter_ansible_debugging_output.md @@ -10,12 +10,12 @@ Who knew that ansible output could be actually readable? 💡 Yesterday I saw [this post](https://jpmens.net/2021/03/12/alter-ansible-s-output-on-debugging/) on lobste.rs showing how changing the environment variable `ANSIBLE_STDOUT_CALLBACK` to `yaml` helps improving the output of the `ansible-playbook` command. While I was happy to find a way to improve readability there were two things which didn't fit my needs: -- First, the author uses a wrapper script to call `ansible-playbook` and I regularly call the command directly from the command line so I needed something directly available and transparent. -- Secondly, the author `export`s the `ANSIBLE_STDOUT_CALLBACK` variable so if I want to get rid of the yaml formatting I need to change the variable manually again. +- First, the author uses a wrapper script to call `ansible-playbook` and I regularly call the command directly from the command line so I needed something directly available and transparent. +- Secondly, the author `export`s the `ANSIBLE_STDOUT_CALLBACK` variable so if I want to get rid of the yaml formatting I need to change the variable manually again. So I came up with a short solution to be added to one's `.bash_aliases` (or any prefered rc file holding your aliases): -``` bash +```bash alias ansible-playbook='ansiblePlaybookDebug' function ansiblePlaybookDebug { export ANSIBLE_STDOUT_CALLBACK=default diff --git a/src/posts/2021/03/breaking_habits_floating_window.md b/src/posts/2021/03/breaking_habits_floating_window.md index 5a3636b4e..357f8a437 100644 --- a/src/posts/2021/03/breaking_habits_floating_window.md +++ b/src/posts/2021/03/breaking_habits_floating_window.md @@ -39,11 +39,11 @@ Here is the solution I came up with: This floating window is shown when I press a command I want to avoid. There are a few features I wanted to have in this solution: -- The window is spawned in the middle of Neovim UI so that I can't miss it; -- The borders of the window are delimited with ascii characters to make it more visible; -- It should be easy to close the window both with regular commands (like `:close`) and with specific keys like Escape, Enter or Space (which is my leader key); -- I should have a convenient command to "disable" several normal mode commands with this window; -- The message shown in the window should be multi-lines, centered and configurable depending on the disabled command. +- The window is spawned in the middle of Neovim UI so that I can't miss it; +- The borders of the window are delimited with ascii characters to make it more visible; +- It should be easy to close the window both with regular commands (like `:close`) and with specific keys like Escape, Enter or Space (which is my leader key); +- I should have a convenient command to "disable" several normal mode commands with this window; +- The message shown in the window should be multi-lines, centered and configurable depending on the disabled command. ### Implementing the solution @@ -82,9 +82,9 @@ We then need to call [`nvim_list_uis()`](https://neovim.io/doc/user/api.html#nvi Finally we can use `nvim_open_win()` to open the floating window. The function takes three arguments: -- The handle of the buffer we previously created and which will be used in the window. -- A boolean specifying if the window should be focused immediately after its creation. Here we set it to `v:true` because we want the window to be focused to close it easily. -- A map defining the window configuration. [The doc](https://neovim.io/doc/user/api.html#nvim_open_win%28%29) describes all the available options. Here we especially make use of `width` and `height` to define the size of the window and `anchor`, `col` and `row` to define where on the screen we will place it. +- The handle of the buffer we previously created and which will be used in the window. +- A boolean specifying if the window should be focused immediately after its creation. Here we set it to `v:true` because we want the window to be focused to close it easily. +- A map defining the window configuration. [The doc](https://neovim.io/doc/user/api.html#nvim_open_win%28%29) describes all the available options. Here we especially make use of `width` and `height` to define the size of the window and `anchor`, `col` and `row` to define where on the screen we will place it. Calling this function with `:call BreakHabitsWindow()` will spawn a simple empty floating window: @@ -157,15 +157,15 @@ endfor The parameters are as follow: -- The buffer handle which we already used in the `nvim_open_win()`; -- The mode of the mapping, here we use `n` for normal mode mappings (this is the equivalent of using [`nmap`](http://vimhelp.appspot.com/map.txt.html#%3Anmap)); -- The `closingKey` variable holds the left hand side of the mapping; -- The 4th parameter is the right hand side of the mapping; -- And finally we give the mapping options in a map: - - `silent` to avoid showing the command used, - - `nowait` to avoid waiting for follow up keys in the mapping - - and `noremap` to create a non recursive mapping. - If you are not familiar with these options see [`:h :map-arguments`](https://neovim.io/doc/user/map.html#%3amap-arguments). +- The buffer handle which we already used in the `nvim_open_win()`; +- The mode of the mapping, here we use `n` for normal mode mappings (this is the equivalent of using [`nmap`](http://vimhelp.appspot.com/map.txt.html#%3Anmap)); +- The `closingKey` variable holds the left hand side of the mapping; +- The 4th parameter is the right hand side of the mapping; +- And finally we give the mapping options in a map: + - `silent` to avoid showing the command used, + - `nowait` to avoid waiting for follow up keys in the mapping + - and `noremap` to create a non recursive mapping. + If you are not familiar with these options see [`:h :map-arguments`](https://neovim.io/doc/user/map.html#%3amap-arguments). With this added to `BreakHabitsWindow()` we can now close the window quickly with our defined keys. diff --git a/src/posts/2021/03/travis-to-githubactions.md b/src/posts/2021/03/travis-to-githubactions.md index e5f243f67..5a050b1d3 100644 --- a/src/posts/2021/03/travis-to-githubactions.md +++ b/src/posts/2021/03/travis-to-githubactions.md @@ -12,9 +12,9 @@ Last November travis-ci [announced](https://blog.travis-ci.com/2020-11-02-travis When I created this website I needed to setup a CI environment which would build the site using the static generator [eleventy](https://www.11ty.dev/) before it gets deployed to Github Pages. I wanted a tool which would be: -- Free, because given the traffic on this website and the frequency of my commits I am not ready to put money in it; -- Simple to use, because I figured out from my previous attempt at creating a blog that any friction in the creation of a post reduce drastically the chances that I'll write anything; -- ... And that's it. +- Free, because given the traffic on this website and the frequency of my commits I am not ready to put money in it; +- Simple to use, because I figured out from my previous attempt at creating a blog that any friction in the creation of a post reduce drastically the chances that I'll write anything; +- ... And that's it. I had already used travis on a previous side project and I knew I could make something work pretty easily, especially because I had already an account set up. So I didn't give it too much thoughts and started creating my `.travis-ci.yml` file. And I have to say that after about 200 commits and 8 months of use I am still mostly satisfied this their service. @@ -30,41 +30,41 @@ Before having a look at what changed let's see what my CI workflow needs to do. It is a pretty straight forward process with three main steps: -- Run a script I made to check if I committed new posts with a reference to a Github issue that [I use to host comments]({{'../comments/'}}) and create the corresponding issue if it doesn't exists; -- Build the website using eleventy. This requires to checkout the last changes I made in my templates and run a npm script I have which execute eleventy and output the resulting HTML in a specified directory. -- Commit this built HTML to a `gh-pages` branch on the repo which is then served by Github Pages. +- Run a script I made to check if I committed new posts with a reference to a Github issue that [I use to host comments]({{'../comments/'}}) and create the corresponding issue if it doesn't exists; +- Build the website using eleventy. This requires to checkout the last changes I made in my templates and run a npm script I have which execute eleventy and output the resulting HTML in a specified directory. +- Commit this built HTML to a `gh-pages` branch on the repo which is then served by Github Pages. To do that I had a 20 lines `travis-ci.yml` file which looked like this: ```yaml language: node_js node_js: - - "stable" + - 'stable' cache: - directories: - - node_modules + directories: + - node_modules script: # Script to generate the issue in github used for comments - - npm run create-issues -- $GITHUB_TOKEN false - - npm run build + - npm run create-issues -- $GITHUB_TOKEN false + - npm run build deploy: - provider: pages - skip_cleanup: true - github_token: $GITHUB_TOKEN - keep_history: true - on: - branch: master - local_dir: docs/ - fqdn: www.statox.fr + provider: pages + skip_cleanup: true + github_token: $GITHUB_TOKEN + keep_history: true + on: + branch: master + local_dir: docs/ + fqdn: www.statox.fr ``` -- The first 3 blocks are used to setup the environment which will run the workflow: NodeJS is used to run my npm scripts, and we cache the `node_modules` which is still bigger than I'd want it to be (but that's another topic I'll try to tackle later). +- The first 3 blocks are used to setup the environment which will run the workflow: NodeJS is used to run my npm scripts, and we cache the `node_modules` which is still bigger than I'd want it to be (but that's another topic I'll try to tackle later). -- The `script` block simply runs my npm script which creates the issues on Github and the one I use to run eleventy against my sources and output the result in a `docs` directory. +- The `script` block simply runs my npm script which creates the issues on Github and the one I use to run eleventy against my sources and output the result in a `docs` directory. -- Finally the `deploy` block uses travis' deploy built-in provider ["pages"](https://docs.travis-ci.com/user/deployment/pages/) which commits the `docs` directory to the `gh-pages` branch. +- Finally the `deploy` block uses travis' deploy built-in provider ["pages"](https://docs.travis-ci.com/user/deployment/pages/) which commits the `docs` directory to the `gh-pages` branch. For all of this to work properly I had to [generate a personal access token](https://docs.github.com/en/enterprise-server@2.22/github/authenticating-to-github/creating-a-personal-access-token), store it in my repository secrets and copy it in my travis CI settings to be able to call the Github API to create issues and to commit my changes. @@ -108,15 +108,15 @@ The `name` block is only to set up the name of the action which will be shown in ![deploy workflow](../../../../images/github_workflow.png) -- The `on` blocks defines when the workflow is ran. I want to deploy every time I push something to the `master` branch and I also added the `workflow_dispatch` directive which create the `Run workflow` button in the UI to trigger it manually. I don't know if I'll ever need it but it doesn't hurt to have it. +- The `on` blocks defines when the workflow is ran. I want to deploy every time I push something to the `master` branch and I also added the `workflow_dispatch` directive which create the `Run workflow` button in the UI to trigger it manually. I don't know if I'll ever need it but it doesn't hurt to have it. -- The `jobs` block defines the different steps will will be executed ([Github's doc](https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions#the-components-of-github-actions) explain that clearly) and that's where I needed to recreate what was happening on travis. +- The `jobs` block defines the different steps will will be executed ([Github's doc](https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions#the-components-of-github-actions) explain that clearly) and that's where I needed to recreate what was happening on travis. -- The `checkout` and `setup-node` built-in actions setup the environment (`git checkout` the `master` branch and install node 12 with npm). +- The `checkout` and `setup-node` built-in actions setup the environment (`git checkout` the `master` branch and install node 12 with npm). -- The `🔧 Install and build` step pulls the npm dependencies (which was done automatically on travis) and run my build script. And the `💬 Create issues for comments` step runs my custom script for issues. +- The `🔧 Install and build` step pulls the npm dependencies (which was done automatically on travis) and run my build script. And the `💬 Create issues for comments` step runs my custom script for issues. -- Finally I use an action [from the action market place](https://github.com/marketplace/actions/deploy-to-github-pages) to commit my build to my `gh-pages` branch which is still served automatically by Github. +- Finally I use an action [from the action market place](https://github.com/marketplace/actions/deploy-to-github-pages) to commit my build to my `gh-pages` branch which is still served automatically by Github. Note that the main difference here compared to the travis configuration is that my access token was already in the secrets of my repository so I could access it directly with `{%raw%}${{ secrets.ACCESS_TOKEN }}{%endraw%}` without additional config. diff --git a/src/posts/2021/04/color_picker_shaders.md b/src/posts/2021/04/color_picker_shaders.md index fe0a7985d..bef1cfb6e 100644 --- a/src/posts/2021/04/color_picker_shaders.md +++ b/src/posts/2021/04/color_picker_shaders.md @@ -25,7 +25,7 @@ Before we begin here is a very quick reminder of what a shader is: To start working with shaders I wanted to keep things as simple as possible and I believe using [p5js](https://p5js.org/) is a great way to achieve that. I was already familiar with this framework and it makes it very easy to get going with a project. The cool thing with p5js is that we can start implementing our code in Javascript and then drop in the replacement for the particular areas we want to use a shader on. - So I first created a simple fully javascript version on codepen. This was the opportunity for me to learn how [the HSB color system](https://en.wikipedia.org/wiki/HSL_and_HSV) works: +So I first created a simple fully javascript version on codepen. This was the opportunity for me to learn how [the HSB color system](https://en.wikipedia.org/wiki/HSL_and_HSV) works:

@@ -42,9 +42,9 @@ _Note that one could have used the [`pixels`](https://p5js.org/reference/#/p5/pi This first version allowed me to define the 3 main areas that I want to render with a shader: -- The rainbow: This is the line on the top which allows the user to change the hue of the current color; -- The picker: The rectangle on the left with a gradient on the current hue, the user will be able to choose a specific saturation and brightness by clicking there; -- The sample: The rectangle on the right which shows the currently selected color. +- The rainbow: This is the line on the top which allows the user to change the hue of the current color; +- The picker: The rectangle on the left with a gradient on the current hue, the user will be able to choose a specific saturation and brightness by clicking there; +- The sample: The rectangle on the right which shows the currently selected color. ![Picker zones](../../../../images/color_picker_shader/picker_zones.png) @@ -54,58 +54,56 @@ Before starting to dig into the code it's better to understand the color system There is nothing too complex about it, and [this website](https://learnui.design/blog/the-hsb-color-system-practicioners-primer.html) does a great job at explaining in details how it works. Here are a few points to keep in mind: -- **HSB** stands for **H**ue, **S**aturation and **B**rightness. -- The hue value represent the raw color. It is expressed as an angle as shown on the color wheel here. This angle is between `0`-`360` degrees, but in a p5 sketch we map these values to the range `0`-`100`, and in a shader we will map it in the range `0.0`-`1.0`. But no matter which range we use the relative difference between the color is always the same. - ![color wheel](../../../../images/color_picker_shader/color_wheel.png) -

- Color wheel borrowed from this site -
-- The saturation is how rich the color is: A 100% saturation is very colorful while a 0% saturation is grey. With a 0% saturation no matter which hue you use you will always get the same grey - -

- 0% - 25% - 50% - 75% - 100% -

-

- 0% - 25% - 50% - 75% - 100% -

- -- The brightness is how... bright is your color? Simply put: 0% brightness is complete black no matter the hue, 100% is white if saturation is 0% or just a very bright color. -

- 0% - 25% - 50% - 75% - 100% -

-

- 0% - 25% - 50% - 75% - 100% -

+- **HSB** stands for **H**ue, **S**aturation and **B**rightness. +- The hue value represent the raw color. It is expressed as an angle as shown on the color wheel here. This angle is between `0`-`360` degrees, but in a p5 sketch we map these values to the range `0`-`100`, and in a shader we will map it in the range `0.0`-`1.0`. But no matter which range we use the relative difference between the color is always the same. + ![color wheel](../../../../images/color_picker_shader/color_wheel.png) +
+ Color wheel borrowed from this site +
+- The saturation is how rich the color is: A 100% saturation is very colorful while a 0% saturation is grey. With a 0% saturation no matter which hue you use you will always get the same grey + +

+ 0% + 25% + 50% + 75% + 100% +

+

+ 0% + 25% + 50% + 75% + 100% +

+ +- The brightness is how... bright is your color? Simply put: 0% brightness is complete black no matter the hue, 100% is white if saturation is 0% or just a very bright color. +

+ 0% + 25% + 50% + 75% + 100% +

+

+ 0% + 25% + 50% + 75% + 100% +

Note that you'll also find online some references to the color systems **HSV** and **HSL**. If I believe the wikipedia page **HSV** is the exact same thing as **HSB** excepted **B**rightness is named **V**alue. And **HSL** is **H**ue, **S**aturation, **L**ightness which is kind of the inverse of Brightness (if you want details, check wiki they explain it better than I'll do). - ### Our first shader Now that we know what our project should look like and we understand our color system let's get started with shaders! The first step is to use a very basic shader in a p5js project. Fortunately the doc gives [a good example](https://p5js.org/examples/3d-basic-shader.html) of a very simple usage: - ```javascript // this variable will hold our shader object let theShader; -function preload(){ +function preload() { // load the shader theShader = loadShader('assets/basic.vert', 'assets/basic.frag'); } @@ -127,8 +125,8 @@ function draw() { This will use a shader named `basic` to draw a rectangle on screen. The interesting part is then to implement this shader. As we can see in the `loadShader()` call we need 2 files to define a shader: -- `shader.vert` -- `shader.frag` +- `shader.vert` +- `shader.frag` These files are written with [GLSL](https://en.wikipedia.org/wiki/OpenGL_Shading_Language) which is a widely used language to program shaders, this is the language we will use the shader files in the articles. @@ -216,7 +214,7 @@ vec3 hsb2rgb(vec3 c) void main() { // Normalize the position (to be in range 0.0 - 1.0) // The .xy notation applies the same operation on both x and y components - vec2 st = gl_FragCoord.xy/u_resolution.xy; + vec2 st = gl_FragCoord.xy/u_resolution.xy; // Use the x position of the pixel to define its hue // (with saturation and brightness to 1) @@ -273,7 +271,7 @@ vec3 hsb2rgb(vec3 c) void main() { // Normalize the position between 0 and 1 - vec2 st = gl_FragCoord.xy/u_resolution.xy; + vec2 st = gl_FragCoord.xy/u_resolution.xy; // Set the pixel color using the current hue // and the position to make a gradient @@ -286,8 +284,8 @@ void main() { The principle is very similar to the rainbow shader, two things change: -- First we pass new uniform `u_hue` which determines the hue selected by the user. This hue is passed as a float ranging from `0.0` to `1.0` by the p5 sketch. -- Then we use this hue as well as the normalized position to get the color of the pixel. +- First we pass new uniform `u_hue` which determines the hue selected by the user. This hue is passed as a float ranging from `0.0` to `1.0` by the p5 sketch. +- Then we use this hue as well as the normalized position to get the color of the pixel. In the `draw()` function we will use the following line to set the hue depending on the x component of the mouse position: @@ -310,8 +308,8 @@ Now comes the part where we put everything together to get a working color picke The user will have two ways to interact with the picker: -- When they click on the rainbow and move the mouse on it we will update the hue used in the picker rectangle. -- When they click the picker and move the mouse on it we will update the saturation and brightness. +- When they click on the rainbow and move the mouse on it we will update the hue used in the picker rectangle. +- When they click the picker and move the mouse on it we will update the saturation and brightness. Each change in hue, saturation or brightness will be reflected in the sample rectangle showing the currently selected color. @@ -438,15 +436,15 @@ An online demo is available [here](https://statox.github.io/color-picker/), it d ### A new world of opportunities -This project was really fun to do and I hope that if you never played with shaders before it gave you some inspiration to do the same! I am really excited to use this new skill I learned because I've done [a few](https://www.statox.fr/posts/2020/11/ants/) [simulations](https://www.statox.fr/posts/2020/09/boids/) in the browser with p5 before but they were all limited by their performances. Offloading some of the work to the GPU should allow me to create simulations with much larger definitions. I'm thinking about trying to make a [Critters cellular automaton](https://en.wikipedia.org/wiki/Critters_(cellular_automaton)), exploring [fractal](https://www.shadertoy.com/view/lsX3W4) or maybe maps generation as seen in some of [Sebastian Lague's videos](https://www.youtube.com/channel/UCmtyQOKKmrMVaKuRXz02jbQ). +This project was really fun to do and I hope that if you never played with shaders before it gave you some inspiration to do the same! I am really excited to use this new skill I learned because I've done [a few](https://www.statox.fr/posts/2020/11/ants/) [simulations](https://www.statox.fr/posts/2020/09/boids/) in the browser with p5 before but they were all limited by their performances. Offloading some of the work to the GPU should allow me to create simulations with much larger definitions. I'm thinking about trying to make a [Critters cellular automaton](), exploring [fractal](https://www.shadertoy.com/view/lsX3W4) or maybe maps generation as seen in some of [Sebastian Lague's videos](https://www.youtube.com/channel/UCmtyQOKKmrMVaKuRXz02jbQ). If you also want to get started with shaders here is a list of some interesting resources: -- [itp-xstory.github.io](https://itp-xstory.github.io/p5js-shaders) - An amazing introduction to using shaders with p5js -- [aferriss/p5jsShaderExamples](https://github.com/aferriss/p5jsShaderExamples) - Some useful examples of GLSL shaders for p5js -- [p5js.org](https://p5js.org/reference/#/p5/shader) - The p5js doc about shaders -- [lea.codes](https://lea.codes/webgl/) - The personal website of a dev who does cool things with shaders and threejs -- [The book of shaders](https://thebookofshaders.com/) - The Bible of shader programming +- [itp-xstory.github.io](https://itp-xstory.github.io/p5js-shaders) - An amazing introduction to using shaders with p5js +- [aferriss/p5jsShaderExamples](https://github.com/aferriss/p5jsShaderExamples) - Some useful examples of GLSL shaders for p5js +- [p5js.org](https://p5js.org/reference/#/p5/shader) - The p5js doc about shaders +- [lea.codes](https://lea.codes/webgl/) - The personal website of a dev who does cool things with shaders and threejs +- [The book of shaders](https://thebookofshaders.com/) - The Bible of shader programming If you know of other good resources or want to discuss a project idea, leave a comment on this article! diff --git a/src/posts/2021/04/firefox_autohide_bookmarks.md b/src/posts/2021/04/firefox_autohide_bookmarks.md index 8666dfff0..5d472a4c9 100644 --- a/src/posts/2021/04/firefox_autohide_bookmarks.md +++ b/src/posts/2021/04/firefox_autohide_bookmarks.md @@ -12,10 +12,10 @@ A very quick tip that I don't want to forget about: One can automatically hide t The `userChrome.css` file holds style rules for modifying Firefox's user interface. [This website](https://www.userchrome.org) is a good learning resource. -On linux the file is either in +On linux the file is either in - - `$HOME/.mozilla/firefox/[PROFILE_ID].default/chrome/userChrome.css` - - or `$HOME/snap/firefox/common/.mozilla/firefox/[PROFILE_ID].default/chrome/userChrome.css` if you use the snap version +- `$HOME/.mozilla/firefox/[PROFILE_ID].default/chrome/userChrome.css` +- or `$HOME/snap/firefox/common/.mozilla/firefox/[PROFILE_ID].default/chrome/userChrome.css` if you use the snap version To locate the profile currently used by Firefox one can visit `about:support` and check the line `Profile Directory`. @@ -29,42 +29,49 @@ The code is available on [MrOtherGuy's Github](https://github.com/MrOtherGuy/fir /* Source file https://github.com/MrOtherGuy/firefox-csshacks/tree/master/chrome/autohide_bookmarks_toolbar.css made available under Mozilla Public License v. 2.0 See the above repository for updates as well as full license text. */ -#PersonalToolbar{ - --uc-bm-height: 20px; /* Might need to adjust if the toolbar has other buttons */ - --uc-bm-padding: 4px; /* Vertical padding to be applied to bookmarks */ - --uc-autohide-toolbar-delay: 600ms; /* The toolbar is hidden after 0.6s */ +#PersonalToolbar { + --uc-bm-height: 20px; /* Might need to adjust if the toolbar has other buttons */ + --uc-bm-padding: 4px; /* Vertical padding to be applied to bookmarks */ + --uc-autohide-toolbar-delay: 600ms; /* The toolbar is hidden after 0.6s */ - /* 0deg = "show" ; 90deg = "hide" ; Set the following to control when bookmarks are shown */ - --uc-autohide-toolbar-focus-rotation: 0deg; /* urlbar is focused */ - --uc-autohide-toolbar-hover-rotation: 0deg; /* cursor is over the toolbar area */ + /* 0deg = "show" ; 90deg = "hide" ; Set the following to control when bookmarks are shown */ + --uc-autohide-toolbar-focus-rotation: 0deg; /* urlbar is focused */ + --uc-autohide-toolbar-hover-rotation: 0deg; /* cursor is over the toolbar area */ } -:root[uidensity="compact"] #PersonalToolbar{ --uc-bm-padding: 1px } -:root[uidensity="touch"] #PersonalToolbar{ --uc-bm-padding: 7px } +:root[uidensity='compact'] #PersonalToolbar { + --uc-bm-padding: 1px; +} +:root[uidensity='touch'] #PersonalToolbar { + --uc-bm-padding: 7px; +} -#PersonalToolbar:not([customizing]){ - position: relative; - margin-bottom: calc(0px - var(--uc-bm-height) - 2 * var(--uc-bm-padding)); - transform: rotateX(90deg); - transform-origin: top; - transition: transform 135ms linear var(--uc-autohide-toolbar-delay) !important; - z-index: 1; +#PersonalToolbar:not([customizing]) { + position: relative; + margin-bottom: calc(0px - var(--uc-bm-height) - 2 * var(--uc-bm-padding)); + transform: rotateX(90deg); + transform-origin: top; + transition: transform 135ms linear var(--uc-autohide-toolbar-delay) !important; + z-index: 1; } -#PlacesToolbarItems > .bookmark-item{ padding-block: var(--uc-bm-padding) !important; } +#PlacesToolbarItems > .bookmark-item { + padding-block: var(--uc-bm-padding) !important; +} -#nav-bar:focus-within + #PersonalToolbar{ - transition-delay: 100ms !important; - transform: rotateX(var(--uc-autohide-toolbar-focus-rotation,0)); +#nav-bar:focus-within + #PersonalToolbar { + transition-delay: 100ms !important; + transform: rotateX(var(--uc-autohide-toolbar-focus-rotation, 0)); } -#navigator-toolbox:hover > #PersonalToolbar{ - transition-delay: 100ms !important; - transform: rotateX(var(--uc-autohide-toolbar-hover-rotation,0)); +#navigator-toolbox:hover > #PersonalToolbar { + transition-delay: 100ms !important; + transform: rotateX(var(--uc-autohide-toolbar-hover-rotation, 0)); } -#navigator-toolbox:hover > #nav-bar:focus-within + #PersonalToolbar { - transform: rotateX(0); +#navigator-toolbox:hover > #nav-bar:focus-within + #PersonalToolbar { + transform: rotateX(0); } ``` + diff --git a/src/posts/2021/05/javascript_golf_tips/index.md b/src/posts/2021/05/javascript_golf_tips/index.md index 97593cc91..7cf4e3148 100644 --- a/src/posts/2021/05/javascript_golf_tips/index.md +++ b/src/posts/2021/05/javascript_golf_tips/index.md @@ -16,6 +16,7 @@ I love to play on [codingame](https://www.codingame.com) it's a great way to kee There are several types of clashes (fastest to get the right solution, reverse engineering and shortest code) and recently the shortest code became one of my favorite. Since I start to have a decent ranking I compiled some tricks I use in javascript to shorten my code. Some save several bytes and other only save a few but learning how to combine them and when to use them can lead a long way. ![My ranking on June 16th 2023](./clash_of_code_rank.png) +
My ranking on 13/06/23: 78/571.284 Top 0.01%😎
@@ -24,8 +25,8 @@ There are several types of clashes (fastest to get the right solution, reverse e A list of the various codingame problems with their stats: [chadok.info](https://chadok.info/codingame/puzzles_list.html) - ### Type conversions + #### Decimal string to Number When an input is expected to be a number examples usually use the following code: @@ -37,24 +38,24 @@ n = parseInt(readline()); This is 10 characters just to parse a number from a string. Using the unary operator `+` on a string will cast it to a number. Example: ```javascript -let s='12'; -let n=+s; +let s = '12'; +let n = +s; typeof n; // 'number' -let t=+s[0]+s[1]; // t=3 +let t = +s[0] + s[1]; // t=3 ``` Note that if you need to add a variable which is a number to a string representing a number you will need to use the string first or add a whitespace before the `+` operator: ```javascript -let s='1'; -let n=1; +let s = '1'; +let n = 1; -n+s; // string: '11' -n+ +s; // number: 2 +n + s; // string: '11' +n + +s; // number: 2 -+s+n // number: 2 ++s + n; // number: 2 ``` #### Binary string to Number @@ -66,14 +67,15 @@ Number('0b101'); // 5 ``` #### Number to binary string + See [this SO answer](https://stackoverflow.com/a/16155417) + ```javascript -let a=6; +let a = 6; a.toString(2); // '110' // For negative numbers -let a=-6 -(a >>> 0).toString(2) +let a = -6(a >>> 0).toString(2); ``` ### ASCII @@ -86,23 +88,22 @@ let a=-6 ### Position in the alphabet ```javascript +'a'.charCodeAt(0) - 97; // 0 +'b'.charCodeAt(0) - 97; // 1 +'c'.charCodeAt(0) - 97; // 2 -'a'.charCodeAt(0)-97 // 0 -'b'.charCodeAt(0)-97 // 1 -'c'.charCodeAt(0)-97 // 2 - -'A'.charCodeAt(0)-65 // 0 -'B'.charCodeAt(0)-65 // 1 -'C'.charCodeAt(0)-65 // 2 +'A'.charCodeAt(0) - 65; // 0 +'B'.charCodeAt(0) - 65; // 1 +'C'.charCodeAt(0) - 65; // 2 ``` If not considering the case ```javascript -parseInt(c,36)-10; +parseInt(c, 36) - 10; // Example -[...'abcdefghijklmnopqrstuvwxyz'].map(c => parseInt(c, 36)-10); // [0, 1, 2, 3..., 25]; +[...'abcdefghijklmnopqrstuvwxyz'].map((c) => parseInt(c, 36) - 10); // [0, 1, 2, 3..., 25]; ``` ### Loops @@ -133,7 +134,11 @@ for(b=realine(a=readline();a+c) // Better -for(c of l)t+=+c // Top +l.map(Number); // Good +l.map((c) => +c); // Better +for (c of l) t += +c; // Top ``` ### Variables declaration and initialization @@ -156,14 +161,14 @@ You should always have in mind the implications of not implicit scopes but that ```javascript // 58 bytes -let n=10; -for(let i=0; i>1 -i/4|0 === i>>2 +(i / 2) | (0 === i >> 1); +(i / 4) | (0 === i >> 2); ``` #### Get rid of leading zeros ```javascript -0.5 === .5 // true +0.5 === 0.5; // true ``` ### Conditions + #### The ternary operator [The ternary operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) is precious to avoid lengthy conditional expressions. @@ -225,11 +232,11 @@ i/4|0 === i>>2 Always be mindful of what you use in your conditional expressions, sometimes putting them in another way can save a byte or two while preserving the same feature: ```javascript -r=a<10?a+'0':a -r=a>9?a:a+'0' +r = a < 10 ? a + '0' : a; +r = a > 9 ? a : a + '0'; -r=a==b?'same':a>b?'higher':'lower' -r=a>b?'higher':a b ? 'higher' : 'lower'; +r = a > b ? 'higher' : a < b ? 'lower' : 'same'; ``` #### Using `&&` and `||` @@ -246,18 +253,20 @@ Consider a problem where you need to find the `n`th digit in a string of all pal You will need to generate the string `123456789112233445566778899101...`. One way to do it is the following: ```javascript -for(n=+readline(s=""),i=0;!s[n];i++)if([...i+""].reverse().join``==i){s+=i} -print(s[n]) +for (n = +readline((s = '')), i = 0; !s[n]; i++) + if ([...(i + '')].reverse().join`` == i) { + s += i; + } +print(s[n]); ``` While the `n`th digit of the string doesn't exists continue to search for number which are palindromes. ```javascript -for(n=+readline(s=""),i=0;!s[n];i++)([...i+""].reverse().join``==i)&&(s+=i) -print(s[n]) +for (n = +readline((s = '')), i = 0; !s[n]; i++) [...(i + '')].reverse().join`` == i && (s += i); +print(s[n]); ``` - ### Codingame particularities These tips are only useful on the codingame website. @@ -270,13 +279,13 @@ If you need to use `readline()` more than one it will be cheaper to store it in ```javascript // 27 bytes -n=readline() -m=readline() +n = readline(); +m = readline(); // 24 bytes -r=readline -n=r() -m=r() +r = readline; +n = r(); +m = r(); ``` This is only 3 characters saved but the more calls to `readline()` you need to write the more characters you save. @@ -286,20 +295,21 @@ This is only 3 characters saved but the more calls to `readline()` you need to w You can use javascript scope smartly to reduce the number of bytes needed to get the inputs. To do so keep in mind that if you pass a variable assignation as the argument of a function the variable is still available in your current scope. For example let's say you need to read two strings `n` and `m` and to init a counter `i` to zero: + ```javascript // 28 bytes -r=readline -n=r() -m=r() -i=0 +r = readline; +n = r(); +m = r(); +i = 0; ``` This can be shortened like this: ```javascript // 26 bytes -r=readline -m=r(n=r(i=0)) +r = readline; +m = r((n = r((i = 0)))); ``` ⚠ The order of calls made to `r()` is important here: The first call made to the function should be the most nested. @@ -309,12 +319,8 @@ m=r(n=r(i=0)) I'm not completely sure how this trick works because I can't reproduce it in a browser or in node, but in codingame if you need to pass a string as an argument you can avoid the whole `('string')` syntax with a pair of back ticks. This can be useful to split a string or join an array: ```javascript -'ab cd ef'.split(' ') -'ab cd ef'.split` ` - -[1, 2, 3].join('\r') -[1, 2, 3].join`\r` - +'ab cd ef'.split(' '); +'ab cd ef'.split` `[(1, 2, 3)].join('\r')[(1, 2, 3)].join`\r`; ``` #### Replace `console.log` by `print` @@ -322,8 +328,8 @@ I'm not completely sure how this trick works because I can't reproduce it in a b To pass your result to codingame validators you need to write it to the standard output. In regular javascript this is done with `console.log` however codingame's environment supports the deprecated `print` which works exactly the same: ```javascript -console.log('valid') -print('valid') +console.log('valid'); +print('valid'); ``` #### Stop your program with an invalid command diff --git a/src/posts/2021/10/race_generator.md b/src/posts/2021/10/race_generator.md index 4235181e6..8ca0deaf9 100644 --- a/src/posts/2021/10/race_generator.md +++ b/src/posts/2021/10/race_generator.md @@ -8,18 +8,18 @@ commentIssueId: 29 For several weeks now I have been working on and off on an ambitious project in which I want to procedurally generate a racetrack to create autonomous vehicles which will learn to drive on it thanks to a genetic algorithm. I did a lot of different experiments and even got some ~~cool~~ passable results but I am hitting a point where there is too much refactoring to be done to complete the project with the current code base. As I don't have the motivation to put more work to do things properly and as I also have other projects in mind that I want to start working on I'm writing this post to keep a trace of what I have done and what I would like to do differently. This writing will be messy and probably not very interesting for anyone else than myself but I hope that it will be helpful in a few months when I decide to build something on top of this experiment. - ### Generating a racetrack My goal was to generate a racetrack which would be a loop with several turns with angles sufficiently difficult to be interesting. To do that I took a lot of inspiration from these two articles: - - [How to generate procedural racetracks](http://blog.meltinglogic.com/2013/12/how-to-generate-procedural-racetracks/) - - [Procedural racetrack generation](https://bitesofcode.wordpress.com/2020/04/09/procedural-racetrack-generation/) +- [How to generate procedural racetracks](http://blog.meltinglogic.com/2013/12/how-to-generate-procedural-racetracks/) +- [Procedural racetrack generation](https://bitesofcode.wordpress.com/2020/04/09/procedural-racetrack-generation/) In the first post Gustavo Maciel describes an idea to generate a racetrack in three simple steps: - - Generate a bunch of random points in the plan - - Calculate the hull of the polygon formed by these points. That is, simply put, the list which will wrap around all the points - - Finally interpolate the points of this hull to get a nice racetracky shape on which a car could drive + +- Generate a bunch of random points in the plan +- Calculate the hull of the polygon formed by these points. That is, simply put, the list which will wrap around all the points +- Finally interpolate the points of this hull to get a nice racetracky shape on which a car could drive The second post is a second implementation of Maciel's idea, and my project is yet another implementation of the same idea but with my _very own_ bugs and design flaws which makes it very special to my eyes 🤩. @@ -54,10 +54,9 @@ Fortunately the page has a pseudo code implementation so I was able to port that

- After this successful attempt I decided that the hulls generated were too boring: I want some deadly turns! -The gift wrapping algorithm generates a convex hull which is a simple, boring envelope of the points. A concave hull however is a polygon with at least one angle between 180 and 360 degrees exclusive, which means that this polygon has a much more interesting shape with much more complex turns. It is however more complex to generate. So I explored the web a bit more and ended up on [Concave hull: A k-nearest neighbours approach for the computation of the region occupied by a set of points](https://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf). This paper describes an algorithm to generate a concave hull using the same idea as the gift wrapping but at each step, instead of choosing the next point among the complete list of generated point, we restrict the search the nearest neighbors of the current point. +The gift wrapping algorithm generates a convex hull which is a simple, boring envelope of the points. A concave hull however is a polygon with at least one angle between 180 and 360 degrees exclusive, which means that this polygon has a much more interesting shape with much more complex turns. It is however more complex to generate. So I explored the web a bit more and ended up on [Concave hull: A k-nearest neighbours approach for the computation of the region occupied by a set of points](https://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf). This paper describes an algorithm to generate a concave hull using the same idea as the gift wrapping but at each step, instead of choosing the next point among the complete list of generated point, we restrict the search the nearest neighbors of the current point. This looks much more interesting: @@ -70,20 +69,19 @@ This looks much more interesting: However with concave hull come two big issues: -- It is not uncommon that two segments of the polygon intersect: that creates an issue because if you create a crossroad in a racetrack things get messy (Well, there _are_ some [figure 8 racing](https://youtu.be/6ZZyP7VlZcM) competitions but for our purpose that creates too many issues to count laps and detect if cars followed the right path) -- Even when they don't intersect sometimes the vertices are at an angle which is just too narrow to make a good race track. +- It is not uncommon that two segments of the polygon intersect: that creates an issue because if you create a crossroad in a racetrack things get messy (Well, there _are_ some [figure 8 racing](https://youtu.be/6ZZyP7VlZcM) competitions but for our purpose that creates too many issues to count laps and detect if cars followed the right path) +- Even when they don't intersect sometimes the vertices are at an angle which is just too narrow to make a good race track. There are several ways to get rid of these issues: -- Detecting the intersection of two segments is super easy when you have a [`collideLineLine()`](https://github.com/bmoren/p5.collide2D/blob/0988172c15aeef/p5.collide2d.js#L196-L235) function like the one I shamelessly stole from the [p5.collide2D](https://github.com/bmoren/p5.collide2D) library. Determining the angle formed by three points and comparing it to an arbitrary threshold is enough too (cf. `threePointsAngle` in the previous codepen). Using this information one could simply ignore the bad tracks and keep generating new tracks until one fits all the criterion. -- For intersections a correction can be made by taking the point where two lines intersect, making this point a new point of the hull replacing the ones which are in the inner loop. -- For angles it is also possible to use the same principle as what I used to push my initial points apart: Take all your points, when three of them form an angle too narrow move one of them, and keep going until things are stable. +- Detecting the intersection of two segments is super easy when you have a [`collideLineLine()`](https://github.com/bmoren/p5.collide2D/blob/0988172c15aeef/p5.collide2d.js#L196-L235) function like the one I shamelessly stole from the [p5.collide2D](https://github.com/bmoren/p5.collide2D) library. Determining the angle formed by three points and comparing it to an arbitrary threshold is enough too (cf. `threePointsAngle` in the previous codepen). Using this information one could simply ignore the bad tracks and keep generating new tracks until one fits all the criterion. +- For intersections a correction can be made by taking the point where two lines intersect, making this point a new point of the hull replacing the ones which are in the inner loop. +- For angles it is also possible to use the same principle as what I used to push my initial points apart: Take all your points, when three of them form an angle too narrow move one of them, and keep going until things are stable. **I think that the important thing here anyway is to have your hull as clean as possible before you move on to the next step otherwise it will inevitably bite you.** I'm putting the previous sentence in bold because of course I was too impatient to get to the next step so, of course, after thinking about the possible solutions for half an hour I decided to ignore the problem until it's too late. - #### Interpolating the points Earlier this year I did [an experiment with Bezier curves](https://statox.github.io/bezier-experiment/) when this project was just a vague idea in the back of my mind. The basic idea is to generate a bunch of points (hence the two previous parts) to get a polygon and use the formula created by Bezier to add more points to this polygon to make the curves smoother. @@ -99,7 +97,6 @@ So I kept my two hull algorithms (the gift wrapping and the k-nearest neighbors) The results are quite nice we can see something which could look like a racetrack, that's the right path! However our issues with interesctions and narrow angles are still here and once again I decided that fixing them was a problem for future me 🤷. - #### Adding some width So now I have a racetrack with a few hundreds points but they still form a simple line. There are a lot of options to make this line an actual road, my goal is to have cars detecting their position on the road with a ray tracing algorithm that I'll describe more later. To run this ray tracing I need to have a bunch of segments which represent the borders of my road. @@ -117,7 +114,6 @@ Here you can see in blue the initial hull, in < It's also at this point that I finally decided to start fixing the intersection issue. And of course I did it in a not very bright way: When the angle between three point is less than an arbitrary threshold I simply remove the middle point. That give this weird correction algorithm that you see in action in the previous codepen. And that introduces a worst problem which is that by doing that I change the width of the track on some portions. - While I was trying to fix this issue I also wondered how I would paint the road because p5.js doesn't have a way to create concave shapes. I came up with a simple solution which is to change the way I draw the line of the hull: By making the stroke weight much bigger I get something which looks even more like a racetrack:

@@ -129,7 +125,6 @@ While I was trying to fix this issue I also wondered how I would paint the road With this implementation I thought I could display the track and have not-very-precise-but-still-good-enough border representation for the cars to run but realized that this is not good enough. So I came up with a solution which I find quite interesting. - #### Detecting that a car is not on the road At this point I started merging everything I had done before into [a real Github project](https://www.github.com/statox/procedural-race/) with typescript, a real linter and all the right tools to develop this project properly. I tagged my commits properly at each steps so that I could showcase the progress of my project, but now I'm just too lazy to do that so I won't show demos. @@ -138,12 +133,13 @@ One of the interesting ideas I had in the project was to find a way to easily ch I think this is a pertinent approach as once the track is stored in memory the check is in constant time however my implementation was not the smartest: -- As I draw the track on the canvas, I need to wait the first iteration of `draw()` to run to check the color of the canvas, this is super inconvenient. I should have drawn the track on a different `P5.Graphic` than the canvas this way I can do all the computations in `setup()` making the `draw()` function much simpler. -- I should have normalized my track to a simple white on black display, instead I used the colors I has already started to use to show my track which makes the color comparison unnecessarily tricky. +- As I draw the track on the canvas, I need to wait the first iteration of `draw()` to run to check the color of the canvas, this is super inconvenient. I should have drawn the track on a different `P5.Graphic` than the canvas this way I can do all the computations in `setup()` making the `draw()` function much simpler. +- I should have normalized my track to a simple white on black display, instead I used the colors I has already started to use to show my track which makes the color comparison unnecessarily tricky. ### Making smart cars #### Ray tracing + As I am impatient and stubborn I started creating my vehicles before my racetrack was fully ready, which unsurprisingly made things harder. At first things were going well: I had a codepen, I reused some ray tracing code made by the amazing Daniel Shiffman for [one of his coding challenges](https://editor.p5js.org/codingtrain/sketches/Nqsq3DFv-), I plugged that on a car that is controlled with the arrow keys, WASD or ZQSD and here we are:

@@ -167,10 +163,10 @@ The evaluation function also has an issue to keep track of laps: It is super imp At this point I started to frantically code more and more ~~technical debt~~ features in my code: -- I created some basic cars which trace several rays in front of them, gather average distance on the right and on the left and decide to steer proportionally to these distances. This simple model works surprisingly well. I also wanted to factor the acceleration based on these distances but I didn't get to that and simply made them accelerate more and more for each laps they do. -- I then made the factors used to steer some random numbers acting as the genes of a genetic algorithm, I created a pool of cars and made them reproduce and mix these genes. At these points the results were not super convincing because my algorithm isn't super well tuned. But I think I validated the basic idea: With a better track generation, a mode decoupled data model I should be able to make a working algorithm. -- I also tried to use [dannjs](https://dannjs.org/) which is a nice little neural network library. My idea was to train a neural network via neuro-evolution algorithms. I did several tests but I ended up realizing that neural networks aren't that simple and that you still need to study the math underneath to use them properly. -- I added some broken statistics capabilities to my pool to have an idea of how my cars perform. I'm not convinced my stats are really relevant and I had a big plan of creating real time graphs visualization a bit like the [Primer videos](https://www.youtube.com/c/PrimerLearning) but that didn't happen. +- I created some basic cars which trace several rays in front of them, gather average distance on the right and on the left and decide to steer proportionally to these distances. This simple model works surprisingly well. I also wanted to factor the acceleration based on these distances but I didn't get to that and simply made them accelerate more and more for each laps they do. +- I then made the factors used to steer some random numbers acting as the genes of a genetic algorithm, I created a pool of cars and made them reproduce and mix these genes. At these points the results were not super convincing because my algorithm isn't super well tuned. But I think I validated the basic idea: With a better track generation, a mode decoupled data model I should be able to make a working algorithm. +- I also tried to use [dannjs](https://dannjs.org/) which is a nice little neural network library. My idea was to train a neural network via neuro-evolution algorithms. I did several tests but I ended up realizing that neural networks aren't that simple and that you still need to study the math underneath to use them properly. +- I added some broken statistics capabilities to my pool to have an idea of how my cars perform. I'm not convinced my stats are really relevant and I had a big plan of creating real time graphs visualization a bit like the [Primer videos](https://www.youtube.com/c/PrimerLearning) but that didn't happen. ### Thinking of the future diff --git a/src/posts/2021/11/eleventy_search_bar.md b/src/posts/2021/11/eleventy_search_bar.md index 1036f4273..70304b374 100644 --- a/src/posts/2021/11/eleventy_search_bar.md +++ b/src/posts/2021/11/eleventy_search_bar.md @@ -39,6 +39,7 @@ _I am also in the process of adding a `creationDate` field so that I can have mo Now that the list is available to my templates I created a new nunjucks template to show all my songs: {% raw %} + ```jinja2 {% set sortedChords = chords | sortChords | groupby("artist") %} @@ -62,6 +63,7 @@ Now that the list is available to my templates I created a new nunjucks template {% endfor %}
``` + {% endraw %} There is nothing particularly clever here: In the first line `chords` is the raw data coming from the JSON file. `sortChords` is [an eleventy filter I wrote](https://github.com/statox/blog/blob/83c9fd3/tools/eleventy/filters.js#L96-L103) which sorts the data alphabetically by artist and by title. And the `groupby("artist")` nunjucks [buit-in filter](https://mozilla.github.io/nunjucks/templating.html#groupby) takes all the data and turns it into a map where the keys are the artist names and the values are lists of the items from the json file. @@ -78,9 +80,9 @@ I'm happy with my list of 500 songs but it's not super easy to navigate: I need I'm sure there are a lot of clever ways to do that but I decided to go with the inefficient and dirty way: -- Create a text input; -- Use its `oninput` property to call a short javascript function; -- The javascript function will iterate over all the HTML elements and add a `.hidden` CSS class to the ones which don't match the query. +- Create a text input; +- Use its `oninput` property to call a short javascript function; +- The javascript function will iterate over all the HTML elements and add a `.hidden` CSS class to the ones which don't match the query. #### CSS @@ -92,13 +94,12 @@ The first easy step is to create the `.hidden` class in a dedicated css file. Wh } ``` - #### Input The input element is super basic too: ```html - + ``` I used the `oninput` property instead of `onchange` because I want to see the songs filtered as I type and not when I'm done typing. That makes the search bar more convenient on mobile. @@ -109,8 +110,8 @@ Before I can create the function to filter the HTML elements I need to bind some Here I have two types of elements I want to show/hide: -- The `` rows which holds the artist and their songs; -- The `

  • ` items which holds only the song titles. +- The `` rows which holds the artist and their songs; +- The `
  • ` items which holds only the song titles. And I want the search to act independently on each type of element: If an artist matches my search I want to see all of their songs and I also want to see all of the songs which title matches the search: @@ -118,13 +119,14 @@ And I want the search to act independently on each type of element: If an artist To make that work I decided to add different data for each type of element: -- For both the artist `` and the song `
  • ` I put in the data attribute the name of the artist; -- For the artists `` I add all the concatenated titles of their songs; -- For the song `
  • ` I add only the song title; +- For both the artist `` and the song `
  • ` I put in the data attribute the name of the artist; +- For the artists `` I add all the concatenated titles of their songs; +- For the song `
  • ` I add only the song title; I put all of this data in a `data-values` attribute and I used nunjucks loops once again to concatenate what needs to be concatenated. I also add a `;` separator between each value so that I'm sure the search will not overlap two different entries: {% raw %} + ```jinja2 {% set sortedChords = chords | sortChords | groupby("artist") %} @@ -148,6 +150,7 @@ I put all of this data in a `data-values` attribute and I used nunjucks loops on {% endfor %}
    ``` + {% endraw %} I also added a CSS class `datarow` to both the `` and `
  • `. This is the selector I'll be using in my javascript to find the elements I need to manipulate in my page. @@ -184,9 +187,9 @@ And here we have a working search bar which you can try live [on this page]({{'/ It doesn't take an experienced web developer to see that this solution has its flaws: -- I think that adding all this data in the data attributes increases the size of the page which is probably not great for loading times; -- The search algorithm is quite dumb and it scans all the elements in the page, using a more efficient data structure would improve the performances; -- I'm really not sure how this would scale with a list 10x or 100x bigger. +- I think that adding all this data in the data attributes increases the size of the page which is probably not great for loading times; +- The search algorithm is quite dumb and it scans all the elements in the page, using a more efficient data structure would improve the performances; +- I'm really not sure how this would scale with a list 10x or 100x bigger. But for my specific use case this is working fine and as it took me ~10 years to create the list of 500 songs I don't think the size will become an issue before many more years. Also I'm fairly confident that I'd be able to reuse these pieces of code to create other search bar on my site: All I need is to have html elements with the class `.datarow` and some data to filter in their `data-value` attributes. diff --git a/src/posts/2021/11/eleventy_secrets.md b/src/posts/2021/11/eleventy_secrets.md index 8ad22debb..358ab9ba0 100644 --- a/src/posts/2021/11/eleventy_secrets.md +++ b/src/posts/2021/11/eleventy_secrets.md @@ -8,30 +8,28 @@ commentIssueId: 30 This blog is a platform for me to experiment with front end technologies and to write about random stuff I do on my free time. I keep everything versioned [on Github](https://www.github.com/statox/blog) in a public repository because it's easy and because so far I didn't have anything to keep secret in this repo. This changed recently so I decided to use [`EncFS`](https://vgough.github.io/encfs/) to keep secret files encrypted in my Github repo and have them easily accessible on my local copy of the repo. Here is how I did it. - ### Context and requirements Recently I started to collect pieces of wisdom I hear at my workplace. I intend to share this knowledge in some form because I believe it could be useful to a lot of my peers, but so far I'm not sure how I will do that so I want to keep track of them somewhere and this blog's repo is an easy place. So I started writing a couple of them in a draft and I also wrote the name of the coworkers who gave me these pieces of advice. Before I committed the document I realized that I was about to publish some personal thoughts of people I really appreciate and respect without asking for their consent, not cool. So here is what I wanted to have: -- The ability to create new "secret" pages on this blog like I do with any other article. -- They should be easily accessible on my local clone and they should be handled by eleventy on my local build. -- They should not be built by eleventy on the CI I use to deploy the blog. -- They should be versioned in the repo so that I don't have to keep track of data on different places. -- The solution should work on Linux and I don't really care about the other OSes as I don't use them (though my solution also works on MacOS) +- The ability to create new "secret" pages on this blog like I do with any other article. +- They should be easily accessible on my local clone and they should be handled by eleventy on my local build. +- They should not be built by eleventy on the CI I use to deploy the blog. +- They should be versioned in the repo so that I don't have to keep track of data on different places. +- The solution should work on Linux and I don't really care about the other OSes as I don't use them (though my solution also works on MacOS) To do that I opted for the following solution: -- I will have a `src/.secrets` directory in my repo. This directory will hold my secret files encrypted. -- I will have another directory `src/secrets/` in which the secrets will be decrypted when I build the site locally, eleventy will have to build this directory too. -- To make things easy I will have the decryption script running in the CI I use locally when building my site. +- I will have a `src/.secrets` directory in my repo. This directory will hold my secret files encrypted. +- I will have another directory `src/secrets/` in which the secrets will be decrypted when I build the site locally, eleventy will have to build this directory too. +- To make things easy I will have the decryption script running in the CI I use locally when building my site. **An important warning** **The solution I'm presenting here is dealing with very low importance documents. I don't plan to store any applications secrets like an API key or any sensitive document. So while I'm confident this solution fits my threat model I am in no way encouraging you to use it in production without a proper review of your risks.** - ### The tool I'll be using [`EncFS`](https://vgough.github.io/encfs/) a tool providing an encrypted file system in the user space: It translates the file system operations one would be doing into the equivalent encrypted operations on the file system. @@ -88,9 +86,9 @@ encfs "${PWD}/$SECRET_DIR" "${PWD}/$CLEAR_DIR" This script will run when I build my site locally so it does the following: -- Check that the clear directory is empty. If it's not it means that I had already mounted the secret directory (e.g. I restarted the build process because something broke it) so I can skip what's next. -- Create the clear directory if needed. -- Use `encfs` to mount the decrypted directory. +- Check that the clear directory is empty. If it's not it means that I had already mounted the secret directory (e.g. I restarted the build process because something broke it) so I can skip what's next. +- Create the clear directory if needed. +- Use `encfs` to mount the decrypted directory. ```bash # unmount_secrets.sh @@ -102,7 +100,6 @@ fusermount -u "${PWD}/$CLEAR_DIR" This one is not in my local CI. I use it when I'm done working to unmount the clear directory. - ### Configuring my build With the `decrypt_secrets.sh` wrapper created I can update my `package.json`: @@ -119,28 +116,27 @@ With the `decrypt_secrets.sh` wrapper created I can update my `package.json`: So now when I use `npm run dev` in my local environment my decryption script is run. However when my CI on Github runs `npm run build` my secrets are not touched. - ### Setting up the ignores There is one last piece of configuration to add: the ignored files. Until now I only had a `.gitignore` file in my repo with two purposes: -- It prevented me to track changes in `node_modules/` and `docs/` (my local build file) -- And it also prevented eleventy to try to build these directories. +- It prevented me to track changes in `node_modules/` and `docs/` (my local build file) +- And it also prevented eleventy to try to build these directories. Now I have a new directory `src/secrets` which I want eleventy to build (locally) but I don't want to track on git (since that would completely defeat the purpose of all this encryption thing I've been doing). So I had to do the following: -- First tell eleventy to not use the `.gitignore` file. This is done by adding this to my `.eleventy.js` config file. Now git will still respect `.gitignore` but eleventy will use the `.eleventyignore` file to exclude files from the build. +- First tell eleventy to not use the `.gitignore` file. This is done by adding this to my `.eleventy.js` config file. Now git will still respect `.gitignore` but eleventy will use the `.eleventyignore` file to exclude files from the build. - eleventyConfig.setUseGitIgnore(false); + eleventyConfig.setUseGitIgnore(false); -- Then my `.gitignore` remains untouched -- And finally I added `.eleventyignore` with the following content: +- Then my `.gitignore` remains untouched +- And finally I added `.eleventyignore` with the following content: - node_modules/ - docs/ - src/secrets/ + node_modules/ + docs/ + src/secrets/ ### Voila! diff --git a/src/posts/2022/03/dockerizing_dev_env.md b/src/posts/2022/03/dockerizing_dev_env.md index f5aa76b64..23bcf9174 100644 --- a/src/posts/2022/03/dockerizing_dev_env.md +++ b/src/posts/2022/03/dockerizing_dev_env.md @@ -74,6 +74,7 @@ docker exec -ti provitools sh -c "python --version" Adding docker compose (maybe too early but at least it will be there) `docker-compose.yml` + ```dockerfile version: '3.7' @@ -84,6 +85,7 @@ services: ``` `build.sh` + ```shell #!/usr/bin/env bash sudo docker build . -t provisioning/tools @@ -91,6 +93,7 @@ sudo docker-compose build ``` `start.sh` + ```shell #!/usr/bin/env bash sudo docker-compose up -d @@ -154,7 +157,6 @@ RUN pip install -r pip_requirements.txt ENTRYPOINT ["tail", "-f", "/dev/null"] ``` - ### Step 6 We can run ansible playbook. And (somehow?) `aws` is installed on the container 🤷 @@ -186,5 +188,3 @@ I added `-e SSH_AUTH_SOCK=$SSH_AUTH_SOCK` to my alias but the directory is rando So I need to fix that. [This](https://www.jamesridgway.co.uk/sharing-an-ssh-agent-between-a-host-machine-and-a-docker-container/) provides a solution but my `SSH_AUTH_SOCK` variable is empty - - diff --git a/src/posts/2022/04/updates_04_22.md b/src/posts/2022/04/updates_04_22.md index 821d859d0..42c279c1c 100644 --- a/src/posts/2022/04/updates_04_22.md +++ b/src/posts/2022/04/updates_04_22.md @@ -36,16 +36,15 @@ With that I also got two buddies of mine to regularly play music together: We tr I also try to play with more people, so I've been playing with a couple of teammates at Dashlane this is very casual but it was the opportunity to see that I _can_ play with other people which I'm pretty happy about! - ### Making this site useful So I'm playing music but I'm still a nerd and what's better than when two of your hobbies come together? I decided to use this site I'm creating to be a useful resource when I'm playing. So I added several sections: -- I worked a lot on my [chords](/chords) page. I use it to list all the chords of the songs I can play so I worked on a clean presentation which works on desktop and mobile and is convenient to search. I added a "Random song" button to avoid playing always the same songs as well as a list of the last songs I added. What I'm at the same time the most proud of and the most ashamed of is the "poor's man CRM" system I created: I use a Github workflow on the repo of this website to monitor a specific issue of the repo and transform my comments into JSON data that is then committed to my list of chords. This is not super clean but I think it's a creative and easy way to ease my life. I want to write about that at some point. +- I worked a lot on my [chords](/chords) page. I use it to list all the chords of the songs I can play so I worked on a clean presentation which works on desktop and mobile and is convenient to search. I added a "Random song" button to avoid playing always the same songs as well as a list of the last songs I added. What I'm at the same time the most proud of and the most ashamed of is the "poor's man CRM" system I created: I use a Github workflow on the repo of this website to monitor a specific issue of the repo and transform my comments into JSON data that is then committed to my list of chords. This is not super clean but I think it's a creative and easy way to ease my life. I want to write about that at some point. -- On the [music](/music) page I also added different sections. One with all the online resources I'm using to learn music theory. This is not super convenient to use and I'm still thinking out how I want to rework it. And another sections with the manuals and default settings of the pedals I own. This page too is more a first try of something that I want to improve in the future. +- On the [music](/music) page I also added different sections. One with all the online resources I'm using to learn music theory. This is not super convenient to use and I'm still thinking out how I want to rework it. And another sections with the manuals and default settings of the pedals I own. This page too is more a first try of something that I want to improve in the future. So now when I setup myself to play some guitar I can pull my site and have everything I need right at my fingertips! I'm happy with that because I think making things which make your life easier is what the internet and technology is all about. @@ -63,23 +62,22 @@ I have also started 3 different articles for this website that I never finished When I'm not playing music and tweaking this website I also keep practicing my useless side projects! Since November I have worked on several projects which are in the [project](/project) page: -- I implemented an ["instrument"](https://codepen.io/statox/full/qBPwaNo) with p5js. The idea was to experiment with the MIDI capabilities of the library with a dumb project. I am not completely convinced by what I've seen and I think in the future I will experiment with other musical JS library to see what I can gete out of them. -- In December I did the advent of code. Stopping at day 14 which is two day earlier than in 2020. -- To help me learn music scales I implemented the [chord wheel](https://statox.github.io/chord-wheel/) which is originally a paper tool coming from a book that I've turned into a web page. It would probably deserve a full rewrite to be better organized, bugfree and allow to implement other similar tools (like the "circle of fiths"), again: maybe later... -- At Dashlane I've been revamping the exercise that we use to interview our candidates and following my usual hobbies I came up with a exercise based on cellular automata. So that was a good excuse to implement some new ones! First I made a simple [Langton's Ant](https://statox.github.io/pixijs-langton-ant/) which is not super impressive but was fun to do. I twisted it a bit by coloring the sections created by the pattern. On this project I used pixijs to try alternatives to p5js, that was a long time ago, I didn't write about it and my memory doesn't serve me well but I'd say it wasn't a great experience because otherwise I would have tried it in another project. -- And while I was thinking of cellular automata I finally implemented a 1D automata platform for the first time! I called the project [circular automata](https://github.com/statox/circular-automata/) because my initial thought was to represent a classical automata with concentric circles. The implementation of the automata was quite simple and the visualization too but this project got super interesting when I decided to use sveltejs in it. With svelte I created a super convenient architecture where I can have one component to run the p5js animation and dedicated components to control the simulation. This component system allows me to easily plug new visualizations, or to test new graphic frameworks (I'm currently experimenting with a threejs component). I think setting up my first project with p5 and svelte is a major break through in my side project as it should allow me to be faster to create well architectured, more complete and more convenient projects, so I'm looking forward to have more time to do new experiments. -- In a completely different area I also finally created my [setup repository](https://github.com/statox/setup). This is something I had been wanting to do for many many years but when my Dashlane computer broke and I had to reinstall of my setup I decided it was the final straw. The great thing is that at Dashlane I have had the opportunity to work a lot with Ansible and to better understand how it is supposed to work and to be used. Also a teammate of mine kindly let me look into his own repository of Ansible playbooks and that got me started. Now I have a collection of playbooks which allow me to setup most of my common tools of a Linux machine from the Desktop environment, to my favorite packages and configurations, to my own dotfiles. This is a project which will keep growing and that I'll need to evolve regularly like [my dotfiles](https://github.com/statox/dotfiles) but it's incredibly satisfying to use. I'm also evolving it to handle my different server setups like my OVH box or my local raspberry! -- And I also created a script which scraps [Nasa's Astronomy picture of the day](https://apod.nasa.gov/) every day and sets it up as my desktop background as well as my Zoom virtual background. I intended to write an article about that because Zoom doesn't allow to you programmatically change your background and I was happy to find a solution but I haven't found the motivation to do it yet +- I implemented an ["instrument"](https://codepen.io/statox/full/qBPwaNo) with p5js. The idea was to experiment with the MIDI capabilities of the library with a dumb project. I am not completely convinced by what I've seen and I think in the future I will experiment with other musical JS library to see what I can gete out of them. +- In December I did the advent of code. Stopping at day 14 which is two day earlier than in 2020. +- To help me learn music scales I implemented the [chord wheel](https://statox.github.io/chord-wheel/) which is originally a paper tool coming from a book that I've turned into a web page. It would probably deserve a full rewrite to be better organized, bugfree and allow to implement other similar tools (like the "circle of fiths"), again: maybe later... +- At Dashlane I've been revamping the exercise that we use to interview our candidates and following my usual hobbies I came up with a exercise based on cellular automata. So that was a good excuse to implement some new ones! First I made a simple [Langton's Ant](https://statox.github.io/pixijs-langton-ant/) which is not super impressive but was fun to do. I twisted it a bit by coloring the sections created by the pattern. On this project I used pixijs to try alternatives to p5js, that was a long time ago, I didn't write about it and my memory doesn't serve me well but I'd say it wasn't a great experience because otherwise I would have tried it in another project. +- And while I was thinking of cellular automata I finally implemented a 1D automata platform for the first time! I called the project [circular automata](https://github.com/statox/circular-automata/) because my initial thought was to represent a classical automata with concentric circles. The implementation of the automata was quite simple and the visualization too but this project got super interesting when I decided to use sveltejs in it. With svelte I created a super convenient architecture where I can have one component to run the p5js animation and dedicated components to control the simulation. This component system allows me to easily plug new visualizations, or to test new graphic frameworks (I'm currently experimenting with a threejs component). I think setting up my first project with p5 and svelte is a major break through in my side project as it should allow me to be faster to create well architectured, more complete and more convenient projects, so I'm looking forward to have more time to do new experiments. +- In a completely different area I also finally created my [setup repository](https://github.com/statox/setup). This is something I had been wanting to do for many many years but when my Dashlane computer broke and I had to reinstall of my setup I decided it was the final straw. The great thing is that at Dashlane I have had the opportunity to work a lot with Ansible and to better understand how it is supposed to work and to be used. Also a teammate of mine kindly let me look into his own repository of Ansible playbooks and that got me started. Now I have a collection of playbooks which allow me to setup most of my common tools of a Linux machine from the Desktop environment, to my favorite packages and configurations, to my own dotfiles. This is a project which will keep growing and that I'll need to evolve regularly like [my dotfiles](https://github.com/statox/dotfiles) but it's incredibly satisfying to use. I'm also evolving it to handle my different server setups like my OVH box or my local raspberry! +- And I also created a script which scraps [Nasa's Astronomy picture of the day](https://apod.nasa.gov/) every day and sets it up as my desktop background as well as my Zoom virtual background. I intended to write an article about that because Zoom doesn't allow to you programmatically change your background and I was happy to find a solution but I haven't found the motivation to do it yet ### Staying healthy Another huge change I have experienced other the last 6 months is that I realized I actually like doing sport. Ever since I was a kid I have never felt particularly attracted by sports (despite doing fencing for close to 10 years 🤷) and I blame the French school system for that (I should write about it one day, maybe that would be more interesting than my random coding adventures). But since December I'm taking yoga classes and I'm loving it! -I think I'm lucky because I found a great setup where I can do yoga with people I love which is super motivating but I also dared taking classes by myself! I have really enjoyed getting into this yoga thing I knew nothing about, learning the different types of yoga, trying to understand the meaning and the origin of this practice, figuring out what I like and what I don't... So know I can have a conversation about Yoga and I've been going (almost) every week for the past 4 months which I'm really proud about. Maybe at one point I'll actually start to feel physical benefits but at least for now it's a good mental health thing to do! +I think I'm lucky because I found a great setup where I can do yoga with people I love which is super motivating but I also dared taking classes by myself! I have really enjoyed getting into this yoga thing I knew nothing about, learning the different types of yoga, trying to understand the meaning and the origin of this practice, figuring out what I like and what I don't... So know I can have a conversation about Yoga and I've been going (almost) every week for the past 4 months which I'm really proud about. Maybe at one point I'll actually start to feel physical benefits but at least for now it's a good mental health thing to do! I'm also looking into getting enrolled in a mentoring program for young people who struggle finding a job, I'm still figuring out the details but it might be a great way to give back what life gave me which would be quite cool. - ### I'm doing well So yeah if I reflect on the past 6 months I've been doing stuff, I think I'm pretty happy, I have great stuff going on at work and in my personal life too that I won't write about here and the weather is getting great again! Life is good. diff --git a/src/posts/2024/04/api_logging_elasticsearch/index.md b/src/posts/2024/04/api_logging_elasticsearch/index.md index 51dae7120..fe6aff13c 100644 --- a/src/posts/2024/04/api_logging_elasticsearch/index.md +++ b/src/posts/2024/04/api_logging_elasticsearch/index.md @@ -8,54 +8,51 @@ commentIssueId: 36 Notes about how I set up an [OpenSearch](https://docs.aws.amazon.com/opensearch-service/) cluster to manage the logs of [my api](https://github.com/statox/api.statox.fr). -Goal: Set up the cheapest ELK cluster possible to - - Ingest the logs of my API - - Maybe use as a tracking database for my habits +Goal: Set up the cheapest ELK cluster possible to - Ingest the logs of my API - Maybe use as a tracking database for my habits TODO: -- Review costs after some usage. The idea was to benefit from the free tier, after one week it seems I don't get it. -- Review [UltraWarm](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ultrawarm.html) nodes they might be usefull for my usage if I can't benefit from the free tier. - +- Review costs after some usage. The idea was to benefit from the free tier, after one week it seems I don't get it. +- Review [UltraWarm](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ultrawarm.html) nodes they might be usefull for my usage if I can't benefit from the free tier. ## Setup the cluster in AWS Everything done easily from the AWS console. I had to use "Standard create" to be able to setup a smaller cluster than what AWS recommends because I have very small needs. ![Setup screen 1](./cluster_standard_create.png) +
    Not using the default Easy mode
    - ![Setup screen 2](./cluster_single_availability_zone.png) +
    Specifying a single availability zone to limit the costs.
    ![Setup screen 3](./cluster_single_data_node.png) +
    Using a single node both for master and data with the smallest possible EBS and minimum IOPS.
    - I also set up a custom endpoint so that I can reach my cluster from logs.statox.fr instead of the default long endpoint provided by ELK. The setup is simple: Create a certicate in ACM, setup a DNS verification to validate the certificate and use the certificate in the setup screen of OpenSearch. - -The security is far from ideal but pretty convenient for a very-not-critical API like mine: The cluster is open to internet and the auth is done with ELK's built-in user store. (Note that the automatic software updates are enabled thank to the managed aspect of the cluster, I'll need to make sure it doesn't break stuff when I don't do anything). +The security is far from ideal but pretty convenient for a very-not-critical API like mine: The cluster is open to internet and the auth is done with ELK's built-in user store. (Note that the automatic software updates are enabled thank to the managed aspect of the cluster, I'll need to make sure it doesn't break stuff when I don't do anything). ![Setup screen 4](./cluster_security.png) +
    Using a single node both for master and data with the smallest possible EBS and minimum IOPS.
    - ![Setup screen 5](./cluster_security_2.png) +
    TODO: Check if I can better restrict this policy.
    - By default the cluster has hourly snapshots. The cluster takes several minutes to start up, once it is up we can test that it answers properly: ```bash @@ -93,9 +90,9 @@ This is the template of settings which will be applied to all the indices which Important settings: -- Specify that we are working with data streams -- Specify the `timestamp` field as the time field (which will allow the proper indexing and temporal search). -- We are not really using index pattern as with regular indices because we are using a data stream, so the `index pattern` property must have the name of the stream we will create later on. _Note: No * character_ +- Specify that we are working with data streams +- Specify the `timestamp` field as the time field (which will allow the proper indexing and temporal search). +- We are not really using index pattern as with regular indices because we are using a data stream, so the `index pattern` property must have the name of the stream we will create later on. _Note: No \* character_ ![Index template settings 1](./index_template_1.png) @@ -107,7 +104,6 @@ The index mapping are useful to specify that a specific field is not a simple nu ![Index template settings 3](./index_template_3.png) - ### 2. Create the data stream If created with the name used in the index template's `index pattern` field the stream will be associated to the template. @@ -121,7 +117,6 @@ curl -H 'Content-Type: application/json' -XPOST --user "$ELASTIC_USER:$ELASTIC_ -d '{"message": "Test message from bash", "timestamp": $(date +"%s")000}' # With a shitty hack to transform seconds to milliseconds ``` - ### 3. Create the Index pattern This is the entity which tells to Dashboards (Kibana) which indices to search when you are in the `Dicover` tab. @@ -139,19 +134,16 @@ From there the logs are available in the `Discover` tab: This is the final step to make sure the indices backing the data streams are regularly rotated (so that at one point they can be deleted to keep the volume of data below a certain threshold without dropping a unique index with all the logs). - An optional step 1 is to setup a Notification channel to be notified when the policy fails, settings up a notification on a slack webhook is super straight forward, just past the webhook URL and you're done. - ![Index policy settings](./index_policy_1.png) By using the correct index pattern, the policy will be applied to all the indices of the data stream and will rollover them. - ## Conclusion This kind of setup is something I'm used to do on a much bigger scale in my day job so I see the cracks in this current setup but at least it's working conveniently for my small needs. -- It would be great to terraform the AWS part (but, _la flemme_) -- I need to check if I can easily restore the automatic snapshots made by AWS and if that keeps the configurations I did in Kibana (in theory, yes) -- Next I'll use this document store as part of the product of my api. +- It would be great to terraform the AWS part (but, _la flemme_) +- I need to check if I can easily restore the automatic snapshots made by AWS and if that keeps the configurations I did in Kibana (in theory, yes) +- Next I'll use this document store as part of the product of my api. diff --git a/src/posts/2024/04/github_dependabot_auto_merge/index.md b/src/posts/2024/04/github_dependabot_auto_merge/index.md index 20b1f1971..a7d24c4fa 100644 --- a/src/posts/2024/04/github_dependabot_auto_merge/index.md +++ b/src/posts/2024/04/github_dependabot_auto_merge/index.md @@ -31,13 +31,13 @@ Allow Dependabot to regularly create PRs with dependencies update. In the Github repo settings: Security > Code security and analysis > Dependabot ![Dependabot setup screen](./dependabot_setup.png) +
    Enable the Dependabot alerts
    For better configuration a file `github/dependabot.yml` can be created in the repo, see [the doc](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file) for more details. - ### Actions settings Allow GitHub Actions to create and approve pull requests. This is needed because the workflow we will trigger will be responsible for approving the PR automatically. @@ -45,6 +45,7 @@ Allow GitHub Actions to create and approve pull requests. This is needed because In the Github repo settings: Code and automation > Actions > General > Workflow permissions ![Actions settings screen](./allow_action_approve_PR.png) +
    Enable the actions to modify PRs
    @@ -57,15 +58,15 @@ The rule needs to apply to the `main` branch (i.e. the one we'll be merging to). We need two checks enforced by the rule: -- `Require a pull request before merging` and `Require approvals`: The approval will be given by `github-action` bot throught the workflow -- `Require status checks to pass before merging`: The checks will be the success of the workflow itself and we'll make the workflow fail if the tests for the repo are not validated. +- `Require a pull request before merging` and `Require approvals`: The approval will be given by `github-action` bot throught the workflow +- `Require status checks to pass before merging`: The checks will be the success of the workflow itself and we'll make the workflow fail if the tests for the repo are not validated. ![Branch protection screen](./branch_protection_setup.png) +
    Enforce PR approval and checks
    - ## Setup the workflow In the repo create a file for the workflow like `.github/workflows/dependabot-auto-merge.yml` @@ -76,15 +77,15 @@ We will trigger the workflow on PRs. **TODO** Find a way to trigger only for dependabot PRs, for now all MR will be automatically merged if they pass the tests. -- `types`: Using `edited` is useful to debug the workflow while setting it up: once the MR is open, you can edit the workflow, push, comment `@dependabot rebase` on the PR and the workflow will be re-run +- `types`: Using `edited` is useful to debug the workflow while setting it up: once the MR is open, you can edit the workflow, push, comment `@dependabot rebase` on the PR and the workflow will be re-run ```yaml name: Test and AutoMerge PRs on: - pull_request: - types: [opened, synchronize, edited] - branches: [main] + pull_request: + types: [opened, synchronize, edited] + branches: [main] ``` ### Permissions @@ -93,16 +94,16 @@ This changes the permissions of the github token that dependabot gets when creat ```yaml permissions: - # This is needed to approve the PR - pull-requests: write - # This is needed to merge the PR https://github.com/cli/cli/issues/6695#issuecomment-1348430969 - contents: write + # This is needed to approve the PR + pull-requests: write + # This is needed to merge the PR https://github.com/cli/cli/issues/6695#issuecomment-1348430969 + contents: write ``` Without these permissions the calls the the `gh` cli in the next steps fail with errors similar to this: - ![gh cli permission error](./auto_merge_permissions_error.png) +
    Example of gh permission error
    @@ -113,24 +114,23 @@ One way to have the whole workflow triggered only for dependabot's PR is to add ```yaml jobs: - test-and-auto-merge: - if: github.actor == 'dependabot[bot]' - runs-on: ubuntu-latest - steps: - # [...] + test-and-auto-merge: + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + # [...] ``` This could be improved to better factorize the code and have the tests running on all MR and the auto merge running only on dependabot PRs but since I'm the only one working on this repo and I don't use PRs for other reasons I will not bother with that. - ### Repo setup and tests This is dependent on all repos though the important part is to run some tests. Here we need several setup steps: -- Install `python` throught a marketplace Github action to be able to install `podman-compose` with `pip` -- Install `podman` throught a marketplace Github action. -- Install `node` to run build the project and run the tests. -- Checkout the code, install the deps and run the tests. +- Install `python` throught a marketplace Github action to be able to install `podman-compose` with `pip` +- Install `podman` throught a marketplace Github action. +- Install `node` to run build the project and run the tests. +- Checkout the code, install the deps and run the tests. If these steps fail the following one will fail too, so the PRs tests will fail and the code will not be merged. @@ -177,9 +177,9 @@ jobs: Three important steps: -- Call the `fetch-metadata` action to get the `$PR_URL` variable referring to the current PR and used in the next steps. -- Have the `github-action` bot approve the PR -- Have the `github-action` bot set the `auto merge` setting for the PR, which will trigger the merge of the PR because all the checks will succeed. +- Call the `fetch-metadata` action to get the `$PR_URL` variable referring to the current PR and used in the next steps. +- Have the `github-action` bot approve the PR +- Have the `github-action` bot set the `auto merge` setting for the PR, which will trigger the merge of the PR because all the checks will succeed. ```yaml jobs: @@ -214,18 +214,19 @@ jobs: Once all of that is configured the Dependabot PRs should be automatically handled. ![PR checks tab](./PR_checks_tab.png) +
    The check tab of the PR should have some results
    - ![PR checks results](./PR_check_result.png) +
    The checks should point to a succesful action
    - ![PR bots results](./PR_bots_results.png) +
    The various bots should have acted on the PR
    diff --git a/src/posts/posts.md b/src/posts/posts.md index b911fcea3..bb278dc82 100644 --- a/src/posts/posts.md +++ b/src/posts/posts.md @@ -1,7 +1,6 @@ --- layout: layouts/posts.njk eleventyNavigation: - key: Posts - order: 2 + key: Posts + order: 2 --- - diff --git a/src/todo/circular_jump.md b/src/todo/circular_jump.md index e320d7375..3b264e341 100644 --- a/src/todo/circular_jump.md +++ b/src/todo/circular_jump.md @@ -13,12 +13,11 @@ The track should be circular. The player starts at an angle of 0 and the goal is to make as many full cycles as possible. The progress detection could be: - - Start at angle 0 - - On each frame check the current angle - - If it's larger than the previous max value update it - - If it's larger than 2 pi, add 1 turn to the counter and reset with the difference to zero - - Make sure to handle <0 angle to avoid counting "reverse" turns - +- Start at angle 0 +- On each frame check the current angle + - If it's larger than the previous max value update it + - If it's larger than 2 pi, add 1 turn to the counter and reset with the difference to zero + - Make sure to handle <0 angle to avoid counting "reverse" turns The obstacles could be generated either from the angle 0 or directly behind the player and starting in the opposite direction. diff --git a/src/todo/homemade_monitoring.md b/src/todo/homemade_monitoring.md index 1ff01dfc2..ac35ed1de 100644 --- a/src/todo/homemade_monitoring.md +++ b/src/todo/homemade_monitoring.md @@ -9,6 +9,6 @@ Goal: Setup a cheap monitoring and alerting platform for my personal infrastruct Things to monitor: - - Pihole - - This website - - Backups from my NAS +- Pihole +- This website +- Backups from my NAS diff --git a/src/todo/todo_page.md b/src/todo/todo_page.md index 0a415ea47..ee2716220 100644 --- a/src/todo/todo_page.md +++ b/src/todo/todo_page.md @@ -7,6 +7,6 @@ title: Todo page on the blog This page is a work in progress that I want to finish. -- [ ] Rework the UI to better show the items (for now it is mostly based on the notes layouts) -- [X] Add the ability to add an entry from a Github issue like it's done with the chords -- [ ] Add sort and search features (by name, content and tags) +- [ ] Rework the UI to better show the items (for now it is mostly based on the notes layouts) +- [x] Add the ability to add an entry from a Github issue like it's done with the chords +- [ ] Add sort and search features (by name, content and tags) diff --git a/src/todo/weather_station.md b/src/todo/weather_station.md index 943e842b8..370cf09bb 100644 --- a/src/todo/weather_station.md +++ b/src/todo/weather_station.md @@ -9,7 +9,7 @@ Goal: Setup a weather monitoring system for the house. Components: - - Multiple temperature, humidity and air quality sensors across the house - - Probably running on arduino nano or raspberry-pi zero - - A centralized server getting the data from the sensors - - A monitoring system with history of the data +- Multiple temperature, humidity and air quality sensors across the house + - Probably running on arduino nano or raspberry-pi zero +- A centralized server getting the data from the sensors +- A monitoring system with history of the data diff --git a/src/user_manuals/index.md b/src/user_manuals/index.md index f7f46cc69..8720bc783 100644 --- a/src/user_manuals/index.md +++ b/src/user_manuals/index.md @@ -8,36 +8,36 @@ Some user manuals I have collected over time and might need in the future. ### Pedals Manuals -- [Behringer BO100](https://mediadl.musictribe.com/media/sys_master/hed/h3f/8850084069406.pdf) (_[mirror](/pdf/pedal_user_manuals/behringer_bo100_user_manual.pdf)_) -- [Boss BD2](https://static.roland.com/assets/media/pdf/BD-2_eng02_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_bd2_user_manual.pdf)_) -- [Boss CH1](https://static.roland.com/assets/media/pdf/CH-1_M_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_ch1_user_manual.pdf)_) -- [Boss OC3](https://static.roland.com/assets/media/pdf/OC-3_e01_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_oc3_user_manual.pdf)_) -- [Boss RC3](https://static.roland.com/assets/media/pdf/RC-3_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc3_user_manual.pdf)_) -- [Boss RC10-R](https://static.roland.com/assets/media/pdf/RC-10R_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc10r_user_manual.pdf)_) -- [Donner MiniPedal Dark Mouse](https://cdn.shopify.com/s/files/1/1384/9629/files/EC1178_Dark_Mouse__Manual_V02_190809.pdf?v=1597289663) (_[mirror](/pdf/pedal_user_manuals/donner_dark_mouse_manual.pdf)_) -- [Electro-Harmonix Canyon](https://www.ehx.com/wp-content/uploads/2021/01/canyon-manual.pdf) (_[mirror](/pdf/pedal_user_manuals/ehx_canyon_manual.pdf)_) -- [EarthQuaker Devices Afterneath](https://static1.squarespace.com/static/57cebe2c03596e075fca5f24/t/656fc46102d950061c2b9d06/1701823585949/EQD-Afterneath-Manual-R2-WEB.pdf) (_[mirror](/pdf/pedal_user_manuals/eqd_afterneath_manual.pdf)_) -- [Peterson StroboStomp HD tuner](https://www.petersontuners.com/media/pdf/StroboStomp_HD_Manual_v1.1_EN.pdf) (_[mirror](/pdf/pedal_user_manuals/StroboStomp_HD_Manual_v1.1_EN.pdf)_) -- [ZVEX Instant Lofi Junky](https://static1.squarespace.com/static/555e332ce4b0577e788c3a16/t/559efae5e4b0da93269a9ffb/1436482277079/ZVEX+Instant+Lo-Fi+Junky+Instructions.pdf) (_[mirror](/pdf/pedal_user_manuals/zvex_instant_lofi_junky_user_manual.pdf)_) +- [Behringer BO100](https://mediadl.musictribe.com/media/sys_master/hed/h3f/8850084069406.pdf) (_[mirror](/pdf/pedal_user_manuals/behringer_bo100_user_manual.pdf)_) +- [Boss BD2](https://static.roland.com/assets/media/pdf/BD-2_eng02_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_bd2_user_manual.pdf)_) +- [Boss CH1](https://static.roland.com/assets/media/pdf/CH-1_M_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_ch1_user_manual.pdf)_) +- [Boss OC3](https://static.roland.com/assets/media/pdf/OC-3_e01_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_oc3_user_manual.pdf)_) +- [Boss RC3](https://static.roland.com/assets/media/pdf/RC-3_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc3_user_manual.pdf)_) +- [Boss RC10-R](https://static.roland.com/assets/media/pdf/RC-10R_eng03_W.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_rc10r_user_manual.pdf)_) +- [Donner MiniPedal Dark Mouse](https://cdn.shopify.com/s/files/1/1384/9629/files/EC1178_Dark_Mouse__Manual_V02_190809.pdf?v=1597289663) (_[mirror](/pdf/pedal_user_manuals/donner_dark_mouse_manual.pdf)_) +- [Electro-Harmonix Canyon](https://www.ehx.com/wp-content/uploads/2021/01/canyon-manual.pdf) (_[mirror](/pdf/pedal_user_manuals/ehx_canyon_manual.pdf)_) +- [EarthQuaker Devices Afterneath](https://static1.squarespace.com/static/57cebe2c03596e075fca5f24/t/656fc46102d950061c2b9d06/1701823585949/EQD-Afterneath-Manual-R2-WEB.pdf) (_[mirror](/pdf/pedal_user_manuals/eqd_afterneath_manual.pdf)_) +- [Peterson StroboStomp HD tuner](https://www.petersontuners.com/media/pdf/StroboStomp_HD_Manual_v1.1_EN.pdf) (_[mirror](/pdf/pedal_user_manuals/StroboStomp_HD_Manual_v1.1_EN.pdf)_) +- [ZVEX Instant Lofi Junky](https://static1.squarespace.com/static/555e332ce4b0577e788c3a16/t/559efae5e4b0da93269a9ffb/1436482277079/ZVEX+Instant+Lo-Fi+Junky+Instructions.pdf) (_[mirror](/pdf/pedal_user_manuals/zvex_instant_lofi_junky_user_manual.pdf)_) ### Others Manuals -- Amp [Laney LA35C acoustic](https://www.zikinf.com/manuels/laney-la35c-manuel-utilisateur-en-28749.pdf) (_[mirror](/pdf/laney-la35c.pdf)_) -- Amp [Marshall Valvestate 8040 (and similar 8xxx series)](https://manualzz.com/download/8600586) (_[mirror](/pdf/marshall-valvestate.pdf)_) -- Guitar Fender Telecaster American Pro II - - [User manual](https://www.fmicassets.com/Damroot/Original/10001/Fender_ElectricGuitars_Manual.pdf) (_[mirror](/pdf/Fender_ElectricGuitars_Manual.pdf)_) - - [Service manual](https://www.fmicassets.com/Damroot/Original/10001/SM_011394XXXX_Am_Pro_II_Telecaster.pdf) (_[mirror](/pdf/SM_011394XXXX_Am_Pro_II_Telecaster.pdf)_) -- [Lottie Canto - Colour Palette](/pdf/lottie_canto_colour_palette.pdf) -- Midi Controller [Akai MPK mini mk3](https://cdn.inmusicbrands.com/akai/mpk3mini/MPK-mini-Quickstart-Guide-v1_3.pdf) (_[mirror](/pdf/Akai_mpk_mini_mk3_quickstart_guide_v1_3.pdf)_) -- Roland Aira Compact synths: - - [J6 chord synth](https://static.roland.com/assets/media/pdf/J-6_eng02_W.pdf) (_[mirror](/pdf/rolan_j6_owners_manual_english.pdf)_) - - [T8 beat machine](https://static.roland.com/assets/media/pdf/T-8_eng01_W.pdf) (_[mirror](/pdf/rolan_t8_owners_manual_english.pdf)_) +- Amp [Laney LA35C acoustic](https://www.zikinf.com/manuels/laney-la35c-manuel-utilisateur-en-28749.pdf) (_[mirror](/pdf/laney-la35c.pdf)_) +- Amp [Marshall Valvestate 8040 (and similar 8xxx series)](https://manualzz.com/download/8600586) (_[mirror](/pdf/marshall-valvestate.pdf)_) +- Guitar Fender Telecaster American Pro II + - [User manual](https://www.fmicassets.com/Damroot/Original/10001/Fender_ElectricGuitars_Manual.pdf) (_[mirror](/pdf/Fender_ElectricGuitars_Manual.pdf)_) + - [Service manual](https://www.fmicassets.com/Damroot/Original/10001/SM_011394XXXX_Am_Pro_II_Telecaster.pdf) (_[mirror](/pdf/SM_011394XXXX_Am_Pro_II_Telecaster.pdf)_) +- [Lottie Canto - Colour Palette](/pdf/lottie_canto_colour_palette.pdf) +- Midi Controller [Akai MPK mini mk3](https://cdn.inmusicbrands.com/akai/mpk3mini/MPK-mini-Quickstart-Guide-v1_3.pdf) (_[mirror](/pdf/Akai_mpk_mini_mk3_quickstart_guide_v1_3.pdf)_) +- Roland Aira Compact synths: + - [J6 chord synth](https://static.roland.com/assets/media/pdf/J-6_eng02_W.pdf) (_[mirror](/pdf/rolan_j6_owners_manual_english.pdf)_) + - [T8 beat machine](https://static.roland.com/assets/media/pdf/T-8_eng01_W.pdf) (_[mirror](/pdf/rolan_t8_owners_manual_english.pdf)_) ### Misc -- [Boss Guitar Effects Guide Book Vol.20](https://cdn.roland.com/assets/media/pdf/guitar_effects_guidebook_vol_20.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_guitar_effects_guidebook_vol_20.pdf)_) -- [The science of electric guitars and guitar electronics](https://guitarscience.net/papers/guibook.pdf) (_[mirror](/pdf/the_science_of_electric_guitars_and_guitar_electronic.pdf)_) -- [Advanced DIY effect pedals](https://guitar-gear.ru/forum/index.php?app=core&module=attach§ion=attach&attach_id=39391&usg=AOvVaw2IgS-cfBeiFW7RWgrPUhWA) (_[mirror](/pdf/advanced_diy_effect_pedals.pdf)_) +- [Boss Guitar Effects Guide Book Vol.20](https://cdn.roland.com/assets/media/pdf/guitar_effects_guidebook_vol_20.pdf) (_[mirror](/pdf/pedal_user_manuals/boss_guitar_effects_guidebook_vol_20.pdf)_) +- [The science of electric guitars and guitar electronics](https://guitarscience.net/papers/guibook.pdf) (_[mirror](/pdf/the_science_of_electric_guitars_and_guitar_electronic.pdf)_) +- [Advanced DIY effect pedals](https://guitar-gear.ru/forum/index.php?app=core&module=attach§ion=attach&attach_id=39391&usg=AOvVaw2IgS-cfBeiFW7RWgrPUhWA) (_[mirror](/pdf/advanced_diy_effect_pedals.pdf)_) _These manuals are not my creation. They are not distributed under the same licence as the rest of this blog. They includes logos, images and trademarks which belong to their respective owners. I have found these manuals distributed by a lot of different sources on the internet so I assume it is fine to have a mirror here, especially because I link to the official resources too._