feat: massive overhaul
Signed-off-by: Louis Vallat <contact@louis-vallat.fr>
5
.gitignore
vendored
@ -1,4 +1,3 @@
|
|||||||
dst/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
node_modules/
|
public
|
||||||
package-lock.json
|
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
image: docker:latest
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- release
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
# Disable the Gradle daemon for Continuous Integration servers as correctness
|
|
||||||
# is usually a priority over speed in CI environments. Using a fresh
|
|
||||||
# runtime for each build is more reliable since the runtime is completely
|
|
||||||
# isolated from any previous builds.
|
|
||||||
variables:
|
|
||||||
# Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
|
|
||||||
DOCKER_HOST: tcp://docker:2376
|
|
||||||
DOCKER_TLS_CERTDIR: "/certs"
|
|
||||||
CONTAINER_BRANCH_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
|
|
||||||
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
|
|
||||||
|
|
||||||
release:
|
|
||||||
stage: release
|
|
||||||
script:
|
|
||||||
- sed -i "s/__VERSION__/${CI_COMMIT_SHORT_SHA}/" src/_footer.html
|
|
||||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
||||||
- docker build . -t $CONTAINER_BRANCH_IMAGE
|
|
||||||
- docker push $CONTAINER_BRANCH_IMAGE
|
|
||||||
except:
|
|
||||||
- master
|
|
||||||
|
|
||||||
release-master:
|
|
||||||
stage: release
|
|
||||||
script:
|
|
||||||
- sed -i "s/__VERSION__/${CI_COMMIT_SHORT_SHA}/" src/_footer.html
|
|
||||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
||||||
- docker build . -t $CONTAINER_RELEASE_IMAGE
|
|
||||||
- docker push $CONTAINER_RELEASE_IMAGE
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
stage: deploy
|
|
||||||
before_script:
|
|
||||||
- eval $(ssh-agent -s)
|
|
||||||
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
|
|
||||||
- mkdir -p ~/.ssh
|
|
||||||
- chmod 700 ~/.ssh
|
|
||||||
script:
|
|
||||||
- ssh -o StrictHostKeyChecking=no -p $SSH_PORT $SSH_DESTINATION "cd $PATH_TO_APPLICATION; $UPGRADE_COMMAND;"
|
|
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "themes/terminimal"]
|
||||||
|
path = themes/terminimal
|
||||||
|
url = https://github.com/pawroman/zola-theme-terminimal.git
|
16
Dockerfile
@ -1,16 +0,0 @@
|
|||||||
FROM alpine AS builder
|
|
||||||
|
|
||||||
WORKDIR /root
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN apk --no-cache add gcc git make musl-dev npm && \
|
|
||||||
git clone -b VERSION_0_10_0 https://github.com/kristapsdz/lowdown.git && \
|
|
||||||
cd lowdown && ./configure && make && make regress && make install && cd .. && \
|
|
||||||
npm install && \
|
|
||||||
chmod u+x ./ssg5 && mkdir -p dst && \
|
|
||||||
./ssg5 ./src/ ./dst/ "Le blog de Louis Vallat" "https://blog.louis-vallat.xyz" && rm dst/.files
|
|
||||||
|
|
||||||
FROM nginx
|
|
||||||
|
|
||||||
COPY --from=builder /root/dst /usr/share/nginx/html
|
|
18
README.md
@ -1,17 +1,9 @@
|
|||||||
# Blog
|
# Blog
|
||||||
|
|
||||||
> This is the source code from [this live website](https://blog.louis-vallat.xyz).
|
> This is the source code from [this live website](https://blog.louis-vallat.fr).
|
||||||
|
|
||||||
## Todo
|
Build it using [Zola](https://www.getzola.org):
|
||||||
|
|
||||||
- [ ] Add dark and light theme switch and colors
|
```bash
|
||||||
|
zola build
|
||||||
## Sources
|
```
|
||||||
|
|
||||||
- [SSG](https://www.romanzolotarev.com/ssg.html)
|
|
||||||
- [Wolfgang's video](https://youtu.be/N_ttw2Dihn8): `Fast & Minimal Blog Using POSIX Shell and Markdown`
|
|
||||||
|
|
||||||
## Licenses
|
|
||||||
|
|
||||||
The fonts provided in this repository are licensed under the [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL).
|
|
||||||
The code, if not stated otherwise, is licensed under the [GPLv3 License](LICENSE).
|
|
||||||
|
37
config.toml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
base_url = "https://blog.louis-vallat.fr"
|
||||||
|
title = "Le blog de Louis"
|
||||||
|
description = "Le blog très simple d'un développeur très compliqué."
|
||||||
|
|
||||||
|
theme = "terminimal"
|
||||||
|
compile_sass = true
|
||||||
|
default_language = "fr"
|
||||||
|
|
||||||
|
generate_feed = true
|
||||||
|
feed_filename = "atom.xml"
|
||||||
|
|
||||||
|
taxonomies = [
|
||||||
|
{name = "tags"},
|
||||||
|
{name = "categories"}
|
||||||
|
]
|
||||||
|
|
||||||
|
[markdown]
|
||||||
|
highlight_code = true
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
logo_text = "Le blog de Louis"
|
||||||
|
author = "Louis Vallat"
|
||||||
|
page_titles = "combined"
|
||||||
|
|
||||||
|
enable_post_view_navigation = true
|
||||||
|
post_view_navigation_prompt = "En savoir plus"
|
||||||
|
|
||||||
|
menu_items = [
|
||||||
|
{name = "blog", url = "$BASE_URL"},
|
||||||
|
{name = "tags", url = "$BASE_URL/tags"},
|
||||||
|
{name = "archive", url = "$BASE_URL/archive"},
|
||||||
|
{name = "portfolio", url = "https://louis-vallat.fr"},
|
||||||
|
{name = "gitlab", url = "https://gitlab.com/lovallat", newtab = true},
|
||||||
|
]
|
||||||
|
|
||||||
|
favicon = "/favicon.png"
|
||||||
|
favicon_mimetype = "image/png"
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@ -1,12 +1,21 @@
|
|||||||
# Git Workflow
|
+++
|
||||||
|
title = "Git Workflow"
|
||||||
|
description = "Git est un outil de versionnage très utile, voici comment je m'en sers."
|
||||||
|
date = 2021-05-10
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tags = ["git", "workflow"]
|
||||||
|
+++
|
||||||
|
|
||||||
Git est un outil de versionnage très utile, voici comment je m'en sers.
|
Git est un outil de versionnage très utile, voici comment je m'en sers.
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
> À noter que ce guide est destiné aux personnes qui ont déjà utilisé git auparavant,
|
> À noter que ce guide est destiné aux personnes qui ont déjà utilisé git auparavant,
|
||||||
> il ne s'agit pas ici d'un tutoriel pour débutants (bien que je sois ici assez
|
> il ne s'agit pas ici d'un tutoriel pour débutants (bien que je sois ici assez
|
||||||
> exhaustif dans mes explications).
|
> exhaustif dans mes explications).
|
||||||
|
|
||||||
[TL;DR](#TL;DR)
|
[TL;DR](#tldr)
|
||||||
|
|
||||||
## Les fondations
|
## Les fondations
|
||||||
|
|
||||||
@ -21,7 +30,7 @@ par exemple, aka. vous avez committé et quelqu'un d'autre aussi en même temps)
|
|||||||
Nous allons donc demander à git de ne pull que lorsque l'historique est linéaire
|
Nous allons donc demander à git de ne pull que lorsque l'historique est linéaire
|
||||||
(fast-forward), et sinon de faire automatiquement un rebase :
|
(fast-forward), et sinon de faire automatiquement un rebase :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git config pull.rebase true
|
git config pull.rebase true
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -33,7 +42,7 @@ git. Voici un alias, à placer dans votre `~/.gitconfig`, qui vous permet d'affi
|
|||||||
un graphique de vos branches et vos commits de manière plus visuelle que le `git log`
|
un graphique de vos branches et vos commits de manière plus visuelle que le `git log`
|
||||||
de base de git (honteusement volés [ici](https://stackoverflow.com/a/9074343)) :
|
de base de git (honteusement volés [ici](https://stackoverflow.com/a/9074343)) :
|
||||||
|
|
||||||
```git
|
```conf
|
||||||
[alias]
|
[alias]
|
||||||
lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
|
lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
|
||||||
lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
|
lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
|
||||||
@ -43,13 +52,13 @@ de base de git (honteusement volés [ici](https://stackoverflow.com/a/9074343))
|
|||||||
Si vous n'avez pas besoin d'autant de décorations, ou que vous ne pouvez pas définir
|
Si vous n'avez pas besoin d'autant de décorations, ou que vous ne pouvez pas définir
|
||||||
d'alias, cette commande fait quelque chose d'assez similaire :
|
d'alias, cette commande fait quelque chose d'assez similaire :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git log --all --decorate --oneline --graph
|
git log --all --decorate --oneline --graph
|
||||||
```
|
```
|
||||||
|
|
||||||
Pour avoir des messages de commits unifiés et cohérents avec de jolis alias :
|
Pour avoir des messages de commits unifiés et cohérents avec de jolis alias :
|
||||||
|
|
||||||
```git
|
```conf
|
||||||
fix = "!f() { git commit -sm \"fix: $1\"; }; f"
|
fix = "!f() { git commit -sm \"fix: $1\"; }; f"
|
||||||
feat = "!f() { git commit -sm \"feat: $1\"; }; f"
|
feat = "!f() { git commit -sm \"feat: $1\"; }; f"
|
||||||
misc = "!f() { git commit -sm \"misc: $1\"; }; f"
|
misc = "!f() { git commit -sm \"misc: $1\"; }; f"
|
||||||
@ -85,19 +94,19 @@ stabilité par exemple, ou qui n'est pas prête pour la mise en production.
|
|||||||
|
|
||||||
Pour créer une branche fille à partir de la branche sur laquelle vous êtes :
|
Pour créer une branche fille à partir de la branche sur laquelle vous êtes :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git checkout -b nom_de_la_nouvelle_branche
|
git checkout -b nom_de_la_nouvelle_branche
|
||||||
```
|
```
|
||||||
|
|
||||||
Et pour basculer de branche en branche :
|
Et pour basculer de branche en branche :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git checkout nom_de_la_branche
|
git checkout nom_de_la_branche
|
||||||
```
|
```
|
||||||
|
|
||||||
Si vous ne savez pas sur quelle branche vous vous trouvez, vous pouvez faire :
|
Si vous ne savez pas sur quelle branche vous vous trouvez, vous pouvez faire :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git branch
|
git branch
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -120,7 +129,7 @@ notre branche de travail, par exemple la résolution d'un bogue handicapant.
|
|||||||
Un `rebase` va faire changer votre commit de départ et l'échanger avec un autre.
|
Un `rebase` va faire changer votre commit de départ et l'échanger avec un autre.
|
||||||
Prenons le schéma suivant :
|
Prenons le schéma suivant :
|
||||||
|
|
||||||
![Schéma d'un rebase](assets/images/git_flow/git_rebase.svg)
|
![Schéma d'un rebase](images/git_rebase.svg)
|
||||||
|
|
||||||
Nous avons ici la branche `new_feature` qui part d'un commit de master. Disons
|
Nous avons ici la branche `new_feature` qui part d'un commit de master. Disons
|
||||||
que vous êtes en train de développer une fonctionnalité importante, et qu'un bug
|
que vous êtes en train de développer une fonctionnalité importante, et qu'un bug
|
||||||
@ -129,7 +138,7 @@ de votre branche, et qu'il est disponible sur `master`. Pour le récupérer sur
|
|||||||
branche, vous n'avez qu'à `rebase` votre branche sur la dernière version de `master`.
|
branche, vous n'avez qu'à `rebase` votre branche sur la dernière version de `master`.
|
||||||
La commande pour faire ceci serait alors :
|
La commande pour faire ceci serait alors :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git rebase nom_de_branche_parente
|
git rebase nom_de_branche_parente
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -173,7 +182,7 @@ forcément toutes en tête.
|
|||||||
|
|
||||||
Pour fusionner une branche dans celle sur laquelle vous vous trouvez :
|
Pour fusionner une branche dans celle sur laquelle vous vous trouvez :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git merge nom_de_la_branche_à_fusionner_dans_celle_sur_laquelle_vous_êtes
|
git merge nom_de_la_branche_à_fusionner_dans_celle_sur_laquelle_vous_êtes
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -188,7 +197,7 @@ temps de lui dire au revoir !
|
|||||||
|
|
||||||
Commençons d'abord par la supprimer sur le repo distant :
|
Commençons d'abord par la supprimer sur le repo distant :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git push -d origin nom_de_branche
|
git push -d origin nom_de_branche
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -200,7 +209,7 @@ références locales à des branches fantômes distantes avec la commande `git f
|
|||||||
|
|
||||||
Enfin, il est temps de supprimer votre copie locale de cette branche :
|
Enfin, il est temps de supprimer votre copie locale de cette branche :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git branch -d nom_de_branche
|
git branch -d nom_de_branche
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -210,12 +219,12 @@ possible de forcer la suppression d'une branche locale non fusionnée avec l'opt
|
|||||||
|
|
||||||
"Don't cry because it's over, smile because it happened." - *Dr. Seuss*
|
"Don't cry because it's over, smile because it happened." - *Dr. Seuss*
|
||||||
|
|
||||||
## TL;DR
|
## TL;DR {#tldr}
|
||||||
|
|
||||||
On commence par demander à git de ne pull que s'il n'y a pas de divergence avec
|
On commence par demander à git de ne pull que s'il n'y a pas de divergence avec
|
||||||
la version distante de notre branche, et sinon, de rebase notre branche automatiquement :
|
la version distante de notre branche, et sinon, de rebase notre branche automatiquement :
|
||||||
|
|
||||||
```git
|
```bash
|
||||||
git config pull.rebase true
|
git config pull.rebase true
|
||||||
```
|
```
|
||||||
|
|
Before Width: | Height: | Size: 553 KiB After Width: | Height: | Size: 553 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
@ -1,4 +1,11 @@
|
|||||||
# Robot timide – le robot qui fuit la lumière
|
+++
|
||||||
|
title = "Robot timide – le robot qui fuit la lumière"
|
||||||
|
description = "Création d'un robot timide pour un projet scolaire."
|
||||||
|
date = 2022-05-19
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tags = ["maker", "fablab", "robotique"]
|
||||||
|
+++
|
||||||
|
|
||||||
Création d'un robot timide pour un projet scolaire.
|
Création d'un robot timide pour un projet scolaire.
|
||||||
|
|
||||||
@ -9,7 +16,9 @@ en filière Informatique, un robot timide a vu le jour au sein d’Eirlab.
|
|||||||
Cet article est un guide pour expliquer comment reproduire notre travail, dans
|
Cet article est un guide pour expliquer comment reproduire notre travail, dans
|
||||||
le plus pur esprit Maker.
|
le plus pur esprit Maker.
|
||||||
|
|
||||||
![Une vue de 3/4 face du robot finalisé](./assets/images/robot-timide/robot_timide_header.jpg)
|
<!-- more -->
|
||||||
|
|
||||||
|
![Une vue de 3/4 face du robot finalisé](images/robot_timide_header.jpg)
|
||||||
|
|
||||||
## Concept de base
|
## Concept de base
|
||||||
|
|
||||||
@ -20,7 +29,7 @@ mode est aussi disponible, permettant non pas d’aller vers l’endroit le plus
|
|||||||
sombre, mais à l’inverse de fuir l’endroit le plus lumineux, pour avoir deux
|
sombre, mais à l’inverse de fuir l’endroit le plus lumineux, pour avoir deux
|
||||||
manières de voir le problème.
|
manières de voir le problème.
|
||||||
|
|
||||||
![Une vue de face du robot timide dans sa forme finale.](./assets/images/robot-timide/robot_timide_enseirb.jpg)
|
![Une vue de face du robot timide dans sa forme finale.](images/robot_timide_enseirb.jpg)
|
||||||
|
|
||||||
## Du matériel
|
## Du matériel
|
||||||
|
|
||||||
@ -55,7 +64,7 @@ percée pour accueillir les différents modules du robot. Les éléments les plu
|
|||||||
légers ou risquant moins de se détacher sont fixés par un simple système de
|
légers ou risquant moins de se détacher sont fixés par un simple système de
|
||||||
mâchoire, prenant en étau la plaque de bois dans une forme de U.
|
mâchoire, prenant en étau la plaque de bois dans une forme de U.
|
||||||
|
|
||||||
![La forme de U pour prendre la plaque de base en mâchoire et tenir un capteur par pression.](./assets/images/robot-timide/u_shape.jpg)
|
![La forme de U pour prendre la plaque de base en mâchoire et tenir un capteur par pression.](images/u_shape.jpg)
|
||||||
|
|
||||||
### Alimentation
|
### Alimentation
|
||||||
|
|
||||||
@ -69,7 +78,7 @@ et prend donc des tensions entre 5 et 9 Volts.
|
|||||||
Dans ce projet, nous utilisons une pile 9 Volts rechargeable dans un boîtier
|
Dans ce projet, nous utilisons une pile 9 Volts rechargeable dans un boîtier
|
||||||
vissé au châssis.
|
vissé au châssis.
|
||||||
|
|
||||||
![Une vue de la pile 9 Volts que nous utilisons, rechargeable en micro USB.](./assets/images/robot-timide/pile9v.jpg)
|
![Une vue de la pile 9 Volts que nous utilisons, rechargeable en micro USB.](images/pile9v.jpg)
|
||||||
|
|
||||||
### Moteurs
|
### Moteurs
|
||||||
|
|
||||||
@ -92,11 +101,11 @@ aussi appelé breadboard. Dans un second temps et pour éviter toute déconnexio
|
|||||||
de câbles liée à une potentielle accélération violente, nous avons fait un PCB,
|
de câbles liée à une potentielle accélération violente, nous avons fait un PCB,
|
||||||
soudé manuellement sur une carte de prototypage prévu à cet effet.
|
soudé manuellement sur une carte de prototypage prévu à cet effet.
|
||||||
|
|
||||||
![Une vue du dessus de notre PCB fait main.](./assets/images/robot-timide/pcb_irl.jpg)
|
![Une vue du dessus de notre PCB fait main.](images/pcb_irl.jpg)
|
||||||
|
|
||||||
Au niveau électronique, le PCB est routé de la manière suivante :
|
Au niveau électronique, le PCB est routé de la manière suivante :
|
||||||
|
|
||||||
![Le routage du PCB sur une carte de protypage de type Breadboard.](./assets/images/robot-timide/pcb_fritzing.png)
|
![Le routage du PCB sur une carte de protypage de type Breadboard.](images/pcb_fritzing.png)
|
||||||
|
|
||||||
Les différents composants viennent se connecter sur le PCB au moyen de
|
Les différents composants viennent se connecter sur le PCB au moyen de
|
||||||
différentes broches femelles, pour que ce dernier puisse être détaché et modifié
|
différentes broches femelles, pour que ce dernier puisse être détaché et modifié
|
4
content/_index.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
+++
|
||||||
|
paginate_by=5
|
||||||
|
sort_by = "date"
|
||||||
|
+++
|
6
content/pages/archive.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
+++
|
||||||
|
title = "Articles archivés"
|
||||||
|
path = "archive"
|
||||||
|
+++
|
||||||
|
|
||||||
|
N/A
|
@ -1,20 +0,0 @@
|
|||||||
const Prism = require('prismjs');
|
|
||||||
const fs = require('fs');
|
|
||||||
const cheerio = require('cheerio');
|
|
||||||
const loadLanguages = require('prismjs/components/');
|
|
||||||
const fsc = require('fs-cheerio');
|
|
||||||
|
|
||||||
loadLanguages([
|
|
||||||
'rust', 'py', 'cpp', 'c', 'git'
|
|
||||||
]);
|
|
||||||
|
|
||||||
const file = process.argv[2];
|
|
||||||
fsc.readFile(file).then(function ($, err) {
|
|
||||||
$('*[class^="language-"]').replaceWith(function () {
|
|
||||||
return $(this).html(Prism.highlight(
|
|
||||||
$(this).text(),
|
|
||||||
Prism.languages[$(this).attr('class').split('-')[1]])
|
|
||||||
);
|
|
||||||
});
|
|
||||||
fsc.writeFile(file, $);
|
|
||||||
});
|
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"cheerio": "^1.0.0-rc.10",
|
|
||||||
"fs-cheerio": "^3.0.0",
|
|
||||||
"prismjs": "^1.26.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
License: <a href="https://choosealicense.com/licenses/gpl-3.0/" target="_blank">G.P.L. v3</a><br>
|
|
||||||
Made with ❤ and <a href="https://gitlab.com/lovallat/blog" target="_blank">Open Source Software</a> —
|
|
||||||
Version: <code>__VERSION__</code>
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,49 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<!--
|
|
||||||
PAGE CODED BY :
|
|
||||||
__ _____ __ __ ______ ____ __ __ ______ __ __ ______ ______
|
|
||||||
/\ \ /\ __`\/\ \/\ \/\__ _\/\ _`\ /\ \/\ \/\ _ \/\ \ /\ \ /\ _ \/\__ _\
|
|
||||||
\ \ \ \ \ \/\ \ \ \ \ \/_/\ \/\ \,\L\_\ \ \ \ \ \ \ \L\ \ \ \ \ \ \ \ \ \L\ \/_/\ \/
|
|
||||||
\ \ \ __\ \ \ \ \ \ \ \ \ \ \ \ \/_\__ \ \ \ \ \ \ \ __ \ \ \ __\ \ \ __\ \ __ \ \ \ \
|
|
||||||
\ \ \L\ \\ \ \_\ \ \ \_\ \ \_\ \__/\ \L\ \ \ \ \_/ \ \ \/\ \ \ \L\ \\ \ \L\ \\ \ \/\ \ \ \ \
|
|
||||||
\ \____/ \ \_____\ \_____\/\_____\ `\____\ \ `\___/\ \_\ \_\ \____/ \ \____/ \ \_\ \_\ \ \_\
|
|
||||||
\/___/ \/_____/\/_____/\/_____/\/_____/ `\/__/ \/_/\/_/\/___/ \/___/ \/_/\/_/ \/_/
|
|
||||||
-->
|
|
||||||
<html lang="fr">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<link rel="icon" href="/assets/images/icon.png">
|
|
||||||
<!-- SEO -->
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta name="description" content="">
|
|
||||||
<meta name="image" content="https://blog.louis-vallat.fr/assets/images/icon.png">
|
|
||||||
<meta name="twitter:card" content="summary">
|
|
||||||
<meta name="twitter:title" content="">
|
|
||||||
<meta name="twitter:description" content="">
|
|
||||||
<meta name="twitter:site" content="@VallatLouis">
|
|
||||||
<meta name="twitter:creator" content="@VallatLouis">
|
|
||||||
<meta name="twitter:image:src" content="https://blog.louis-vallat.fr/assets/images/icon.png">
|
|
||||||
<meta name="og:title" content="">
|
|
||||||
<meta name="og:description" content="">
|
|
||||||
<meta name="og:image" content="https://blog.louis-vallat.fr/assets/images/icon.png">
|
|
||||||
<meta name="og:url" content="https://blog.louis-vallat.fr/">
|
|
||||||
<meta name="og:site_name" content="Le blog de Louis Vallat">
|
|
||||||
<meta name="og:locale" content="fr_FR">
|
|
||||||
<meta name="og:type" content="website">
|
|
||||||
<!-- Reset CSS -->
|
|
||||||
<link rel="stylesheet" href="/assets/css/reset.css">
|
|
||||||
<!-- Main CSS -->
|
|
||||||
<link rel="stylesheet" href="/assets/css/style.css">
|
|
||||||
<title></title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<nav>
|
|
||||||
<a href="/" id="home_link">accueil</a>
|
|
||||||
<div class="filler"></div>
|
|
||||||
<a href="mailto:contact@louis-vallat.fr" target="_blank">contact</a>
|
|
||||||
<a href="https://gitlab.com/lovallat" target="_blank">gitlab</a>
|
|
||||||
<a href="https://louis-vallat.fr" target="_blank">site</a>
|
|
||||||
<a href="https://twitter.com/VallatLouis" target="_blank">twitter</a>
|
|
||||||
</nav>
|
|
@ -1,73 +0,0 @@
|
|||||||
# AoC 2021 Jour 0: Commencement
|
|
||||||
|
|
||||||
Le début d'une grande aventure !
|
|
||||||
|
|
||||||
Cette année, j'ai découvert l'[Advent Of Code](https://adventofcode.com/).
|
|
||||||
Ce site géré de manière bénévole par [Eric Wastl](http://was.tl/) sur son temps
|
|
||||||
libre met à disposition des puzzles sur un thème différent d'une année sur
|
|
||||||
l'autre. Ces petits puzzles sont à résoudre à l'aide d'un programme informatique
|
|
||||||
ou d'un algorithme que l'on est sensé imaginer et mettre en oeuvre, pour, à partir
|
|
||||||
d'un fichier d'entrée qui nous est propre, donner un résultat à troquer sur le site
|
|
||||||
du challenge en échange d'une étoile. Le langage et la manière d'obtenir le
|
|
||||||
résultat sont libres, mais faire les défis "à la main" est rarement possible
|
|
||||||
(en tous cas en temps humain). Pendant tout le mois de décembre, un défi est publié
|
|
||||||
chaque jour du 1er au 25. Ils sont systématiquement découpés en 2 parties, la
|
|
||||||
seconde n'étant accessible qu'une fois la première résolue.
|
|
||||||
En échange d'une réponse valide au problème, une étoile nous est donnée, pour avoir
|
|
||||||
un total de 50 étoiles par an.
|
|
||||||
|
|
||||||
Il n'y a pas de pénalité si le puzzle n'est pas résolu dans la journée, mais un
|
|
||||||
*leaderboard* est disponible, et les 100 premiers à résoudre les puzzles obtiennent
|
|
||||||
des points sur leur profil. C'est comme cela que j'ai découvert qu'il existait
|
|
||||||
une discipline de [Programmation Compétitive](https://en.wikipedia.org/wiki/Competitive_programming).
|
|
||||||
|
|
||||||
Cette année, le thème c'était la quête de la clé du traîneau du Père Noël au fond
|
|
||||||
d'une crevasse sous-marine, armé d'un petit sous-marin, et rencontrant au passage
|
|
||||||
une faune et flore locale diverse.
|
|
||||||
|
|
||||||
![Vue de l'intérieur d'un sous-marin d'exploration. Photo par Michael Mrozek pour Unsplash.](./assets/images/aoc-2021-day-0/michal-mrozek-BEUd36I1-f8-unsplash.jpg)
|
|
||||||
|
|
||||||
J'ai déjà résolu les 25 jours et obtenu mes 50 étoiles, je souhaite maintenant
|
|
||||||
raconter ici mon aventure, jour après jour, et les algorithmes que j'ai mis en
|
|
||||||
place.
|
|
||||||
|
|
||||||
## Pourquoi
|
|
||||||
|
|
||||||
J'ai choisi de participer à cet évènement pour différentes raisons :
|
|
||||||
|
|
||||||
- j'aime les défis, je trouve amusant et plaisant d'avoir un but à atteindre en code
|
|
||||||
- je voulais m'exercer, j'aime travailler sur des projets sur mon temps libre
|
|
||||||
|
|
||||||
## Comment
|
|
||||||
|
|
||||||
Mes algorithmes sont implémentés en [Rust](https://www.rust-lang.org/), un langage
|
|
||||||
que j'aime beaucoup, pour différentes raisons qui sont hors sujet par rapport à
|
|
||||||
cette série d'articles. Un article spécifique sur ce qui me plaît dans ce
|
|
||||||
langage de programmation arrivera peut-être plus tard.
|
|
||||||
|
|
||||||
Mon but était de résoudre les puzzles en temps limité, si possible en moins d'une
|
|
||||||
journée, et que le programme final donne sa réponse le plus rapidement possible.
|
|
||||||
Si après 3 jours à travailler sur un puzzle, il donne une réponse en 300ms, cela
|
|
||||||
me convient. **Le but n'étant pas ici de faire des algorithmes optimaux.**
|
|
||||||
|
|
||||||
Chaque jour a été mis à disposition sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021).
|
|
||||||
|
|
||||||
## Beurk
|
|
||||||
|
|
||||||
Vous trouvez le code illisible ? C'est **normal**.
|
|
||||||
|
|
||||||
J'ai tendance dans mes programmes à avoir de mauvaises habitudes, *hostiles* à
|
|
||||||
la relecture de code. J'ai tendance à utiliser par exemple des variables dont le
|
|
||||||
nom se résume à une unique lettre. J'ai aussi tendance à vouloir produire un
|
|
||||||
fichier source le plus court possible (tout en respectant les standards du langage
|
|
||||||
utilisé). Le code fourni est alors un bloc compact **difficile à lire**.
|
|
||||||
|
|
||||||
J'ai donc décidé de me faire subir le même sort que celui réservé à mes collègues
|
|
||||||
lorsqu'il doivent relire mon code, afin de me faire **perdre** ces mauvaises
|
|
||||||
habitudes. (*spoiler: c'était efficace*)
|
|
||||||
|
|
||||||
## À suivre
|
|
||||||
|
|
||||||
Seront publiés sur ce blog différents articles retraçant et expliquant jour après
|
|
||||||
jour ce que mes algorithmes et programmes font pour résoudre les différents défis.
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
|||||||
# AoC 2021 Jour 1: Sonar Sweep
|
|
||||||
|
|
||||||
À la recherche de la clef du traîneau à l'aide d'un sonar.
|
|
||||||
|
|
||||||
Le défi peut être trouvé [ici](https://adventofcode.com/2021/day/1), et le code
|
|
||||||
lié est sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021/-/tree/master/day1).
|
|
||||||
|
|
||||||
## Consigne du défi
|
|
||||||
|
|
||||||
Le premier puzzle est assez simple, et peut se résumer en "compter les variations
|
|
||||||
dans un fichier d'entrée".
|
|
||||||
|
|
||||||
## Lecture du fichier d'entrée
|
|
||||||
|
|
||||||
Pour lire le fichier, rien de plus simple. L'entrée étant une simple liste d'entiers
|
|
||||||
séparés par des retours à la ligne, il est assez facile de le transformer en
|
|
||||||
tableau d'entiers.
|
|
||||||
|
|
||||||
En code, cela se traduit par :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn parse_input(s: &str) -> Vec<i32> {
|
|
||||||
return s.split("\n").into_iter().filter_map(|f| f.parse::<i32>().ok()).collect::<Vec<i32>>();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Maintenant que j'ai acquis un peu plus d'expérience en Rust, je le ferais d'une
|
|
||||||
manière assez différente :
|
|
||||||
|
|
||||||
- tout d'abord, `s.split("\n").into_iter()` peut être remplacé par `s.lines()`,
|
|
||||||
plus robuste et [System Agnostic](https://en.wikipedia.org/wiki/Agnostic_(data)),
|
|
||||||
les systèmes Windows utilisant une fin de ligne [différente](https://en.wikipedia.org/wiki/Newline#In_programming_languages).
|
|
||||||
- ensuite, utiliser des `u16`, soit des entiers non signés et encodés sur 16 bits.
|
|
||||||
Ici utiliser 32 bits signés pour stocker des valeurs relativement faibles (dans ce
|
|
||||||
jeu de données) et strictement positives n'est pas optimal.
|
|
||||||
|
|
||||||
## Première partie
|
|
||||||
|
|
||||||
La première partie nécessite de compter les augmentations de nombres un-à-un.
|
|
||||||
|
|
||||||
Nous avons donc un tableau (vecteur) d'entiers, alors il suffira simplement de le
|
|
||||||
parcourir, et si l'entier actuel est plus grand que le précédent, alors on retient
|
|
||||||
qu'on a vu une augmentation supplémentaire.
|
|
||||||
|
|
||||||
Le code de cette partie est le suivant :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn get_increase_nb(e: &Vec<i32>) -> i32 {
|
|
||||||
let mut n = 0;
|
|
||||||
for i in 1..e.len() {
|
|
||||||
n += if e[i] > e[i - 1] { 1 } else { 0 };
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
On a donc un entier `n` qui va retenir le nombre d'augmentations observées,
|
|
||||||
et pour tous les entiers du tableau, de la case 1 (les tableaux en informatique
|
|
||||||
débutent à la case 0), à la case `taille` (non incluse, toujours parce que les
|
|
||||||
tableaux commencent à 0). Si le nombre actuel est plus grand que le précédent,
|
|
||||||
alors `n` augmente de 1.
|
|
||||||
|
|
||||||
## Deuxième partie
|
|
||||||
|
|
||||||
Pour cette deuxième partie, le concept est le même mais cette fois on prend les
|
|
||||||
entiers d'entrée par groupe de 3 et on compte les augmentations entre chaque
|
|
||||||
sommes d'entiers.
|
|
||||||
|
|
||||||
On transforme donc le tableau d'entiers en un second tableau correspondant aux
|
|
||||||
sommes de 3 entiers selon une fenêtre flottante.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn get_sums(v: &Vec<i32>) -> BTreeMap<usize, i32> {
|
|
||||||
let mut sums = BTreeMap::new();
|
|
||||||
for i in 0..(v.len() - 2) {
|
|
||||||
sums.insert(i, v[i] + v[i + 1] + v[i + 2]);
|
|
||||||
}
|
|
||||||
return sums;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, une [BTreeMap](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html)
|
|
||||||
est **totalement** inutile. Je ne sais pas trop pourquoi j'ai choisi ce type de
|
|
||||||
structure de données. Surtout qu'après, cette Map est convertie en simple tableau
|
|
||||||
en ignorant totalement la clef :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let map = get_sums(&vec_in).iter().map(|e| e.1.to_owned()).collect::<Vec<i32>>();
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici il serait *beaucoup plus intéressant* d'utiliser un simple tableau, et à chaque
|
|
||||||
nouvelle somme on peut l'envoyer sur le dessus du tableau (de sommes) d'entiers.
|
|
||||||
|
|
||||||
Ce tableau enfin obtenu sera alors envoyé à la même méthode que pour la partie une
|
|
||||||
pour en obtenir le nombre d'augmentations.
|
|
||||||
|
|
||||||
Évidemment il serait aussi possible de simplement compter les augmentations en même
|
|
||||||
temps que l'on parcourt les tableaux, pour éviter de multiples passages.
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Ce puzzle était loin d'être compliqué. J'ai passé un peu de temps sur la
|
|
||||||
compréhension du système de sommes de la partie 2, ne sachant que faire dans le
|
|
||||||
cas où il n'y avait plus assez d'entiers pour faire une somme.
|
|
||||||
|
|
||||||
Après un peu de temps et d'essais, j'ai pu m'en sortir sans difficulté.
|
|
||||||
|
|
||||||
Concernant la qualité du code, je me demande bien ce que j'ai pu penser sur le
|
|
||||||
moment pour choisir de stocker les sommes dans une map à arbre binaire...
|
|
||||||
|
|
||||||
> À suivre
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
|||||||
# AoC 2021 Jour 2: Dive
|
|
||||||
|
|
||||||
Comment piloter le sous-marin des elfes ?
|
|
||||||
|
|
||||||
Le défi peut être trouvé [ici](https://adventofcode.com/2021/day/2), et le code
|
|
||||||
lié est sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021/-/tree/master/day2).
|
|
||||||
|
|
||||||
## Consigne du défi
|
|
||||||
|
|
||||||
Le sous-marin peut prendre 3 types de commandes :
|
|
||||||
|
|
||||||
- `forward X`
|
|
||||||
- `down X`
|
|
||||||
- `up X`
|
|
||||||
|
|
||||||
Le fichier d'entrée est une suite de ces ordres, que l'on doit interpréter pour
|
|
||||||
trouver la position du sous-marin dans l'espace (horizontalement et verticalement).
|
|
||||||
|
|
||||||
## Lecture du fichier d'entrée
|
|
||||||
|
|
||||||
La lecture du fichier d'entrée se fait à nouveau dans une seule fonction :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn parse_input(s: &str) -> Vec<(String, i32)> {
|
|
||||||
let mut r:Vec<(String, i32)> = vec![];
|
|
||||||
let v = s.split("\n").into_iter();
|
|
||||||
for e in v {
|
|
||||||
let p = e.split_whitespace().collect::<Vec<&str>>();
|
|
||||||
if p.len() == 2 && p[1].parse::<i32>().is_ok() {
|
|
||||||
r.push((p[0].to_owned(), p[1].parse::<i32>().unwrap()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Ici, on va tout simplement transformer une ligne en tuple `(String, i32)`, et
|
|
||||||
de transformer le fichier d'entrée en un tableau `Vec` de ces ordres. On découpe
|
|
||||||
donc une ligne `ordre X` en fonction d'un espace, et si on a bien 2 éléments et
|
|
||||||
que le deuxième peut être converti en entier signé stocké sur 32 bits, alors on
|
|
||||||
l'ajoute à notre tableau d'instructions.
|
|
||||||
|
|
||||||
On pourrait ici utiliser une énumeration telle que `Direction::Forward`,
|
|
||||||
`Direction::Up` et `Direction::Down` pour éviter de stocker les chaînes de
|
|
||||||
caractère en entier dans le tableau. On pourrait aussi utiliser des entiers stockés
|
|
||||||
sur moins de bits, étant relativement petits.
|
|
||||||
|
|
||||||
## Première partie
|
|
||||||
|
|
||||||
Pour la première partie, l'interprétation des instructions est la suivante :
|
|
||||||
|
|
||||||
- `forward X` qui permet de faire avancer le sous-marin de `X` unités horizontalement
|
|
||||||
- `down X` fait plonger le sous-marin et donc augmente sa profondeur de `X` unités
|
|
||||||
- `up X` fait remonter à l'inverse le sous-marin vers la surface
|
|
||||||
|
|
||||||
La réponse à la première partie se trouve en multipliant la position horizontale
|
|
||||||
et verticale. Pour cela, on va faire un `fold` sur le tableau pour calculer toutes
|
|
||||||
les instructions à la suite et donnant le résultat en une seule ligne :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn get_horizontal_depth(v: Vec<(String, i32)>) -> (i32, i32) {
|
|
||||||
return v.into_iter().fold((0, 0), |acc, e| compute_horizontal_depth(e, acc));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Chaque ligne est traitée individuellement pour interprétation, en donnat en valeur
|
|
||||||
de retour les nouvelles positions du sous-marin après application de l'instruction
|
|
||||||
sur la position donnée en entrée :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn compute_horizontal_depth(t: (String, i32), a: (i32, i32)) -> (i32, i32) {
|
|
||||||
return match t.0.as_str() {
|
|
||||||
"up" => (a.0, a.1 - t.1),
|
|
||||||
"down" => (a.0, a.1 + t.1),
|
|
||||||
"forward" => (a.0 + t.1, a.1),
|
|
||||||
_ => a
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Ce traitement est donc très simple. Pour chaque instruction et en partant d'une
|
|
||||||
position `(0, 0)`, on va appliquer les règles les unes après les autres en mettant
|
|
||||||
à jour la position à chaque fois. Cela se fait en temps linéaire sans calcul autre
|
|
||||||
que des additions.
|
|
||||||
|
|
||||||
## Deuxième partie
|
|
||||||
|
|
||||||
Pour la deuxième partie, le traitement est assez similaire, mais on va calculer
|
|
||||||
une composante supplémentaire : la *visée*.
|
|
||||||
|
|
||||||
Les instructions sont désormais à interpréter de cette manière :
|
|
||||||
|
|
||||||
- `forward X` permet deux choses :
|
|
||||||
- augmente la position horizontale de `X` unités
|
|
||||||
- augmente la profondeur par la *visée* multipliée par `X`
|
|
||||||
- `down X` augmente la *visée* de `X` unités
|
|
||||||
- `up X` à l'inverse diminue la *visée* de `X` unités
|
|
||||||
|
|
||||||
Le concept pour calculer est le même, on applique les instructions l'une après
|
|
||||||
l'autre en partant de `(0, 0, 0)`.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn compute_horizontal_depth_aim(t: (String, i32), a: (i32, i32, i32)) -> (i32, i32, i32) {
|
|
||||||
return match t.0.as_str() {
|
|
||||||
"up" => (a.0, a.1, a.2 - t.1),
|
|
||||||
"down" => (a.0, a.1, a.2 + t.1),
|
|
||||||
"forward" => (a.0 + t.1, a.1 + a.2 * t.1, a.2),
|
|
||||||
_ => a
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Enfin, la réponse à donner est la multiplication de la position horizontale et
|
|
||||||
verticale, comme pour la précédente partie.
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Ce défi n'était pas compliqué et ma réponse me semble adaptée. Bien sûr il y a
|
|
||||||
toujours des pistes d'améliorations, telle que le type de données stockées :
|
|
||||||
les chaînes de caractères étant moins performantes à traiter, mais aussi la taille
|
|
||||||
des entiers stockés, ou encore le fait que je ne passe pas de référence aux
|
|
||||||
fonctions, nécéssitant une copie systématique, consommant des cycles supplémentaires.
|
|
||||||
|
|
||||||
Mais globalement je suis satisfait de ma réponse à ce défi.
|
|
||||||
|
|
||||||
> À suivre
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
|||||||
# AoC 2021 Jour 3: Binary Diagnostic
|
|
||||||
|
|
||||||
Grincements étranges et inquiétants dans le sous-marin.
|
|
||||||
|
|
||||||
Le défi peut être trouvé [ici](https://adventofcode.com/2021/day/3), et le code
|
|
||||||
lié est sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021/-/tree/master/day3).
|
|
||||||
|
|
||||||
## Consigne du défi
|
|
||||||
|
|
||||||
Dans ce défi, on nous apprend que le sous-marin fait des bruits *pour le moins
|
|
||||||
étranges*. Le fichier d'entrée se compose d'une longue liste de nombres binaires,
|
|
||||||
que nous devons analyser.
|
|
||||||
|
|
||||||
## Lecture du fichier d'entrée
|
|
||||||
|
|
||||||
La lecture du fichier d'entrée se fait de la manière suivante, assez simplement :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn parse_input(s: &str) -> Vec<Vec<u32>> {
|
|
||||||
let mut r: Vec<Vec<u32>> = vec![];
|
|
||||||
for e in s.split("\n") {
|
|
||||||
if !e.is_empty() {
|
|
||||||
r.push(e.chars().filter_map(|e| e.to_digit(2)).collect());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
On va donc transformer chaque ligne (par exemple `101000001100`) en un tableau
|
|
||||||
de chiffres, ayant pour valeur `0` ou `1` (dans l'exemple
|
|
||||||
`[ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 ]`). On fait cela pour chaque ligne, pour
|
|
||||||
assembler une matrice de chiffres binaires, faciles à parcourir dans toutes les
|
|
||||||
directions.
|
|
||||||
|
|
||||||
## Première partie
|
|
||||||
|
|
||||||
Pour la première partie, il va falloir créer un nombre binaire à partir des chiffres
|
|
||||||
les **plus** fréquents dans les colonnes, et un nombre binaire à partir des chiffres
|
|
||||||
les **moins** fréquents, toujours dans les colonnes. On obtiendra alors deux
|
|
||||||
nombres binaires, qui une fois convertis en base 10 et ensuite multipliés entre eux,
|
|
||||||
donne la réponse à cette première partie.
|
|
||||||
|
|
||||||
Une fonction utilitaire permet de convertir les tableaux de nombres binaires en
|
|
||||||
nombre entier non signé :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn bit_vec_to_u32(v: &Vec<u32>, invert: bool) -> u32 {
|
|
||||||
let mut acc = 0;
|
|
||||||
for i in 0..v.len() {
|
|
||||||
acc += if invert { (v[i] + 1) % 2 } else { v[i] } << (v.len() - 1 - i);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
On a donc ici un accumulateur qui fait la somme des éléments tout en faisant un
|
|
||||||
décalage binaire, produisant un nombre entier en temps linéaire.
|
|
||||||
|
|
||||||
On va donc utiliser une fonction permettant d'obtenir, pour tout le fichier d'entrée,
|
|
||||||
le bit le plus présent dans la colonne `c` :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn get_most_in_col(v: &Vec<Vec<u32>>, c: usize) -> u32 {
|
|
||||||
let mut a = (0, 0);
|
|
||||||
for r in 0..v.len() {
|
|
||||||
if v[r][c] == 0 {
|
|
||||||
a.0 += 1;
|
|
||||||
} else {
|
|
||||||
a.1 += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if a.1 == a.0 { return 1; }
|
|
||||||
else { return if a.1 > a.0 { 1 } else { 0 }; }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
À la fin de la fonction, on peut voir que lors d'une égalité, c'est le `1` qui est
|
|
||||||
considéré comme valeur la plus présente dans la colonne étudiée.
|
|
||||||
|
|
||||||
**Amélioration possible :** il serait potentiellement plus intéressant de faire la somme des entiers stockés
|
|
||||||
dans la colonne, et regarder si cette somme est supérieure au nombre de lignes
|
|
||||||
divisée par deux, car cela nous évite de faire des comparaisons et rend inutiles
|
|
||||||
les conditions `if` qui sont au milieu de la boucle, ralentissent fortement
|
|
||||||
le calcul et sont donc à **proscrire**.
|
|
||||||
|
|
||||||
On va donc ici calculer en un seul coup la suite de chiffres binaires les plus
|
|
||||||
représentés par colonne dans le fichier d'entrée. La valeur `gamma` sera donc la
|
|
||||||
conversion directe de ce tableau en nombre entier non signé, et la valeur `epsilon`
|
|
||||||
sera aussi une conversion, mais inversée.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn get_gamma_epsilon(v: &Vec<Vec<u32>>) -> (u32, u32) {
|
|
||||||
let mut acc: Vec<u32> = vec![];
|
|
||||||
|
|
||||||
for c in 0..v[0].len() {
|
|
||||||
acc.push(get_most_in_col(&v, c));
|
|
||||||
}
|
|
||||||
return (bit_vec_to_u32(&acc, false), bit_vec_to_u32(&acc, true));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
La multiplication de ces deux valeurs nous donne la réponse à cette première partie.
|
|
||||||
|
|
||||||
## Deuxième partie
|
|
||||||
|
|
||||||
Toujours à partir de ce fichier d'entrée, on doit alors calculer deux nouvelles
|
|
||||||
valeurs : le niveau de génération d'oxygène et le niveau de filtrage de CO2.
|
|
||||||
|
|
||||||
Pour obtenir ces deux valeurs, on joue encore sur les bits les plus et les moins
|
|
||||||
communs dans une colonne donnée mais en rajoutant cette fois un concept
|
|
||||||
d'élimination. On va donc faire ceci :
|
|
||||||
|
|
||||||
- On récupère le bit le plus (ou le moins) présent dans la colonne observée
|
|
||||||
- On ne conserve que les nombres binaires qui ont cette valeur de bit pour cette colonne
|
|
||||||
- On répète cette opération jusqu'à ce qu'il ne reste qu'une seule valeur
|
|
||||||
|
|
||||||
Ici, j'ai utilisé une approche récursive :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn get_o2(v: &Vec<Vec<u32>>, c: usize) -> u32 {
|
|
||||||
if v.len() == 1 { return bit_vec_to_u32(&v[0], false); }
|
|
||||||
|
|
||||||
let a = get_most_in_col(&v, c);
|
|
||||||
let f = v.into_iter().filter(|e| e[c] == (a + 1) % 2)
|
|
||||||
.fold(vec![], |mut acc, e| { acc.push(e.to_owned()); return acc;});
|
|
||||||
return get_o2(&f, c + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_co2(v: &Vec<Vec<u32>>, c: usize) -> u32 {
|
|
||||||
if v.len() == 1 { return bit_vec_to_u32(&v[0], false); }
|
|
||||||
|
|
||||||
let a = get_most_in_col(&v, c);
|
|
||||||
let f = v.into_iter().filter(|e| e[c] == a)
|
|
||||||
.fold(vec![], |mut acc, e| { acc.push(e.to_owned()); return acc;});
|
|
||||||
return get_co2(&f, c + 1);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
On va donc récupérer le bit le plus présent dans la colonne `c`, et conserver les
|
|
||||||
valeurs qui nous intéressent, puis rappeler la méthode sur les valeurs restantes
|
|
||||||
en avançant d'une colonne. Une fois qu'il ne reste qu'une valeur, on sait qu'on
|
|
||||||
a notre résultat, et c'est ce qui est renvoyé.
|
|
||||||
|
|
||||||
En relisant ce code, j'ai plusieurs critiques à en faire :
|
|
||||||
|
|
||||||
- J'ai visiblement inversé le calcul de CO2 et de O2. Dans le calcul pour l'oxygène,
|
|
||||||
je ne conserve que les bits les moins présents, alors que c'est les plus présents
|
|
||||||
que je devrais conserver. Cela ne pose pas de problème car je dois multiplier les
|
|
||||||
deux valeurs entre elles, et l'ordre des termes d'un produit importe peu.
|
|
||||||
- J'utilise `(a + 1) % 2` pour obtenir l'inverse d'un bit, alors qu'un simple "différent"
|
|
||||||
suffirait. Cela demande de faire des opérations coûteuses (la division est coûteuse
|
|
||||||
en cycles CPU) pour rien.
|
|
||||||
- Je fais une copie du tableau avec les éléments restants après le tri au lieu de
|
|
||||||
faire un simple `filter`. Une copie est de toutes façons nécessaire, mais filtrer
|
|
||||||
le tableau serait plus élégant.
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Ce défi fait monter très légèrement la difficulté et le temps de réflexion
|
|
||||||
nécessaire, mais cela reste très accessible. Encore une fois, relire son code
|
|
||||||
et se poser les bonnes questions est très important, surtout se demander si telle
|
|
||||||
ou telle copie ou opération est bien nécessaire. C'est une réflexion que je
|
|
||||||
m'appliquerai à faire plus souvent.
|
|
||||||
|
|
||||||
> À suivre
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
/* http://meyerweb.com/eric/tools/css/reset/
|
|
||||||
v2.0 | 20110126
|
|
||||||
License: none (public domain)
|
|
||||||
*/
|
|
||||||
|
|
||||||
html, body, div, span, applet, object, iframe,
|
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
||||||
a, abbr, acronym, address, big, cite, code,
|
|
||||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
|
||||||
small, strike, strong, sub, sup, tt, var,
|
|
||||||
b, u, i, center,
|
|
||||||
dl, dt, dd, ol, ul, li,
|
|
||||||
fieldset, form, label, legend,
|
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
|
||||||
article, aside, canvas, details, embed,
|
|
||||||
figure, figcaption, footer, header, hgroup,
|
|
||||||
menu, nav, output, ruby, section, summary,
|
|
||||||
time, mark, audio, video {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
/* HTML5 display-role reset for older browsers */
|
|
||||||
article, aside, details, figcaption, figure,
|
|
||||||
footer, header, hgroup, menu, nav, section {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
ol, ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
blockquote, q {
|
|
||||||
quotes: none;
|
|
||||||
}
|
|
||||||
blockquote:before, blockquote:after,
|
|
||||||
q:before, q:after {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
@ -1,218 +0,0 @@
|
|||||||
@charset "utf-8";
|
|
||||||
/* Coded by Louis Vallat */
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'JetBrains Mono';
|
|
||||||
src: url("/assets/fonts/JetBrainsMono.ttf");
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'IBM Plex Mono';
|
|
||||||
src: url("/assets/fonts/IBMPlexMono.ttf");
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Open Sans';
|
|
||||||
src: url("/assets/fonts/OpenSans-Regular.ttf");
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--bg: #2A2D34;
|
|
||||||
--light_bg: #414652;
|
|
||||||
--fg: #fefefe;
|
|
||||||
--link: #009DDC;
|
|
||||||
--link_h: #009B72;
|
|
||||||
--link_v: #ACACDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
border-left: 5px solid var(--light_bg);
|
|
||||||
border-radius: 5px;
|
|
||||||
margin-left: 10px;
|
|
||||||
padding-left: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Open Sans', sans-serif;
|
|
||||||
background-color: var(--bg);
|
|
||||||
color: var(--fg);
|
|
||||||
margin-right: 27vw;
|
|
||||||
margin-left: 27vw;
|
|
||||||
text-align: justify;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p, ul {
|
|
||||||
margin-top: 10px;
|
|
||||||
line-height: 1.55em;
|
|
||||||
}
|
|
||||||
|
|
||||||
li>ul {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-left: 15px;
|
|
||||||
list-style-type: circle;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
margin-top: 15px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--link);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: var(--link_v);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: var(--link_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filler {
|
|
||||||
flex-grow: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav>a {
|
|
||||||
font-family: 'Open Sans', sans-serif;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav>a:visited, nav>a:hover, nav>a {
|
|
||||||
color: var(--fg);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav>a:not(#home_link) {
|
|
||||||
margin-left: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-family: 'JetBrains Mono', sans-serif;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: disc;
|
|
||||||
list-style-position: inside;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.85em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.65em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.45em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 1.35em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6 {
|
|
||||||
font-size: 1.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
b, strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
em, i {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre>code {
|
|
||||||
padding: 10px;
|
|
||||||
display: block;
|
|
||||||
overflow: auto;
|
|
||||||
line-height: 1.35em;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
padding: 2px 5px 2px 5px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: var(--light_bg);
|
|
||||||
font-family: 'IBM Plex Mono', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code blocks colors */
|
|
||||||
.token.keyword, .token.macro {
|
|
||||||
color: #D55FDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.function {
|
|
||||||
color: #52ADF2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.class-name {
|
|
||||||
color: #E5C07B;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.punctuation, .token.operator {
|
|
||||||
color: #AAB1C0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.string {
|
|
||||||
color: #89CA78;
|
|
||||||
}
|
|
||||||
|
|
||||||
.token.number {
|
|
||||||
color: #D8985F;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 992px) {
|
|
||||||
body {
|
|
||||||
margin-right: 10vw;
|
|
||||||
margin-left: 10vw;
|
|
||||||
}
|
|
||||||
nav>a:not(#home_link) {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
|
||||||
body {
|
|
||||||
margin-right: 5vw;
|
|
||||||
margin-left: 5vw;
|
|
||||||
}
|
|
||||||
nav>a:not(#home_link) {
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
blockquote {
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 511 KiB |
Before Width: | Height: | Size: 3.7 KiB |
12
src/index.md
@ -1,12 +0,0 @@
|
|||||||
#### Articles
|
|
||||||
|
|
||||||
Un blog on ne peut plus simple.
|
|
||||||
|
|
||||||
- [Archive/Projet Makers ENSEIRB - Robot timide](./robot-timide-robot-qui-fuit-la-lumiere.html)
|
|
||||||
- Advent of Code 2021 :
|
|
||||||
- [Advent of Code 2021 Jour 3 : Binary Diagnostic](./aoc-2021-jour-3-binary-diagnostic.html) - *07/02/2021*
|
|
||||||
- [Advent of Code 2021 Jour 2 : Dive](./aoc-2021-jour-2-dive.html) - *24/01/2021*
|
|
||||||
- [Advent of Code 2021 Jour 1 : Sonar Sweep](./aoc-2021-jour-1-sonar-sweep.html) - *19/01/2021*
|
|
||||||
- [Advent of Code 2021 Jour 0 : le commencement](./aoc-2021-jour-0-commencement.html) - *17/01/2022*
|
|
||||||
- [Git workflow](./git_flow.html) - *10/05/2021*
|
|
||||||
|
|
286
ssg5
@ -1,286 +0,0 @@
|
|||||||
#!/bin/sh -e
|
|
||||||
#
|
|
||||||
# https://rgz.ee/bin/ssg5
|
|
||||||
# Copyright 2018-2019 Roman Zolotarev <hi@romanzolotarev.com>
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
# purpose with or without fee is hereby granted, provided that the above
|
|
||||||
# copyright notice and this permission notice appear in all copies.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
#
|
|
||||||
|
|
||||||
main() {
|
|
||||||
test -n "$1" || usage
|
|
||||||
test -n "$2" || usage
|
|
||||||
test -n "$3" || usage
|
|
||||||
test -n "$4" || usage
|
|
||||||
test -d "$1" || no_dir "$1"
|
|
||||||
test -d "$2" || no_dir "$2"
|
|
||||||
|
|
||||||
|
|
||||||
src=$(readlink_f "$1")
|
|
||||||
dst=$(readlink_f "$2")
|
|
||||||
|
|
||||||
IGNORE=$(
|
|
||||||
if ! test -f "$src/.ssgignore"
|
|
||||||
then
|
|
||||||
printf ' ! -path "*/.*"'
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
while read -r x
|
|
||||||
do
|
|
||||||
test -n "$x" || continue
|
|
||||||
printf ' ! -path "*/%s*"' "$x"
|
|
||||||
done < "$src/.ssgignore"
|
|
||||||
)
|
|
||||||
|
|
||||||
# files
|
|
||||||
|
|
||||||
title="$3"
|
|
||||||
|
|
||||||
h_file="$src/_header.html"
|
|
||||||
f_file="$src/_footer.html"
|
|
||||||
test -f "$f_file" && FOOTER=$(cat "$f_file") && export FOOTER
|
|
||||||
test -f "$h_file" && HEADER=$(cat "$h_file") && export HEADER
|
|
||||||
|
|
||||||
list_dirs "$src" |
|
|
||||||
(cd "$src" && cpio -pdu "$dst")
|
|
||||||
|
|
||||||
fs=$(
|
|
||||||
if test -f "$dst/.files"
|
|
||||||
then list_affected_files "$src" "$dst/.files"
|
|
||||||
else list_files "$1"
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
|
|
||||||
if test -n "$fs"
|
|
||||||
then
|
|
||||||
echo "$fs" | tee "$dst/.files"
|
|
||||||
|
|
||||||
if echo "$fs" | grep -q '\.md$'
|
|
||||||
then
|
|
||||||
if test -x "$(which lowdown 2> /dev/null)"
|
|
||||||
then
|
|
||||||
echo "$fs" | grep '\.md$' |
|
|
||||||
render_md_files_lowdown "$src" "$dst" "$title"
|
|
||||||
else
|
|
||||||
if test -x "$(which Markdown.pl 2> /dev/null)"
|
|
||||||
then
|
|
||||||
echo "$fs" | grep '\.md$' |
|
|
||||||
render_md_files_Markdown_pl "$src" "$dst" "$title"
|
|
||||||
else
|
|
||||||
echo "couldn't find lowdown nor Markdown.pl"
|
|
||||||
exit 3
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$fs" | grep '\.html$' |
|
|
||||||
render_html_files "$src" "$dst" "$title"
|
|
||||||
|
|
||||||
|
|
||||||
echo "$fs" | grep -Ev '\.md$|\.html$' |
|
|
||||||
(cd "$src" && cpio -pu "$dst")
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf '[ssg] ' >&2
|
|
||||||
print_status 'file, ' 'files, ' "$fs" >&2
|
|
||||||
|
|
||||||
|
|
||||||
# sitemap
|
|
||||||
|
|
||||||
base_url="$4"
|
|
||||||
date=$(date +%Y-%m-%d)
|
|
||||||
urls=$(list_pages "$src")
|
|
||||||
|
|
||||||
test -n "$urls" &&
|
|
||||||
render_sitemap "$urls" "$base_url" "$date" > "$dst/sitemap.xml"
|
|
||||||
|
|
||||||
print_status 'url' 'urls' "$urls" >&2
|
|
||||||
echo >&2
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
readlink_f() {
|
|
||||||
file="$1"
|
|
||||||
cd "$(dirname "$file")"
|
|
||||||
file=$(basename "$file")
|
|
||||||
while test -L "$file"
|
|
||||||
do
|
|
||||||
file=$(readlink "$file")
|
|
||||||
cd "$(dirname "$file")"
|
|
||||||
file=$(basename "$file")
|
|
||||||
done
|
|
||||||
dir=$(pwd -P)
|
|
||||||
echo "$dir/$file"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
print_status() {
|
|
||||||
test -z "$3" && printf 'no %s' "$2" && return
|
|
||||||
|
|
||||||
echo "$3" | awk -v singular="$1" -v plural="$2" '
|
|
||||||
END {
|
|
||||||
if (NR==1) printf NR " " singular
|
|
||||||
if (NR>1) printf NR " " plural
|
|
||||||
}'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
echo "usage: ${0##*/} src dst title base_url" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
no_dir() {
|
|
||||||
echo "${0##*/}: $1: No such directory" >&2
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
|
|
||||||
list_dirs() {
|
|
||||||
cd "$1" && eval "find . -type d ! -name '.' ! -path '*/_*' $IGNORE"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
list_files() {
|
|
||||||
cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
list_dependant_files () {
|
|
||||||
e="\\( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \\)"
|
|
||||||
cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE $e"
|
|
||||||
}
|
|
||||||
|
|
||||||
list_newer_files() {
|
|
||||||
cd "$1" && eval "find . -type f ! -name '.' $IGNORE -newer $2"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
has_partials() {
|
|
||||||
grep -qE '^./_.*\.html$|^./_.*\.js$|^./_.*\.css$'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
list_affected_files() {
|
|
||||||
fs=$(list_newer_files "$1" "$2")
|
|
||||||
|
|
||||||
if echo "$fs" | has_partials
|
|
||||||
then list_dependant_files "$1"
|
|
||||||
else echo "$fs"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
render_html_files() {
|
|
||||||
while read -r f
|
|
||||||
do render_html_file "$3" < "$1/$f" > "$2/$f"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
render_md_files_lowdown() {
|
|
||||||
while read -r f
|
|
||||||
do
|
|
||||||
lowdown \
|
|
||||||
--html-no-skiphtml \
|
|
||||||
--html-no-escapehtml < "$1/$f" |
|
|
||||||
render_html_file "$3" "$(head -n 3 ${1}/${f} | tail -n 1)" > "$2/${f%\.md}.html"
|
|
||||||
node ./enrich_codeblocks.js "$2/${f%\.md}.html"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
render_md_files_Markdown_pl() {
|
|
||||||
while read -r f
|
|
||||||
do
|
|
||||||
Markdown.pl < "$1/$f" |
|
|
||||||
render_html_file "$3" \
|
|
||||||
> "$2/${f%\.md}.html"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
render_html_file() {
|
|
||||||
# h/t Devin Teske
|
|
||||||
# TODO IMPROVE THIS IF POSSIBLE
|
|
||||||
awk -v title="${1}" -v brief="${2}" '
|
|
||||||
{ body = body "\n" $0 }
|
|
||||||
END {
|
|
||||||
body = substr(body, 2)
|
|
||||||
if (body ~ /<[Hh][Tt][Mm][Ll]/) {
|
|
||||||
print body
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
if (match(body, /<[[:space:]]*[Hh]1(>|[[:space:]][^>]*>)/)) {
|
|
||||||
t = substr(body, RSTART + RLENGTH)
|
|
||||||
sub("<[[:space:]]*/[[:space:]]*[Hh]1.*", "", t)
|
|
||||||
gsub(/^[[:space:]]*|[[:space:]]$/, "", t)
|
|
||||||
if (t) title = t " — " title
|
|
||||||
}
|
|
||||||
n = split(ENVIRON["HEADER"], header, /\n/)
|
|
||||||
for (i = 1; i <= n; i++) {
|
|
||||||
if (match(tolower(header[i]), "<meta name=\"twitter:title\" content=\"\">")) {
|
|
||||||
head = substr(header[i], 1, RSTART - 1)
|
|
||||||
tail = substr(header[i], RSTART + RLENGTH)
|
|
||||||
print head "<meta name=\"twitter:title\" content=\"" title "\">" tail
|
|
||||||
} else if (match(tolower(header[i]), "<meta name=\"og:title\" content=\"\">")) {
|
|
||||||
head = substr(header[i], 1, RSTART - 1)
|
|
||||||
tail = substr(header[i], RSTART + RLENGTH)
|
|
||||||
print head "<meta name=\"og:title\" content=\"" title "\">" tail
|
|
||||||
} else if (match(tolower(header[i]), "<title></title>")) {
|
|
||||||
head = substr(header[i], 1, RSTART - 1)
|
|
||||||
tail = substr(header[i], RSTART + RLENGTH)
|
|
||||||
print head "<title>" title "</title>" tail
|
|
||||||
} else if (match(tolower(header[i]), "<meta name=\"twitter:description\" content=\"\">")) {
|
|
||||||
head = substr(header[i], 1, RSTART - 1)
|
|
||||||
tail = substr(header[i], RSTART + RLENGTH)
|
|
||||||
print head "<meta name=\"twitter:description\" content=\"" brief "\">" tail
|
|
||||||
} else if (match(tolower(header[i]), "<meta name=\"description\" content=\"\">")) {
|
|
||||||
head = substr(header[i], 1, RSTART - 1)
|
|
||||||
tail = substr(header[i], RSTART + RLENGTH)
|
|
||||||
print head "<meta name=\"description\" content=\"" brief "\">" tail
|
|
||||||
} else if (match(tolower(header[i]), "<meta name=\"og:description\" content=\"\">")) {
|
|
||||||
head = substr(header[i], 1, RSTART - 1)
|
|
||||||
tail = substr(header[i], RSTART + RLENGTH)
|
|
||||||
print head "<meta name=\"og:description\" content=\"" brief "\">" tail
|
|
||||||
} else print header[i]
|
|
||||||
}
|
|
||||||
print body
|
|
||||||
print ENVIRON["FOOTER"]
|
|
||||||
}'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
list_pages() {
|
|
||||||
e="\\( -name '*.html' -o -name '*.md' \\)"
|
|
||||||
cd "$1" && eval "find . -type f ! -path '*/.*' ! -path '*/_*' $IGNORE $e" |
|
|
||||||
sed 's#^./##;s#.md$#.html#;s#/index.html$#/#'
|
|
||||||
}
|
|
||||||
|
|
||||||
render_sitemap() {
|
|
||||||
urls="$1"
|
|
||||||
base_url="$2"
|
|
||||||
date="$3"
|
|
||||||
|
|
||||||
echo '<?xml version="1.0" encoding="UTF-8"?>'
|
|
||||||
echo '<urlset'
|
|
||||||
echo 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
|
|
||||||
echo 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9'
|
|
||||||
echo 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"'
|
|
||||||
echo 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
|
|
||||||
echo "$urls" |
|
|
||||||
sed -E 's#^(.*)$#<url><loc>'"$base_url"'/\1</loc><lastmod>'\
|
|
||||||
"$date"'</lastmod><priority>1.0</priority></url>#'
|
|
||||||
echo '</urlset>'
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
BIN
static/favicon.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
8
templates/404.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{% extends "terminimal/templates/404.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="post">
|
||||||
|
<h1 class="post-title">{% block heading %}Perdu ?{% endblock heading %}</h1>
|
||||||
|
<p>{% block message %}Cette page n'existe pas.{% endblock message %}</p>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
56
templates/index.html
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{% extends "terminimal/templates/index.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="posts">
|
||||||
|
{%- if paginator %}
|
||||||
|
{%- set show_pages = paginator.pages -%}
|
||||||
|
{% else %}
|
||||||
|
{%- set show_pages = section.pages -%}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
{%- for page in show_pages %}
|
||||||
|
<div class="post on-list">
|
||||||
|
{{ post_macros::header(page=page) }}
|
||||||
|
{{ post_macros::content(page=page, summary=true, show_only_description=page.extra.show_only_description | default(value=false)) }}
|
||||||
|
</div>
|
||||||
|
{% endfor -%}
|
||||||
|
<div class="pagination">
|
||||||
|
<div class="pagination__buttons">
|
||||||
|
{%- if paginator.previous %}
|
||||||
|
<span class="button previous">
|
||||||
|
<a href="{{ paginator.previous | safe }}">
|
||||||
|
<span class="button__icon">←</span>
|
||||||
|
<span class="button__text">Posts plus récents</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{% endif -%}
|
||||||
|
{%- if paginator.next %}
|
||||||
|
<span class="button next">
|
||||||
|
<a href="{{ paginator.next | safe }}">
|
||||||
|
<span class="button__text">Posts plus anciens</span>
|
||||||
|
<span class="button__icon">→</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{% endif -%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block footer %}
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="footer__inner">
|
||||||
|
{%- if config.extra.copyright_html %}
|
||||||
|
<div class="copyright copyright--user">{{ config.extra.copyright_html | safe }}</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="copyright">
|
||||||
|
<span>{{ config.extra.author }}</span>
|
||||||
|
<span>
|
||||||
|
<span>:: </span>
|
||||||
|
Made with ❤, <a href="https://gitlab.com/lovallat/blog" target="_blank">Open Source Software</a>, <a href="https://www.getzola.org" target="_blank">Zola</a> and <a href="https://github.com/pawroman/zola-theme-terminimal/" target="_blank">Terminimal</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif -%}
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
{% endblock footer %}
|
17
templates/tags/list.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{% extends "terminimal/templates/tags/list.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="post">
|
||||||
|
<h1 class="post-title">Liste des tags</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for term in terms %}
|
||||||
|
<li class="tag-list">
|
||||||
|
<a href="{{ term.permalink | safe }}">
|
||||||
|
{{ term.name }} ({{ term.pages | length }} post{{ term.pages | length | pluralize }})
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
17
templates/tags/single.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{% extends "terminimal/templates/tags/single.html" %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="post">
|
||||||
|
<h1 class="post-title">
|
||||||
|
tag: #{{ term.name }}
|
||||||
|
({{ term.pages | length }} post{{ term.pages | length | pluralize }})
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<a href="{{ config.base_url | safe }}/tags">
|
||||||
|
Voir tous les tags
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{ post_macros::list_posts(pages=term.pages) }}
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
1
themes/terminimal
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 0ced77898f37eb388181c4bdfa564febe437841e
|