<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.2.0">Jekyll</generator><link href="https://donutblog.fr/feed.xml" rel="self" type="application/atom+xml" /><link href="https://donutblog.fr/" rel="alternate" type="text/html" /><updated>2025-12-17T14:16:57+01:00</updated><id>https://donutblog.fr/feed.xml</id><title type="html">DonutBlog</title><subtitle>Des équations, des livres et du Shell</subtitle><author><name>DonutMan</name></author><entry><title type="html">Etude d’une balance connectée</title><link href="https://donutblog.fr/gnu-linux/balance/" rel="alternate" type="text/html" title="Etude d’une balance connectée" /><published>2025-08-04T01:00:00+02:00</published><updated>2025-08-04T01:00:00+02:00</updated><id>https://donutblog.fr/gnu-linux/balance</id><content type="html" xml:base="https://donutblog.fr/gnu-linux/balance/">&lt;p&gt;En août 2017, je m’étais amusé à pirater ma balance connectée de marque Withing afin de récupérer directement mes mesures de poids sans passer par l’interface Withings.&lt;/p&gt;

&lt;p&gt;L’idée mise en place était une technique classique de &lt;em&gt;man in the middle&lt;/em&gt;, exécutée &lt;em&gt;from scratch&lt;/em&gt; pas à pas (l’exercice fut didactique).&lt;/p&gt;

&lt;p&gt;Aujourd’hui, 8 ans plus tard, je remets la main par hasard sur ce projet que j’avais un peu oublié.&lt;/p&gt;

&lt;p&gt;Je reproduis ci-dessous pour garder une trace. A noter que la plupart des informations ont été anonymisées… mais après 8 ans et un renouvellement intégral de mon matériel informatique (box, pc, raspberry etc.), il n’y a plus grand chose de sensible de toutes façon. Et je me suis depuis débarrassé de cette balance (coucou la &lt;a href=&quot;https://www.laquadrature.net/2023/05/31/transformer-les-objets-connectes-en-mouchards-la-surenchere-securitaire-du-gouvernement/&quot;&gt;Quadrature du Net&lt;/a&gt;).&lt;/p&gt;

&lt;!--more--&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#liste-des-outils-utilisés&quot; id=&quot;markdown-toc-liste-des-outils-utilisés&quot;&gt;Liste des outils utilisés&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#introduction&quot; id=&quot;markdown-toc-introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#installation-du-système&quot; id=&quot;markdown-toc-installation-du-système&quot;&gt;Installation du système&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#passer-en-azerty-mettre-à-jour-le-timezone-et-activer-le-serveur-ssh&quot; id=&quot;markdown-toc-passer-en-azerty-mettre-à-jour-le-timezone-et-activer-le-serveur-ssh&quot;&gt;Passer en Azerty, mettre à jour le timezone et activer le serveur ssh&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#changer-le-nom-et-le-mot-de-passe-de-lutilisateur-par-défaut&quot; id=&quot;markdown-toc-changer-le-nom-et-le-mot-de-passe-de-lutilisateur-par-défaut&quot;&gt;Changer le nom et le mot de passe de l’utilisateur par défaut&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#mise-en-place-du-point-daccès-wifi&quot; id=&quot;markdown-toc-mise-en-place-du-point-daccès-wifi&quot;&gt;Mise en place du point d’accès WiFi&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#installation-de-hostapd&quot; id=&quot;markdown-toc-installation-de-hostapd&quot;&gt;Installation de Hostapd&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#filtrage-par-adresse-mac-via-hostapd&quot; id=&quot;markdown-toc-filtrage-par-adresse-mac-via-hostapd&quot;&gt;Filtrage par adresse MAC via Hostapd&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#serveur-dhcp&quot; id=&quot;markdown-toc-serveur-dhcp&quot;&gt;Serveur DHCP&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#ip-forwarding-et-règles-de-routage&quot; id=&quot;markdown-toc-ip-forwarding-et-règles-de-routage&quot;&gt;IP Forwarding et règles de routage&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#un-premier-sniffing-du-réseau&quot; id=&quot;markdown-toc-un-premier-sniffing-du-réseau&quot;&gt;Un premier sniffing du réseau&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#sniffing-de-la-balance&quot; id=&quot;markdown-toc-sniffing-de-la-balance&quot;&gt;Sniffing de la balance&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#pistes-pour-continuer&quot; id=&quot;markdown-toc-pistes-pour-continuer&quot;&gt;Pistes pour continuer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;liste-des-outils-utilisés&quot;&gt;Liste des outils utilisés&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;hostapd&lt;/li&gt;
  &lt;li&gt;iptables&lt;/li&gt;
  &lt;li&gt;isc-dhcp-server&lt;/li&gt;
  &lt;li&gt;systemctl&lt;/li&gt;
  &lt;li&gt;syslog&lt;/li&gt;
  &lt;li&gt;tcpdump&lt;/li&gt;
  &lt;li&gt;lsof&lt;/li&gt;
  &lt;li&gt;netcat&lt;/li&gt;
  &lt;li&gt;nmap&lt;/li&gt;
  &lt;li&gt;arp&lt;/li&gt;
  &lt;li&gt;dig&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Je dispose depuis quelques temps d’une balance Withings Body WS-50. Cette balance fait partie des objets dits “connectés” : lorsque je me pèse, la balance envoie mon poids aux serveurs Withings. Je dois par la suite me connecter à ces serveurs pour voir l’évolution de mon poids.&lt;/p&gt;

&lt;p&gt;Je trouve dommage de ne pas pouvoir récupérer directement les informations de la balance pour en faire ce que je souhaite et j’ai donc eu envie de capturer les trames que cette balance envoie régulièrement.&lt;/p&gt;

&lt;p&gt;Pour ce faire, je vais utiliser un vieux Raspberry Pi que je vais reconfigurer en mode point d’accès.&lt;/p&gt;

&lt;p&gt;Je pense procéder en deux étapes.&lt;/p&gt;

&lt;p&gt;Tout d’abord il faut faire du &lt;em&gt;Reverse Engineering&lt;/em&gt; sur la balance afin de déterminer comment les informations utiles sont envoyées aux serveurs Withings.
Cela se décompose en plusieurs étapes :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Mettre en place un point d’accès Wi-Fi sur le Raspberry via hostapd&lt;/li&gt;
  &lt;li&gt;Mettre en place un partage de connection  entre les interfaces Wifi et Ethernet du Rasberry&lt;/li&gt;
  &lt;li&gt;Filtrer les accès au Raspberry via Iptables et filtrage par adresse MAC&lt;/li&gt;
  &lt;li&gt;Tester la connection internet d’un portable&lt;/li&gt;
  &lt;li&gt;Tester la connection de la balance au Raspberry&lt;/li&gt;
  &lt;li&gt;Capture des trames émises par la balance via tcpdump sur le Raspberry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dans un second temps, il faudra automatiser la récupération de ces informations. J’essaierai de faire ça le plus proprement possible via la programmation d’un service. Les étapes seront donc :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Mise à jour des règles d’Iptables (inutile de garder le partage de connection Internet…)&lt;/li&gt;
  &lt;li&gt;Programmation d’un service pour gérer la capture des trames et l’extraction des informations utiles&lt;/li&gt;
  &lt;li&gt;Eventuellement mise en forme des données de poids via un petit script Python&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je dispose du matériel suivant :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;un “vieux” Raspberry Pi B embarquant 512 Mo de RAM&lt;/li&gt;
  &lt;li&gt;un dongle WiFi qui traînait chez moi&lt;/li&gt;
  &lt;li&gt;une balance connectée Wihtings Body WS-50&lt;/li&gt;
  &lt;li&gt;deux pc sous Ubuntu qui, placés à deux endroits différents de mon réseau, me permettront de vérifier que tout se passe comme convenu.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Au final, la topologie du réseau que je souhaite mettre en place est résumée ci-dessous (les IPs sont anonymisées).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/diagramme_reseau.png&quot; alt=&quot;Diagramme réseau&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;installation-du-système&quot;&gt;Installation du système&lt;/h2&gt;

&lt;p&gt;Nous récupérons la dernière version de Raspbian Jessie sur www.raspberrypi.org.
Deux versions de Rasbian Jessie&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; sont désormais proposées : Desktop et Lite.
La version Lite, comme son nom l’indique, est livrée avec le strict minimum applicatif et n’intégre pas d’environnement graphique. C’est cette version que j’ai choisi pour notre bridge.&lt;/p&gt;

&lt;p&gt;On prépare tout d’abord la carte SD avec l’incontournable commande &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dd&lt;/code&gt;. L’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conv=sync,noerror&lt;/code&gt; est un petit raffinement qui permet en cas d’erreur de padder le bloc de données avec des 0. Ainsi seul le bloc où l’erreur survient est illisible et ça ne décale pas le reste des données. Bon, personnellement en cas d’erreur je relance la commande ou alors je change de carte SD…&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo dd &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1m &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2017-07-05-raspbian-jessie-lite.img
&lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/rdisk2 &lt;span class=&quot;nv&quot;&gt;conv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sync&lt;/span&gt;,noerror
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On installe ensuite la carte SD dans le raspberry et on boot. L’utilisateur par défaut s’appelle &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pi&lt;/code&gt; et son mot de passe est &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspberry&lt;/code&gt;, le serveur ssh n’est pas activé et le clavier est en qwerty… Bref, y’a du boulot !&lt;/p&gt;

&lt;h3 id=&quot;passer-en-azerty-mettre-à-jour-le-timezone-et-activer-le-serveur-ssh&quot;&gt;Passer en Azerty, mettre à jour le timezone et activer le serveur ssh&lt;/h3&gt;

&lt;p&gt;La première étape est de lancer la commande &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspi-config&lt;/code&gt; pour passer en azerty, régler le timezone sur Europe/Paris et d’activer le serveur ssh. On en profitera également pour renommer la machine “donutbridge”.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;raspi-config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Petite subtilité de raspbian : il faut créer un fichier ssh dans /boot/ pour activier le serveur !&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo touch&lt;/span&gt; /boot/ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On édite avant de rebooter le fichier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/ssh/sshd_config&lt;/code&gt;, essentiellement pour changer le port par défaut de ssh&lt;/p&gt;

&lt;p&gt;On vérifie au reboot que le service ssh est bien lancé :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;systemctl status ssh
ssh.service - OpenBSD Secure Shell server
	Loaded: loaded
	Active : active &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;running&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;changer-le-nom-et-le-mot-de-passe-de-lutilisateur-par-défaut&quot;&gt;Changer le nom et le mot de passe de l’utilisateur par défaut&lt;/h3&gt;

&lt;p&gt;Changer le nom et le mot de passe de l’utilisateur principal est étonnament une tâche assez délicate. Il faut commencer par autoriser le login root avec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo passwd root&lt;/code&gt; puis on utilise la commande &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usermod&lt;/code&gt;. L’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-l&lt;/code&gt; donne le nouveau login, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; définit l’emplacement du nouveau &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME&lt;/code&gt; et enfin &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-m&lt;/code&gt; indique à &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usermod&lt;/code&gt; qu’il faut déplacer le répertoire utilisateur initial.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; /home/donut
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;usermod &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; donut &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; /home/donut pi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On se déloggue de root et on se loggue en tant que donut : ça marche !
Première chose à faire : désactiver le login root via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo passwd -l root&lt;/code&gt;. On remarque au passage que l’utilisateur donut est toujours sudoer, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usermod&lt;/code&gt; fait bien les choses.&lt;/p&gt;

&lt;p&gt;Il ne reste plus qu’à changer les droits de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/donut&lt;/code&gt; et à supprimer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/pi&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; donut:pi /home/donut
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /home/pi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;mise-en-place-du-point-daccès-wifi&quot;&gt;Mise en place du point d’accès WiFi&lt;/h2&gt;

&lt;p&gt;On va commencer par voir ce qu’il se passe lorsqu’on branche notre dongle WiFi&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;lsusb
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
Bus 001 Device 005: ID 0bda:8176 Realtek Semiconductor Corp. RTL8188CUS 802.11n WLAN Adapter
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Bon le dongle semble être correctement détecté.
Vérifions qu’il supporte le mode point d’accès, AP :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;iw list
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
Supported interface modes:
	&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; IBSS
	&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; managed
	&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; AP
	&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; monitor
	&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; P2P-client
	&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; P2P-GO
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;installation-de-hostapd&quot;&gt;Installation de Hostapd&lt;/h3&gt;

&lt;p&gt;On installe ensuite hostapd :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;hostapd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On copie le fichier de configuration par défaut de hostapd&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /etc/hostapd/
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo cp&lt;/span&gt; /usr/share/doc/hostapd/examples/hostapd.conf.gz ./
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo gunzip &lt;/span&gt;hostapd.conf.gz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On prendra également soin d’indiquer l’emplacement de ce fichier de configuration dans le fichier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/default/hostapd&lt;/code&gt; :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;DAEMON_CONF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/etc/hostapd/hostapd.conf&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Le fichier de configuration contient plusieurs paramètres que j’ai configuré ainsi :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;wlan0
&lt;span class=&quot;nv&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;nl80211
&lt;span class=&quot;nv&quot;&gt;logger_syslog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;logger_syslog_level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2
&lt;span class=&quot;nv&quot;&gt;logger_stdout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;logger_stdout_level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2
&lt;span class=&quot;nv&quot;&gt;ctrl_interface&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/run/hostapd
&lt;span class=&quot;nv&quot;&gt;ctrl_interface_group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;ssid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;bridge
&lt;span class=&quot;nv&quot;&gt;country_code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;FR
&lt;span class=&quot;nv&quot;&gt;hw_mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;g
&lt;span class=&quot;nv&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nv&quot;&gt;beacon_int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;100
&lt;span class=&quot;nv&quot;&gt;dtim_period&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2
&lt;span class=&quot;nv&quot;&gt;max_num_sta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;255
&lt;span class=&quot;nv&quot;&gt;rts_threshold&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2347
&lt;span class=&quot;nv&quot;&gt;fragm_threshold&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2346
&lt;span class=&quot;nv&quot;&gt;macaddr_acl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nv&quot;&gt;accept_mac_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/hostapd/hostapd.accept
&lt;span class=&quot;nv&quot;&gt;auth_algs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nv&quot;&gt;ignore_broadcast_ssid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;wpa&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2
&lt;span class=&quot;nv&quot;&gt;wpa_passphrase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;totorototoro
&lt;span class=&quot;nv&quot;&gt;wpa_key_mgmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;WPA-PSK
&lt;span class=&quot;nv&quot;&gt;wpa_pairwise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;CCMP
&lt;span class=&quot;nv&quot;&gt;rsn_pairwise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;CCMP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On remarquera le “channel=1” qui a été choisi particulièrement pour ne pas interférer avec le WiFi de la maison. Le champ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;macaddr_acl&lt;/code&gt; permet de définir la politique de gestion des adresses MAC (liste noire, liste blanche ou désactivé). Je n’ai pas vu d’impact pour le moment… Enfin, le champ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ignore_broadcast_ssid&lt;/code&gt; permet de ne pas diffuser le ssid : pratique en conditions opérationnelles, pour ajouter encore un peu de sécurité !&lt;/p&gt;

&lt;p&gt;On tente notre première connexion : on lance manuellement le serveur côté Raspberry et on tente de s’y connecter via un portable. Ca passe crème !&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl stop hostapd &lt;span class=&quot;c&quot;&gt;# lancement en manuel&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;hostapd ./hostapd.conf 
Configuration file: ./hostapd.conf
Using interface wlan0 with hwaddr 00:13:ef:d0:2e:bc and ssid &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt;
wlan0: interface state UNINITIALIZED-&amp;gt;ENABLED
wlan0: AP-ENABLED 
wlan0: STA 9c:f3:87:aa:fe:62 IEEE 802.11: associated
wlan0: AP-STA-CONNECTED 9c:f3:87:aa:fe:62
wlan0: STA 9c:f3:87:aa:fe:62 RADIUS: starting accounting session 597E4C38-00000000
wlan0: STA 9c:f3:87:aa:fe:62 WPA: pairwise key handshake completed &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;RSN&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;filtrage-par-adresse-mac-via-hostapd&quot;&gt;Filtrage par adresse MAC via Hostapd&lt;/h3&gt;

&lt;p&gt;Commençons maintenant les bonnes pratiques : on va mettre en place un filtrage par adresses MAC directement dans hostapd. Toujours dans notre fichier de configuration &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostapd.conf&lt;/code&gt; nous indiquons :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;macaddr_acl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nv&quot;&gt;accept_mac_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/hostapd/hostapd.accept
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Le fichier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostapd.accept&lt;/code&gt; contiendra uniquement les adresses MAC de mon portable et de la balance.&lt;/p&gt;

&lt;p&gt;Comme indiqué plus haut, cela n’a pas l’air d’avoir d’impact pour le moment : je peux tout à fait me connecter avec un autre poste. A faire : regarder la doc de Hostapd en détail pour voir à quel moment le filtrage se fait…&lt;/p&gt;

&lt;h3 id=&quot;serveur-dhcp&quot;&gt;Serveur DHCP&lt;/h3&gt;

&lt;p&gt;A ce stade, les équipements peuvent se connecter sur le réseau “bridge” mais ils ne peuvent pas faire grand chose d’autre.
Pour commencer il leur faut une adresse IP. Etant donné que seule la balance est sensée se connecter sur “bridge” on pourrait passer par un adressage fixe mais qui peut le plus peut le moins : on va passer par un serveur DHCP qui écoutera en permanence sur wlan0 en attente de datagramme “DHCP Discover”. On le définiera de telle sorte que deux postes tout au plus pourront se connecter en même temps sur l’interface.&lt;/p&gt;

&lt;p&gt;On va tout d’abord définir un réseau d’accès en 192.168.8.0 sur l’interface wlan0&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ifconfig wlan0 down
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ifconfig wlan0 192.168.10.1 netmask 255.255.255.0 up
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ifconfig
eth0    Link encap:Ethernet  HWaddr b8:27:eb:9e:fa:cd  
	inet addr:192.168.0.38  Bcast:192.168.0.255  Mask:255.255.255.0
	inet6 addr: fe80::328c:d511:1ca0:895d/64 Scope:Link
	UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
	RX packets:10439 errors:0 dropped:1 overruns:0 frame:0
	TX packets:3542 errors:0 dropped:0 overruns:0 carrier:0
	collisions:0 txqueuelen:1000 
	RX bytes:2023402 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1.9 MiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;  TX bytes:596809 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;582.8 KiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

lo      Link encap:Local Loopback  
	inet addr:127.0.0.1  Mask:255.0.0.0
	inet6 addr: ::1/128 Scope:Host
	UP LOOPBACK RUNNING  MTU:65536  Metric:1
	RX packets:0 errors:0 dropped:0 overruns:0 frame:0
	TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
	collisions:0 txqueuelen:1 
	RX bytes:0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.0 B&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;  TX bytes:0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.0 B&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

wlan0   Link encap:Ethernet  HWaddr 00:13:ef:d0:2e:bc  
	inet addr:192.168.10.1  Bcast:192.168.10.255  Mask:255.255.255.0
	UP BROADCAST MULTICAST  MTU:1500  Metric:1
	RX packets:0 errors:0 dropped:8 overruns:0 frame:0
	TX packets:0 errors:0 dropped:41 overruns:0 carrier:0
	collisions:0 txqueuelen:1000 
	RX bytes:28355 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;27.6 KiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;  TX bytes:11913 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;11.6 KiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alors là, ça marche bien mais ce n’est pas perenne. Pour que le système se souvienne, il faut éditer le fichier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/network/interfaces&lt;/code&gt; et ajouter les lignes :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;auto wlan0
iface wlan0 inet static
	address 192.168.10.1
	netmask 255.255.255.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On rédémarre ensuite le réseau avec :&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl daemon-reload 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Le serveur DHCPD n’étant pas installé par défaut, on l’installe avec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$ sudo apt install isc-dhcp-server&lt;/code&gt;. Alors là, le serveur échoue lamentablement au démarrage, ce qui est normal : il est configuré par défaut pour gérer eth0…&lt;/p&gt;

&lt;p&gt;On modifie le fichier de configuration &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/dhcp/dhcpd.conf&lt;/code&gt; pour définir le réseau ainsi que la plage d’adresse IP disponibles :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;option domain-name-servers 192.168.10.0&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
authoritative&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Declaration du subnet&lt;/span&gt;
subnet 192.168.10.0 netmask 255.255.255.0 &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        option domain-name &lt;span class=&quot;s2&quot;&gt;&quot;WiFi.localhost&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	option routers 192.168.10.1&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	option subnet-mask 255.255.255.0&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	option broadcast-address 192.168.10.0&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	option domain-name-servers 192.168.10.1&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	range dynamic-bootp 192.168.10.10 192.168.10.11&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On remarquera au passage que le domain-name-servers pointe vers la box Internet (192.168.0.254). Ceci signifie que lorsqu’une machine du réseau 192.168.10.0 demandera une résolution d’adresse (du genre “quelle est l’IP de www.forum.ubuntu.org ?), le raspberry lui répondra en substance “oh je ne sais pas trop, demande à la box Internet”. C’est une solution simple qui nous évite de devoir mettre en place un serveur cache DNS mais il a un coût : si 50 machines sur 192.168.10.0 demandent l’IP de www.forum.ubuntu.org, notre raspberry devra relayer 50 fois cette demande à la box.&lt;/p&gt;

&lt;p&gt;Quoiqu’il en soit on prendra soin de définir l’interface à gérer dans &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/default/isc-dhcp-server&lt;/code&gt; :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;DHCPD_CONF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/dhcp/dhcpd.conf
&lt;span class=&quot;nv&quot;&gt;INTERFACES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;wlan0&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On redémarre les services hostapd et isc-dhcp-server via systemctl et cette fois-ci losqu’on se connecte via le portable, on a une IP en 192.168.10.0 !
Parallèlement nous pouvons vérifier l’ensemble des machines connectées sur l’interface wlan0 avec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostapd_cla&lt;/code&gt;. Cette commande va nous retourner un certain nombre d’informations par poste connecté, dont son adresse MAC. La commande &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arp&lt;/code&gt; permet dans un second temps de voir la correspondance Adresse MAC / Adresse IP.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;hostapd_cli all_sta
Selected interface &lt;span class=&quot;s1&quot;&gt;'wlan0'&lt;/span&gt;
9c:f3:87:aa:fe:62
&lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=[&lt;/span&gt;AUTH][ASSOC][AUTHORIZED]
&lt;span class=&quot;nv&quot;&gt;aid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;capability&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x0
&lt;span class=&quot;nv&quot;&gt;listen_interval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;supported_rates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;timeout_next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;NULLFUNC POLL
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsSTAAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;9c:f3:87:aa:fe:62
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsSelectedPairwiseCipher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;00-0f-ac-4
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsTKIPLocalMICFailures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsTKIPRemoteMICFailures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;hostapdWPAPTKState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;11
&lt;span class=&quot;nv&quot;&gt;hostapdWPAPTKGroupState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;rx_packets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;tx_packets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;rx_bytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;tx_bytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;connected_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;11
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;arp | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;9c:f3:87:aa:fe:62
192.168.10.10            ether   9c:f3:87:aa:fe:62   C                     wlan0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Arrivé à ce stade c’est un peu mieux mais on est loin d’avoir terminé… Il nous reste à établir le partage de la connection Internet. Ca tombe bien, c’est l’enjeu de la section suivante.&lt;/p&gt;

&lt;p&gt;Néanmoins avant de passer à la section suivante, il est intéressant de regarder un moment les log de notre serveur DHCP.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /var/log/syslog | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; dhcp
Aug  2 06:30:43 donutbridge dhcpd: DHCPDISCOVER from 00:13:ef:d0:2e:bc &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;donutbridge&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; via wlan0
Aug  2 06:30:44 donutbridge dhcpd: DHCPOFFER on 192.168.10.10 to 00:13:ef:d0:2e:bc &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;donutbridge&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; via wlan0
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Tiens, des messages DHCP Discover sont envoyés par l’adresse MAC 00:13:ef:d0:2e:bc ? Si on regarde la sortie de notre ifconfig (cf. les sections précédentes), on se rend compte que cela correspond à notre interface wlan0 ! Notre serveur DHCP tente régulièrement d’adresser une IP à wlan0, ce n’est pas du tout correct !&lt;/p&gt;

&lt;p&gt;Pourquoi est-ce que wlan0 émet régulièrement des datagrammes DHCP Discover ? La réponse se trouve grâce à systemctl :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl status dhcpcd.service 
	dhcpcd.service - dhcpcd on all interfaces
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notre système embarque un client DHCP qui, par défaut, écoute sur TOUTES les interfaces (donc également sur wlan0 !). Il nous faut changer ce comportement pour être plus propre. Il suffit de rajouter la ligne suivante dans &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/dhcpcd.conf&lt;/code&gt; :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Donut 2017-08-02 : we DO NOT listen on wlan0 interface&lt;/span&gt;
denyinterfaces wlan0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On recharge ensuite la configuration avec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl daemon-reload&lt;/code&gt;. A présent si on regarde les logs de dhcpd, on ne voit plus passer de datagrammes DHCPDISCOVER sur wlan0 ! Passons maintenant au partage de connection proprement dit.&lt;/p&gt;

&lt;h3 id=&quot;ip-forwarding-et-règles-de-routage&quot;&gt;IP Forwarding et règles de routage&lt;/h3&gt;

&lt;p&gt;Pour commencer, il faut autoriser sur notre Raspberry l’IP Forwarding, c’est-à-dire la capacité à trasmettre sur une interface (ex: wlan0) des trames reçues sur une autre interface (ex: eth0). Encore une fois deux solutions existent : la temporaire et la pérenne.&lt;/p&gt;

&lt;p&gt;Pour activer l’IP Forwarding le temps de la session, il suffit de mettre un “1” dans le fichier /&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proc/sys/net/ipv4/ip_forward&lt;/code&gt;. pour la solution pérenne, il faut éditer le fichier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/sysctl.conf&lt;/code&gt; et ajouter la ligne :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Uncomment the next line to enable packet forwarding for IPv4&lt;/span&gt;
net.ipv4.ip_forward&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A ce stade, les interfaces devraient être correctement configurées et notre Rasberry devrait savoir où envoyer n’importe quel paquet qu’il reçoit. Vérifions cela :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.0.254   0.0.0.0         UG    202    0        0 eth0
192.168.0.0     &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;               255.255.255.0   U     202    0        0 eth0
192.168.10.0    &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;               255.255.255.0   U     0      0        0 wlan0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Comme nous le voyons, il sait que :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;les paquets à destination du réseau 192.168.10.0 sont à envoyer sur l’interface wlan0&lt;/li&gt;
  &lt;li&gt;les paquets à destination du réseau 192.168.0.0 sont à envoyer sur l’interface eth0&lt;/li&gt;
  &lt;li&gt;tous les autres paquets (principalement à destination d’internet donc) devront être envoyé au routeur par défaut, ici 192.168.0.254, autrement dit ma box internet… qui est accessible sur l’interface eth0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A ce stade, les routes sont correctes et le Rasberry est autorisé à faire de la redirection de paquet.
Si depuis le réseau 192.168.10.0 je ping une machine sur 192.168.0.0, la machine destinataire devrait voir arriver le ping. Elle va y répondre mais notre émetteur ne recevra jamais la réponse.&lt;/p&gt;

&lt;p&gt;Regardons cela plus en détail et rappelons-nous la topologie actuelle de notre réseau (voir la figure en début d’article). J’utilise deux PC additionnels que je place judicieusement sur le réseau :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$PC_1$, d’IP 192.168.10.11, est placé au même niveau que la balance,&lt;/li&gt;
  &lt;li&gt;$PC_2$, d’IP 192.168.0.39 est placé au même niveau que l’interface eth0 du Raspberry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les IP de nos différents équipements sont donc résumées dans le tableau ci-dessous&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Machine&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Interface&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;IP&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;$PC_1$&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;eth0&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;192.168.10.11&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;$PC_2$&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;eth0&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;192.168.0.39&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Raspberry&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;eth0&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;192.168.0.38&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Raspberry&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;wlan0&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;192.168.10.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Box internet&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;eth0&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;192.168.0.1&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Maintenant regardons ce qu’il se passe si, à ce stade de nos développements, nous&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;essayons de pinger $PC_2$ depuis le Raspberry&lt;/li&gt;
  &lt;li&gt;essayons de pinger $PC_2$ depuis $PC_1$&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voici ce que nous obtenons dans le premier cas : la colonne de gauche représente le Raspberry, celle de droite $PC_2$. Je me sers de la commande &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping&lt;/code&gt; pour envoyer des pings et &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tcpdump&lt;/code&gt; pour écouter (remarquez l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p icmp&lt;/code&gt; qui me permet de n’afficher que les trames du protocole icmp auquel ping appartient). On remarque non seulement $PC_2$ reçoit bien les ping du Raspberry, mais le Raspberry reçoit les réponses de $PC_2$ !&lt;/p&gt;

&lt;p&gt;Raspberry :&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 3 192.168.0.39 &lt;span class=&quot;c&quot;&gt;# Terminal du raspberry&lt;/span&gt;
PING 192.168.0.39 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;192.168.0.39&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 56&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;84&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; bytes of data.
64 bytes from 192.168.0.39: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.469 ms
64 bytes from 192.168.0.39: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.521 ms
64 bytes from 192.168.0.39: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.534 ms
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; 192.168.0.39 ping statistics &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
3 packets transmitted, 3 received, 0% packet loss, &lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;2003ms
rtt min/avg/max/mdev &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0.469/0.508/0.534/0.028 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Terminal de $PC_2$&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tcpdump &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; icmp &lt;span class=&quot;c&quot;&gt;# Terminal de PC_2&lt;/span&gt;
tcpdump: verbose output suppressed, use &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; or &lt;span class=&quot;nt&quot;&gt;-vv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;full protocol decode
listening on enp7s0, link-type EN10MB &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Ethernet&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, capture size 262144 bytes
23:29:17.783149 IP 192.168.0.38 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1, length 64
23:29:17.783160 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.38: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1, length 64
23:29:18.785152 IP 192.168.0.38 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;2, length 64
23:29:18.785171 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.38: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;2, length 64
23:29:19.786414 IP 192.168.0.38 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3, length 64
23:29:19.786437 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.38: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3, length 64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Maintenant, refaisons la même manip mais cette fois-ci c’est $PC_1$ qui envoie les pings. On s’aperçoit bien que $PC_2$ reçoit bien les pings et qu’il y répond, ce qui prouve que le routage et l’IP Forwarding du Raspberry fonctionnent bien comme il faut. En revanche, le echo reply ne parvient jamais à $PC_1$, car ni $PC_1$ ni le Rasberry ni même la box Internet ne savent où diable se cachent les machines en 192.168.10.XX. Il nous manque bien une étape, et c’est la NAT.&lt;/p&gt;

&lt;p&gt;Terminal de $PC_1$&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 3 192.168.0.39 &lt;span class=&quot;c&quot;&gt;# Terminal de PC_1&lt;/span&gt;
PING 192.168.0.39 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;192.168.0.39&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 56&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;84&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; bytes of data.
Request &lt;span class=&quot;nb&quot;&gt;timeout &lt;/span&gt;foricmp_seq 1
Request &lt;span class=&quot;nb&quot;&gt;timeout &lt;/span&gt;foricmp_seq 2
Request &lt;span class=&quot;nb&quot;&gt;timeout &lt;/span&gt;foricmp_seq 3
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; 192.168.0.39 ping statistics &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
3 packets transmitted, 0 received, 100% packet loss
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Terminal de $PC_2$&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tcpdump &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; icmp &lt;span class=&quot;c&quot;&gt;# Terminal de PC_2&lt;/span&gt;
tcpdump: verbose output suppressed, use &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; or &lt;span class=&quot;nt&quot;&gt;-vv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;full protocol decode
listening on enp7s0, link-type EN10MB &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Ethernet&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, capture size 262144 bytes
23:29:42.560148 IP 192.168.10.11 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;22340, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1, length 64
23:29:42.560161 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.11: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;22340, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1, length 64
23:29:43.549379 IP 192.168.10.11 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;22340, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;2, length 64
23:29:43.549394 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.11: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;22340, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;2, length 64
23:29:44.549742 IP 192.168.10.11 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;22340, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3, length 64
23:29:44.549756 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.11: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;22340, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3, length 64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Petit aparté avant de poursuivre : afin de partager la connection Internet, nous aurions pu choisir de mettre en place un &lt;em&gt;pont réseau&lt;/em&gt;, c’est-à-dire une interconnection de réseaux de niveau 2 au niveau du Raspberry (donc wlan0 et eth0). Dans ce cas, toutes mes machines connectées sur wlan0 auraient eu une IP en 192.168.0.X attribuée directement par ma box internet. Le serveur DHCP aurait été superflu, de même que le routage NAT. C’est une autre option possible mais qui n’est pas détaillée ici.&lt;/p&gt;

&lt;p&gt;Quoiqu’il en soit, regardons comment modifier la NAT pour gérer les machines connectées sur wlan0. Principalement deux solutions existent : la SNAT et l’IP Mascarade. Etant donné que l’IP de mon interface eth0 peut-être définie en statique (via les options du serveur DHCP de ma box Internet), j’ai opté pour la SNAT qui est je pense plus légère.&lt;/p&gt;

&lt;p&gt;La commande pour mettre en place la SNAT est la suivante :&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; nat &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; POSTROUTING &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; eth0 &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; SNAT &lt;span class=&quot;nt&quot;&gt;--to-source&lt;/span&gt; 192.168.0.38
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cette commande demande au Rasberry de modifier sa table NAT (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-t nat&lt;/code&gt;), et d’ajouter une nouvelle règle dans la chaîne POSTROUTING (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-A POSTROUTING&lt;/code&gt;) pour les paquets s’apprétant à sortir sur son interface eth0 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-o eth0&lt;/code&gt;). Pour ces paquets là, et ces paquets là seulement, on met en place une SNAT (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-j SNAT&lt;/code&gt;) et on spécifie que l’IP source du paquet sera celle de l’interface eth0 du Raserry (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--to-source 192.168.0.38&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;En clair, cela veut dire que tout paquet qui s’apprête à sortir du Rasberry via son interface eth0 (par exemple le ping du $PC_1$ de tout à l’heure) verra son IP source changée en celle du Raspberry/eth0 et le Raspberry se charge tout seul comme un grand de garder les comptes et, lorsque les paquets retour lui sont adressés, de redispatcher ensuite sur le réseau 192.168.10.0.&lt;/p&gt;

&lt;p&gt;Ainsi, après avoir appliqué la règle iptables, refaisons notre manip du ping $PC_1 \to$ Raspberry. Cette fois-ci, on remarque ça marche et que les requêtes reçues par $PC_2$ ne proviennent non plus de $PC_1$ (192.168.10.11) mais du Raspberry (192.168.0.38) !&lt;/p&gt;

&lt;p&gt;Terminal de $PC_1$&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 3 192.168.0.39 &lt;span class=&quot;c&quot;&gt;# Terminal de PC_1&lt;/span&gt;
PING 192.168.0.39 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;192.168.0.39&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 56&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;84&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; bytes of data.
64 bytes from 192.168.0.39: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.469 ms
64 bytes from 192.168.0.39: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.521 ms
64 bytes from 192.168.0.39: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;64 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.534 ms
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; 192.168.0.39 ping statistics &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
3 packets transmitted, 3 received, 0% packet loss, &lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;2003ms
rtt min/avg/max/mdev &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0.469/0.508/0.534/0.028 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Terminal de $PC_2$&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tcpdump &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; icmp &lt;span class=&quot;c&quot;&gt;# Terminal de PC_2&lt;/span&gt;
tcpdump: verbose output suppressed, use &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; or &lt;span class=&quot;nt&quot;&gt;-vv&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;full protocol decode
listening on enp7s0, link-type EN10MB &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Ethernet&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, capture size 262144 bytes
23:29:17.783149 IP 192.168.0.38 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1, length 64
23:29:17.783160 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.38: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1, length 64
23:29:18.785152 IP 192.168.0.38 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;2, length 64
23:29:18.785171 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.38: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;2, length 64
23:29:19.786414 IP 192.168.0.38 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.39: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;request, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3, length 64
23:29:19.786437 IP 192.168.0.39 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.38: ICMP &lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;reply, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2107, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3, length 64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Dernière façon amusante de voir la SNAT en action, nous allons utiliser &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ncat&lt;/code&gt;. C’est un outil qui permet de mettre en place des sockets réseau très simplement.
Sur notre $PC_2$, nous écoutons sur le port (arbitraire) 52201 via la commande : &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$ sudo ncat -v -l 52201&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sur notre $PC_1$, nous tentons une connection sur ce (mini) serveur via la commande : &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo ncat -v 192.168.0.39 52201&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Si tout se passe bien, les deux machines se connectent et tout ce qu’on écrit dans le terminal de l’une se retrouvera en miroir dans le terminal de l’autre !
Du côté du client $PC_1$ tout se passe comme si on dialoguait directement avec $PC_2$ d’IP 192.168.0.39 et sur son port 52201. Et comme on s’y attendait, on remarque que $PC_2$ est persuadé de communiquer avec notre Raspberry (d’IP 192.168.0.38, rappelons-le). On comprend désormais mieux le nom d’IP &lt;em&gt;masquerade&lt;/em&gt; !&lt;/p&gt;

&lt;p&gt;Terminal de $PC_1$ (client)&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ncat &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; 192.168.0.39 52201 &lt;span class=&quot;c&quot;&gt;# Terminal de PC_1 (client)&lt;/span&gt;
found 0 associations
found 1 connections:
	1: &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;82&amp;lt;CONNECTED,PREFERRED&amp;gt;
	   outif en0
	   src 192.168.10.10 port 64333
	   dst 192.168.0.39 port 52201
	   rank info not available
	   TCP aux info available
Connection to 192.168.0.39 port 52201 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;tcp/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; succeeded!
Hello
Coucou
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Terminal de $PC_2$ (serveur)&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ncat &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; 52201 &lt;span class=&quot;c&quot;&gt;# Terminal de PC_2 (serveur)&lt;/span&gt;
Ncat: Version 7.01 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; https://nmap.org/ncat &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Ncat: Listening on :::52201
Ncat: Listening on 0.0.0.0:52201
Ncat: Connection from 192.168.0.38.
Ncat: Connection from 192.168.0.38:64333.
Hello
Coucou
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Bref, tout marche bien dans le meilleur des mondes. Il nous reste néanmoins encore une dernière étape pour iptables : rendre notre régle persistente après un reboot. On va faire ça à l’ancienne avec une démarche à la SystemV. L’intérêt est plus didactique qu’autre chose, vu que je verrai après les scripts d’init systemd, on verra la différence de philosophie ! Si j’ai bien compris de toute façon, c’est systemd qui va gérer notre script systemV en rétro-compatibilité.&lt;/p&gt;

&lt;p&gt;La démarche pour SystemV est la suivante. Si je veux créer un nouveau service, je crée un script de lancement dans &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/init.d/&lt;/code&gt; avec un entête contenant différentes informations comme le runlevel où le service doit être activé et les dépendances (par exemple, pour iptables il faut attendre que le réseau soit activé). Notre script devra également contenir les actions à effectuer lorsqu’on arrête, démarre ou interroge le service. On utilise ensuite la commande &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update-rc.d mon_service defaults&lt;/code&gt; pour mettre en place le service. Init.d va lire l’entête de notre fichier et placer des liens symboliques dans les différents &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rcN.d/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Mais dans notre cas, c’est une démarche bien trop lourde : notre commande iptables a vocation à être exécutée une seule fois au démarrage du raspberry. Pour cela, on dispose du script &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rc.local/&lt;/code&gt; : il suffit de rajouter notre règle là dedans et le tour est joué !&lt;/p&gt;

&lt;p&gt;Voici l’état actuel de notre table NAT :&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; nat &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt;
Chain PREROUTING &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&quot;nb&quot;&gt;source               &lt;/span&gt;destination         

Chain INPUT &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&quot;nb&quot;&gt;source               &lt;/span&gt;destination         

Chain OUTPUT &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&quot;nb&quot;&gt;source               &lt;/span&gt;destination         

Chain POSTROUTING &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&quot;nb&quot;&gt;source               &lt;/span&gt;destination         
SNAT       all  &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;  anywhere             anywhere             to:192.168.0.38
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On demande à iptables de sauvegarder l’état actuel des règles dans un fichier de sortie grâce à la commande &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iptables-save&lt;/code&gt;. Cette commande est optionnelle dans la mesure où notre firewall est pour le moment très simple (une seule règle !) mais bon…&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;sh &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;iptables-save &amp;gt; /etc/iptables.rules&quot;&lt;/span&gt;
donut@donutbridge:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /etc/iptables.rules 
&lt;span class=&quot;c&quot;&gt;# Generated by iptables-save v1.4.21 on Fri Aug  4 07:14:07 2017&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;nat
:PREROUTING ACCEPT &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;18:2088]
:INPUT ACCEPT &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;18:2088]
:OUTPUT ACCEPT &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;8:692]
:POSTROUTING ACCEPT &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;0:0]
&lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; POSTROUTING &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; eth0 &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; SNAT &lt;span class=&quot;nt&quot;&gt;--to-source&lt;/span&gt; 192.168.0.38
COMMIT
&lt;span class=&quot;c&quot;&gt;# Completed on Fri Aug  4 07:14:07 2017&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Generated by iptables-save v1.4.21 on Fri Aug  4 07:14:07 2017&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;filter
:INPUT ACCEPT &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;624:77121]
:FORWARD ACCEPT &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;0:0]
:OUTPUT ACCEPT &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;335:33556]
COMMIT
&lt;span class=&quot;c&quot;&gt;# Completed on Fri Aug  4 07:14:07 2017&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Il nous reste maintenant à ajouter une ligne dans &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rc.local/&lt;/code&gt; pour charger notre règle :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Donut 2017-08-04 : regle iptables pour SNAT wlan0 vers eth0&lt;/span&gt;
iptables-restore /etc/iptables.rules
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Désormais lorsqu’on redémarre le raspberry, le réseau sera convenablement paramétré pour la SNAT !&lt;/p&gt;

&lt;h2 id=&quot;un-premier-sniffing-du-réseau&quot;&gt;Un premier sniffing du réseau&lt;/h2&gt;

&lt;p&gt;La topologie (simplifiée) de notre réseau est rappelée ci-dessous. Nous allons reprendre l’exemple du serveur minimaliste &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ncat&lt;/code&gt; de la section précédente afin de voir passer les trames sur le Raspberry. Rappelons que $PC_2$ écoute sur le port 52201 et que $PC_1$ va le contacter. $PC_1$ va envoyer le message “HELLO” auquel le $PC_2$ répondra “COUCOU”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/diagramme_snif.png&quot; alt=&quot;Topologie simplifiée&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Les options de notre tcpdump sont les suivantes :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n&lt;/code&gt; impose l’affichage décimal des IP et des ports,&lt;/li&gt;
  &lt;li&gt;l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-X&lt;/code&gt; demande l’affichage hexadécimal et ASCII des trames IP&lt;/li&gt;
  &lt;li&gt;l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; de mande à tcpdump d’être plus bavard, en particulier il va décortiquer plus en profondeur le header IP (pour notamment afficher le protocole de niveau supérieur)&lt;/li&gt;
  &lt;li&gt;l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c N&lt;/code&gt; demande à tcpdump de s’arrêter après avoir reçu N trames&lt;/li&gt;
  &lt;li&gt;et enfin l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i interface&lt;/code&gt; indique l’interface de capture, par exemple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i wlan0&lt;/code&gt; ou &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i any&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On peut également spécifier à tcpdump une expression de filtrage comme argument, nous indiquons ici “port not 22 and host 192.168.0.27” qui demande de tout écouter sauf le port 22 (la session ssh qui me permet de piloter le Raspberry) du moment qu’un des interlocuteurs (émetteur ou destinataire) est $PC_2$ (d’IP 192.168.0.27). Pour des raisons de clarté, je ne remets pas les lignes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netcat&lt;/code&gt; des clients/serveur mais juste les informations capturées par tcpdump.&lt;/p&gt;

&lt;p&gt;Dans un premier temps, on ne capture que la connection entre client et serveur; c’est-à-dire qu’on arrête tcpdump juste après avoir exécuté &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nc -v 192.168.0.27 52201&lt;/code&gt; sur le $PC_1$. Nous indiquons également à tcpdump de s’arrêter après avoir reçu 3 trames. J’ai remarqué que l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i any&lt;/code&gt; rencontrait quelques difficultés pour capturer le tout premier paquet. J’ai donc executé la même commande sur deux shells différents : l’une avec l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i wlan0&lt;/code&gt; et l’autre avec l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i eth0&lt;/code&gt;. J’ai rajouté un grep ensuite pour ne garder que le header de chaque trame.&lt;/p&gt;

&lt;p&gt;Nous obtenons le résultat suivant. Il y a plein de choses à en dire !&lt;/p&gt;

&lt;p&gt;Tout d’abord, si on regarde bien, on se rend compte que chaque trame IP est dupliquée. La toute première trame IP est sur l’interface wlan0 et les correspondants sont 192.168.10.10.54064 &amp;gt; 192.168.0.27.52201. On voit que le raspberry émet aussitôt une trame sur sont interface eth0 dont les correpondants sont  192.168.0.38.54064 &amp;gt; 192.168.0.27.52201. Cette comparaison met en évidence le mécanisme de NAT : $PC_2$ cherche à contacter $PC_1$ sur son port 52201, il indique aussi son port de retour (ici 54064). Le Raspberry a modifié l’IP source en sa propre IP (ainsi 192.168.10.10 devient 192.168.0.38), le port source quant à lui n’a pas changé.&lt;/p&gt;

&lt;p&gt;Par ailleurs, si on regarde l’entête de chaque trame (enrichi rappelons-le grâce à l’option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; de tcpdump) on remarque que le protocole de niveau supérieur est le TCP. On s’en rend compte également quand on remarque que les trois premières trames échangées (le &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n 3&lt;/code&gt; n’était pas choisi au hasard) ont le flag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[S]&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[S.]&lt;/code&gt; et enfin &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[.]&lt;/code&gt;. Cela correspond aux trois indicateurs SYN, SYN-ACK et ACK qui constituent la fameuse triple poignée de main d’une ouverture de connexion TCP.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tcpdump &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 3 &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; wlan0 &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; host 192.168.0.27 | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; 1 IP
08:53:19.542053 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 64, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;35611, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 64&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.10.10.54064 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.27.52201: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;S], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0xb9ce &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1191224222, win 65535, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;mss 1460,nop,wscale 5,nop,nop,TS val 1045703083 ecr 0,sackOK,eol], length 0
08:53:19.543017 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 63, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;0, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 60&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.27.52201 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.10.54064: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;S.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0xd86f &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3811497698, ack 1191224223, win 28960, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;mss 1460,sackOK,TS val 11781739 ecr 1045703083,nop,wscale 7], length 0
08:53:19.546340 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 64, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;46676, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.10.10.54064 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.27.52201: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x6840 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, ack 1, win 4117, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;nop,nop,TS val 1045703090 ecr 11781739], length 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tcpdump &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 3 &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; eth0 &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; port not 22 and host 192.168.0.27 | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; 1 IP
08:53:19.542387 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 63, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;35611, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 64&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.38.54064 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.27.52201: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;S], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0xc3b2 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1191224222, win 65535, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;mss 1460,nop,wscale 5,nop,nop,TS val 1045703083 ecr 0,sackOK,eol], length 0
08:53:19.542874 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 64, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;0, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 60&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.27.52201 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.38.54064: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;S.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0xe253 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3811497698, ack 1191224223, win 28960, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;mss 1460,sackOK,TS val 11781739 ecr 1045703083,nop,wscale 7], length 0
08:53:19.546498 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 63, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;46676, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.38.54064 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.27.52201: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x7224 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, ack 1, win 4117, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;nop,nop,TS val 1045703090 ecr 11781739], length 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Bref tout ceci est plutôt positif. Regardons maintenant si nous pouvons, via tcpdump, récupérer les informations échangées entre $PC_1$ et $PC_2$. Ecouter sur les deux interfaces eth0 et wlan0 n’avait qu’un but didactique pour voir la SNAT en action, nous nous restreindrons désormais à la seule interface wlan0. Voici donc ce qu’on obtient lorsque $PC_1$ envoie le message “HELLO” auquel le $PC_2$ répond “COUCOU”.&lt;/p&gt;

&lt;p&gt;Nous retrouvons bien au début notre triple poignée de main, suivie de deux messages avec leur acquittement. On remarque que les chaînes “HELLO” et “COUCOU” apparaissent bien en clair sur la sortie de tcpdump et qu’elles sont en fin de trame IP.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;tcpdump &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; wlan0 host 192.168.0.27
tcpdump: listening on wlan0, link-type EN10MB &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Ethernet&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, capture size 262144 bytes
13:48:51.967582 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 64, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;15450, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 64&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.10.10.54265 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.27.52201: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;S], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x464a &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;4251772336, win 65535, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;mss 1460,nop,wscale 5,nop,nop,TS val 1063428825 ecr 0,sackOK,eol], length 0
	0x0000:  4500 0040 3c5a 4000 4006 72e8 c0a8 0a0a  E..@&amp;lt;Z@.@.r.....
	0x0010:  c0a8 001b d3f9 cbe9 fd6c e5b0 0000 0000  .........l......
	0x0020:  b002 ffff 464a 0000 0204 05b4 0103 0305  ....FJ..........
	0x0030:  0101 080a 3f62 a2d9 0000 0000 0402 0000  ....?b..........
13:48:51.969090 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 63, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;0, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 60&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.27.52201 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.10.54265: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;S.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0xcfe8 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;820279578, ack 4251772337, win 28960, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;mss 1460,sackOK,TS val 16214845 ecr 1063428825,nop,wscale 7], length 0
	0x0000:  4500 003c 0000 4000 3f06 b046 c0a8 001b  E..&amp;lt;..@.?..F....
	0x0010:  c0a8 0a0a cbe9 d3f9 30e4 791a fd6c e5b1  ........0.y..l..
	0x0020:  a012 7120 cfe8 0000 0204 05b4 0402 080a  ..q.............
	0x0030:  00f7 6b3d 3f62 a2d9 0103 0307            ..k&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;?b......
13:48:51.971944 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 64, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;37412, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.10.10.54265 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.27.52201: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x5fb8 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, ack 1, win 4117, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;nop,nop,TS val 1063428833 ecr 16214845], length 0
	0x0000:  4500 0034 9224 4000 4006 1d2a c0a8 0a0a  E..4.&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;.@..&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;....
	0x0010:  c0a8 001b d3f9 cbe9 fd6c e5b1 30e4 791b  .........l..0.y.
	0x0020:  8010 1015 5fb8 0000 0101 080a 3f62 a2e1  ...._.......?b..
	0x0030:  00f7 6b3d                                ..k&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
13:48:58.172315 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 64, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;52612, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 58&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.10.10.54265 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.27.52201: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;P.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x63df &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1:7, ack 1, win 4117, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;nop,nop,TS val 1063435024 ecr 16214845], length 6
	0x0000:  4500 003a cd84 4000 4006 e1c3 c0a8 0a0a  E..:..@.@.......
	0x0010:  c0a8 001b d3f9 cbe9 fd6c e5b1 30e4 791b  .........l..0.y.
	0x0020:  8018 1015 63df 0000 0101 080a 3f62 bb10  ....c.......?b..
	0x0030:  00f7 6b3d 4845 4c4c 4f0a                 ..k&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;HELLO.
13:48:58.173811 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 63, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;4645, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 52&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.27.52201 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.10.54265: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x50a6 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, ack 7, win 227, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;nop,nop,TS val 16216396 ecr 1063435024], length 0
	0x0000:  4500 0034 1225 4000 3f06 9e29 c0a8 001b  E..4.%@.?..&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;....
	0x0010:  c0a8 0a0a cbe9 d3f9 30e4 791b fd6c e5b7  ........0.y..l..
	0x0020:  8010 00e3 50a6 0000 0101 080a 00f7 714c  ....P.........qL
	0x0030:  3f62 bb10                                ?b..
13:49:02.396522 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 63, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;4646, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 60&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.27.52201 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.10.54265: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;P.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x4084 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1:9, ack 7, win 227, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;nop,nop,TS val 16217452 ecr 1063435024], length 8
	0x0000:  4500 003c 1226 4000 3f06 9e20 c0a8 001b  E..&amp;lt;.&amp;amp;@.?.......
	0x0010:  c0a8 0a0a cbe9 d3f9 30e4 791b fd6c e5b7  ........0.y..l..
	0x0020:  8018 00e3 4084 0000 0101 080a 00f7 756c  ....@.........ul
	0x0030:  3f62 bb10 2443 4f55 434f 550a            ?b..&lt;span class=&quot;nv&quot;&gt;$COUCOU&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
13:49:02.604305 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 63, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;4647, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 60&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.27.52201 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.10.54265: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;P.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x4050 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;1:9, ack 7, win 227, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;nop,nop,TS val 16217504 ecr 1063435024], length 8
	0x0000:  4500 003c 1227 4000 3f06 9e1f c0a8 001b  E..&amp;lt;.&lt;span class=&quot;s1&quot;&gt;'@.?.......
	0x0010:  c0a8 0a0a cbe9 d3f9 30e4 791b fd6c e5b7  ........0.y..l..
	0x0020:  8018 00e3 4050 0000 0101 080a 00f7 75a0  ....@P........u.
	0x0030:  3f62 bb10 2443 4f55 434f 550a            ?b..$COUCOU.
13:49:02.683637 IP (tos 0x0, ttl 64, id 61743, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.10.10.54265 &amp;gt; 192.168.0.27.52201: Flags [.], cksum 0x2bb0 (correct), ack 9, win 4117, options [nop,nop,TS val 1063439532 ecr 16217452], length 0
	0x0000:  4500 0034 f12f 4000 4006 be1e c0a8 0a0a  E..4./@.@.......
	0x0010:  c0a8 001b d3f9 cbe9 fd6c e5b7 30e4 7923  .........l..0.y#
	0x0020:  8010 1015 2bb0 0000 0101 080a 3f62 ccac  ....+.......?b..
	0x0030:  00f7 756c                                ..ul
13:49:02.683664 IP (tos 0x0, ttl 64, id 44468, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.10.10.54265 &amp;gt; 192.168.0.27.52201: Flags [.], cksum 0x2b7c (correct), ack 9, win 4117, options [nop,nop,TS val 1063439532 ecr 16217504], length 0
	0x0000:  4500 0034 adb4 4000 4006 019a c0a8 0a0a  E..4..@.@.......
	0x0010:  c0a8 001b d3f9 cbe9 fd6c e5b7 30e4 7923  .........l..0.y#
	0x0020:  8010 1015 2b7c 0000 0101 080a 3f62 ccac  ....+|......?b..
	0x0030:  00f7 75a0                                ..u.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Maintenant que nous maîtrisons bien la manipulation de tcpdump, passons à l’espionnage de la balance !&lt;/p&gt;

&lt;h2 id=&quot;sniffing-de-la-balance&quot;&gt;Sniffing de la balance&lt;/h2&gt;

&lt;p&gt;Rappelons le fonctionnement de la balance :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;quand je me pèse, mon poids s’affiche sur la balance (normal),&lt;/li&gt;
  &lt;li&gt;après un court instant, la balance me reconnaît et affiche mes initiales sur son écran,&lt;/li&gt;
  &lt;li&gt;régulièrement, la balance mesure le taux de CO2 dans l’air&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A ce stade, je me pose plusieurs questions :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Est-ce que la balance chiffre ses données lorsqu’elle communique avec l’extérieur ? Si c’est le cas, toute mon opération tombe à l’eau !&lt;/li&gt;
  &lt;li&gt;Est-ce que la transmission du poids est synchrone avec le moment où je me pèse ? Existe-t’il un délai ?&lt;/li&gt;
  &lt;li&gt;A quelle fréquence est-ce que le taux de CO2 est-il mesuré ?&lt;/li&gt;
  &lt;li&gt;Est-ce une communication unilatérale ou bien la balance attend-elle un retour du serveur ?&lt;/li&gt;
  &lt;li&gt;En particulier, à quel endroit (côté balance ou côté serveur) s’effectue l’identification entre un poids et une personne ?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il faudra garder tout ça en tête lorsqu’on regardera les trames…&lt;/p&gt;

&lt;p&gt;Pour épurer au maximum les trames, je coupe la connexion WiFi de $PC_1$ et je vérfie que je ne le vois plus dans les associations de Hostapd :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;hostapd_cli all_sta
Selected interface &lt;span class=&quot;s1&quot;&gt;'wlan0'&lt;/span&gt;
9c:f3:87:aa:fe:62
&lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=[&lt;/span&gt;AUTH][ASSOC][AUTHORIZED]
&lt;span class=&quot;nv&quot;&gt;aid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;capability&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x0
&lt;span class=&quot;nv&quot;&gt;listen_interval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;supported_rates&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;timeout_next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;NULLFUNC POLL
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsSTAAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;9c:f3:87:aa:fe:62
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsVersion&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsSelectedPairwiseCipher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;00-0f-ac-4
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsTKIPLocalMICFailures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;dot11RSNAStatsTKIPRemoteMICFailures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;hostapdWPAPTKState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;11
&lt;span class=&quot;nv&quot;&gt;hostapdWPAPTKGroupState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;rx_packets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;tx_packets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;rx_bytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;tx_bytes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
&lt;span class=&quot;nv&quot;&gt;connected_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;20612

&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;ON COUPE LE WIFI DE PC_1]

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;hostapd_cli all_sta
Selected interface &lt;span class=&quot;s1&quot;&gt;'wlan0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alors voici les trames intéressantes qui sont survenues juste après la pesée. Je remarque au passage que l’association poids-personne se fait vraisemblablement AVANT l’envoi d’une trame…&lt;/p&gt;

&lt;p&gt;Tout d’abord, notre balance d’adresse MAC 00:24:e4:0d:34:38 se réveille et demande une IP à la cantonade. Notre serveur DHCP lui en servira une.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;14:09:27.665777 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 255, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;0, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;none], proto UDP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;17&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 328&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    0.0.0.0.68 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 255.255.255.255.67: BOOTP/DHCP, Request from 00:24:e4:0d:34:38, length 300, xid 0x343606f7, Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;none]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Peu après, notre balance qui a obtenu l’IP 192.168.10.12 demande à notre box internet quelle est l’IP du serveur wbs02-ws.withings.net. Notre box lui répond que ce serveur est accessible à l’adresse 89.30.121.150. Si on est parano, on peut contrôler que c’est bien correct grâce à la commande &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dig&lt;/code&gt; qui permet de faire des requêtes dns. Vraisemblablement, ce sera avec ce serveur que la balance va communiquer, retenons donc cette adresse elle sera sans doute utile dans la suite.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;14:09:28.627479 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 255, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;2, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;none], proto UDP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;17&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 67&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.10.12.49153 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.0.254.53: 0+ A? wbs02-ws.withings.net. &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;39&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
14:09:28.628502 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 63, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;38040, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto UDP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;17&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 112&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.0.254.53 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.12.49153: 0 2/0/0 wbs02-ws.withings.net. CNAME ws.withings.net., ws.withings.net. A 89.30.121.150 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;84&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;dig wbs02-ws.withings.net

&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &amp;lt;&amp;lt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; DiG 9.10.3-P4-Ubuntu &amp;lt;&amp;lt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; wbs02-ws.withings.net
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt; global options: +cmd
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt; Got answer:
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt; -&amp;gt;&amp;gt;HEADER&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;opcode&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;: QUERY, status: NOERROR, id: 35231
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;wbs02-ws.withings.net.		IN	A

;; ANSWER SECTION:
wbs02-ws.withings.net.	926	IN	CNAME	ws.withings.net.
ws.withings.net.	1561	IN	A	89.30.121.150

;; Query time: 0 msec
;; SERVER: 127.0.1.1#53(127.0.1.1)
;; WHEN: Sun Aug 06 14:19:31 CEST 2017
;; MSG SIZE  rcvd: 95
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Les trois trames suivantes sont la triple poignée de main TCP avec le serveur 89.30.121.150. On se rend compte que la balance communique bien en TCP avec son serveur et pas en UDP comme je l’espérais… Mais pire, on remarque que le port de destination est le 443 et ça correspond à de l’https, autrement dit un port http sécurisé et chiffré… Je ne risque donc hélas pas de voir passer en clair les informations de ma balance.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;14:09:28.649462 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 255, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;3, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;none], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 44&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.10.12.49153 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 89.30.121.150.443: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;S], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0xe961 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;672401143, win 8400, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;mss 1400], length 0
	0x0000:  4500 002c 0003 0000 ff06 1e60 c0a8 0a0c  E..,.......&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;....
	0x0010:  591e 7996 c001 01bb 2814 06f7 0000 0000  Y.y.....&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;.......
	0x0020:  6002 20d0 e961 0000 0204 0578            &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;....a.....x
14:09:28.651938 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 48, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;0, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;DF], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 44&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    89.30.121.150.443 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 192.168.10.12.49153: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;S.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0xd24f &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;nb&quot;&gt;seq &lt;/span&gt;3580817429, ack 672401144, win 29200, options &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;mss 1460], length 0
	0x0000:  4500 002c 0000 4000 3006 ad63 591e 7996  E..,..@.0..cY.y.
	0x0010:  c0a8 0a0c 01bb c001 d56e f015 2814 06f8  .........n..&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;...
	0x0020:  6012 7210 d24f 0000 0204 05b4            &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;.r..O......
14:09:28.684596 IP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;tos 0x0, ttl 255, &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;4, offset 0, flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;none], proto TCP &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;6&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, length 40&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    192.168.10.12.49153 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; 89.30.121.150.443: Flags &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;.], &lt;span class=&quot;nb&quot;&gt;cksum &lt;/span&gt;0x3b4d &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;correct&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, ack 1, win 8400, length 0
	0x0000:  4500 0028 0004 0000 ff06 1e63 c0a8 0a0c  E..&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;.......c....
	0x0010:  591e 7996 c001 01bb 2814 06f8 d56e f016  Y.y.....&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;....n..
	0x0020:  5010 20d0 3b4d 0000 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pour tout n’est pas perdu pour autant mais l’affaire se complique drôlement… Le petit espoir que j’ai est que la balance ne semble communiquer qu’avec 89.30.121.150. En particulier, cela signifie qu’elle n’effectue jamais de demande OCSP à l’autorité tierce qui aurait donné son certificat à 89.30.121.150. Autrement dit, le serveur wbs02-ws.withings.net signe lui même son certificat.&lt;/p&gt;

&lt;h2 id=&quot;pistes-pour-continuer&quot;&gt;Pistes pour continuer&lt;/h2&gt;

&lt;p&gt;Ajout d’un DNAT pour faire pointer les requêtes HTTPS vers un serveur local&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; nat &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; PREROUTING &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; wlan0 &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; tcp &lt;span class=&quot;nt&quot;&gt;--dst&lt;/span&gt; 89.30.121.150 &lt;span class=&quot;nt&quot;&gt;--dport&lt;/span&gt; 443 &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; DNAT &lt;span class=&quot;nt&quot;&gt;--to-destination&lt;/span&gt; 192.168.0.27:443

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; nat &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt;
Chain PREROUTING &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&quot;nb&quot;&gt;source               &lt;/span&gt;destination         
DNAT       tcp  &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;  anywhere             89.30.121.150        tcp dpt:https to:192.168.0.27:443

Chain INPUT &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&quot;nb&quot;&gt;source               &lt;/span&gt;destination         

Chain OUTPUT &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&quot;nb&quot;&gt;source               &lt;/span&gt;destination         

Chain POSTROUTING &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&quot;nb&quot;&gt;source               &lt;/span&gt;destination         
SNAT       all  &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;  anywhere             anywhere             to:192.168.0.38
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Serveur HTTPS sur ma machine 192.168.0.38 :&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ncat &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--ssl&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; 443
Ncat: Version 7.01 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; https://nmap.org/ncat &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Ncat: Generating a temporary 1024-bit RSA key. Use &lt;span class=&quot;nt&quot;&gt;--ssl-key&lt;/span&gt; and &lt;span class=&quot;nt&quot;&gt;--ssl-cert&lt;/span&gt; to use a permanent one.
Ncat: SHA-1 fingerprint: 4912 450C 3AA7 782D AAFC BA0B 6842 BABB 2C2B 16C8
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 192.168.0.38.
Ncat: Connection from 192.168.0.38:49153.
Ncat: Failed SSL connection from 192.168.0.38: error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On cherche le certificat&lt;/p&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Q'&lt;/span&gt; | openssl s_client &lt;span class=&quot;nt&quot;&gt;-connect&lt;/span&gt; wbs02-ws.withings.net:443 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; withings_raw.cert
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;withings_raw.cert | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/BEGIN/,/END/p'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; withings.cert
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;nous étions en 2017 &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>DonutMan</name></author><category term="gnu-linux" /><category term="hacking" /><category term="wifi" /><category term="network" /><category term="NAT" /><category term="Sniffing" /><summary type="html">En août 2017, je m’étais amusé à pirater ma balance connectée de marque Withing afin de récupérer directement mes mesures de poids sans passer par l’interface Withings. L’idée mise en place était une technique classique de man in the middle, exécutée from scratch pas à pas (l’exercice fut didactique). Aujourd’hui, 8 ans plus tard, je remets la main par hasard sur ce projet que j’avais un peu oublié. Je reproduis ci-dessous pour garder une trace. A noter que la plupart des informations ont été anonymisées… mais après 8 ans et un renouvellement intégral de mon matériel informatique (box, pc, raspberry etc.), il n’y a plus grand chose de sensible de toutes façon. Et je me suis depuis débarrassé de cette balance (coucou la Quadrature du Net).</summary></entry><entry><title type="html">Chiffrement par cheminement d’une fourmi de Langton</title><link href="https://donutblog.fr/mathematique/langton-encryption/" rel="alternate" type="text/html" title="Chiffrement par cheminement d’une fourmi de Langton" /><published>2025-05-22T01:00:00+02:00</published><updated>2025-05-22T01:00:00+02:00</updated><id>https://donutblog.fr/mathematique/langton-encryption</id><content type="html" xml:base="https://donutblog.fr/mathematique/langton-encryption/">&lt;p&gt;Récemment, j’ai réalisé qu’une &lt;a href=&quot;https://en.wikipedia.org/wiki/Langton%27s_ant&quot;&gt;fourmi de Langton&lt;/a&gt; se promenant sur une image noir et blanc et la modifiant à la volée était un phénomène parfaitement réversible : en programmant une &lt;em&gt;fourmi duale&lt;/em&gt; ayant des mécaniques inversées par rapport à la fourmi initiale, on peut en théorie reconstruire le film en sens inverse et retrouver &lt;em&gt;in fine&lt;/em&gt; l’image originale. Chiche ? Chiche !&lt;/p&gt;

&lt;!--more--&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#la-fourmi-de-langton&quot; id=&quot;markdown-toc-la-fourmi-de-langton&quot;&gt;La fourmi de Langton&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#image-de-départ&quot; id=&quot;markdown-toc-image-de-départ&quot;&gt;Image de départ&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#algorithme-de-destruction&quot; id=&quot;markdown-toc-algorithme-de-destruction&quot;&gt;Algorithme de destruction&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#paramètres-globaux&quot; id=&quot;markdown-toc-paramètres-globaux&quot;&gt;Paramètres globaux&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#préparation-de-limage&quot; id=&quot;markdown-toc-préparation-de-limage&quot;&gt;Préparation de l’image&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#marche-de-la-fourmi&quot; id=&quot;markdown-toc-marche-de-la-fourmi&quot;&gt;Marche de la fourmi&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#dégradation-de-limage-par-la-fourmi&quot; id=&quot;markdown-toc-dégradation-de-limage-par-la-fourmi&quot;&gt;Dégradation de l’image par la fourmi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#emergence-de-boucles-dinversion&quot; id=&quot;markdown-toc-emergence-de-boucles-dinversion&quot;&gt;Emergence de boucles d’inversion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#quantification-de-la-perturbation&quot; id=&quot;markdown-toc-quantification-de-la-perturbation&quot;&gt;Quantification de la perturbation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#procédure-de-reconstruction&quot; id=&quot;markdown-toc-procédure-de-reconstruction&quot;&gt;Procédure de reconstruction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#programme-de-chiffrement-non&quot; id=&quot;markdown-toc-programme-de-chiffrement-non&quot;&gt;Programme de chiffrement (non.)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#considérations-sur-la-sécurité-de-lalgorithme&quot; id=&quot;markdown-toc-considérations-sur-la-sécurité-de-lalgorithme&quot;&gt;Considérations sur la sécurité de l’algorithme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#pour-aller-un-peu-plus-loin&quot; id=&quot;markdown-toc-pour-aller-un-peu-plus-loin&quot;&gt;Pour aller un peu plus loin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;la-fourmi-de-langton&quot;&gt;La fourmi de Langton&lt;/h2&gt;

&lt;p&gt;La fourmi de Langton est un petit automate cellulaire que j’aime beaucoup. Les règles, dans leur formulation originelle, sont très simples. La fourmi se déplace sur une grille bi-dimensionnelle dont les cases ne peuvent prendre que deux couleurs : noir ou blanc. La grille est historiquement blanche mais on peut envisager n’importe quel motif de départ (un damier d’échec, une image binarisée, du bruit etc.).&lt;/p&gt;

&lt;p&gt;La fourmi est représentée par sa position, que nous notons $(x,y)$ et son orientation (haut, bas, gauche, droite).&lt;/p&gt;

&lt;p&gt;Ses règles d’évolution sont les suivantes (l’ordre d’application est important) :&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;si elle est sur une case &lt;strong&gt;noire&lt;/strong&gt;, elle fait un quart de tour vers la &lt;strong&gt;gauche&lt;/strong&gt;; sinon (i.e. si elle est sur une case &lt;strong&gt;blanche&lt;/strong&gt;) elle fait un quart de tour vers la &lt;strong&gt;droite&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;elle &lt;strong&gt;change la couleur&lt;/strong&gt; de la case sur laquelle elle se trouve&lt;/li&gt;
  &lt;li&gt;elle &lt;strong&gt;avance&lt;/strong&gt; d’une case&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voici un exemple d’évolution de la fourmi sur une grille $11 \times 11$ et pour les 200 premiers pas, tiré de Wikipédia :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/langton.gif&quot; alt=&quot;Animation de la fourmi de Langton sur 200 pas&quot; /&gt;&lt;/p&gt;

&lt;p&gt;La grille est supposée être infinie ce qui n’est pas réalisable en pratique. D’ordinaire, on peut choisir une grille suffisamment grande pour que la fourmi n’ait pas le temps d’atteindre les bords lors de sa pérégrination qui sera d’un temps fini. Mais on peut également définir des règles sur ce qu’il se passe lorsque la fourmi dépasse les limites de la grille. Habituellement, on considère un comportement &lt;em&gt;à la pacman&lt;/em&gt; : si la fourmi sort à droite, elle réapparaît à gauche. Idem pour tous les autres bords (si elle sort en haut, elle réapparaît en bas etc.). C’est ce comportement là que nous envisagerons dans le cas du traitement de nos images (puisqu’idéalement, on aimerait que la fourmi se promène vraiment partout sur notre image et pas seulement “loin des bords”).&lt;/p&gt;

&lt;p&gt;Une conjecture étonnante a été émise sur le comportement de la fourmi : au bout d’un certain temps, cette dernière partira à l’infini dans un schéma périodique appelé l’&lt;strong&gt;autoroute&lt;/strong&gt;. Un exemple d’une telle autoroute est donnée ci-dessous (la fourmi y est représentée par un pixel rouge). C’est un comportement émergent, structuré, qui apparaît après une phase de mouvement en apparence chaotique (à tout le moins, irrégulière) de la fourmi. Cet attracteur apparaît systématiquement pour l’ensemble des damiers finis étudiés jusqu’à présent par la communauté scientifique.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/autoroute.png&quot; alt=&quot;Autoroute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Néanmoins, ce n’est pas le sujet de mon article. Je me concentre surtout sur la façon dont la fourmi désagrège, méthodiquement et &lt;strong&gt;sans perte d’information&lt;/strong&gt; une image de départ.&lt;/p&gt;

&lt;p&gt;La transformation est à chaque pas totalement réversible puisqu’il suffit d’effectuer les étapes en sens inverser (reculer d’une case, changer la couleur, tourner). A noter que cette caractéristique de réversibilité n’est pas propre à tous les automates cellulaires ainsi que l’illustre cet exemple du &lt;a href=&quot;https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life&quot;&gt;jeu de la vie&lt;/a&gt; tiré de l’&lt;a href=&quot;https://xkcd.com/2293/&quot;&gt;hommage rendu par XKCD&lt;/a&gt; lors du décès de &lt;a href=&quot;https://en.wikipedia.org/wiki/John_Horton_Conway&quot;&gt;John Conway&lt;/a&gt;, l’inventeur du jeu. Dans cette configuration, le petit bonhomme se transforme en glyder, une structure qui se déplace à l’infini par translation. On devine bien qu’une fois le glyder établi, il sera impossible de reformer le bonhomme en passant le film en sens inverse : de l’information a été perdue à un moment du processus.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/rip_john_conway.gif&quot; alt=&quot;RIP John Conway&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;image-de-départ&quot;&gt;Image de départ&lt;/h2&gt;

&lt;p&gt;Mais revenons en au propos principal de mon article. Pour les besoins de l’expérience, je me suis basé sur une photo de moi prise à la webcam de mon ordinateur. Je vous prie de bien vouloir excuser le caractère quelque peu narcissique de cette initiative… Je me suis aussi limité à une image de 501 pixels de large par 501 pixels de haut, pour des raisons que nous discuterons bientôt.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/a_cropped.png&quot; alt=&quot;Image de travail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;C’est, comme vous pouvez le conster, une photo en couleur. Il faut donc la convertir en Noir et Blanc de façon purement binaire (soit Noir, soit Blanc, pas de nuances de gris).&lt;/p&gt;

&lt;p&gt;Ma première idée a été de partir sur une fonction de seuillage classique. En choisissant le bon seuil (ici $76$) on garde une bonne compréhension de l’image.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/impact_seuillage.png&quot; alt=&quot;Impact du seuillage&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Néanmoins, je trouve qu’on perd quand même pas mal d’information. Je suis alors parti sur une méthode de diffusion d’erreur (le &lt;em&gt;dithering&lt;/em&gt;), par un &lt;a href=&quot;https://fr.wikipedia.org/wiki/Algorithme_de_Floyd-Steinberg&quot;&gt;algorithme de Floyd-Steinberg&lt;/a&gt;. L’idée est de rendre compte des nuances de gris en utilisant des techniques de propagation d’erreur. Un exemple récent de &lt;em&gt;dithering&lt;/em&gt; dans l’univers du jeu vidéo est le réputé &lt;a href=&quot;https://store.steampowered.com/app/653530/Return_of_the_Obra_Dinn/?l=french&quot;&gt;Return of the Obra Dinn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Voici ce qu’on obtient si on applique l’algorithme sur mon image initiale.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/b_dither.png&quot; alt=&quot;Dithering&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Comprenons bien que l’image obtenue n’est constituée que de pixels soit Noir, soit Blanc; ainsi qu’on peut s’en rendre compte en regardant un zoom sur mon oeil droit.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/oeil_bleu_blanc_2b.png&quot; alt=&quot;Oeil bleu blanc&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Outre le fait qu’on a l’impression de perdre moins d’informations sur l’image, ce procédé créé un damier de départ assez complexe qui perturbera le cheminement de la fourmi.&lt;/p&gt;

&lt;h2 id=&quot;algorithme-de-destruction&quot;&gt;Algorithme de destruction&lt;/h2&gt;

&lt;p&gt;L’algorithme est assez simple et se base sur la &lt;a href=&quot;https://he-arc.github.io/livre-python/pillow/index.html&quot;&gt;bibliothèque Pillow&lt;/a&gt; qui permet de manipuler (trop) simplement des images.&lt;/p&gt;

&lt;h3 id=&quot;paramètres-globaux&quot;&gt;Paramètres globaux&lt;/h3&gt;

&lt;p&gt;Je commence par définir quelques constantes de l’automate :&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;WIDTH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;501&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;HEIGHT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;501&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Iteration of the Langton's ant
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DIR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'L'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'U'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'R'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'D'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Ordered directions (Left, Up, Right, Down)
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;COLORS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'B'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'W'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Black or White
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dither&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dither&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dither&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FLOYDSTEINBERG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# I should try which one is cooler
&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WIDTH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HEIGHT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;L’image est, comme nous l’avons dit, de taille constante : &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WIDTH = 501&lt;/code&gt; pixels de large et &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEIGHT = 501&lt;/code&gt; pixels de haut. J’ai choisi une taille d’image assez grande pour qu’on puisse voir quelque chose mais pas trop afin de ne pas attendre trop longtemps que la fourmi ait marché un peu partout. Le choix d’un nombre &lt;strong&gt;impair&lt;/strong&gt; permet de placer la fourmi &lt;em&gt;exactement&lt;/em&gt; au centre de l’image. C’est un choix purement esthétique, la position de départ de la fourmi important au final assez peu.&lt;/p&gt;

&lt;p&gt;On définit également le nombre &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;N&lt;/code&gt; d’itérations maximum (ici 5 millions de pas) afin d’arrêter le processus.&lt;/p&gt;

&lt;p&gt;Les variables &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DIR&lt;/code&gt; et &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COLORS&lt;/code&gt; contiennent respectivement les 4 orientations de la fourmi (gauche, haut, droite, bas) et la couleur des cases de l’image (noir ou blanc donc). Les orientations sont stockées de telle façon que l’évolution de la fourmi ne la fait se déplacer que d’une case vers l’avant ou vers l’arrière dans le vecteur d’état : étant intialement orientée “DROITE”, la fourmi ne peut ainsi passer que dans l’état “HAUT” (quart de tour vers la gauche) ou “BAS” (quart de tour vers la droite). J’aurai aimé coder ça sous la forme d’une liste circulaire. Je sais par exemple que la bibliothèque itertools dispose d’une fonction &lt;a href=&quot;https://docs.python.org/3/library/itertools.html#itertools.cycle&quot;&gt;cycle&lt;/a&gt; qui permet de faire ce genre de chose. Mais j’ai été un peu fainéant et j’ai rajouté un gros modulo…&lt;/p&gt;

&lt;p&gt;La liste &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dither&lt;/code&gt; contient les deux seuls algorithmes actuellement implémentés avec Pillow (version 11.2.1) permettant de réduire la palette chromatique d’une image : le seuillage simple (accessible avec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Image.Dither.NONE&lt;/code&gt;) et l’algorithme de Floyd-Steinberg dont nous avons déjà parlé et qui s’applique avec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Image.Dither.FLOYDSTEINBERG&lt;/code&gt;. Hélas, les fonctions proposées par Pillow sont souvent trop basique et il n’est pas possible de définir un paramètre de seuillage. J’ignore comment ce paramètre est choisi (peut-être un simple $255/2$). Mais en pratique, j’ai du me passer de cette fonction de Pillow pour le faire à la main.&lt;/p&gt;

&lt;p&gt;L’état initial de la fourmi est finalement représenté par ses coordonnées &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x = (WIDTH-1)//2&lt;/code&gt; et &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y = (HEIGHT-1)//2&lt;/code&gt; et son orientation intiale, vers le haut, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;d = 1&lt;/code&gt;. C’est un choix qui est ici purement arbitraire. Remarquons au passage qu’avec Pillow, l’origine du repère est situé au bord supérieur gauche de l’image, comme c’est souvent le cas avec les autres bibliothèques de traitement d’image.&lt;/p&gt;

&lt;h3 id=&quot;préparation-de-limage&quot;&gt;Préparation de l’image&lt;/h3&gt;

&lt;p&gt;Voici les quelques fonctions clefs qui m’ont permis de préparer mon image avant traitement. Je ne les détaille pas plus et je l’indique uniquement pour en garder une trace.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#%% Loading the picture
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'./input.jpg'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;#%% Pre-processing of the picture
&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Crop to a square format
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thumbnail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WIDTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HEIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Resampling&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BICUBIC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Interpolation to a 501x501 picture
&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# Dithered picture
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;img_dithered&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dither&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dither&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# Thresholded picture (with a custom threshold parameter)
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;img_thresholded&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'L'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img_thresholded&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_thresholded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;76&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;img_thresholded&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_thresholded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dither&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dither&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;marche-de-la-fourmi&quot;&gt;Marche de la fourmi&lt;/h3&gt;

&lt;p&gt;Voici le coeur du programme, qui code l’évolution de la fourmi pas à pas. Il faut bien entendu encapsuler tout ça dans une boucle sur le nombre de pas (donc jusqu’à &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;N&lt;/code&gt; qui vaut cinq millions). Ce n’est pas forcément très bien optimisé. Encore un fois, les listes circulaires pour éviter le recours systématique aux modulo (à la fois sur la direction et sur les pixels de l’image) pourrait améliorer un peu les choses. Mais je ne crois pas qu’on puisse drastiquement réduire le temps de calcul, la fourmi de Langton n’étant par définition pas parallélisable.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getpixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Black case
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
    
&lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;putpixel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
&lt;span class=&quot;n&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# (0,0) coin en haut à gauche !
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WIDTH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WIDTH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HEIGHT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HEIGHT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;dégradation-de-limage-par-la-fourmi&quot;&gt;Dégradation de l’image par la fourmi&lt;/h2&gt;

&lt;p&gt;Voici ce qu’on obtient lorsqu’on fait se promener la fourmi en partant du centre et que nous la laissons évoluer sur cinq millions de pas.&lt;/p&gt;

&lt;iframe width=&quot;800&quot; height=&quot;600&quot; src=&quot;https://www.youtube.com/embed/nT3eq0jEPq0?si=Djw86H3evMK_PWsW&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;On voit que la fourmi finit bien par passer partout. Je ne sais pas si c’était attendu compte tenu de l’univers clôt et périodique de notre image, mais peut-être la fourmi aurait-elle pu partir à l’infini et rester cloisonnée dans une partie de l’image (par exemple, en allant uniquement de gauche à droite sur une petite bande en hauteur). Ce n’est ici heureusement pas le cas et on arrive &lt;em&gt;cahin-caha&lt;/em&gt; à barbouiller l’intégralité de l’image jusqu’à obtenir une littérale bouillie de pixels.&lt;/p&gt;

&lt;p&gt;Néanmoins, &lt;strong&gt;qu’est-ce que c’est lent&lt;/strong&gt; ! Avec une image de $501 \times 501$ pixels, une fourmi bien disciplinée qui lirait les pixels ligne après ligne en allant de la gauche vers la droite aurait mis environ 250 000 mouvement pour parcourir l’image. Ici, il nous faut &lt;strong&gt;un nombre de mouvements de l’ordre du million&lt;/strong&gt;, c’est à dire quatre fois plus. Compte tenu du mouvement en apparence erratique de la fourmi, ce n’est pas &lt;strong&gt;énormément&lt;/strong&gt; plus, mais ça reste néanmoins très fastidieux.&lt;/p&gt;

&lt;p&gt;On remarque également que, sur la durée de la simulation, la fourmi ne semble jamais être partie en autoroute. Son chemin a &lt;em&gt;toujours&lt;/em&gt; été entravé par des pixels qui ont localement perturbé son évolution.&lt;/p&gt;

&lt;p&gt;En revanche, il y a quelques chose de plus troublant qui m’a interpelé.&lt;/p&gt;

&lt;h2 id=&quot;emergence-de-boucles-dinversion&quot;&gt;Emergence de boucles d’inversion&lt;/h2&gt;

&lt;p&gt;A plusieurs reprises, particulièrement au début de la vidéo, la fourmi repasse sur ses pas et &lt;strong&gt;rétablit&lt;/strong&gt; l’image originelle. Je ne m’attendais pas du tout à ce comportement, que je décide d’appeler des &lt;strong&gt;boucles d’inversion&lt;/strong&gt; faute d’un meilleur terme.&lt;/p&gt;

&lt;p&gt;En voici un exemple ci-dessous, qui survient très grossièrement entre l’étape $1 \times 10^5$ et $2 \times 10^5$. La fourmi avait commencé à perturber mon crâne et mes joues (à gauche, étape 105 545). A un moment, elle a changé d’avis et a bien tout remis en place (au milieu, étape 177 760). Puis elle est parti barbouiller mes yeux (à droite, étape 205 535).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/boucle_langton.png&quot; alt=&quot;Boucle d'inversion&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Elle reconstruit ainsi &lt;em&gt;localement&lt;/em&gt; (en espace) et &lt;em&gt;temporairement&lt;/em&gt; ce qu’elle a déconstruit avant de reprendre sa marche destructrice.&lt;/p&gt;

&lt;p&gt;Elle ne repasse bien évidemment pas &lt;em&gt;exactement&lt;/em&gt; sur ses pas (car sinon, elle reviendrait exactement au point de départ), mais pendant un certain nombre d’itérations ses actions font localement ressortir de l’information. Je me demande si cela n’est pas du aux motifs localement périodique introduits par l’algorithme de &lt;em&gt;ditherisation&lt;/em&gt; et dont on peut voir un exemple dans le bord inférieur droit de mes lunettes, entouré en rouge ci-dessous.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/oeil_bleu_blanc_zoom.png&quot; alt=&quot;Oeil bleu blanc, motif en évidence&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Creuser un peu plus cette question m’aménerait trop loin mais j’ai néanmoins voulu tester ce que donnerait la marche de la fourmi sur une image seuillée plutot que &lt;em&gt;dithérisée&lt;/em&gt;. J’ai donc refait tourner l’algorithme sur une telle image en ayant choisi le seuil de 76  que nous avions considéré en début d’article. En-deça d’une nuance de gris de $76/255$, le pixel devient Noir et Blanc sinon.&lt;/p&gt;

&lt;iframe width=&quot;800&quot; height=&quot;600&quot; src=&quot;https://www.youtube.com/embed/YeQxwbFxY8A?si=w9yEQHVX5VBLiCZn&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Un oeil attentif remarquera toujours quelques boucles, moins elles sont moins nombreuses et surtout &lt;strong&gt;beaucoup plus courtes&lt;/strong&gt; (en temps et en espace). Il y a peut-être quelque chose à creuser dans le fait qu’une fourmi placée au voisinage du passage d’une autre fourmi est susceptible de reconstruire l’image originelle. Sans doute des blocs de transformations équivalent, quelque chose comme ça.&lt;/p&gt;

&lt;p&gt;En revanche, les grands aplats uniformes introduits par le seuillage rendent beaucoup plus fréquentes l’émergence d’autoroutes.&lt;/p&gt;

&lt;p&gt;Enfin, on remarque que la fourmi peine beaucoup plus à brouiller l’image, le processus semble sensiblement plus lent.&lt;/p&gt;

&lt;h2 id=&quot;quantification-de-la-perturbation&quot;&gt;Quantification de la perturbation&lt;/h2&gt;

&lt;p&gt;Nous pouvons introduire deux mesures de la perturbation causée par la fourmi au fil de sa marche : l’&lt;strong&gt;écart quadratique moyen&lt;/strong&gt; (EQM) par rapport à l’image initiale et l’&lt;strong&gt;entropie&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;C’est ce qui est tracé ci-dessous pour l’image &lt;em&gt;dithérisée&lt;/em&gt; (en bleu) et seuillée (en orange) avec l’EQM en haut et l’entropie en bas.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/langton_entropy.png&quot; alt=&quot;Ecart quadratique moyen et entropie&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Plusieurs observations notables :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;L’EQM part bien évidemment de zéro au tout début : la fourmi n’introduit initialement aucune modification&lt;/li&gt;
  &lt;li&gt;On le voit à la fois sur l’EQM et sur l’entropie : l’image &lt;em&gt;dithérisée&lt;/em&gt; se brouille plus rapidement que l’image simplement seuillée. Nous nous en étions rendu compte en comparant les deux vidéos.&lt;/li&gt;
  &lt;li&gt;On observe bien les boucles d’inversion lorsque, tout à coup, l’EQM par rapport à l’image initiale chute avant de reprendre. On constate bien de nouveau que l’amplitude de cet effet est plus important pour l’image &lt;em&gt;dithérisée&lt;/em&gt;. Gardons en tête que ces courbes sont fortement décimées (un point tous les 10 000 pas). De fait, on est susceptible de ne pas capturer les micro boucles d’inversion qui s’étalent sur moins de 10 000 pas.&lt;/li&gt;
  &lt;li&gt;L’entropie initiale est plus élevée pour l’image &lt;em&gt;dithérisée&lt;/em&gt; que pour l’image seuillée. La encore c’est attendu puisque dans le cas de l’image seuillée on a tendance à avoir de grands aplats de couleur. L’image est en quelque sorte beaucoup plus ordonnée, beaucoup plus structurée. Alors qu’il s’agit à l’origine de la même image source !&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bien entendu, pour l’EQM il faut garder en tête que ce n’est qu’un indicateur. Un EQM maximal n’est pas forcément souhaitable en ce sens où on obtiendrait une image &lt;em&gt;solarisée&lt;/em&gt; de l’image initiale… donc parfaitement lisible !&lt;/p&gt;

&lt;h2 id=&quot;procédure-de-reconstruction&quot;&gt;Procédure de reconstruction&lt;/h2&gt;

&lt;p&gt;Pour la reconstruction, on pourrait coder le comportement inverse de la fourmi et la faire aller à reculons. Néanmoins, il y a légéremment plus malin : il suffit de lui faire faire un virage à 180° et à lui laisser continuer sa marche. Elle reviendra ainsi sur ses pas et reconstruira méthodiquement tout ce qu’elle avait préalablement détruit.&lt;/p&gt;

&lt;p&gt;Ainsi, la fourmi inverse consiste simplement à écrire à un moment :&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voici le genre de résultat que nous obtenons alors, en partant de l’image &lt;em&gt;dithérisée&lt;/em&gt;, en la brouillant pendant &lt;strong&gt;un milliard&lt;/strong&gt; d’étapes (oui oui), en retournant la fourmi et en la laissant évoluer à nouveau pendant un milliard d’étape (on s’accroche petite fourmi !) :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/walk_N_9.png&quot; alt=&quot;Destruction et reconstruction&quot; /&gt;&lt;/p&gt;

&lt;p&gt;J’ai trouvé ça très cool à regarder : la fourmi semble se promener de façon chaotique au milieu d’une bouillie de pixels et tout à coup un détail de l’image émerge ! Il grandit petit à petit. Puis la fourmi part faire autre chose… et d’autres détails arrivent, etc.&lt;/p&gt;

&lt;h2 id=&quot;programme-de-chiffrement-non&quot;&gt;Programme de chiffrement (non.)&lt;/h2&gt;

&lt;p&gt;Bon, idéalement j’aurais aimé faire un petit code autonome qui chiffrerait/déchiffrerait une image donnée.&lt;/p&gt;

&lt;p&gt;Je me suis alors posé plusieurs questions :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;comment connaître le &lt;strong&gt;nombre minimal de pas&lt;/strong&gt; à partir duquel l’image est bien brouillée ? Une étude dynamique de l’évolution de l’entropie et de l’EQM ? Une boucle qui ne s’arréterait que lorsqu’un seuil à définir aurait été atteint ? (avec un garde fou sur le nombre maximum d’itération &lt;em&gt;quoi qu’il arrive&lt;/em&gt; ?)&lt;/li&gt;
  &lt;li&gt;comment estimer la &lt;strong&gt;montée en charge&lt;/strong&gt; de l’algorithme ? Doit-on gérer tous les cas de figure de taille d’image ? Que faire si l’utilisateur branche l’algo sur une image 4K (pauvre fourmi !) ?&lt;/li&gt;
  &lt;li&gt;si l’utilisateur fournit une &lt;strong&gt;image couleur&lt;/strong&gt; ou en &lt;strong&gt;nuances de gris&lt;/strong&gt;, que fait-on ? Le paramètre de seuillage étudié ici a été estimé &lt;em&gt;visuellement&lt;/em&gt;, doit-on coder cela ?&lt;/li&gt;
  &lt;li&gt;l’algorithme est-il sensé gérer différents types d’image ? Il est &lt;strong&gt;évident&lt;/strong&gt; que le format de l’image de sortie DOIT ETRE conservateur (adieu le jpg, on n’écrit qu’en png)&lt;/li&gt;
  &lt;li&gt;idéalement, il faudrait coder ça en travaillant sur &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;argv&lt;/code&gt; pour récupérer les différents paramètres&lt;/li&gt;
  &lt;li&gt;mettre ça dans un paquet qui se piloterait soit directement, soit en import python&lt;/li&gt;
  &lt;li&gt;en sortie de l’algorithme de chiffrement, il faudrait indiquer les paramètres permettant de déchiffrer l’image obtenue : position et orientation de la fourmi en fin de parcours ainsi que le nombre d’itérations à faire en sens inverse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je pensais avoir la motivation de le faire mais je sens que je me dégonfle un peu. C’est précisément ce que je n’aime pas trop en informatique : parfois le &lt;em&gt;decorum&lt;/em&gt; prend une place démesurée par rapport au coeur du code. Le diable est toujours, toujours, dans les détails.&lt;/p&gt;

&lt;h2 id=&quot;considérations-sur-la-sécurité-de-lalgorithme&quot;&gt;Considérations sur la sécurité de l’algorithme&lt;/h2&gt;

&lt;p&gt;Mais &lt;em&gt;in fine&lt;/em&gt;, je ne suis pas sûr que cet algorithme soit d’une quelconque utilité. Pour les raisons suivantes.&lt;/p&gt;

&lt;p&gt;Déjà il est très lent. Nous l’avons déjà dit et il est inutile de s’attarder plus sur ce fait.&lt;/p&gt;

&lt;p&gt;Ensuite, le nombre d’itérations nécessaires pour avoir un brouillage efficace n’est pas connu &lt;em&gt;a priori&lt;/em&gt; et peut varier selon les images (comparons pour s’en assurer les performances obtenues avec les images &lt;em&gt;dithérisée&lt;/em&gt; et seuillée). Ce n’est pas rédhibitoire en soit mais il faudrait s’interroger sur les performances en moyenne (et en moyenne sur quelle population d’images ?). Il pourrait s’avérer que l’algorithme ne soit pas simplement lent, il pourrait devenir &lt;em&gt;très lent&lt;/em&gt; dans la plupart des cas. Notre image &lt;em&gt;dithérisée&lt;/em&gt; possède des caractéristiques très particulières qui pourraient optimiser le parcours de la fourmi…&lt;/p&gt;

&lt;p&gt;Par ailleurs j’ignore s’il existe des cas où la fourmi ne passe pas par tous les pixels, quel que soit le nombre d’itérations.&lt;/p&gt;

&lt;p&gt;On pourrait attendre &lt;em&gt;a minima&lt;/em&gt; que la fourmi soit passée au moins une fois par toutes les cases avant d’envisager de s’arrêter. A supposer que cet état puisse seulement être atteint (cf. ma remarque précédente), et en notant $N_0$ ce nombre d’étapes, on pourrait envisager de laisser tourner l’algorithme pendant, mettons, $2\times N_0$ pour s’assurer qu’on mélange vraiment bien toute l’image. Mais ne se pourrait-il pas que, ce faisant, nous arrêtions le processus en plein milieu d’une boucle d’inversion et que l’image, pourtnat longuement chiffrée, laisse transparaître des informations critiques ?&lt;/p&gt;

&lt;p&gt;Finalement, le chiffrement n’est pas fort : quiconque dispose de la clef (le triplet $(x,y,d)$ à partir duquel faire cheminer notre fourmi inverse), quiconque dispose de cette clef donc, peut totalement reconstruire l’image. La sécurité de l’image brouillée réside notamment dans la sécurité entourant la clef !&lt;/p&gt;

&lt;p&gt;Par ailleurs, si on souhaite décrypter une telle image chiffrée par force brute, il faut considérer ici $501 \times 501 \times 4$ clefs possibles pour la fourmi, soit un million de possibilités. Ajoutons à cela que nous ignorons le nombre d’étapes à attendre avant d’avoir une image lisible. Peut-être, sur ce point là précisément, est-on pas trop mal finalement ? Mais le nombre &lt;em&gt;précis&lt;/em&gt; d’itération n’est pas super important, on peut accepter d’avoir une petite trace de fourmi résiduelle…&lt;/p&gt;

&lt;h2 id=&quot;pour-aller-un-peu-plus-loin&quot;&gt;Pour aller un peu plus loin&lt;/h2&gt;

&lt;p&gt;Je me doute bien que je n’ai rien découvert de nouveau concernant la fourmi de Langton. Mais j’ai été bien content d’expérimenter un peu quelques trucs cool avec cet automate.&lt;/p&gt;

&lt;p&gt;Je sais que la fourmi de Langton appartient à une catégorie assez large de machines de Turing appelées les &lt;a href=&quot;https://en.wikipedia.org/wiki/Turmite&quot;&gt;turmites&lt;/a&gt;. En gros, si j’ai bien compris, les turmites sont des machines de Turing particulières qui évoluent sur un plan bi-dimensionnel (notre image) et qui sont caractérisées notamment par leur orientation. Je me demande trois choses.&lt;/p&gt;

&lt;p&gt;La première : peut-on “accélérer” un peu le brouillage de l’image en plaçant un nombre arbitraire de fourmis ? Attention c’est casse-gueule car les fourmis risquent de se marcher sur les pieds (littéralement). Il faut bien garder l’&lt;strong&gt;ordre&lt;/strong&gt; du déplacement des différentes fourmis.&lt;/p&gt;

&lt;p&gt;La deuxième : nous avons travaillé ici sur des images en Noir et Blanc (soit Noir, soit Blanc) ce qui est le cadre de travail canonique de la fourmi de Langton. Mais peut-on généraliser le processus à des images en niveaux de gris (déjà) et en couleur (ensuite) ?&lt;/p&gt;

&lt;p&gt;La troisième : j’ai évoqué en fin d’article l’impossibilité de choisir le jpeg comme format de sortie d’image. Mais j’aimerais bien savoir ce que ça donnerait de volontairement abîmer l’image avant de faire fonctionner la fourmi en sens inverse. Trouverait-on tout de même des bouts d’images reconnaissables ou bien n’obtiendrions-nous qu’une bouillie de pixels (les perturbations du jpeg, localisées au niveau des forts gradients de l’image, ayant en quelque sorte contaminé toute l’image) ?&lt;/p&gt;</content><author><name>DonutMan</name></author><category term="mathematique" /><category term="X" /><category term="Y" /><summary type="html">Récemment, j’ai réalisé qu’une fourmi de Langton se promenant sur une image noir et blanc et la modifiant à la volée était un phénomène parfaitement réversible : en programmant une fourmi duale ayant des mécaniques inversées par rapport à la fourmi initiale, on peut en théorie reconstruire le film en sens inverse et retrouver in fine l’image originale. Chiche ? Chiche !</summary></entry><entry><title type="html">Python et caméra virtuelle</title><link href="https://donutblog.fr/gnu-linux/webcam/" rel="alternate" type="text/html" title="Python et caméra virtuelle" /><published>2025-04-28T01:00:00+02:00</published><updated>2025-04-28T01:00:00+02:00</updated><id>https://donutblog.fr/gnu-linux/webcam</id><content type="html" xml:base="https://donutblog.fr/gnu-linux/webcam/">&lt;p&gt;Un petit article rapide sur un petit projet de vacances qui me trottait en tête depuis quelques temps.&lt;/p&gt;

&lt;p&gt;J’avais dans l’idée de développer un petit programme qui viendrait modifier en temps réel le flux d’une webcam et qui écrirait dans une webcam virtuelle. De cette façon, on peut utiliser le flux modifié dans un programme tiers comme VLC ou Zoom.&lt;/p&gt;

&lt;p&gt;Je ne pensais pas spécialement faire d’article sur ce sujet (juste pas le temps) mais j’ai rencontré un problème curieux avec apt-get que j’ai réussi à résoudre donc allez c’est parti.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#le-programme&quot; id=&quot;markdown-toc-le-programme&quot;&gt;Le programme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#présentation-des-filtres&quot; id=&quot;markdown-toc-présentation-des-filtres&quot;&gt;Présentation des filtres&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#filtre-des-petits-ronds&quot; id=&quot;markdown-toc-filtre-des-petits-ronds&quot;&gt;Filtre des petits ronds&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#filtre-des-disques-verts&quot; id=&quot;markdown-toc-filtre-des-disques-verts&quot;&gt;Filtre des disques verts&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#filtre-des-caractères-ascii&quot; id=&quot;markdown-toc-filtre-des-caractères-ascii&quot;&gt;Filtre des caractères ASCII&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#filtre-temporel&quot; id=&quot;markdown-toc-filtre-temporel&quot;&gt;Filtre temporel&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#ecriture-dans-une-caméra-virtuelle&quot; id=&quot;markdown-toc-ecriture-dans-une-caméra-virtuelle&quot;&gt;Ecriture dans une caméra virtuelle&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#un-problème-de-paquet&quot; id=&quot;markdown-toc-un-problème-de-paquet&quot;&gt;Un problème de paquet&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#création-dune-caméra-virtuelle&quot; id=&quot;markdown-toc-création-dune-caméra-virtuelle&quot;&gt;Création d’une caméra virtuelle&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#utilisation-dans-des-logiciels-tiers&quot; id=&quot;markdown-toc-utilisation-dans-des-logiciels-tiers&quot;&gt;Utilisation dans des logiciels tiers&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;le-programme&quot;&gt;Le programme&lt;/h2&gt;

&lt;p&gt;Le programme se trouve sur &lt;a href=&quot;https://github.com/DonutMan06/webcam&quot;&gt;mon profil GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Je me suis inspiré du code présenté dans la chaîne YouTube de &lt;a href=&quot;https://www.youtube.com/watch?v=3N7fRURLz4A&quot;&gt;Giovanni Code&lt;/a&gt;. Il s’agit d’un script Python qui utilise les paquets suivants :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://opencv.org/&quot;&gt;OpenCV2&lt;/a&gt; pour la lecture et la manipulation des images d’une webcam&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pypi.org/project/pyvirtualcam/&quot;&gt;PyVirtualCam&lt;/a&gt; pour l’écriture d’un flux dans un périphérique de Webcam virtuelle&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://numpy.org/&quot;&gt;Numpy&lt;/a&gt; pour la création d’un filtre avancé de décalage temporel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A partir de maintenant je parle de “filtres” pour désigner les fonctions qui viennent modifier le flux de la caméra. Ce terme doit s’entendre par rapport aux filtres Unix qui lisent et écrivent des flux de données et non pas au sens du traitement du signal (ce qui n’aurait pas grand sens).&lt;/p&gt;

&lt;p&gt;Les filtres qui modifient le flux de la webcam en temps réel sont au nombre impressionant de quatre.&lt;/p&gt;

&lt;p&gt;On passe d’un filtre à l’autre en appuyant sur &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;space&lt;/code&gt; et on quitte en appuyant sur &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;q&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Le programme est perfectible, mais je voulais un petit prototype pour tester quelques idées rigolotes.&lt;/p&gt;

&lt;h2 id=&quot;présentation-des-filtres&quot;&gt;Présentation des filtres&lt;/h2&gt;

&lt;h3 id=&quot;filtre-des-petits-ronds&quot;&gt;Filtre des petits ronds&lt;/h3&gt;

&lt;p&gt;Celui là est directement inspiré du code de Giovanni Code : on crée des petits cercles colorés et ça donne une petite impression pixelisée très sympa.&lt;/p&gt;

&lt;p&gt;Je ne détaille pas plus et vous renvoie vers sa chaîne pour plus d’information sur le sujet.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/wc1.png&quot; alt=&quot;Filtre 1&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;filtre-des-disques-verts&quot;&gt;Filtre des disques verts&lt;/h3&gt;

&lt;p&gt;Pour celui la, j’ai fait une version modifiée où l’image est remplacé par une grille de disques verts. Le rayon des différents disques est proportionnel à la luminosité du point considéré.&lt;/p&gt;

&lt;p&gt;Je suis parti sur une expression assez simple de la luminosité d’un pixel en partant de ses niveaux de rouge $r$, vert $g$ et bleu $b$ (des entiers compris entre 0 et 255) :&lt;/p&gt;

\[I = 0.2126 \cdot r + 0.7152 \cdot g + 0.0722 \cdot b\]

&lt;p&gt;La primitive de la fonction qui trace les disques est :&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cv2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;centerX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;centerY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thickness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Prendre un paramètre de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;thickness&lt;/code&gt; de $-1$ trace un disque plein plutôt qu’un cercle. Pour les paramètres de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(r,g,b)&lt;/code&gt;, je suis parti sur un vert un peu foncé de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0,175,0)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Enfin, le rayon doit être un entier, ce qui fait qu’on aura un effet de seuilage de la luminosité perçue. Mais le rendu n’est pas trop mal pour autant bien que l’image soit moins claire que pour le filtre précédent.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/wc2.png&quot; alt=&quot;Filtre 2&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;filtre-des-caractères-ascii&quot;&gt;Filtre des caractères ASCII&lt;/h3&gt;

&lt;p&gt;En gros ici, on représente chaque pixel par un caractère ASCCI. Plus le pixel est lumineux, et plus on choisira un caractère avec beaucoup de pixel.&lt;/p&gt;

&lt;p&gt;Alors, bien entendu, ça dépendra de la police de caractères choisie, de la graisse etc. Par ailleurs, la variation du nombre de pixels entre deux caractères voisin n’est pas forcément toujours la même. Mais enfin, on arrive malgré tout à capturer l’effet.&lt;/p&gt;

&lt;p&gt;Je me suis basé sur l’alphabet suivant, ordonné par luminosité croissante :&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.-\':_,^=;&amp;gt;&amp;lt;+!rc*/z?sLTv)J7(|Fi{C}fI31tlu[neoZ5Yxjya]2ESwqkP6h9d4VpOGbUAKXHm8RD#$Bg0MNWQ%&amp;amp;@&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Le premier caractère de la liste est une espace, qui ne sera donc traduit par aucun pixel et qui codera la luminosité nulle.
Le dernier caractère est l’arobase &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@&lt;/code&gt; qui représentera la luminosité maximale.&lt;/p&gt;

&lt;p&gt;On peut amplifier l’effet en donnant une intensité de couleur différente pour les différents caractères mais les combinaisons que j’ai testées ne m’ont pas parues convaincantes. J’ai gardé une couleur constante de &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0,150,0)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;L’effet est désormais beaucoup moins visuel : arrivez vous à distinguer les formes ? L’image est toujours peu ou prou la même que dans les deux cas précédent : je lève le bras droit pour faire coucou. On peut essayer d’affiner la grille et d’afficher plus de caractères mais je n’ai pas poussé plus avant ces investigations.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/wc3.png&quot; alt=&quot;Filtre 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A noter qu’en vidéo, ça reste quand même un peu plus lisible. Si j’ai la foi, je rajouterai ça dans le corps de l’article&lt;/p&gt;

&lt;h3 id=&quot;filtre-temporel&quot;&gt;Filtre temporel&lt;/h3&gt;

&lt;p&gt;Alors ça c’est mon petit chouchou. Il y a longtemps j’avais vu je ne sais plus trop où (fête de la Science ?) une série de webcam qui introduisaient des décalages temporels dans le flux vidéo. Grosso modo, la Webcam écrit en permanence dans un buffer et le flux de sortie vient piocher dans le buffer pour afficher des pixels d’un temps passé. Si on note $E(x,y,t)$ la couleur du pixel de coordonnées $(x,y)$ à un instant $t$, tel que lu par la webcam d’entrée, alors la webcam de sortie verra un pixel $S$ défini par :&lt;/p&gt;

\[S(x,y,t) = E(x,y, t')\]

&lt;p&gt;Avec bien entendu $t’&amp;lt;=t$ (on ne lit pas dans le futur)&lt;/p&gt;

&lt;p&gt;On peut imaginer plein de façons différentes de mélanger espace et temps et d’une manière générale, on écrira :&lt;/p&gt;

\[t'(x,y) = f(x,y)\]

&lt;p&gt;De mon côté, j’ai introduit un décalage ligne à ligne : ce qui se situe en haut de l’image est mis à jour plus rapidement que ce qui est en bas. Avec le repère qui va bien ($x$ selon la largeur, de gauche à droite; $y$ selon la hauteur, du haut vers le bas; origine du repère au coin supérieur gauche de l’image), on aurait donc quelque chose du genre :&lt;/p&gt;

\[t'(x,y) = t(x,y) + y\]

&lt;p&gt;A ce niveau de la rédaction de l’article, il faudrait que je fasse un petit dessin de la façon dont le buffer se remplit. Il y a une analogie assez directe avec ce qu’on obtiendrait, dans le cadre de la Relativité Restreinte, si on se regardait dans un grand miroir crénelé incliné de 45° (la partie haute étant la plus proche de nous). Bon j’y reviendrait probablement plus tard…&lt;/p&gt;

&lt;p&gt;Voici quoiqu’il en soit ce qu’on obtient. Une fois encore, on comprend mieux ce qu’il se passe avec une vidéo mais comment dire… on le sent que j’ai hâte de finir cette première version de l’article et de &lt;a href=&quot;https://www.youtube.com/watch?v=je9EZ2AtGZM&quot;&gt;passer à autre chose&lt;/a&gt; ?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/wc4.png&quot; alt=&quot;Filtre 4&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ecriture-dans-une-caméra-virtuelle&quot;&gt;Ecriture dans une caméra virtuelle&lt;/h2&gt;

&lt;p&gt;Assez vite, je me suis dit que ça serait cool si je pouvais envoyer tout ça dans une caméra virtuelle.&lt;/p&gt;

&lt;p&gt;Une petite recherche m’a orienté vers &lt;a href=&quot;https://pypi.org/project/pyvirtualcam/&quot;&gt;PyVirtualCam&lt;/a&gt; qui semble totalement répondre à mon besoin.&lt;/p&gt;

&lt;p&gt;Ce paquet s’appuie, sur Linux, sur le paquet &lt;a href=&quot;https://github.com/v4l2loopback/v4l2loopback&quot;&gt;v4l2loopback-dkms&lt;/a&gt; qui est un module du noyau qui permet de créer des périphériques virtuels type webcam à la volée.&lt;/p&gt;

&lt;h3 id=&quot;un-problème-de-paquet&quot;&gt;Un problème de paquet&lt;/h3&gt;

&lt;p&gt;Hélas, lors de l’installation de ce paquet via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt;, mon système d’ordinaire courtois s’est mis à m’insulter :&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;v4l2loopback-dkms

&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]

Préparation &lt;span class=&quot;nb&quot;&gt;du &lt;/span&gt;dépaquetage de 
.../v4l2loopback-dkms_0.12.7-2ubuntu2~22.04.1_all.deb ...
Dépaquetage de v4l2loopback-dkms &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.12.7-2ubuntu2~22.04.1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; ...
Paramétrage de v4l2loopback-dkms &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.12.7-2ubuntu2~22.04.1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; ...
Loading new v4l2loopback-0.12.7 DKMS files...
Building &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;6.8.0-57-generic
Building initial module &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;6.8.0-57-generic
ERROR: Cannot create report: &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Errno 17] File exists: 
&lt;span class=&quot;s1&quot;&gt;'/var/crash/v4l2loopback-dkms.0.crash'&lt;/span&gt;
Error! Bad &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;status &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;module build on kernel: 6.8.0-57-generic 
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;x86_64&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Consult /var/lib/dkms/v4l2loopback/0.12.7/build/make.log &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;more 
information.
dpkg: erreur de traitement &lt;span class=&quot;nb&quot;&gt;du &lt;/span&gt;paquet v4l2loopback-dkms &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--configure&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; :
  le sous-processus paquet v4l2loopback-dkms script post-installation 
installé a renvoyé un état de sortie d&lt;span class=&quot;s1&quot;&gt;'erreur 10
Des erreurs ont été rencontrées pendant l'&lt;/span&gt;exécution :
  v4l2loopback-dkms
E: Sub-process /usr/bin/dpkg returned an error code &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Le fichier de log de compilation indiqué dans l’erreur (que je n’ai pas eu la présence d’esprit de sauvegarder du coup je ne peux pas le reproduire ici) indiquait en gros que la fonction &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strlcpy&lt;/code&gt; appelée dans &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v4l2loopback.c&lt;/code&gt; n’était pas définie. Une petite recherche m’a appris qu’il s’agissait d’une fonction venant de l’univers BSD.&lt;/p&gt;

&lt;p&gt;Ce problème précis est mentionné à divers endroit du net et pour différents systèmes (&lt;a href=&quot;https://gitlab.archlinux.org/archlinux/packaging/packages/v4l2loopback/-/issues/2&quot;&gt;ArchLinux&lt;/a&gt;, &lt;a href=&quot;https://forums.linuxmint.com/viewtopic.php?t=438634&quot;&gt;LinuxMint&lt;/a&gt;, &lt;a href=&quot;https://forum.ubuntu-fr.org/viewtopic.php?id=2084897&quot;&gt;Ubuntu&lt;/a&gt;, et directement sur &lt;a href=&quot;https://github.com/v4l2loopback/v4l2loopback/issues/575&quot;&gt;le github du projet v4l2loopback&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Le problème semble être une incompatibilité du paquet v4l2loopback-dkms avec les noyaux trop récents (ou les dépôts trop anciens, selon comment vous regardez le problème).&lt;/p&gt;

&lt;p&gt;On peut résoudre ça en installant v4l2loopback depuis les sources ou alors, comme j’ai fait, en mettant une énorme rustine de fortune dans le code source du dépôt. A la réflexion ce n’est pas super propre car apt pense que le paquet provient du dépôt alors qu’on utilise une version modifiée… Mais enfin ça fera le job le temps du test…&lt;/p&gt;

&lt;p&gt;La fonction &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strlcpy&lt;/code&gt; a son équivalent dans la librairie standard du C : &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strscpy&lt;/code&gt; :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://linux.die.net/man/3/strlcpy&quot;&gt;man page de strlcpy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html&quot;&gt;man page de strscpy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok les prototypes sont les mêmes, on peut tenter un truc un peu bourrin :&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/strlcpy/strscpy/'&lt;/span&gt; ./src/v4l2loopback-0.12.7/v4l2loopback.c &lt;span class=&quot;c&quot;&gt;# PAS TESTE !&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Attention, je n’ai pas testé ça. J’ai fait la modif sous vim :&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;vim ./src/v4l2loopback-0.12.7/v4l2loopback.c
:%s/strlcpy/strscpy/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On tente ensuite la réinstallation (sans faire de remove/install avec apt sinon on écrase nos modifications) :&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dpkg &lt;span class=&quot;nt&quot;&gt;--configure&lt;/span&gt; v4l2loopback-dkms
Paramétrage de v4l2loopback-dkms &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.12.7-2ubuntu2~22.04.1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; ...
Removing old v4l2loopback-0.12.7 DKMS files...
Deleting module v4l2loopback-0.12.7 completely from the DKMS tree.
Loading new v4l2loopback-0.12.7 DKMS files...
Building &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;6.8.0-57-generic
Building initial module &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;6.8.0-57-generic
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Done, ça a marché !&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;création-dune-caméra-virtuelle&quot;&gt;Création d’une caméra virtuelle&lt;/h3&gt;

&lt;p&gt;On crée une caméra virtuelle en chargeant le module &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v4l2loopback&lt;/code&gt; avec &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modprobe&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ll /dev/vid&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;

crw-rw----+ 1 root video 81, 0 avril 18 13:59 /dev/video0
crw-rw----+ 1 root video 81, 1 avril 18 13:59 /dev/video1
crw-rw----+ 1 root video 81, 2 avril 18 13:59 /dev/video2
crw-rw----+ 1 root video 81, 3 avril 18 13:59 /dev/video3

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;modprobe v4l2loopback

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ll /dev/vid&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
crw-rw----+ 1 root video 81, 0 avril 18 13:59 /dev/video0
crw-rw----+ 1 root video 81, 1 avril 18 13:59 /dev/video1
crw-rw----+ 1 root video 81, 2 avril 18 13:59 /dev/video2
crw-rw----+ 1 root video 81, 3 avril 18 13:59 /dev/video3
crw-rw----+ 1 root video 81, 4 avril 21 07:47 /dev/video4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ici on constate qu’une nouvelle caméra fait son apparition dans &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/&lt;/code&gt;, la caméra &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;video4&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Mon code python prend la première caméra virtuelle qu’il trouve, il n’y a qu’à s’assurer que dans le fichier &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webcam.py&lt;/code&gt; la variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VIRTUAL_CAM&lt;/code&gt; est bien positionnée à &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;utilisation-dans-des-logiciels-tiers&quot;&gt;Utilisation dans des logiciels tiers&lt;/h3&gt;

&lt;p&gt;On peut ensuite connecter VLC sur le flux de cette caméra virtuelle. Il faut aller dans &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Média &amp;gt; Ouvrir un périphérique de capture...&lt;/code&gt; et ensuite sélectionner notre caméra (ici donc &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;video4&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/vlc1.png&quot; alt=&quot;VLC 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Et là, ça fonctionne !&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/vlc2.png&quot; alt=&quot;VLC 2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Pareil avec Zoom, il suffit de sélectionner notre caméra au début du meeting. En appuyant sur &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;space&lt;/code&gt; on pourra en outre changer les effets. On peut même imaginer ajouter un petit texte affichant le nom du participant et divers informations. Je crois d’ailleurs que c’est ce que propose &lt;a href=&quot;https://obsproject.com/&quot;&gt;ObsProject&lt;/a&gt; qui est une alternative à v4l2loopback un peu &lt;em&gt;overkill&lt;/em&gt; pour d’autres plateformes mais enfin rien ne vaut le DIY ^^&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/zoom1.png&quot; alt=&quot;Zoom 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/zoom2.png&quot; alt=&quot;Zoom 2&quot; /&gt;&lt;/p&gt;</content><author><name>DonutMan</name></author><category term="gnu-linux" /><category term="python" /><category term="webcam" /><summary type="html">Un petit article rapide sur un petit projet de vacances qui me trottait en tête depuis quelques temps.</summary></entry><entry><title type="html">Un jeu de l’oie coloré</title><link href="https://donutblog.fr/mathematique/jeu-oie/" rel="alternate" type="text/html" title="Un jeu de l’oie coloré" /><published>2025-03-04T00:00:00+01:00</published><updated>2025-03-04T00:00:00+01:00</updated><id>https://donutblog.fr/mathematique/jeu-oie</id><content type="html" xml:base="https://donutblog.fr/mathematique/jeu-oie/">&lt;p&gt;Avec ma fille Alice, nous aimons bien jouer au jeu de l’oie. Nous avons à la maison un coffret de jeux de société pour enfant de la marque Oxybul qui possède une particularité intéressante. En effet, enfin de permettre aux plus petits de jouer, l’un des dés à six faces présente des faces colorées en lieu et place des motifs à base de point.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/oie1.jpg&quot; alt=&quot;Le dé coloré&quot; /&gt;&lt;/p&gt;

&lt;p&gt;La mécanique est intéressante et je me suis interrogé sur l’équivalence avec le jeu de l’oie classique.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#présentation-du-jeu-de-loie-simplifié&quot; id=&quot;markdown-toc-présentation-du-jeu-de-loie-simplifié&quot;&gt;Présentation du jeu de l’oie simplifié&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cas-dun-dé-pipé&quot; id=&quot;markdown-toc-cas-dun-dé-pipé&quot;&gt;Cas d’un dé pipé&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#analyse-des-probabilités&quot; id=&quot;markdown-toc-analyse-des-probabilités&quot;&gt;Analyse des probabilités&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;présentation-du-jeu-de-loie-simplifié&quot;&gt;Présentation du jeu de l’oie simplifié&lt;/h1&gt;

&lt;p&gt;Le plateau de jeu va de la case départ à la case d’arrivée en 36 cases avec trois échelles  ci et là qui permettent d’avancer ou reculer plus rapidement si on s’arrête à leur niveau.&lt;/p&gt;

&lt;p&gt;Les cases alternent les couleurs Rouge, Jaune, Bleu et Vert. Le motif périodique contient donc ces quatres couleurs et on remarque que le nombre total de cases est bien un multiple de 4.&lt;/p&gt;

&lt;p&gt;Observons maintenant le dé. Je ne résiste pas au plaisir d’en faire un rapide patron sous TikZ.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/patron_oie.png&quot; alt=&quot;Patron du dé&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Le dé dispose ainsi de :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;deux faces Croix&lt;/li&gt;
  &lt;li&gt;une face Bleu&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;une face Jaune&lt;/li&gt;
  &lt;li&gt;une face Rouge&lt;/li&gt;
  &lt;li&gt;une face Vert&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si on tire une face Croix, on passe son tour&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Si on tire une face colorée, le pion doit se rendre à la première case de cette couleur située devant lui. Nous pouvons donc avancer au maximum de 4 cases, dans le cas où nous tirons la couleur sur laquelle le pion est actuellement placé.&lt;/p&gt;

&lt;p&gt;Le tableau ci-dessous résume de combien de cases nous avançons, d’une part selon la case où nous nous trouvons (axe horizontal du tableau) et d’autre part selon la couleur donnée par le dé (axe vertical du tableau).&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Je tire la face \ Je suis sur la case&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Bleu&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Jaune&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Rouge&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Vert&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Bleu&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Jaune&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Rouge&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Vert&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;4&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Dans le cas d’un dé parfaitement équilibré, on se rend bien compte que la mécanique de jeu est complétement équivalente à un jeu de l’oie classique où on remplacerait, dans le dé à six faces numérotées, les 5 et 6 par des Croix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oui mais, cela est-il toujours vrai dans le cas où on joue avec un dé pipé ?&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;cas-dun-dé-pipé&quot;&gt;Cas d’un dé pipé&lt;/h1&gt;

&lt;p&gt;Pour simplifier l’étude d’un dé pipé, je suis parti sur un jeu de l’oie particulier où les cases sont soit noires soit blanches. Nous remplaçons notre dé par une pièce présentant une face blanche et une face noire. La mécanique est similaire à notre jeu de période 4. Plus précisément, le tableau devient :&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Je tire la face \ Je suis sur la case&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Blanc&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Noir&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Blanc&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Noir&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Notons $p$ la probabilité que la pièce tombe sur Blanc. La pièce est truquée si $p \neq 1/2$.&lt;/p&gt;

&lt;p&gt;On peut représenter les probabilités de transition par le petit diagramme simplifié ci-dessous (w pour white, c’est-à-dire blanc; b pour black, c’est-à-dire noir).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/diag_oie.png&quot; alt=&quot;Diagramme transition&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;analyse-des-probabilités&quot;&gt;Analyse des probabilités&lt;/h1&gt;

&lt;p&gt;Si on note $p_1$ (respectivement $p_2$) la probabilité d’avancer d’une case (respectivement de deux cases), alors nous pouvons montrer assez facilement, d’une part que :&lt;/p&gt;

\[p_1 = 2 p \cdot (1-p)\]

&lt;p&gt;et d’autre part que :&lt;/p&gt;

\[p_2 = 1 - p_1 = p^2+(1-p)^2\]

&lt;p&gt;Si nous traçons $p_1$ en fonction de $p$, nous obtenons la figure ci-dessous.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2025/jeu_oie.png&quot; alt=&quot;Simulation des probabilités&quot; /&gt;&lt;/p&gt;

&lt;p&gt;La courbe rouge correspond à notre modèle et la courbe bleue correspond à une simulation informatique sur un million de tirage. L’adéquation est parfaite.&lt;/p&gt;

&lt;p&gt;Analysons un petit peu ces résultats.&lt;/p&gt;

&lt;p&gt;Si $p=0$, alors la pièce tombera &lt;em&gt;toujours&lt;/em&gt; sur Noir. Dès le premier coup, le joueur tombera donc nécessairement sur une case Noir. Après cela, toute nouvelle face Noir fera avancer systèmatiquement de DEUX cases. Nous aurons donc $p_1=0$ et $p_2=1$.&lt;/p&gt;

&lt;p&gt;De la même façon, si $p=1$ alors la pièce tombera &lt;em&gt;toujours&lt;/em&gt; sur Blanc. Juste après le premier coup nous serons donc sur une case blanche et nous avancerons alors systématiquement de DEUX cases. Nous avons donc de même $p_1=0$ et $p_2=1$.&lt;/p&gt;

&lt;p&gt;Si $p=0.5$, nous sommes dans le cas d’une pièce parfaitement équilibrée. Dans ce cas là, Blanc et Noir sont interchangeables et nous avons autant de chances d’avancer d’une case que d’avancer de deux cases. Nous avons donc $p_1 = p_2 = 0.5$.&lt;/p&gt;

&lt;p&gt;Nous remarquons ensuite deux choses :&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;nous avons toujours $p_1 \leq 0.5$&lt;/li&gt;
  &lt;li&gt;la courbe de $p_1$ est symétrique par rapport à l’axe $p=0.5$&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Du premier constat, nous en déduisons qu’un joueur avançant plus fréquemment d’une case plutôt que de deux est plus susceptible d’utiliser une pièce truquée numérotée plutôt qu’une pièce truquée colorée.&lt;/p&gt;

&lt;p&gt;Du second constat, nous apprenons que truquer la pièce en favorisant un côté donnera le même résultat quel que soit le côté favorisé.&lt;/p&gt;

&lt;p&gt;On pourrait généraliser cela en utilisant notre dé à six faces et en le truquant mais… la flemme.&lt;/p&gt;

&lt;p&gt;Par ailleurs et d’une façon complétement indépendante, les tarifications de mon hébergeur Gandi étant devenues scandaleuses, je pense ne pas renouveler mon abonnement et migrer ce blog sur une autre adresse. Du coup, cet article ne sortira probablement pas à la date à laquelle il a été écrit. C’est facheux mais c’est comme ça.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;je n’accorde pas “bleu” avec “face” parce que j’ai mis une majuscule… &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;ce qui est en fait différent de dire “on avance de zéro”. Prenez le cas où le joueur est pile au niveau d’une échelle : s’il passe son tour, il ne se passe rien; s’il avance de zéro, je suppose qu’il doit utiliser l’échelle. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>DonutMan</name></author><category term="mathematique" /><category term="probabilite" /><category term="jeu" /><summary type="html">Avec ma fille Alice, nous aimons bien jouer au jeu de l’oie. Nous avons à la maison un coffret de jeux de société pour enfant de la marque Oxybul qui possède une particularité intéressante. En effet, enfin de permettre aux plus petits de jouer, l’un des dés à six faces présente des faces colorées en lieu et place des motifs à base de point.</summary></entry><entry><title type="html">Gagne ta Maman</title><link href="https://donutblog.fr/mathematique/dancing-links/" rel="alternate" type="text/html" title="Gagne ta Maman" /><published>2024-12-10T00:00:00+01:00</published><updated>2024-12-10T00:00:00+01:00</updated><id>https://donutblog.fr/mathematique/dancing-links</id><content type="html" xml:base="https://donutblog.fr/mathematique/dancing-links/">&lt;p&gt;Il y a quelques temps, j’ai offert à ma fille ainée le jeu de société “Gagne ta Maman” (qui existe aussi en une version identique “Gagne ton Papa”).&lt;/p&gt;

&lt;p&gt;Dans ce jeu, il faut remplir le plus rapidement possible un espace rectangulaire initialement vide à l’aide de &lt;strong&gt;polyominos&lt;/strong&gt;. Formellement, un polyomino est une réunion connexe (i.e. en un seul morceau) de carrés unitaires, ces carrés se touchant par un côté entier.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/gtpp.png&quot; alt=&quot;Le jeu Gagne ton Papa / ta Maman&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ce qui est très appréciable dans “Gagne ta Maman”, c’est que &lt;strong&gt;la difficulté s’adapte entre parents et enfants&lt;/strong&gt;, ces derniers devant ainsi paver le rectangle avec des formes plus “simples” que celles des parents. Ou alors le rectangle des parents est initialement plus grand que celui des enfants.&lt;/p&gt;

&lt;p&gt;Dans une variante du jeu, appelée les &lt;strong&gt;défis 3D&lt;/strong&gt;, il faut constituer une figure géométrique tri-dimensionnelle (habituellement un cube ou un pavé) à l’aide d’un sous-ensemble prédéfinis de ces polyominos&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Dans un cas comme dans l’autre, je me suis rendu compte que mon algorithme instinctif de résolution était assez ruste : je procédais un peu par essai-erreur sans vraiment de méthode, en suivant l’intuition du moment. La plupart du temps, ça finissait par rentrer et je m’étonnais des performances de cette heuristique naïve.&lt;/p&gt;

&lt;p&gt;Quelques rares fois pourtant, mon procédé ne terminait pas et j’avais l’impression d’&lt;strong&gt;arpenter un labyrinthe&lt;/strong&gt; en aveugle sans jamais pouvoir trouver une sortie. Parfois j’avais l’impression de m’y perdre : cette configuration là n’a-t’elle pas déjà été testée précedemment ?&lt;/p&gt;

&lt;p&gt;Depuis, j’ai eu l’occasion de creuser un peu la question et je me rends compte qu’algorithmiquement le problème revient bien à parcourir un (très grand) labyrinthe avec un nombre fini de sorties. Je trouve a posteriori que cette analogie est finalement assez profonde, comme nous le verrons.&lt;/p&gt;

&lt;p&gt;Quoiqu’il en soit, cette question là traînait un peu dans un coin de ma tête et puis je l’avais oublié… Jusqu’à ce jour où un ami, à qui j’avais suggéré ce jeu pour son fils, me sollicite sur les réseaux sociaux à propos d’un défi 3D qu’ils n’arrivaient pas à résoudre. Je l’ai tenté et m’y suis moi aussi cassé les dents. Il s’agit du défi 28 où l’on doit reconstituer un cube $3\times 3 \times 3$ à l’aide des 7 polyominos suivants (un monomino, 4 tétraminos et 2 pentaminos) :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/defi28.png&quot; alt=&quot;Les 7 polyominos du défi 28&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/carte28.jpeg&quot; alt=&quot;La carte du défi 28&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Nous avons bien $1+4+4+4+4+5+5 = 27 = 3^3$ ce qui est une condition nécessaire (mais pas suffisante) pour que le problème ait une solution.&lt;/p&gt;

&lt;p&gt;Après bien des essais, j’ai enfin convergé vers UNE des multiples solutions de ce problème (voir ci-dessous). J’ai aussitôt averti mon ami et je me suis dit “&lt;strong&gt;plus jamais ça&lt;/strong&gt;”, il faut que je comprenne comment résoudre ce genre de problème.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/sol28a.jpeg&quot; alt=&quot;Solution du défi, photo 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/sol28b.jpeg&quot; alt=&quot;Solution du défi, photo 2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Je détaille ici quelques éléments pour systématiser la résolution de tels problèmes. Ca part vite dans tous les sens, j’espère réussir à en tirer quelques lignes claires.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#le-problème-du-recouvrement&quot; id=&quot;markdown-toc-le-problème-du-recouvrement&quot;&gt;Le problème du recouvrement&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#premier-cas--un-seul-polyomino-qui-rempli-le-cube-2times-2-times-2&quot; id=&quot;markdown-toc-premier-cas--un-seul-polyomino-qui-rempli-le-cube-2times-2-times-2&quot;&gt;Premier cas : un seul polyomino qui rempli le cube $2\times 2 \times 2$&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#deuxième-cas--8-monominos-qui-remplissent-le-cube-2times-2-times-2&quot; id=&quot;markdown-toc-deuxième-cas--8-monominos-qui-remplissent-le-cube-2times-2-times-2&quot;&gt;Deuxième cas : 8 monominos qui remplissent le cube $2\times 2 \times 2$&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#troisième-cas--le-cube-soma&quot; id=&quot;markdown-toc-troisième-cas--le-cube-soma&quot;&gt;Troisième cas : le cube Soma&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#quatrième-cas--le-défi-28&quot; id=&quot;markdown-toc-quatrième-cas--le-défi-28&quot;&gt;Quatrième cas : le défi 28&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#pour-aller-plus-loin&quot; id=&quot;markdown-toc-pour-aller-plus-loin&quot;&gt;Pour aller plus loin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;le-problème-du-recouvrement&quot;&gt;Le problème du recouvrement&lt;/h1&gt;

&lt;p&gt;La résolution du défi 28, comme le jeu standard “Gagne ton Papa/ta Maman” à base de rectangles à paver, rentre dans le cadre d’une vaste classe de problèmes mathématiques appelée &lt;strong&gt;problème du recouvrement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Le &lt;a href=&quot;https://fr.wikipedia.org/wiki/Probl%C3%A8me_des_huit_dames&quot;&gt;problème des huits dames&lt;/a&gt; en est un exemple classique, de même que le &lt;a href=&quot;https://fr.wikipedia.org/wiki/Cube_Soma&quot;&gt;cube Soma&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Au début des années 2000, le mathématicien Donald Knuth a développé un algorithme pour résoudre ces problèmes dans le cas général. Sa solution s’appelle l’&lt;strong&gt;algorithme X&lt;/strong&gt; et il en a proposé une implémentation astucieuse grâce aux &lt;strong&gt;liens dansants&lt;/strong&gt;. Le principe de base repose sur la possibilité d’effectuer un retour sur trace et d’effectuer un parcours en profondeur d’un arbre donné.
Son article est &lt;a href=&quot;https://www.ocf.berkeley.edu/~jchu/publicportal/sudoku/0011047.pdf&quot;&gt;librement accessible&lt;/a&gt; sur le site de l’Open Computing Facility de l’Université de Berkeley.&lt;/p&gt;

&lt;p&gt;Au début, je voulais présenter une synthèse de son algorithme&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; ainsi qu’une implémentation minimaliste. Mais entre temps je suis tombé sur SageMath qui en contient une &lt;a href=&quot;https://doc.sagemath.org/html/en/reference/games/sage/games/quantumino.html&quot;&gt;version robuste et éprouvée&lt;/a&gt;. Ce code est développé et maintenu par &lt;a href=&quot;http://www.slabbe.org/&quot;&gt;Sébastien Labbé&lt;/a&gt; qui est, à l’heure de rédaction de ce billet, chargé de recherches au &lt;a href=&quot;http://www.slabbe.org/&quot;&gt;Laboratoire Bordelais de Recherche en Informatique&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;SageMath, que j’ai découvert à l’occasion des mes recherches sur ce sujet, est un logiel libre dédié aux mathématiques. De ce que j’en comprends c’est un enrobage d’outils Python comme Numpy, Scipy ou Matplotlib afin de présenter une interface unifiée à l’utilisateur ainsi qu’une collection d’un certain nombre d’outils mathématiques (algèbre linéaire, polynômes, graphiques etc.).&lt;/p&gt;

&lt;p&gt;Avant de passer à la présentation de la résolution du défi 28, j’aimerais illustrer ce que je pressentais lorsque j’évoquais le parcours du labyrinthe.&lt;/p&gt;

&lt;p&gt;La page Wikipédia dédiée à la modélisation mathématique des labyrinthes propose plusieurs algorithmes générant des labyrinthes dit “parfaits” (chaque cellule du labyrinthe est reliée à toutes les autres de manière unique). Très vite apparaît la correspondance avec les &lt;a href=&quot;https://fr.wikipedia.org/wiki/Graphe_orient%C3%A9_acyclique&quot;&gt;graphes orientés acycliques&lt;/a&gt;. La notion d’acyclicité me paraît immédiate : on ne peut pas former de “boucles” car ça contredirait l’hypothèse d’unicité de chemin. Ce qui m’a plu, ce sont ces deux figures qui relient labyrinthe et graphe :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/maze.png&quot; alt=&quot;Equivalence avec un graphe&quot; /&gt;&lt;/p&gt;

&lt;p&gt;L’article de Knuth illustre le parcours des solutions (ici dans le cas particulier du pavage de Scott) par la figure suivante :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/knuth.png&quot; alt=&quot;Parcours des solutions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On voit bien que la question est finalement similaire : sortir d’un labyrinthe et trouver un recouvrement exact reviennent tout deux à trouver une feuille de sortie en parcourant un arbre. Dans le cas du labyrinthe, cette feuille est généralement unique (il n’existe qu’une seule sortie) alors que dans le cas du pavage, il y a souvent plusieurs solutions.&lt;/p&gt;

&lt;p&gt;Je vais maintenant tester le logiciel SageMath sur plusieurs cas particuliers afin de bien le prendre en main.&lt;/p&gt;

&lt;h1 id=&quot;premier-cas--un-seul-polyomino-qui-rempli-le-cube-2times-2-times-2&quot;&gt;Premier cas : un seul polyomino qui rempli le cube $2\times 2 \times 2$&lt;/h1&gt;

&lt;p&gt;Tout d’abord, on s’attaque au problème trivial qui consiste à paver un cube $2\times 2 \times 2$ à l’aide d’un unique Polyomino qui est le cube lui-même. On s’attend à ne trouver qu’une seule solution (aux éventuelles symétries du Polyomino près).&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sage.combinat.tiling&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TilingSolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sage.all_cmdline&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# import sage library
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])];&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TilingSolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;solve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show3d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.85&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Graphics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aspect_ratio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'tachyon'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_of_solutions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Le solveur retourne $1$, ce qui est le résultat attendu. J’en déduis que l’implémentation SageMath de l’algorithme, lorsqu’elle construit sa matrice d’entrée, vérifie que les pièces tournées sont bien distinctes.&lt;/p&gt;

&lt;h1 id=&quot;deuxième-cas--8-monominos-qui-remplissent-le-cube-2times-2-times-2&quot;&gt;Deuxième cas : 8 monominos qui remplissent le cube $2\times 2 \times 2$&lt;/h1&gt;

&lt;p&gt;Cette fois-ci nous avons 8 petits cubes unitaires discernables (on va leur mettre à chacun une couleur différentes) qui doivent paver ce même cube $2\times 2 \times 2$. Les solutions sont là aussi triviales et on s’attend à en trouver autant qu’il n’y a de façons de permuter ces 8 petits cubes. Autrement dit, on devrait trouver $8! = 40 320$.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;P1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'green'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'blue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'yellow'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'magenta'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'cyan'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'black'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'white'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;P1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TilingSolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;solve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show3d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.85&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Graphics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aspect_ratio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'tachyon'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_of_solutions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 40 320 solutions, soit 8! --&amp;gt; OK les pièces sont discernables
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cette dernière instruction retourne bien $40 320$. Ci-dessous voici une de ces nombreuses solutions (telle que tracée par la fonction &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G.show()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/8cubes.png&quot; alt=&quot;Huit petits cubes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Remarquons que nous aurions pu utiliser des monominos &lt;em&gt;indiscernables&lt;/em&gt; à l’aide de l’argument &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reusable&lt;/code&gt; qu’il faut positionner sur &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt;, auquel cas nous retrouvons bien une et une seule solution.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TilingSolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reusable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_of_solutions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 1 seule solution...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;troisième-cas--le-cube-soma&quot;&gt;Troisième cas : le cube Soma&lt;/h1&gt;

&lt;p&gt;On augmente un peu en difficulté et on s’attaque maintenant à l’exploration du &lt;a href=&quot;https://fr.wikipedia.org/wiki/Cube_Soma&quot;&gt;cube Soma&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Il s’agit d’un casse-tête inventé dans les années 1930 par Piet Hein. Le nom fait référence au Soma du Meilleur des Mondes d’Aldous Huxley, une drogue addictive grâce à laquelle chaque élément de la société est heureux et ne revendique rien.&lt;/p&gt;

&lt;p&gt;L’objectif du casse-tête est de paver un cube $3\times 3 \times 3$ à l’aide de sept polyominos prédéfinis.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/soma.jpg&quot; alt=&quot;Les sept polyominos du cube Soma&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Le problème a été étudié par Martin Gardner et John Horton Conway et le livre “Winning Ways for your Mathematical Plays” en contient une analyse détaillée. Ce dernier livre, coécrit par Conway, est sorti en 1982 et se trouve aujourd’hui en occasion aux alentours de 90 € (dommage, il me branchait bien mais pas à ce prix là…). Il existe 240 façons distinctes de reconstituer le cube Soma à partir des sept polyominos de base. Arriverons-nous à retrouver ce résultat ?&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;P1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# G = P1.show3d()
# G.show(aspect_ratio=1, viewer='jmol')
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TilingSolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;P1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_of_solutions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cette fois-ci nous trouvons $11 520$ solutions ce qui est beaucoup trop ! Mais l’étude du pavage par huit petits cubes nous permet de comprendre que les symétries du cube ne sont pas prise en compte par l’algortihme ! On compte ainsi certaines solutions plusieurs fois !&lt;/p&gt;

&lt;p&gt;Il nous faut donc diviser ce nombre par le nombre d’isométries du cube, qui est de $48$, et on retrouve bien le résultat attendu :&lt;/p&gt;

\[\frac{11 520}{48} = 240\]

&lt;p&gt;En faisant cela, on fait l’hypothèse (vérifiée) que l’image de n’importe quelle solution du cube Soma par l’application d’une isométrie du cube est également une solution envisageable. Si les rotations ne posent généralement pas problème (en tournant le cube, on tourne également les pièces), ce n’est pas forcément le cas des symétries (qu’elles soient centrales ou planaires).&lt;/p&gt;

&lt;p&gt;On ne peut par exemple pas exprimer une symétrie planaire comme composée de rotation 3D car la symétrie ne conserve pas l’orientation alors que la composée de rotation oui (une sombre histoire de signe du déterminant toute cette affaire là). Et lorsqu’on manipule les petites pièces avec nos gros doigts, on ne peut leur faire subir que des translations ou des rotations. On peut néanmoins s’en sortir si les pièces elles-mêmes possèdent certaines symétries de façon intrinsèque (quelque part, c’est lié à la chiralité des pièces).&lt;/p&gt;

&lt;p&gt;Le fait que les cubes élémentaires constituant les polyominos sont indiscernables facilite grandement la non-chiralité des pièces. Mais ce n’est pas une condition suffisante, en témoigne cet exemple de pièce problématique que j’ai imaginé pour appuyer mon propos :&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;P&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show3d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aspect_ratio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'tachyon'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/polyopb.png&quot; alt=&quot;Une pièce problématique&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On voit bien que l’image de cette pièce par une symétrie planaire ne serait pas, il me semble, accessible par une combinaison de rotations…&lt;/p&gt;

&lt;p&gt;Peut-être que la clef serait de se demander si ces pièces sont orientées, c’est-à-dire qu’elles nous permettraient de définir un repère orienté de façon univoque. En cherchant du côté des mathématiques voire de la chimie on trouverait très probablement une réponse rigoureuse à cette question là.&lt;/p&gt;

&lt;p&gt;Mais tout compte fait, le fait que tous les polyominos de Gagne ta Maman / ton Papa sont planaires me fait penser que nous ne rencontrerons jamais le moindre problème de chiralité en fait. Désolé si c’est un peu décousu, je rédige à peu près en même temps que je réflechis car j’ai envie de clôturer cet article et passer à autre chose ^^&lt;/p&gt;

&lt;p&gt;Je m’éloigne finalement de mon propos premier, donc raison de plus de laisser là ces considérations et de passer au dernier cas, qui nous intéresse le plus.&lt;/p&gt;

&lt;h1 id=&quot;quatrième-cas--le-défi-28&quot;&gt;Quatrième cas : le défi 28&lt;/h1&gt;

&lt;p&gt;Nous pouvons maintenant nous attaquer au fameux défi 28.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;P1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'red'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'green'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'blue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'yellow'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'purple'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'black'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;P7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Polyomino&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'blue'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;P1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TilingSolver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_of_solutions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;iterT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;solve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'- Iteration '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show3d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.85&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;solution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Graphics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aspect_ratio&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;viewer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'tachyon'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;camera_position&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'./figures/solution_'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'.png'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cette fois-ci nous trouvons $1 152$ solutions.&lt;/p&gt;

&lt;p&gt;Il me semble que le miroir des pièces du défi sont toutes atteignables. En particulier j’avais une méfiance envers $Z_4$, $Z_5$ et (dans une moindre mesure) $L_4$. Mais je n’ai pas réussi à imaginer un cas de figure où une symétrie planaire conduirait à une solution impossible à réaliser. Je suppose que c’est OK.&lt;/p&gt;

&lt;p&gt;En prenant en compte les isométries du cube, nous nous ramenons &lt;em&gt;in fine&lt;/em&gt; à &lt;strong&gt;24 solutions&lt;/strong&gt;. Ces 24 solutions ne peuvent pas se déduire l’une de l’autre par le biais des symétries et rotations du cube.&lt;/p&gt;

&lt;p&gt;Voici les 9 premières solutions retournées par le solveur de SageMath.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/sol28.jpg&quot; alt=&quot;Neuf solutions du défi 28&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;pour-aller-plus-loin&quot;&gt;Pour aller plus loin&lt;/h1&gt;

&lt;p&gt;Quelques idées que j’ai en tête :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ce défi 28 était-il particulièrement difficile ? Il faudrait tester les autres défis et calculer leur nombre de solutions&lt;/li&gt;
  &lt;li&gt;en mettant ce nombre de solutions en regard de certains paramètres (complexité des polyominos en terme de degré, nombre de polyominos, chiralité si cette dernière a un impact ?), des choses amusantes pourraient apparaître&lt;/li&gt;
  &lt;li&gt;on pourrait de même quantifier la difficulté du jeu standard (à savoir paver le rectangle)&lt;/li&gt;
  &lt;li&gt;on pourrait aussi étudier l’arbre des solutions : combien y-a-t’il de feuilles qui ne terminent pas ? Partant de cet arbre, nous pourrions générer le (gros) labyrinthe équivalent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quelques liens utiles :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;le &lt;a href=&quot;http://familygamesamerica.com/mainsite/consumers/productview.php?pro_id=274&quot;&gt;jeu Quantumino&lt;/a&gt; dont s’inspire Gagne ta Maman.&lt;/li&gt;
  &lt;li&gt;une &lt;a href=&quot;https://www.maxgcoding.com/algorithm-x&quot;&gt;implémentation en C++&lt;/a&gt; des liens dansants&lt;/li&gt;
  &lt;li&gt;la &lt;a href=&quot;https://en.wikipedia.org/wiki/Exact_cover&quot;&gt;page Wikipédia de la couverture exacte&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;la &lt;a href=&quot;https://en.wikipedia.org/wiki/Knuth%27s_Algorithm_X&quot;&gt;page Wikipédia de l’algorithme X de Knuth&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;la &lt;a href=&quot;https://en.wikipedia.org/wiki/Polycube&quot;&gt;définition des polycubes&lt;/a&gt; sur Wikipédia&lt;/li&gt;
  &lt;li&gt;la &lt;a href=&quot;https://www.gigamic-adds.com/game/gagne-ton-papa&quot;&gt;page officielle du jeu Gagne ta Maman / ton Papa&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;l’&lt;a href=&quot;https://www.ocf.berkeley.edu/~jchu/publicportal/sudoku/0011047.pdf&quot;&gt;article de Knuth&lt;/a&gt; sur les liens dansants&lt;/li&gt;
  &lt;li&gt;une &lt;a href=&quot;https://www-cs-faculty.stanford.edu/~knuth/programs/dance.w&quot;&gt;implémentation des liens dansants&lt;/a&gt; par Knuth&lt;/li&gt;
  &lt;li&gt;une &lt;a href=&quot;https://vimeo.com/35348052&quot;&gt;vidéo illustrant les liens dansants&lt;/a&gt; sur la plateforme Viméo : on voit bien que ça danse !&lt;/li&gt;
  &lt;li&gt;la &lt;a href=&quot;https://doc.sagemath.org/html/en/reference/combinat/sage/combinat/tiling.html#module-sage.combinat.tiling&quot;&gt;documentation&lt;/a&gt; du module Tiling Solver de SageMath&lt;/li&gt;
  &lt;li&gt;un &lt;a href=&quot;http://www.slabbe.org/blogue/2011/11/resoudre-le-puzzle-quantumino-avec-le-logiciel-sage/&quot;&gt;article de Sébastien Labbé&lt;/a&gt;, issu de son blog personnel, sur le pavage par polyominos à l’aide de SageMath&lt;/li&gt;
  &lt;li&gt;le &lt;a href=&quot;https://fr.wikipedia.org/wiki/Herzberger_Quader&quot;&gt;quadrilatère de Herzberger&lt;/a&gt;, un casse-tête similaire au cube Soma mais plus récent&lt;/li&gt;
  &lt;li&gt;le &lt;a href=&quot;https://en.wikipedia.org/wiki/Soma_cube&quot;&gt;cube Soma&lt;/a&gt; sur Wikipédia&lt;/li&gt;
  &lt;li&gt;une &lt;a href=&quot;https://playwithalgos.github.io/dancing-links/&quot;&gt;animation interactive des liens dansants&lt;/a&gt; sur github.io&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;dans le cas tridimensionnel, on préfère parfois le terme “polycubes” à “polyomino”. Dans la suite de ce billet, j’emploierai toujours “polyomino” pour simplifier. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;la lecture de son article est très intéressante et instructive. Comme bien souvent en algorithmique, on se rend compte que la résolution élégante d’un problème passe en grande partie par la façon de modéliser les données du problème. Et l’algorithme proprement dit s’en déduit ensuite assez facilement. Je regrette juste qu’à un moment Knuth évoque la nomenclature de Golomb&lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; pour classifier les pentominos mais la référence associée pointe vers un livre assez rare (et donc cher)… Dommage… &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;ça m’a néanmoins permis de découvrir le mathématicien américain Solomon Wolf Golomb ainsi que ses &lt;a href=&quot;https://en.wikipedia.org/wiki/Golomb_ruler&quot;&gt;règles éponymes&lt;/a&gt; &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>DonutMan</name></author><category term="mathematique" /><category term="dancing" /><category term="links" /><category term="knuth" /><category term="polyomino" /><summary type="html">Il y a quelques temps, j’ai offert à ma fille ainée le jeu de société “Gagne ta Maman” (qui existe aussi en une version identique “Gagne ton Papa”).</summary></entry><entry><title type="html">Vulgariser le triangle</title><link href="https://donutblog.fr/mathematique/triangle/" rel="alternate" type="text/html" title="Vulgariser le triangle" /><published>2024-12-10T00:00:00+01:00</published><updated>2024-12-10T00:00:00+01:00</updated><id>https://donutblog.fr/mathematique/triangle</id><content type="html" xml:base="https://donutblog.fr/mathematique/triangle/">&lt;p&gt;Soient trois nombres réels strictement positifs $(a,b,c) \in \mathbb{R}_+^3$. Sous quelle(s) condition(s) ce triplet forme-t’il les trois distances d’un triangle dans le plan euclidien ?&lt;/p&gt;

&lt;p&gt;On répond assez vite à la question en faisant intervenir la bien-nommée &lt;strong&gt;inégalité triangulaire&lt;/strong&gt;. En supposant que le triplet est ordonné, c’est-à-dire $a \leq b \leq c$, il est nécessaire et suffisant que :&lt;/p&gt;

\[a + b \geq c\]

&lt;p&gt;L’égalité $a+b=c$ étant vérifiée lorsque le triangle est totalement aplati.&lt;/p&gt;

&lt;p&gt;J’ai voulu tester cela avec ma fille de six ans qui vient de rentrer en CP et qui maîtrise désormais les additions et les comparaisons.&lt;/p&gt;

&lt;p&gt;Dans ce but, j’ai créé un certain nombre de bandelettes de différentes tailles que j’ai coloriées puis découpées.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/triangle.jpg&quot; alt=&quot;Bande de couleurs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Avec Marie, nous choisissons ensuite trois bandes au hasard et nous essayons de les disposer en triangle. J’espérais qu’on puisse en déduire une loi sur le choix des trois bandes qui nous aurait assuré la possibilité de construire un triangle.&lt;/p&gt;

&lt;p&gt;Hélas, l’expérience ne fut pas très fructueuse, pour plusieurs raisons :&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;le papier de l’imprimante, trop fin, n’est pas agréable à manipuler&lt;/li&gt;
  &lt;li&gt;la largeur des bandelettes, pourtant nécessaire afin de faire figurer le nombre de cases, rend possible la construction de certains triplets qui ne vérifient pas l’inégalité&lt;/li&gt;
  &lt;li&gt;on visualise mal les triangles, il aurait peut-être fallu utiliser des longueurs plus grande&lt;/li&gt;
  &lt;li&gt;l’exploration n’est pas facile, peut-être aurait-il été plus judicieux de fixer la longueur du plus grand côté&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sans doute faudrait-il une autre approche, peut-être utiliser des baguettes Mikado ou autre. La vulgarisation mathématique, ce n’est pas toujours très simple. Néanmoins, c’était amusant de tester cela.&lt;/p&gt;

&lt;p&gt;Je laisse le &lt;a href=&quot;/docs/inegalite_triangulaire.svg&quot;&gt;fichier vectoriel des bandelettes&lt;/a&gt; en libre accès, si cela intéresse quelqu’un.&lt;/p&gt;</content><author><name>DonutMan</name></author><category term="mathematique" /><category term="DIY" /><category term="geometrie" /><summary type="html">Soient trois nombres réels strictement positifs $(a,b,c) \in \mathbb{R}_+^3$. Sous quelle(s) condition(s) ce triplet forme-t’il les trois distances d’un triangle dans le plan euclidien ?</summary></entry><entry><title type="html">Un loups-garous maison</title><link href="https://donutblog.fr/mathematique/loups_garous/" rel="alternate" type="text/html" title="Un loups-garous maison" /><published>2024-11-05T00:00:00+01:00</published><updated>2024-11-05T00:00:00+01:00</updated><id>https://donutblog.fr/mathematique/loups_garous</id><content type="html" xml:base="https://donutblog.fr/mathematique/loups_garous/">&lt;p&gt;Pour son prochain goûter d’anniversaire, ma fille Marie de six ans conviera quelques amis à elle. Je me suis demandé s’ils étaient en âge d’apprécier une petite partie de loups-garous simplifiée (et édulcorée).&lt;/p&gt;

&lt;p&gt;Pour estimer cela, j’ai créé avec Marie un jeu de cartes simplifié et j’ai mis à contribution la famille proche pour une partie pilote.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/lgfront.jpg&quot; alt=&quot;un loup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Nous étions en tout six personnes : Marie, ses grands-parents (Pépé et Mémé), ma soeur et son mari (Tata et Tonton) et moi-même. Durant l’expérience nous étions donc trois joueurs aguerris (Tata, Tonton et Papa), deux joueurs débutant (Pépé et Mémé) et une enfant de six ans dont je scrutais les réactions et les stratégies lors de la partie. Notons que le cousin nous a rejoint en milieu de séjour et nous avons pu tester également quelques variations.&lt;/p&gt;

&lt;p&gt;Je n’envisageais pas spécialement de billet de blog autour de cette histoire sauf que je me suis heurté rapidement à une petite difficulté : les Loups-Garous de Thiercelieux, tels qu’ils ont été conçus par leurs créateurs Philippe des Pallières et Hervé Marly, ne sont prévus que pour 8 à 18 joueurs.&lt;/p&gt;

&lt;p&gt;Etant donné qu’il faut au moins un loup-garou pour que la partie ait un sens et qu’il y a exactement un loup-garou dans la partie minimale à 8 joueurs, le problème fut vite réglé : à six joueurs, nous devons avoir également 1 loup-garou. Néanmoins, je me suis dit que la partie sera plus facile pour ce loup-garou étant donné qu’il y a 2 villageois en moins.&lt;/p&gt;

&lt;p&gt;Plus facile mais de combien exactement ?&lt;/p&gt;

&lt;p&gt;Les nombres de villageois et de loups-garous étant par définition entiers, le ratio de loups-garous dans le village ne peut prendre que certaines valeurs rationnelles. Partant de là, la difficulté de la partie pour un loup-garou, doit sensiblement varier selon le nombre de participant.&lt;/p&gt;

&lt;p&gt;Le présent billet de blog a la prétention d’étudier un peu tout cela par l’intermédiaire d’un modèle simplifié.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#les-règles-du-loup-garou-simplifié&quot; id=&quot;markdown-toc-les-règles-du-loup-garou-simplifié&quot;&gt;Les règles du loup-garou simplifié&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#modèle-du-jeu&quot; id=&quot;markdown-toc-modèle-du-jeu&quot;&gt;Modèle du jeu&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#loi-discrète-uniforme&quot; id=&quot;markdown-toc-loi-discrète-uniforme&quot;&gt;Loi discrète uniforme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#simulation-du-jeu-selon-les-répartitions-v-l-canoniques&quot; id=&quot;markdown-toc-simulation-du-jeu-selon-les-répartitions-v-l-canoniques&quot;&gt;Simulation du jeu selon les répartitions $(v, l)$ canoniques&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#simulation-du-jeu-avec-une-répartition-v-l-quelconque&quot; id=&quot;markdown-toc-simulation-du-jeu-avec-une-répartition-v-l-quelconque&quot;&gt;Simulation du jeu avec une répartition $(v, l)$ quelconque&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#améliorations-du-modèle&quot; id=&quot;markdown-toc-améliorations-du-modèle&quot;&gt;Améliorations du modèle&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#le-cas-particulier-à-6-joueurs&quot; id=&quot;markdown-toc-le-cas-particulier-à-6-joueurs&quot;&gt;Le cas particulier à 6 joueurs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#le-jeu-de-loups-garous-maison&quot; id=&quot;markdown-toc-le-jeu-de-loups-garous-maison&quot;&gt;Le jeu de loups-garous maison&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;les-règles-du-loup-garou-simplifié&quot;&gt;Les règles du loup-garou simplifié&lt;/h1&gt;

&lt;p&gt;Nous avons une ensemblée de $N$ habitant d’un village.
Ces habitants comportent $N_l$ loups-garous, qui se transforment en loup la nuit, et $N_v$ villageois normaux. Nous avons donc :&lt;/p&gt;

\[N = N_v + N_l\]

&lt;p&gt;Je fais ici la distinction entre habitant (qui peut être loup-garou ou non) et villageois (qui n’est par définition pas un loup-garou).&lt;/p&gt;

&lt;p&gt;Un tour de jeu se déroule en deux phases : la nuit et le jour.&lt;/p&gt;

&lt;p&gt;Pendant la nuit, les loups-garous se réveillent et éliminent un villageois de leur choix.&lt;/p&gt;

&lt;p&gt;Pendant la journée, les habitants (qui incluent donc les loups-garous) discutent entre eux pour éliminer l’un des leurs. Avec un peu de chance, ils élimineront un loup-garou.&lt;/p&gt;

&lt;p&gt;La partie s’arrête lorsqu’il n’y a plus de loup-garou auquel cas les villageois restant gagnent la partie; ou bien lorsqu’il n’y a plus de villageois auquel cas les loups-garous restant gagnent.&lt;/p&gt;

&lt;p&gt;Remarquons déjà que les villageois ne peuvent gagner la partie que lors d’une phase de jour lorsqu’ils éliminent le dernier loup.&lt;/p&gt;

&lt;p&gt;Les loups-garous de leur côté peuvent gagner de nuit comme de jour : de nuit, comme conséquence d’une de leur attaque; et de jour comme défaillance des villageois à éliminer un loup.&lt;/p&gt;

&lt;p&gt;Dans le jeu de base se trouvent quelques autres personnages (Cupidon, le Voleur, la Voyante, la Sorcière, le Chasseur…) mais j’ai décidé de les ignorer afin de garder la mécanique la plus simple possible. Je garde toutefois à l’esprit que l’ensemble de ces personnages (à l’exception de Cupidon et du Voleur) ont tendance à &lt;strong&gt;favoriser&lt;/strong&gt; les villageois (ainsi la Voyante est une villageoise qui peut une fois par nuit découvrir la véritable nature d’un habitant de son choix). Globalement ces cartes augmentent sensiblement le pouvoir des villageois (pensons à la Sorcière, une villageoise qui une fois dans la partie peut tuer la personne de son choix) : bien utilisé, ce pouvoir est profitable aux villageois mais il peut aussi se retourner contre eux.&lt;/p&gt;

&lt;h1 id=&quot;modèle-du-jeu&quot;&gt;Modèle du jeu&lt;/h1&gt;

&lt;p&gt;Un tour de jeu se déroule donc en deux phases successives : la nuit, puis le jour.&lt;/p&gt;

&lt;p&gt;Nous pouvons représenter l’état du jeu en un instant $t$ comme un point sur le quart de plan $(v, l)$.&lt;/p&gt;

&lt;p&gt;Voici un exemple dans le cas général, le point en rouge correspond à l’état courant :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/lg-0.png&quot; alt=&quot;Loups-garous cas général&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Les règles du jeu font que nous ne pouvons créer ni de villageois ($v$ augmente) ni de loups-garous ($l$ augmente).&lt;/p&gt;

&lt;p&gt;Afin de simplifier les schémas, nous considérons (sans perte de généralité) le cas particulier où $N_v=5$ et $N_l=2$&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/lg-1.png&quot; alt=&quot;Loups-garous cas particulier&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Lors d’une phase nuit et selon nos règles simplifiées, un villageois est nécessairement tué. Cet opérateur descend donc le compteur $v$ d’une unité avec une probabilité de $1$.&lt;/p&gt;

&lt;p&gt;Lors d’une phase jour, ce sera soit un villageois soit un loup-garou qui sera tué. Si nous notons $p$ la probabilité qu’un loup soit tué, alors nous pouvons représenter un tour de jeu selon le diagramme suivant :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/lg-2.png&quot; alt=&quot;Un cycle de jeu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;La partie s’arrête lorsque le point courant touche l’axe des abscisses, auquel cas $l=0$ et les villageois gagnent; ou bien lorsque ce point touche l’axe des ordonnées et dans ce cas $v=0$ et les loups-garous gagnent. Selon notre modèle, le point de coordonnées $(0,0)$ où il ne reste plus ni loups-garous ni villageois est indéterminé (qui est le vainqueur dans un village vide ?). Mais cela n’est pas important car ce point est inacessible sans passer au préalable par une condition de victoire.&lt;/p&gt;

&lt;p&gt;Tout l’interêt du modèle est ici de choisir une valeur de $p$ réaliste. Alors bien sûr cela va dépendre de plusieurs facteurs qui peuvent difficilement être intégrés dans mon modèle :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;les loups-garous peuvent être maladroit, faire du bruit, bousculer les voisins en désignant leur victime etc.&lt;/li&gt;
  &lt;li&gt;mais de la même façon, un villageois agité pendant la phase nuit pourra être accusé à tort d’être un loup&lt;/li&gt;
  &lt;li&gt;les joueurs peuvent être plus ou moins persuasifs dans leur argumentation, plus ou moins manipulateurs dans leur stratégie&lt;/li&gt;
  &lt;li&gt;les inimitiés ou les amitiés fortes peuvent influencer peut-être le vote&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;J’élimine rapidement le cas trop simplifié qui consisterait à prendre $p$ constant, par exemple $1/2$. Dans ce cas, lors de la phase jour, les habitants auraient une chance sur deux d’éliminer un loup-garou et une chanche sur deux de tuer un innocent villageois. La simplicité de ce modèle est attirante mais nous n’aboutirions pas à une situation réaliste : imaginez par exemple 100 habitants parmis lesquels se cache un unique loup-garou. Il faudrait avoir une sacré chance pour l’éliminer lors d’une phase jour.&lt;/p&gt;

&lt;h1 id=&quot;loi-discrète-uniforme&quot;&gt;Loi discrète uniforme&lt;/h1&gt;

&lt;p&gt;Une loi plus réaliste serait de choisir uniformément parmi les $v+l$ survivants la personne à éliminer. Nous utilisons donc une loi de probabilité discrète uniforme et chaque personne à la même probabilité $1/(v+l)$ d’être éliminé. Nous avons donc :&lt;/p&gt;

\[p = \frac{l}{v+l}\]

&lt;p&gt;et donc logiquement :&lt;/p&gt;

\[1-p = \frac{v}{v+l}\]

&lt;p&gt;Rappelons que $p$ représente la probabilité que les habitants désignent un loup-garou lors du vote. Le fait d’utiliser une loi uniforme traduit le fait que ces derniers n’ont aucun &lt;em&gt;a priori&lt;/em&gt; sur l’identité des loups. C’est justifiable pour les villageois, qui n’auraient ainsi jamais aucune information sur les loups. Ca l’est quand même moins pour les loups qui, eux aussi, votent un peu au hasard. Notre modèle montrera donc ses limites dans le cas où le nombre de loups n’est plus négligeable devant le nombre de villageois. Mais enfin qu’importe c’est une premier essai.&lt;/p&gt;

&lt;h1 id=&quot;simulation-du-jeu-selon-les-répartitions-v-l-canoniques&quot;&gt;Simulation du jeu selon les répartitions $(v, l)$ canoniques&lt;/h1&gt;

&lt;p&gt;Avec cette mécanique, nous pouvons modéliser le comportement des joueurs de loups pour les couples $(N_v, N_l)$ prévus dans les règles :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/lg.png&quot; alt=&quot;Nombre de joueurs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Rappelons que dans notre modèle simplifié, la voyante est une villageoise comme les autres.&lt;/p&gt;

&lt;p&gt;Nous avons simulé 10 000 parties pour chaque couple $(N_v, N_l)$ prévus dans le livret. Nous en extrayons ensuite la fréquence de victoire des loups pour chaque cas. Nous obtenons alors le diagramme suivant :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/model1.png&quot; alt=&quot;Probabilité de victoire des loups&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Nous observons plusieurs faits remarquables :&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;globalement les loups sont super avantagés par le jeu ! Ils gagnent en moyenne dans 68% des cas. On peut penser que les personnages additionnels (le Chasseur, la Sorcière, la Voyante) peuvent potentiellement leur compliquer la tâche… à condition que leur pouvoir soit utilisé à bon escient&lt;/li&gt;
  &lt;li&gt;la gigue qu’on observe n’est pas due à un faible échantillon mais est caractéristique du modèle : &lt;strong&gt;les loups ont toujours un avantage supplémentaire avec un nombre initial pair de joueurs&lt;/strong&gt;. Ceci peut sembler étrange mais s’explique facilement par la dissymétrie jour/nuit. Je serai intéressé de savoir si des gros gros joueurs du jeu ont noté cet effet…&lt;/li&gt;
  &lt;li&gt;on voit très nettement l’effet de l’introduction du 3eme loup-garou à partir de 12 joueurs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Je reste malgré tout perplexe devant les forts taux de victoire des loups… Ce n’est pas le sentiment que j’avais en jouant au jeu. Et n’oublions pas que nous modélisons des loups idiots qui peuvent potentiellement voter contre leurs congénères.&lt;/p&gt;

&lt;p&gt;Peut-être ai-je minimisé notre capacité à débusquer la duperie.&lt;/p&gt;

&lt;h1 id=&quot;simulation-du-jeu-avec-une-répartition-v-l-quelconque&quot;&gt;Simulation du jeu avec une répartition $(v, l)$ quelconque&lt;/h1&gt;

&lt;p&gt;Voici cette même fréquence de victoire des loups lorsqu’on fait varier, d’une part le nombre de villageois entre 1 et 20 et d’autre part le nombre de loups entre 1 et 5.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/model2.png&quot; alt=&quot;Probabilité de victoire des loups&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sans surprise on constate que ce taux augmente avec le ratio $l/v$. Nous retrouvons également la dissymétrie jour/nuit qui confère un avantage aux loups lorsque le nombre de joueur est pair.&lt;/p&gt;

&lt;h1 id=&quot;améliorations-du-modèle&quot;&gt;Améliorations du modèle&lt;/h1&gt;

&lt;p&gt;Peut être faudrait-il modéliser plus finement les mécaniques du vote diurne. Ainsi, on pourrait particulariser chaque intention de vote en s’assurant que les loups n’ont qu’une faible probabilité de voter contre un des leurs.&lt;/p&gt;

&lt;p&gt;On pourrait aussi imaginer une chaîne de Markov qui tiendrait compte, par exemple, des résultats des 2 tours précédents. Si un habitant a désigné et contribué à mettre-à-mort un innocent villageois au tour N, il augmente la suspicion à son égard et donc la probabilité qu’il soit désigné par les villageois au tour N+1. J’aimerais bien avoir un peu de temps pour faire ça.&lt;/p&gt;

&lt;h1 id=&quot;le-cas-particulier-à-6-joueurs&quot;&gt;Le cas particulier à 6 joueurs&lt;/h1&gt;

&lt;p&gt;Pour terminer concernant l’objet initial de ce billet, dans le cas d’un jeu à 6 joueurs parmi lesquels se cache un loup-garou.&lt;/p&gt;

&lt;p&gt;Sur un million de parties selon nos règles simplifiées, les loups gagnent dans 53% des cas.&lt;/p&gt;

&lt;h1 id=&quot;le-jeu-de-loups-garous-maison&quot;&gt;Le jeu de loups-garous maison&lt;/h1&gt;

&lt;p&gt;Pour terminer, voici notre réalisation du jeu du loups-garous. J’ai rajouté un loup supplémentaire pour tester.&lt;/p&gt;

&lt;p&gt;Le jeu a été un grand succès et je crois que Marie a beaucoup aimé ! On a d’ailleurs été amené par la suite à créer des cartes supplémentaires (Sorcière et Capitaine).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/lgmaison.jpg&quot; alt=&quot;Loups-garous maison&quot; /&gt;&lt;/p&gt;</content><author><name>DonutMan</name></author><category term="mathematique" /><category term="jeu" /><category term="modelisation" /><category term="statistiques" /><summary type="html">Pour son prochain goûter d’anniversaire, ma fille Marie de six ans conviera quelques amis à elle. Je me suis demandé s’ils étaient en âge d’apprécier une petite partie de loups-garous simplifiée (et édulcorée).</summary></entry><entry><title type="html">Vertige d’un footballeur</title><link href="https://donutblog.fr/mathematique/football/" rel="alternate" type="text/html" title="Vertige d’un footballeur" /><published>2024-07-10T01:00:00+02:00</published><updated>2024-07-10T01:00:00+02:00</updated><id>https://donutblog.fr/mathematique/football</id><content type="html" xml:base="https://donutblog.fr/mathematique/football/">&lt;p&gt;Hier, le mardi 9 juillet 2024, j’ai regardé le match France-Espagne de la coupe d’Europe.&lt;/p&gt;

&lt;p&gt;A un moment, s’est affiché rapidement la disposition des joueurs français, avec un titre du genre “3-4-3”.&lt;/p&gt;

&lt;p&gt;Alors je sais qu’il s’agit de la répartition des joueurs entre la défense (à gauche) et l’attaque (à droite). Mais en fait, il existe plein de façons différentes de disposer 10 joueurs sur le terrain.&lt;/p&gt;

&lt;p&gt;Combien exactement ? C’est l’objet de ce billet de blog.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#le-problème&quot; id=&quot;markdown-toc-le-problème&quot;&gt;Le problème&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#la-résolution&quot; id=&quot;markdown-toc-la-résolution&quot;&gt;La résolution&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#exploration-de-lintégralité-des-tactiques-possibles&quot; id=&quot;markdown-toc-exploration-de-lintégralité-des-tactiques-possibles&quot;&gt;Exploration de l’intégralité des tactiques possibles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#deux-résonances&quot; id=&quot;markdown-toc-deux-résonances&quot;&gt;Deux résonances&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#liens-pour-approfondir&quot; id=&quot;markdown-toc-liens-pour-approfondir&quot;&gt;Liens pour approfondir&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;le-problème&quot;&gt;Le problème&lt;/h2&gt;

&lt;p&gt;Donc en fait, il nous faut trouver de combien de façons différentes nous pouvons écrire 10 comme somme d’une suite d’entiers tous strictement positifs.&lt;/p&gt;

&lt;p&gt;Par exemple nous avons $3+4+3 = 10$.&lt;/p&gt;

&lt;p&gt;La page Wikipédia &lt;a href=&quot;https://fr.wikipedia.org/wiki/Tactique_(football)#Principaux_sch%C3%A9mas_tactiques_du_football_moderne&quot;&gt;dédiée aux tactiques du football&lt;/a&gt; en dénombre au total 6, dans la version moderne du football. Ces dispositions sont :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;$4+4+2=10$&lt;/li&gt;
  &lt;li&gt;$4+3+3=10$&lt;/li&gt;
  &lt;li&gt;$4+2+3+1=10$&lt;/li&gt;
  &lt;li&gt;$5+3+2=10$&lt;/li&gt;
  &lt;li&gt;$5+4+1=10$&lt;/li&gt;
  &lt;li&gt;$3+4+3=10$&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mais on sens bien qu’il y en a plein d’autres.&lt;/p&gt;

&lt;p&gt;Quid par exemple de la configuration $1+1+1+1+1+1+1+1+1+1 = 10$ où tous les joueurs seraient alignés à la queue leu-leu sur une longueur du terrain ?&lt;/p&gt;

&lt;p&gt;Quid également de la configuration $10=10$ où cette fois-ci tous les joueurs seraient alignés sur une largeur du terrain et présenteraient, pour ainsi dire, un front uni face à l’équipe adverse ?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/rangee.jpg&quot; alt=&quot;Rangée de footballeurs&quot; /&gt;&lt;/p&gt;
&lt;center&gt;&lt;i&gt;Ceci n'est pas un penalty : l'équipe bleue se met simplement en configuration 10&lt;/i&gt;&lt;/center&gt;

&lt;h2 id=&quot;la-résolution&quot;&gt;La résolution&lt;/h2&gt;

&lt;p&gt;Répondre à cette question de dénombrement revient mathématiquement à s’interroger sur la &lt;strong&gt;composition des entiers&lt;/strong&gt;. La &lt;a href=&quot;https://fr.wikipedia.org/wiki/Composition_(combinatoire)&quot;&gt;page Wikipédia sur le sujet&lt;/a&gt; en parle très bien et avec de très jolis schémas. Je ne vais pas tout détailler mais simplement énoncer les faits qui me paraissent intéressant.&lt;/p&gt;

&lt;p&gt;D’une façon générale, le nombre de compositions $c(n)$ d’un entier $n$ est égal à :&lt;/p&gt;

\[c(n) = 2^{n-1}\]

&lt;p&gt;Dans notre cas de figure avec $n = 10$ joueurs, cela nous donne donc $2^9 = 512$ tactiques différentes.&lt;/p&gt;

&lt;p&gt;Il s’agit bien entendu d’un nombre prenant en compte toutes les permutations : $4+3+3$ est différent de $3+4+3$ par exemple.&lt;/p&gt;

&lt;p&gt;Si on ne souhaitait pas comptabiliser les permutations, alors le problème revient à calculer le &lt;strong&gt;nombre de partitions d’un entier&lt;/strong&gt;, qui est un problème voisin &lt;a href=&quot;https://fr.wikipedia.org/wiki/Partition_d%27un_entier&quot;&gt;très bien documenté également&lt;/a&gt; sur Wikipédia.&lt;/p&gt;

&lt;p&gt;La formule générale qui donne le nombre de partitions $p(n)$ est assez complexe et n’a été obtenue qu’en 1937, par le mathématicien Hans Rademacher. Sa formule fait intervenir les suites de Farey, les cercles de Ford et l’analyse complexe !&lt;/p&gt;

&lt;p&gt;Les premiers termes de la suite $p(n)$ apparaît dans l’OEIS sous l’identifiant &lt;a href=&quot;https://oeis.org/A000041&quot;&gt;A000041&lt;/a&gt;. Hardy et Ramanujan en ont donné de leur côté une approximation asymptotique :&lt;/p&gt;

\[p(n) \sim \frac{1}{4n \sqrt{3}} exp \left(\pi \sqrt{\frac{2n}{3}} \right)\]

&lt;p&gt;J’ai tracé ci-dessous l’évolution du nombre de partitions (en noir) et de combinaisons (en rouge) pour $N&amp;lt;50$. L’échelle est logarithmique, on voit que le nombre de combinaisons croît beaucoup plus vite !&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/partitions.png&quot; alt=&quot;Nombre de partitions et de combinaisons&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Pour terminer la présentation rapide des nombres de partitions, les &lt;strong&gt;diagrammes de Ferrers&lt;/strong&gt; peuvent aider à visualiser les différentes possibilités de façon très schématique. Ci-dessous le cas pour $n=8$&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/ferrer.png&quot; alt=&quot;Diagramme de Ferrers&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;exploration-de-lintégralité-des-tactiques-possibles&quot;&gt;Exploration de l’intégralité des tactiques possibles&lt;/h2&gt;

&lt;p&gt;Il existe une façon très élégante d’énumérer toutes les dispositions possibles en se basant sur l’écriture binaire des entiers de $0$ à $2^9$. Je me suis basé sur cette bijection pour établir les schémas exhaustifs du placement des joueurs.&lt;/p&gt;

&lt;p&gt;Par exemple, pour avoir la configuration n°167, on remarque que&lt;/p&gt;

&lt;p&gt;$167 \equiv 010100111|_{\textrm{bin}}$&lt;/p&gt;

&lt;p&gt;On compte ensuite le nombre de 1 (potentiellement nul) entre deux 0 qui se suivent en partant de la gauche.&lt;/p&gt;

&lt;p&gt;Dans notre cas de figure, on compte cinq groupes :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;un premier groupe ne contenant pas de chiffre 1 : $\textcolor{red}{*}010100111$&lt;/li&gt;
  &lt;li&gt;un deuxième groupe contenant 1 fois le chiffre 1 : $0\textcolor{red}{1}0100111$&lt;/li&gt;
  &lt;li&gt;un troisième groupe contenant 1 fois le chiffre 1 : $010\textcolor{red}{1}00111$&lt;/li&gt;
  &lt;li&gt;un quatrième groupe ne contenant pas de chiffre 1 : $01010\textcolor{red}{*}0111$&lt;/li&gt;
  &lt;li&gt;un cinquième groupe contenant 3 fois le chiffre 1 : $010100\textcolor{red}{111}$&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La séquence des tailles de groupe est donc ici $(0, 1, 1, 0, 3)$. On ajoute 1 à chaque élément pour obtenir &lt;em&gt;in fine&lt;/em&gt; la configuration $1+2+2+1+4 = 10$.&lt;/p&gt;

&lt;p&gt;Ca parait un peu artificel comme procédé, mais il faut se dire que les 0 qui apparaissent dans l’écriture binaire jouent le rôle de délimiteurs entre les joueurs de football.&lt;/p&gt;

&lt;p&gt;Voici quoiqu’il en soit ce que me génère mon programme d’exploration des 512 tactiques différentes, regroupées par paquets de 64 tactiques. En rouge, les 6 tactiques qui sont mentionnées par la page Wikipédia précemment citée. Pour les voir en grand :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/img/2024/strategy_1.png&quot;&gt;de 0 à 63&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/img/2024/strategy_2.png&quot;&gt;de 64 à 127&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/img/2024/strategy_3.png&quot;&gt;de 128 à 191&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/img/2024/strategy_4.png&quot;&gt;de 192 à 255&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/img/2024/strategy_5.png&quot;&gt;de 256 à 319&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/img/2024/strategy_6.png&quot;&gt;de 320 à 383&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/img/2024/strategy_7.png&quot;&gt;de 384 à 447&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/img/2024/strategy_8.png&quot;&gt;de 448 à 511&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/strategy_1.png&quot; alt=&quot;Stratégie 1&quot; /&gt;
&lt;img src=&quot;/img/2024/strategy_2.png&quot; alt=&quot;Stratégie 2&quot; /&gt;
&lt;img src=&quot;/img/2024/strategy_3.png&quot; alt=&quot;Stratégie 3&quot; /&gt;
&lt;img src=&quot;/img/2024/strategy_4.png&quot; alt=&quot;Stratégie 4&quot; /&gt;
&lt;img src=&quot;/img/2024/strategy_5.png&quot; alt=&quot;Stratégie 5&quot; /&gt;
&lt;img src=&quot;/img/2024/strategy_6.png&quot; alt=&quot;Stratégie 6&quot; /&gt;
&lt;img src=&quot;/img/2024/strategy_7.png&quot; alt=&quot;Stratégie 7&quot; /&gt;
&lt;img src=&quot;/img/2024/strategy_8.png&quot; alt=&quot;Stratégie 8&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;deux-résonances&quot;&gt;Deux résonances&lt;/h2&gt;

&lt;p&gt;Ces petites figures m’ont rappelées le tableau “Vertige d’une chaise” de Max Charvolen, tableau que j’aime beaucoup.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/vertige_chaise.png&quot; alt=&quot;Vertige d'une chaise&quot; /&gt;&lt;/p&gt;

&lt;p&gt;La capture d’écran est de mauvaise qualité et provient du &lt;a href=&quot;https://nice.cmcas.com/wp-content/uploads/sites/59/2018/06/dossier-Artotheque.pdf&quot;&gt;catalogue&lt;/a&gt; de l’artothèque de la CMCAS de la ville de Nice.&lt;/p&gt;

&lt;p&gt;Je me souviens m’être demandé à une époque s’il était possible de créer un “générateur de chaises” à partir des quelques 720 réalisations proposées par Charvolen. Mais le processus ne me semble pas évident. De mémoire, j’espérais une superposition de projections de chaises dont on aurait fait varier l’orientation (rotation) ou la dimension (homothétie). Mais ça semble plus complexe que cela, et il y a des perturbations sur la structure même de la chaise. Du coup, j’ai un peu laissé tomber. Mais cet article me permet de me souvenir de ce truc là tiens. Le résultat final du tableau fait penser à une écriture à base de sinogrammes.&lt;/p&gt;

&lt;p&gt;Par ailleurs, régulièrement quand je me prête au jeu de dénombrer pour le simple plaisir de le faire, je repense toujours à Georges Perec qui disait :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Rien ne semble plus simple que de dresser une liste, en fait c’est beaucoup plus compliqué que ça n’en a l’air : on oublie toujours quelque chose, on est tenté d’écrire etc., mais justement un inventaire, c’est quand on écrit pas etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Le plaisir de la combinatoire mathématique, c’est justement quand on refuse d’écrire etc.&lt;/p&gt;

&lt;h2 id=&quot;liens-pour-approfondir&quot;&gt;Liens pour approfondir&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://fr.wikipedia.org/wiki/Tactique_(football)&quot;&gt;Les tactiques du football&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fr.wikipedia.org/wiki/Tableau_de_Young&quot;&gt;Les diagrammes de Young&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fr.wikipedia.org/wiki/Treillis_de_Young&quot;&gt;Les treillis de Young&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>DonutMan</name></author><category term="mathematique" /><category term="combinatoire" /><summary type="html">Hier, le mardi 9 juillet 2024, j’ai regardé le match France-Espagne de la coupe d’Europe.</summary></entry><entry><title type="html">Un Rubik’s Cube Mirror</title><link href="https://donutblog.fr/mathematique/cube/" rel="alternate" type="text/html" title="Un Rubik’s Cube Mirror" /><published>2024-05-22T01:00:00+02:00</published><updated>2024-05-22T01:00:00+02:00</updated><id>https://donutblog.fr/mathematique/cube</id><content type="html" xml:base="https://donutblog.fr/mathematique/cube/">&lt;!--&lt;script type=&quot;text/javascript&quot; src=&quot;https://donutblog.fr/assets/js/toto.js&quot;&gt;&lt;/script&gt; --&gt;
&lt;!-- &lt;script src=&quot;math.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt; --&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.3.0/math.min.js&quot;&gt;&lt;/script&gt;

&lt;script src=&quot;https://cdn.jsdelivr.net/npm/p5@1.5.0/lib/p5.js&quot;&gt;&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/assets/js/cube.js&quot;&gt;&lt;/script&gt;

&lt;!--&lt;script type=&quot;text/javascript&quot; src=&quot;/assets/js/reference_frame.js&quot;&gt;&lt;/script&gt;--&gt;

&lt;p&gt;A l’occasion de mon récent anniversaire, on m’a offert un Rubik’s cube dit “miroir”. Ce dernier a exactement la même mécanique qu’un Rubik’s cube traditionnel. La seule différence, redoutable, est que désormais les cubes ont la même couleur (miroir). Ce qui identifie de façon unique leur position et leur orientation, c’est les &lt;strong&gt;dimensions des cubes&lt;/strong&gt; (qui ne sont ainsi plus des cubes mais des pavés).&lt;/p&gt;

&lt;p&gt;Je n’ai jamais vraiment manipulé de Rubik’s cube et l’exercice a été parsemé de fausses manip qui m’ont fait quasiment tout recommencer ! Il y a plein de sites en ligne qui expliquent comment résoudre ce genre de casse-tête. Avec un peu d’entraînement, on y arrive.&lt;/p&gt;

&lt;p&gt;Ca a aussi été l’occasion pour moi de découvrir que la famille des Rubik’s cube est très diversifiée avec plein de déclinaisons différentes (dont un magnifique cube 1x1x1).&lt;/p&gt;

&lt;p&gt;Mais ce que j’aime beaucoup dans celui qu’on m’a offert c’est qu’en le manipulant, on obtient des formes complexes que je trouve agréable à regarder.&lt;/p&gt;

&lt;p&gt;Sauf qu’après l’avoir dérangé, il faut résoudre le puzzle !&lt;/p&gt;

&lt;p&gt;Du coup, je me suis dit que j’allais développer un petit programme qui allait explorer à ma place et de façon aléatoire, les différentes configurations du cube.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#le-cube&quot; id=&quot;markdown-toc-le-cube&quot;&gt;Le cube&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#considérations-sur-limplémentation&quot; id=&quot;markdown-toc-considérations-sur-limplémentation&quot;&gt;Considérations sur l’implémentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;le-cube&quot;&gt;Le cube&lt;/h2&gt;

&lt;p&gt;Voici le résultat, obtenu en &lt;a href=&quot;https://p5js.org/&quot;&gt;p5.js&lt;/a&gt;. J’ai utilisé pour la première fois les primitives de rendu 3D du moteur (qui s’appuie sur WebGL).&lt;/p&gt;

&lt;p&gt;On peut bouger la vue en cliquant/déplaçant la souris. Désolé si ça ne s’affiche pas bien sur smartphone, je n’ai pas géré le côté responsive du truc. On réinitialise le cube en appuyant sur &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r&lt;/code&gt; (en fait j’adore ce bouton, compte tenu du temps qu’il m’a fallu pour résoudre le cube dans la vraie vie).&lt;/p&gt;

&lt;div id=&quot;sketch-holder&quot;&gt;
&lt;!-- Our sketch will go here! --&gt;
&lt;/div&gt;

&lt;h2 id=&quot;considérations-sur-limplémentation&quot;&gt;Considérations sur l’implémentation&lt;/h2&gt;

&lt;p&gt;La principale difficulté de ce programme a résidé en deux points :&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Il faut tenir à jour un &lt;strong&gt;index de la position&lt;/strong&gt; des différents cubes. En effet, souhaitant faire tourner la face supérieure selon l’axe vertical nécessite de connaître les 9 cubes qui composent cette face. J’ai décidé de stocker cette information dans une liste. D’une part pour des raisons de simplicité et d’autre part parce que je ne connais pas assez bien les autres solutions offertes par JavaScript. Le point qui fut délicat ici aura été de mettre correctement cette liste à jour après n’importe quel demi-tour.&lt;/li&gt;
  &lt;li&gt;Un cube participant à une rotation élémentaire verra son orientation propre évoluer, il faut garder cela à jour également. J’ai décidé de partir sur une représentation classique de l’orientation par &lt;strong&gt;angles d’Euler&lt;/strong&gt;, comme nous le verrons.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La bibliothèque p5.js nous offre trois primitives qui vont nous permettre de tourner les cubes, à savoir &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rotateX()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rotateY()&lt;/code&gt; et &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rotateZ()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;La documentation indique :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Rotates the coordinate system about the x-axis in WebGL mode.&lt;/p&gt;

  &lt;p&gt;The parameter, angle, is the amount to rotate. For example, calling rotateX(1) rotates the coordinate system about the x-axis by 1 radian.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alors déjà, les angles de rotation sont exprimés en radian ce qui est une bonne chose. Ensuite, on s’aperçoit que p5.js fait tourner le système de coordonnées et pas le cube ! C’est &lt;strong&gt;la fameuse ambiguité entre alias et alibi&lt;/strong&gt;, il nous faudra donc considérer systématiquement les &lt;strong&gt;transposées&lt;/strong&gt; des matrices de rotation (tourner le cube d’un angle $\theta$ selon un axe donné revient à tourner le repère d’un angle $-\theta$ selon ce même axe). En pratique, ça ne me semblait pas super gênant vu que le sens de rotation m’importait peu, c’est une convention. Une petite difficulté surgira pourtant lorsqu’il faudra identifier quels cubes ont tourné.&lt;/p&gt;

&lt;p&gt;Quoiqu’il en soit, on comprend que la rotation totale se calcule par l’intermédiaire des &lt;strong&gt;angles d’Euler&lt;/strong&gt;. Pour des rotations du système de coordonnées selon les axes X, Y et Z, cela revient à considérer les trois matrices suivantes :&lt;/p&gt;

\[R_X(\alpha) = \begin{pmatrix}
1 &amp;amp; 0 &amp;amp; 0\\
0 &amp;amp; \cos \alpha &amp;amp; \sin \alpha \\
0  &amp;amp; -\sin \alpha &amp;amp; \cos \alpha
\end{pmatrix}\]

\[R_Y(\beta) = \begin{pmatrix}
\cos \beta &amp;amp; 0 &amp;amp; -\sin \beta\\
0 &amp;amp; 1 &amp;amp; 0 \\
\sin \beta  &amp;amp; 0 &amp;amp; \cos \beta
\end{pmatrix}\]

\[R_Z(\gamma) = \begin{pmatrix}
\cos \gamma &amp;amp; \sin \gamma &amp;amp; 0\\
-\sin \gamma &amp;amp; \cos \gamma &amp;amp; 0 \\
0  &amp;amp; 0 &amp;amp; 1
\end{pmatrix}\]

&lt;p&gt;En pratique, les transformations élémentaires ne sont que des quarts de tour et donc $(\alpha, \beta, \gamma) \in \{-\pi/2, 0, +\pi/2\}^3$. Les matrices d’Euler sont donc très simple et consistent essentiellement en des permutations d’axe. Plus précisément :&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Axe&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Sens&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;X devient&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Y devient&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Z devient&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;X&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+X&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-Z&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+Y&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;X&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+X&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+Z&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-Y&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Y&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+Z&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+Y&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-X&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Y&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-Z&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+Y&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+X&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Z&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-Y&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+X&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+Z&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Z&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+Y&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;-X&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;+Z&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;La documentation indique également :&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Note: The order the rotations are called is important, ie. if used together, it must be called in the order Z-X-Y or there might be unexpected behaviour.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On comprend donc que la rotation totale s’écrit :&lt;/p&gt;

\[R = R_Y \cdot R_X \cdot R_Z\]

&lt;p&gt;Mes premiers développements se heurtaient à des comportements étranges. Je me suis alors rendu compte que le repère de référence de p5.js ne forme pas un trièdre direct… J’ai commencé à prendre cela en compte dans le programme mais en fait, les choses semblent se compenser (rappelons-nous que nous faisons tourner le repère, pas l’objet).&lt;/p&gt;

&lt;p&gt;Partant d’un état donné du cube, j’ai décidé de ne pas tenir compte de l’avertissement de la documentation (qui, je pense, ne traduit que le fait que les rotations ne commutent pas. Une fois qu’on sait ça, il faut juste savoir ce qu’on fait) et j’ai allégremment mélangé les rotations.&lt;/p&gt;

&lt;p&gt;Partant d’un cube orienté $YXZ = (\beta, \alpha, \gamma)$ si je rajoute, mettons, une rotation $D$ d’un demi-tour positif selon l’axe X, comment s’exprimera la nouvelle orientation ? Cela revient à trouver $(\alpha’, \beta’, \gamma’)$ qui satisfait :&lt;/p&gt;

\[R_Y(\beta)\cdot R_X(\alpha) \cdot R_Z(\gamma)\cdot D = R_Y(\beta')\cdot R_X(\alpha') \cdot R_Z(\gamma')\]

&lt;p&gt;La solution n’est pas unique et il suffit de trouver un triplet qui fonctionne pour mettre correctement les informations à jour.&lt;/p&gt;

&lt;p&gt;Hormis le cas trivial d’un demi-tour selon l’axe Z, la résolution de ce système dépendra de l’état actuel des rotations pour savoir quel axe se transformera en quel autre axe. Il faut alors propager les permutations d’axe de rotation en rotation jusqu’à revenir sur la première. Regardant par exemple $R_X(\alpha)$ qui me demande de tourner d’un angle $\alpha$ autour de X, si entre temps, mon axe X est devenu -Y, alors il faudra mettre à jour en indiquant une rotation d’angle $-\alpha$ autour de l’axe Y.&lt;/p&gt;</content><author><name>DonutMan</name></author><category term="mathematique" /><category term="rubik" /><category term="cube" /><category term="p5" /><category term="animation" /><summary type="html"></summary></entry><entry><title type="html">Mon premier verger</title><link href="https://donutblog.fr/mathematique/premier_verger/" rel="alternate" type="text/html" title="Mon premier verger" /><published>2024-03-14T00:00:00+01:00</published><updated>2024-03-14T00:00:00+01:00</updated><id>https://donutblog.fr/mathematique/premier_verger</id><content type="html" xml:base="https://donutblog.fr/mathematique/premier_verger/">&lt;p&gt;J’ai récemment joué quelques partie du jeu de société “Mon Premier Verger” avec mes enfants (Marie 5 ans et Alice 2 ans). Nous avons gagné toutes les parties et je me suis demandé dans quelle mesure nous avons eu de la chance.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Rien ne semble plus simple que de dresser une liste, en fait c’est beaucoup plus compliqué que ça n’en a l’air : on oublie toujours quelque chose, on est tenté d’écrire etc., mais justement, un inventaire, c’est quand on n’écrit pas etc.&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;Georges Perec, Penser/Classer&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#tldr&quot; id=&quot;markdown-toc-tldr&quot;&gt;TL;DR&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#description-du-jeu&quot; id=&quot;markdown-toc-description-du-jeu&quot;&gt;Description du jeu&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#modélisation&quot; id=&quot;markdown-toc-modélisation&quot;&gt;Modélisation&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#défaite-en-n-coups&quot; id=&quot;markdown-toc-défaite-en-n-coups&quot;&gt;Défaite en N coups&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#victoire-en-n-coups&quot; id=&quot;markdown-toc-victoire-en-n-coups&quot;&gt;Victoire en N coups&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#cas-général&quot; id=&quot;markdown-toc-cas-général&quot;&gt;Cas général&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#simulation-de-monte-carlo&quot; id=&quot;markdown-toc-simulation-de-monte-carlo&quot;&gt;Simulation de Monte-Carlo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;Une simulation de Monte-Carlo sur un million de partie montre que les joueurs gagnent dans 63% des cas.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/pie.png&quot; alt=&quot;Statistiques de gain&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;description-du-jeu&quot;&gt;Description du jeu&lt;/h2&gt;

&lt;p&gt;Ce jeu de société, édité par Haba, est un jeu collaboratif où le but est de cueillir tous les fruits du verger avant que le corbeau ne les mange.&lt;/p&gt;

&lt;p&gt;Voici le descriptif donné par le dos de la boîte de jeu :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Dans le verger, les arbres fruitiers croulent sous les fruits. Attention à l’effronté corbeau Théo qui aimerait tous les dérober ! Suivant le symbole du dé, ramassez un fruit et placez-le dans votre panier ou faites avancer Théo sur le chemin vers le verger. Vous gagnerez tous ensemble si vous cueillez tous les fruits dans que Théo n’arrive dans le verge.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Il est indiqué que le jeu est dimensionné pour 1 à 4 joueurs à partir de deux ans et qu’une partie dure environ 10 minutes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/verger1b.png&quot; alt=&quot;Mon Premier Verger&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Le jeu se compose de :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;4 tuiles arbres sur lesquels sont initialement posés 4 fruits en bois (4 pommes rouge sur le premier,  4 pommes vertes sur le deuxième, 4 poires jaune sur le troisième et enfin 4 prunes bleues sur le quatrième)&lt;/li&gt;
  &lt;li&gt;un pion corbeau placé devant une piste de cinq cases&lt;/li&gt;
  &lt;li&gt;un dé&lt;/li&gt;
  &lt;li&gt;un panier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/verger3b.png&quot; alt=&quot;Mon Premier Verger&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Le dé présente les six faces suivantes : Rouge, Vert, Jaune, Bleu, Panier et Corbeau.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/verger4b.png&quot; alt=&quot;Mon Premier Verger&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Chacun à tour de rôle, les joueurs lancent le dé :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;si le résultat est Rouge, Vert, Jaune ou Bleu, le joueur doit retirer un fruit de la couleur du dé et le déposer dans le panier. S’il n’y a plus de fruit dans l’arbre, il ne se passe rien.&lt;/li&gt;
  &lt;li&gt;si le résultat est Panier, le joueur choisit un fruit dans n’importe quel arbre et le dépose dans son panier&lt;/li&gt;
  &lt;li&gt;si le résultat est Corbeau, le corbeau avance d’une case sur sa piste&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si les joueurs arrivent à cueillir tous les fruits avant que le corbeau n’atteigne la dernière case de sa piste, ils gagnent la partie tous ensemble.&lt;/p&gt;

&lt;p&gt;Si le Corbeau arrive sur la dernière case de sa piste avant que tous les fruits n’aient pu être cueillis alors les joueurs perdent la partie tous ensemble.&lt;/p&gt;

&lt;h2 id=&quot;modélisation&quot;&gt;Modélisation&lt;/h2&gt;

&lt;p&gt;Alors déjà, commençons par remarquer que ce jeu de société n’est pas un &lt;strong&gt;jeu normal&lt;/strong&gt; au sens de Raymond Smullyan.&lt;/p&gt;

&lt;p&gt;Dans son livre &lt;em&gt;Satan, Cantor and Infinity&lt;/em&gt;&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, ce dernier considérait en effet comme normal tout jeu à deux joueurs se terminant en un nombre fini de coups. Ce concept fut introduit par le mathématicien William Zwicker pour pouvoir ensuite parler d’&lt;em&gt;hyperjeu&lt;/em&gt;. Les réflexions autour de l’hyperjeu ont pu conduire à une démonstration inédite du théorème de Cantor qui stipule que le cardinal d’un ensemble $E$ est toujours strictement inférieur au cardinal de l’ensemble de ses parties $\mathcal{P}(E)$.&lt;/p&gt;

&lt;p&gt;Dans le cas qui nous intéresse, les joueurs peuvent entrer dans un jeu sans fin où, comble de malchance, le dé tombe systématiquement sur la face bleue. La probabilité associée à cet événement est certes nulle mais la possibilité existe.&lt;/p&gt;

&lt;p&gt;Alors, je tords un peu la définition originale car Raymond Smullyan ne parle aucunement de hasard. Par ailleurs, cela n’a aucune incidence sur la suite de nos développements. Alors je ferme là cette petite parenthèse mais je suis bien content d’avoir parlé de ce logicien dont j’avais dévoré les ouvrages étant lycéen.&lt;/p&gt;

&lt;p&gt;Concernant la modélisation du jeu, on constate déjà que le nombre de joueurs importe peu puisqu’ils ont tous le même objectif et que leur marge de manoeuvre est très faible. Le seul degré de liberté dont ils disposent est de choisir la couleur du fruit lorsque le dé tombe sur la face Panier. La solution la plus rationnelle est alors de dépeupler l’arbre le plus fourni, et de choisir aléatoirement en cas d’égalité.&lt;/p&gt;

&lt;p&gt;Nous supposons le dé parfaitement équilibré. A chaque tour, une des six faces est susceptible de sortir avec une probabilité de $1/6$. Voyons déjà ce que nous pouvons en tirer&lt;/p&gt;

&lt;h3 id=&quot;défaite-en-n-coups&quot;&gt;Défaite en N coups&lt;/h3&gt;

&lt;p&gt;Dans un premier temps, nous ne prenons pas en compte les mécanismes susceptibles de faire gagner les joueurs et nous nous concentrons sur le Corbeau.&lt;/p&gt;

&lt;p&gt;Soit $F(N)$ la probabilité que le Corbeau gagne en exactement $N$ coups.&lt;/p&gt;

&lt;p&gt;Comme il faut que la face Corbeau sorte cinq fois pour gagner, nous avons déjà trivialement $F(1) = F(2) = F(3) = F(4) = 0$.&lt;/p&gt;

&lt;p&gt;Pour gagner en exactement 5 coups, il faut que le Corbeau soit sorti systématiquement lors des 5 premiers coups. Les lancés étant indépendant, nous avons donc :&lt;/p&gt;

\[F(5) = \frac{1}{6^5}\]

&lt;p&gt;De façon générale, la probabilité que le Corbeau gagne en $N$ coups s’écrit :&lt;/p&gt;

\[F(N) = \frac{\Omega_F (N)}{\Omega}\]

&lt;p&gt;Ici $\Omega$ représente l’ensemble des configurations accessibles après $N$ lancés de dé. Nous avons donc $\Omega = 6^N$.&lt;/p&gt;

&lt;p&gt;$\Omega_F (N)$ représente l’ensemble des configurations pour lesquelles le Corbeau gagne en exactement $N$ coups. Un peu de combinatoire nous donne :&lt;/p&gt;

\[\Omega_F (N) = {N-1 \choose 4} \cdot 5^{N-5}\]

&lt;p&gt;Dans cette équation le terme $n \choose k$ représente le coefficient binomial classique, il correspond ici au nombre de façons différentes d’avoir tiré $5-1=4$ Corbeaux lors des $N-1$ tours précédents. C’est trivialement un cas d’application de loi binomiale de paramètres $p=1/6$ et $n = N-1$ avec la condition supplémentaire que le Nème lancé soit exactement un Corbeau.&lt;/p&gt;

&lt;p&gt;La probabilité que le Corbeau gagne en exactement $N$ coups s’écrit finalement :&lt;/p&gt;

\[F(N) = {N-1 \choose 4} \cdot \frac{5^{N-5}}{6^N}\]

&lt;p&gt;En pratique, si nous simulons la mécanique du Corbeau sur un million de parties successives, nous avons une parfaite adéquation entre notre fonction $F(N)$ et l’histrogramme des victoires du Corbeau, ainsi que l’illustre la figure ci-dessous.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/corbeau1.png&quot; alt=&quot;Modélisation du corbeau&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sans cueillette des fruits, nous voyons que le Corbeau peut ainsi espérer gagner en moyenne en environ une trentaine de coups.&lt;/p&gt;

&lt;p&gt;Il s’agit là pour lui d’une solution idyllique : il est quasi-certain (au sens des probabilités) de gagner en un nombre fini de coups. Dans les conditions de jeu réelles, les joueurs vont gagner cetaines parties ce qui va réduire drastiquement l’arbre des possibles pour notre pauvre corbeau. La figure ci-dessous illustre la modification de notre histogramme lorsqu’on introduit la possibilité de gagner pour les joueurs.&lt;/p&gt;

&lt;p&gt;La surface d’histogramme manquante sur la figure de droite correspond exactement aux cas où les joueurs ont gagné.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/corbeau2.png&quot; alt=&quot;Modélisation du corbeau&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Il nous faut donc affiner notre modèle. Mais avant cela, nous allons étudier de la même façon la possibilité de victoire pour les joueurs.&lt;/p&gt;

&lt;h3 id=&quot;victoire-en-n-coups&quot;&gt;Victoire en N coups&lt;/h3&gt;

&lt;p&gt;Ici nous faisons l’hypothèse inverse : le Corbeau ne peut jamais gagner.&lt;/p&gt;

&lt;p&gt;Par soucis de simplification, je désactive également l’effet de la face Panier : lorsqu’on tombe dessus, il ne se passe rien de particulier (mais le tour est tout de même consommé). On peut assimiler cela à une face dont l’effet est simplement “Passez votre tour”.&lt;/p&gt;

&lt;p&gt;Soit $W(N)$ la probabilité que les joueurs gagnent en exactement $N$ coups.&lt;/p&gt;

&lt;p&gt;L’estimation de $W(N)$ est cette fois-ci un peu plus complexe dans le calcul des différents dénombrements. Je note les différents évenements R (Rouge), G (Vert), B (Bleu), Y (Jaune), P (Panier) et C (Corbeau). Comme on l’a dit, les faces P et C sont sans effet.&lt;/p&gt;

&lt;p&gt;Déjà, une partie victorieuse en exactement $N$ coups se terminera nécessairement avec R, G, B ou Y. Supposons que ce soit R. Parmi les N-1 tirages précédents, on sait qu’il y a :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Exactement 3 R&lt;/li&gt;
  &lt;li&gt;Au moins 4 G&lt;/li&gt;
  &lt;li&gt;Au moins 4 B&lt;/li&gt;
  &lt;li&gt;Au moins 4 Y&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;J’introduis le nombre $S(n_r, n_g, n_b, n_y, N)$ qui représente le nombre de façons différentes d’avoir exactement $n_r$ faces R, $n_g$ faces G, $n_b$ faces B et $n_y$ faces Y parmi $N$ tirages successifs. On suppose bien entendu que $(n_r+n_g+n_b+n_y)&amp;lt;=N$. Un peu de combinatoire me donne directement :&lt;/p&gt;

\[S = {N \choose n_r}\cdot{N-n_r \choose n_g}\cdot{N-(n_r+n_g) \choose n_b}\cdot{N-(n_r+n_g+n_b) \choose n_y}\cdot2^{N-(n_r+n_g+n_b+n_y)}\]

&lt;p&gt;Le nombre $\Omega_W (N)$ de façons différentes de gagner en exactement $N$ coups s’écrit alors :&lt;/p&gt;

\[\Omega_W (N) = 4\cdot \sum_{k_g=4}^{N-12} \quad \sum_{k_b=4}^{N-8-k_g} \quad \sum_{k_y=4}^{N-4-(k_g+k_b)} S(3, k_g, k_b, k_y, N-1)\]

&lt;p&gt;Ce qui peut éventuellement se réécrire :&lt;/p&gt;

\[\Omega_W (N) = 4\cdot \sum_{k_g=0}^{N-16} \quad \sum_{k_b=0}^{N-12-k_g} \quad \sum_{k_y=0}^{N-8-(k_g+k_b)} S(3, 4+k_g, 4+k_b, 4+k_y, N-1)\]

&lt;p&gt;La probabilité $W(N)$ s’écrit finalement :&lt;/p&gt;

\[W(N) = \frac{\Omega_W (N)}{\Omega} = \frac{\Omega_W (N)}{6^N}\]

&lt;p&gt;La formule est un peu complexe (d’aucuns diraient &lt;em&gt;imbitable&lt;/em&gt;) mais est en adéquation avec la simulation (comme nous allons le voir). J’aurais aimé obtenir quelque chose de plus simple. Le point délicat qui survient systématiquement concerne le calcul du nombre de permutations. Ainsi, dans le cas particulier d’une suite victorieuse d’exactement 16 coups, nous avons 4 possibilités de base, selon la couleur du 16ème et dernier lancé. Les 15 tirages précédents comportent exactement : 3 R, 4 B, 4G et 4 Y. Combien de suites de 15 tirages contiennent ces quantités exactes de couleurs ? Il faut passer par les permutations et ce nombre est :&lt;/p&gt;

\[\frac{15!}{(4!)^3\cdot3!}\]

&lt;p&gt;La probabilité de victoire en 16 coups est alors :&lt;/p&gt;

\[W(16) = 4\cdot \frac{15!}{(4!)^3\cdot3!\cdot 6^{16}} \approx 2.24\cdot 10^{-5}\]

&lt;p&gt;On obtient exactement le même résultat avec la formule de la triple somme (qui dans ce cas se limite à un simple terme où, nous n’avons pas le choix, $k_g=k_b=k_y=4$).&lt;/p&gt;

&lt;p&gt;Mais si nous considérons maintenant le calcul de $W(17)$, et en supposant (pour simplifier) que le 17eme lancé soit un R, alors nous devons envisager pour les nombres $[n_r, n_g, n_b, n_y]$ des 16 premiers lancés les cas suivants :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;$[3, 4, 4, 4]$ : nous avons obtenu exactement un dé neutre (P ou C)&lt;/li&gt;
  &lt;li&gt;$[3, 5, 4, 4]$ : nous aurons tiré un G de trop et aucun dé neutre&lt;/li&gt;
  &lt;li&gt;$[3, 4, 5, 4]$ : nous aurons tiré un B de trop et aucun dé neutre&lt;/li&gt;
  &lt;li&gt;$[3, 4, 4, 5]$ : nous aurons tiré un Y de trop et aucun dé neutre&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour chacun de ces 4 cas, le dénombrement des permutations possibles sera sensiblement différent, d’où la tronche de l’équation de $W(N)$. Peut-être qu’il y a une astuce (par exemple, compter plutôt le complémentaire, à savoir aucune victoire en exactement $N$ coups; ou encore compter dénombrer différemment). Il est également possible que la formule se simplifie, en se rappelant que :&lt;/p&gt;

\[{n \choose k} = \frac{n!}{k!\cdot(c-k)!}\]

&lt;p&gt;En y regardant de plus près, il est possible que les termes de la sommation se regroupent en utilisant la formule de Pascal :&lt;/p&gt;

\[{n \choose k} +{n \choose k+1} = {n+1 \choose k+1}\]

&lt;p&gt;par exemple, voici à quoi ressemble, sans simplification, la distribution des coefficients $n \choose k$ qui interviennent dans le calcul de $W(N)$ pour différentes valeurs de $N$ (entre 16 et 30).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/win3.gif&quot; alt=&quot;Coefficients binomiaux&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Mais enfin, laissons cela de côté et restons sur cette expression de $W(N)$. Ce nombre représente, je le rappelle car il est facile de s’y perdre lorsqu’on manipule tous ces coefficients, la probabilité de gagner le jeu en exactement $N$ lancés de dé dans l’hypothèse simplificatrice où le corbeau ne peut pas gagner et où la face Panier ne fait rien.&lt;/p&gt;

&lt;p&gt;Si nous comparons notre expression de $W(N)$ avec une simulation de Monte-Carlo de 1 million de parties jouées selon les règles de cette mécanique simplifiée, alors nous obtenons une excellente adéquation (comme pour le cas de $F(N)$) :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/win1.png&quot; alt=&quot;Modélisation de la victoire&quot; /&gt;&lt;/p&gt;

&lt;p&gt;SI nous rajoutons la possibilité au Corbeau de gagner, nous observons de même que précedemment, un &lt;em&gt;dépeuplement&lt;/em&gt; des états gagnants à haute valeur de $N$, comme on l’observe ci-dessous. C’est un comportement prévisible : faire durer une partie gagnante c’est augmenter la probabilité du Corbeau de gagner. Et de la même façon, si le Corbeau prend trop son temps pour gagner, il laisse la main aux joueurs pour cueillir tous les fruits !&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/win2.png&quot; alt=&quot;Modélisation de la victoire&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Plus amusant, si on ajoute la mécanique de la face Panier, alors la distribution se resserre drastiquement ! La encore c’est prévisible : utiliser le Panier, c’est augmenter les chances de succès et donc diminuer les probabilités de succès associées aux longues parties.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/win3.png&quot; alt=&quot;Modélisation de la victoire&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ces informations sont résumées dans le tableau ci-dessous (statistiques estimées sur un million de parties à chaque fois).&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Dé Corbeau&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Dé Panier&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Nombre de coups moyen pour gagner&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Désactivé&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Désactivé&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;37&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Activé&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Désactivé&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;30&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Activé&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Activé&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;22&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;cas-général&quot;&gt;Cas général&lt;/h3&gt;

&lt;p&gt;Pour calculer les probabilités de gain ou de défaite en $N$ coups avec toutes les règles du jeu actives en même temps, il nous faut raisonner différement.&lt;/p&gt;

&lt;p&gt;Par exemple, perdre une partie à l’étape $k$ avec $k &amp;lt; N$, c’est restreindre l’univers $\Omega$ des configurations possibles à envisager pour les parties de $N$ coups. Ainsi nous ne devrions plus normaliser par $6^N$ mais par un nombre plus petit. Pour le dire autrement, le fait qu’on gagne en $N$ coups &lt;em&gt;présuppose&lt;/em&gt; que nous n’avons ni gagné ni perdu pendant les $N-1$ coups précédents.&lt;/p&gt;

&lt;p&gt;La solution pour s’en sortir est, je pense, de faire appel aux probabilités conditionnelles. On aboutirait peut-être à quelque chose qui ressemble à ça :&lt;/p&gt;

\[W(n) = \frac{\Omega_W (N)}{\Omega}\]

\[F(n) = \frac{\Omega_F (N)}{\Omega}\]

&lt;p&gt;Avec :&lt;/p&gt;

\[\Omega = 6^N-\sum_{k=1}^{N-1} (\Omega_W(k) + \Omega_F(k))\cdot 6^{N-k}\]

&lt;p&gt;Mais je ne suis pas trop sûr de ce truc là&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;. Et par ailleurs, le fait de ne pas avoir d’expression simple de $W(N)$ est assez décourageant à ce stade. Peut-être aussi qu’il n’existe pas de formule simple pour capturer la dynamique de ce jeu.&lt;/p&gt;

&lt;p&gt;Par ailleurs, une autre approche possible serait de modéliser le nombre de fruits restant sur un arbre par un automate fini probabiliste. On aurait un truc comme ça :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/auto.png&quot; alt=&quot;Automate fini probabiliste&quot; /&gt;&lt;/p&gt;

&lt;p&gt;La matrice de transition pourrait s’écrire :&lt;/p&gt;

\[M = \begin{pmatrix}
5/6 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 \\
1/6 &amp;amp; 5/6 &amp;amp; 0 &amp;amp; 0 &amp;amp; 0 \\
0 &amp;amp; 1/6 &amp;amp; 5/6 &amp;amp; 0 &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 1/6 &amp;amp; 5/6 &amp;amp; 0 \\
0 &amp;amp; 0 &amp;amp; 0 &amp;amp; 1/6 &amp;amp; 1 \\
\end{pmatrix}\]

&lt;p&gt;Et donc pour connaître la probabilité d’état au bout de $k$ lancés, il suffit de regarder le vecteur $M^k\cdot u_4$ avec $u_4 = (1\, 0\, 0\, 0\, 0)’$ qui représente l’état initial. On obtient alors ce genre de graphiques qui représente l’évolution en fonction du nombre de lancés de dé de la probabilité qu’il reste $n$ fruits sur un arbre donné (avec $0 \leq n \leq 4)$ :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/auto2.png&quot; alt=&quot;Automate fini probabiliste&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Mais bon, je vais en rester là.&lt;/p&gt;

&lt;h2 id=&quot;simulation-de-monte-carlo&quot;&gt;Simulation de Monte-Carlo&lt;/h2&gt;

&lt;p&gt;Il est en revanche beaucoup plus facile d’estimer les chances de succès et d’échecs en laissant l’ordinateur jouer un très grand nombre de parties différentes (un million). C’est ce que je présente ici.&lt;/p&gt;

&lt;p&gt;Pour le modèle de jeu, la seule incertitude (si j’ose dire) concerne la face Panier qui laisse le joueur décider de l’arbre sur lesquel prélever un fruit. Paradoxalement, c’est quand notre action est certaine que cela rend mon programme plus incertain.&lt;/p&gt;

&lt;p&gt;J’ai opté pour la stratégie suivante : si la face Panier sort, alors on prélève un fruit sur l’arbre qui en contient le plus. C’est la stratégie la plus efficace. Mais si plusieurs arbres possèdent exactement ce nombre maximal, alors je prélève selon un ordre arbitraire (toujours le même).&lt;/p&gt;

&lt;p&gt;Au final, comme nous l’avons dit au début, ce qui ressort de ces simulations c’est que &lt;strong&gt;les joueurs gagnent dans 63% des parties&lt;/strong&gt; environ.&lt;/p&gt;

&lt;p&gt;Le Corbeau gagne en moyenne en 18 coups alors que les joueurs gagnent en moyenne en 22 coups, ce qui fait qu’&lt;strong&gt;une partie se déroule en moyenne en 20 coups&lt;/strong&gt;. A raison de 30 secondes par tour, une partie moyenne dure 10 minutes ce qui est conforme aux indications fournies sur la boîte de jeu. Ces trente secondes peuvent paraître excessive mais n’oublions pas d’une part que ce jeu est à partir de 2 ans et que d’autre part on papote et on rigole pendant qu’on joue !&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/montecarlo.png&quot; alt=&quot;Statistiques sur un million de parties&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Plus amusant, on peut aussi modifier légéremment les règles du jeu et voir en quoi cela affecte les chances de succès.&lt;/p&gt;

&lt;p&gt;Les paramètres d’entrées que nous faisons varier :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;le nombre de cases du Corbeau (plus ce nombre est élevé, plus grandes sont nos chances de succès)&lt;/li&gt;
  &lt;li&gt;le nombre de fruits par arbre (plus ce nombre est élevé, plus faibles sont nos chances de succès)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les paramètres de contrôle :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;probabilité empirique de succès&lt;/li&gt;
  &lt;li&gt;durée moyenne d’une partie (en minute)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/img/2024/modifr.png&quot; alt=&quot;Impact d'une modification des règles&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;SMULLYAN, Raymond M. &lt;em&gt;Satan, Cantor and infinity (and other mind-boggling puzzles)&lt;/em&gt;. New York : Knopf, 1992. Plus d’informations sur &lt;a href=&quot;https://www.penguinrandomhouse.com/books/169723/satan-cantor-and-infinity-and-other-mind-bogglin-by-raymond-m-smullyan/&quot;&gt;la page de l’éditeur&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;je me relis et je me rends compte que c’est faux. Dans les parties gagnantes en $N$ coups, il y en a nécessairement certaines qui ont échoués au bout de $k &amp;lt; N$ coups lorsque $N$ est suffisament grand. Pas le choix, il faut vraiment passer par les probabilités conditionnelles. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>DonutMan</name></author><category term="mathematique" /><category term="jeux" /><category term="probabilite" /><summary type="html">J’ai récemment joué quelques partie du jeu de société “Mon Premier Verger” avec mes enfants (Marie 5 ans et Alice 2 ans). Nous avons gagné toutes les parties et je me suis demandé dans quelle mesure nous avons eu de la chance.</summary></entry></feed>