<?xml version='1.0' encoding='UTF-8'?>
<rss version='2.0'>
<channel>
<title>clavier ouvert</title>
<link>https://adfoucart.be/clavier</link>
<description>Blog d'enseignement d'Adrien Foucart. Toutes les opinions présentées ici n'engagent que moi. Blog garanti sans pub, sans traqueurs, et 100% rédigé par un humain.</description>
<language>fr-be</language>

<item>
    <title>2026.05.22 Spirales et chevaliers | 5. finitions et visualisations</title>
    <link>https://adfoucart.be/clavier/2026.05.22.html</link>
    <description>
        <h2><span class='emoji'>♟</span>2026.05.22<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.05.22.html">Spirales
et chevaliers | 5. finitions et visualisations</a></span></h2>
        <p>Nous sommes arrivés, fonctionnellement, au bout de l’exercice
        en produisant cette figure-ci lors de l’<a
        href="./2026.05.19.html">épisode précédent</a>:</p>
        <figure>
        <img src="./img/spiral_12.png"
        alt="500 premières couches, quatre joueurs, cavalier pour tout le monde" />
        <figcaption aria-hidden="true">500 premières couches, quatre
        joueurs, cavalier pour tout le monde</figcaption>
        </figure>
        <p>Restent quelques finitions, à commencer par le fait de
        pouvoir utiliser un fichier JSON pour décrire la figure
        attendue. Ensuite, on pourra essayer de générer des images
        sympa!</p>
        <h2 id="configuration">Configuration</h2>
        <p>On cherche à décrire la configuration dans un fichier avec
        cette structure:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode json"><code class="sourceCode json"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;n_layers&quot;</span><span class="fu">:</span> <span class="dv">20</span><span class="fu">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;players&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;r&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;k&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="ot">]</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
        <p>Créons une <code>dataclass</code> pour la configuration:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Config:</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    n_layers: <span class="bu">int</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    players: <span class="bu">list</span>[Player]</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    <span class="at">@classmethod</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> from_json(cls, path: pathlib.Path):</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>        <span class="cf">with</span> path.<span class="bu">open</span>(<span class="st">&quot;r&quot;</span>, encoding<span class="op">=</span><span class="st">&quot;utf-8&quot;</span>) <span class="im">as</span> fp:</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>            data <span class="op">=</span> json.load(fp)</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">not</span> (<span class="st">&quot;n_layers&quot;</span> <span class="kw">in</span> data <span class="kw">and</span> <span class="st">&quot;players&quot;</span> <span class="kw">in</span> data):</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>            <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;Config: missing n_layers or players&quot;</span>)        </span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>        players <span class="op">=</span> [Player.from_dict(p) <span class="cf">for</span> p <span class="kw">in</span> data[<span class="st">&#39;players&#39;</span>]]</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> cls(<span class="bu">int</span>(data[<span class="st">&#39;n_layers&#39;</span>]), players)</span></code></pre></div>
        <p>En modifiant <code>Player</code> pour pouvoir aussi être créé
        à partir d’un dictionnaire:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># player.py</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Player:</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="at">@classmethod</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> from_dict(cls, data: <span class="bu">dict</span>):</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">not</span> (<span class="st">&quot;color&quot;</span> <span class="kw">in</span> data <span class="kw">and</span> <span class="st">&quot;offsets&quot;</span> <span class="kw">in</span> data):</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>            <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;Player: missing color or offsets&quot;</span>)</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>        </span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> cls(<span class="bu">str</span>(data[<span class="st">&quot;color&quot;</span>]), [(<span class="bu">int</span>(offset[<span class="dv">0</span>]), <span class="bu">int</span>(offset[<span class="dv">1</span>])) <span class="cf">for</span> offset <span class="kw">in</span> data[<span class="st">&#39;offsets&#39;</span>]])</span></code></pre></div>
        <p>Le <code>main</code>, enfin, va lire le fichier JSON passé en
        paramètre de l’appel au programme, ou fourni par l’utilisateur
        via un <code>input</code>:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="bu">len</span>(sys.argv) <span class="op">&gt;=</span> <span class="dv">2</span>:</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>        path <span class="op">=</span> pathlib.Path(sys.argv[<span class="dv">1</span>])</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span>:</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>        path <span class="op">=</span> pathlib.Path(<span class="bu">input</span>(<span class="st">&quot;Path to config file: &quot;</span>))</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    config <span class="op">=</span> Config.from_json(path)</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    board <span class="op">=</span> Board(config.n_layers, config.players)</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>    show_board(board, save_as<span class="op">=</span>pathlib.Path(<span class="bu">str</span>(path).replace(<span class="st">&#39;.json&#39;</span>, <span class="st">&#39;.png&#39;</span>)))</span></code></pre></div>
        <p>Si on lance <code>python spiral.py two_knights.json</code>
        (avec bien sûr la configuration ci-dessus dans
        <code>two_knights.json</code>!), on obtient bien notre
        figure:</p>
        <figure>
        <img src="./img/two_knights.png"
        alt="20 couches, deux cavaliers" />
        <figcaption aria-hidden="true">20 couches, deux
        cavaliers</figcaption>
        </figure>
        <p>Parfait. Voyons ce qu’on peut faire de plus.</p>
        <h2 id="visualisations">Visualisations</h2>
        <p>Prenons notre configuration à quatre cavaliers, et remplaçons
        deux des cavaliers par des “super-cavaliers”, capable d’aller
        deux fois plus loin. Donc, au lieu de (1, 2), ils ont un offset
        de (2, 4), etc:</p>
        <figure>
        <img src="./img/two_knights_two_superknights.png"
        alt="500 couches, deux cavaliers, deux super-cavaliers" />
        <figcaption aria-hidden="true">500 couches, deux cavaliers, deux
        super-cavaliers</figcaption>
        </figure>
        <p>Un cavalier, un super-cavalier, et un super-super-cavalier,
        avec un offset (3, 6):</p>
        <figure>
        <img src="./img/trifecta.png"
        alt="500 couches, un cavalier, un super-cavalier, un super-super-cavalier" />
        <figcaption aria-hidden="true">500 couches, un cavalier, un
        super-cavalier, un super-super-cavalier</figcaption>
        </figure>
        <p>Un cavalier, un super-cavalier, et un “vizir” (offsets en
        “croix” (1, 0), (0, 1), (-1, 0), (0, -1)):</p>
        <figure>
        <img src="./img/k_sk_c.png"
        alt="500 couches, un cavalier, un super-cavalier, un vizir" />
        <figcaption aria-hidden="true">500 couches, un cavalier, un
        super-cavalier, un vizir</figcaption>
        </figure>
        <p>Jusqu’à présent, nous n’avons fait que des pièces
        “symétriques”, mais rien ne nous empêche de définir des pattern
        plus arbitraires pour les déplacements des pièces. Prenons la
        configuration suivante:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode json"><code class="sourceCode json"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;n_layers&quot;</span><span class="fu">:</span> <span class="dv">500</span><span class="fu">,</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;players&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;r&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;k&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;y&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">0</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">0</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">0</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">0</span><span class="ot">,</span> <span class="dv">-3</span><span class="ot">]]</span><span class="fu">}</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    <span class="ot">]</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
        <p>On a une pièce qui va plus loin à droite que à gauche, une
        autre qui va plus loin à gauche que à droite, et une dernière
        qui avance plus loin en vertical qu’en horizontal. Le
        résultat:</p>
        <figure>
        <img src="./img/irregular.png"
        alt="500 couches, trois pièces bizarres" />
        <figcaption aria-hidden="true">500 couches, trois pièces
        bizarres</figcaption>
        </figure>
        <p>Je pense qu’on peut dire que l’expérience est réussie !</p>
        <p>Aller, un dernier pour la route, faisons se batailler les
        sept cavaliers de l’arc-en-ciel sur une spirale à 1000
        couches:</p>
        <div class="sourceCode" id="cb6"><pre
        class="sourceCode json"><code class="sourceCode json"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;n_layers&quot;</span><span class="fu">:</span> <span class="dv">1000</span><span class="fu">,</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;players&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;red&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;orange&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;yellow&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;green&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;cyan&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;blue&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;violet&quot;</span><span class="fu">,</span> <span class="dt">&quot;offsets&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">]]</span><span class="fu">}</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>    <span class="ot">]</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
        <figure>
        <img src="./img/rainbow.png"
        alt="1000 couches, sept cavaliers" />
        <figcaption aria-hidden="true">1000 couches, sept
        cavaliers</figcaption>
        </figure>
        <p>Bon, ça met quelques minutes à tourner et je ne pense pas
        qu’on me demandera de designer le drapeau de la prochaine Pride…
        mais ça marche.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.05.22.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Thu, 21 May 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.05.19 Spirales et chevaliers | 4. plus joli, plus pratique</title>
    <link>https://adfoucart.be/clavier/2026.05.19.html</link>
    <description>
        <h2><span class='emoji'>♟</span>2026.05.19<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.05.19.html">Spirales
et chevaliers | 4. plus joli, plus pratique</a></span></h2>
        <p>Le code est maintenant disponible sur <a
        href="https://codeberg.org/adfoucart/spirals">https://codeberg.org/adfoucart/spirals</a>.</p>
        <p>Nous sommes maintenant capable de <a
        href="./2026.05.18.html">calculer le pattern</a> pour (au moins)
        deux “joueurs” avec cavalier:</p>
        <figure>
        <img src="./img/spiral_8.png"
        alt="1000 premiers points, deux joueurs, cavalier pour les deux" />
        <figcaption aria-hidden="true">1000 premiers points, deux
        joueurs, cavalier pour les deux</figcaption>
        </figure>
        <p>Mais le code commence à devenir un peu brouillon.
        Refactorisons.</p>
        <h2 id="module-vue">Module vue</h2>
        <p>Première chose: prenons les codes de visualisation et donnons
        leur leur propre module, <code>view.py</code>:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># view.py</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Iterable</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> matplotlib <span class="im">import</span> pyplot <span class="im">as</span> plt</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> player <span class="im">import</span> Player</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_points_with_lines(positions: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    i <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> (x1, y1), (x2, y2) <span class="kw">in</span> itertools.pairwise(positions):</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> i <span class="op">&gt;=</span> <span class="dv">10</span>:</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>            <span class="cf">break</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>        plt.plot(x1, y1, <span class="st">&#39;bo&#39;</span>)</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>        plt.plot([x1, x2], [y1, y2], <span class="st">&#39;k-&#39;</span>)</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>        i <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>    plt.show()</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_points(positions: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>, Player]]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y, player <span class="kw">in</span> positions:</span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a>        plt.plot(x, y, <span class="ss">f&#39;</span><span class="sc">{</span>player<span class="sc">.</span>color<span class="sc">}</span><span class="ss">+&#39;</span>)</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>    plt.axis(<span class="st">&#39;equal&#39;</span>)</span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <p>Cela nous permet d’alléger <code>spiral.py</code>, qui a
        terme devrait uniquement se préoccuper de récupérer la
        configuration à afficher, et d’appeler des fonctions des
        différents modules.</p>
        <h2 id="visualisation-du-plateau">Visualisation du plateau</h2>
        <p>Je n’aime pas trop le fait d’envoyer ce générateur à
        <code>show_point</code>. Ni d’ailleurs d’afficher l’image avec
        des <code>plt.plot</code>. Outre le fait que ce n’est pas très
        joli, cela prend beaucoup de temps à générer si on augmente le
        nombre de points. Je préfèrerais pouvoir visualiser le plateau
        comme une image, où chaque position correspond à un pixel, et
        contrôler le nombre de “couches” de la spirale à afficher. Une
        fonction comme:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># view.py</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_board(board: Board, layers: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    ...</span></code></pre></div>
        <p>Pour créer l’image, on peut alors parcourir tous les pixels à
        générer et récupérer le joueur qui se trouve (ou non) à cette
        position. On doit d’abord modifier <code>board.py</code> pour
        implémenter <code>__getitem__</code>, et ainsi pouvoir obtenir
        le joueur d’une position via <code>board[pos]</code>:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># board.py</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Board:</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> <span class="fu">__getitem__</span>(<span class="va">self</span>, position: <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]) <span class="op">-&gt;</span> Player:</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="va">self</span>.values[position]</span></code></pre></div>
        <p>Et la vue:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># view.py</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_board(board: Board, layers: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    image <span class="op">=</span> []</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> y <span class="kw">in</span> <span class="bu">range</span>(<span class="op">-</span>layers, layers<span class="op">+</span><span class="dv">1</span>):</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>        row <span class="op">=</span> []</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> x <span class="kw">in</span> <span class="bu">range</span>(<span class="op">-</span>layers, layers<span class="op">+</span><span class="dv">1</span>):</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>            player <span class="op">=</span> board[(x, y)]</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>            row.append(player)</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>        image.append(row)</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>    plt.imshow(image)</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <p>Le problème, maintenant, est que <code>matplotlib</code> ne
        sait pas traduire des <code>Player</code> en couleur. Je ne peux
        pas non plus juste utiliser <code>player.color</code>: il me
        faut des valeurs numériques pour les pixels. Modifions un peu
        <code>Player</code> pour qu’à chaque joueur soit attribué un
        identifiant unique, qu’on va générer automatiquement à chaque
        fois qu’on instancie un nouveau joueur.</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co"># player.py</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Iterable</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass, field</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>_current_id <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> nextid():</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">global</span> _current_id</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    _current_id <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> _current_id</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Player:</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>    color: <span class="bu">str</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>    offsets: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]]</span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>    _id: <span class="bu">int</span> <span class="op">=</span> field(default_factory<span class="op">=</span>nextid)</span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>    <span class="at">@property</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> <span class="bu">id</span>(<span class="va">self</span>):</span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="va">self</span>._id</span></code></pre></div>
        <p>On peut maintenant utiliser cet <code>id</code> dans
        <code>show_board</code>, en traitant au passage le cas
        <code>None</code> pour mettre la valeur <code>0</code>:</p>
        <div class="sourceCode" id="cb6"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co"># view.py</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_board(board: Board, layers: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    image <span class="op">=</span> []</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> y <span class="kw">in</span> <span class="bu">range</span>(<span class="op">-</span>layers, layers<span class="op">+</span><span class="dv">1</span>):</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>        row <span class="op">=</span> []</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> x <span class="kw">in</span> <span class="bu">range</span>(<span class="op">-</span>layers, layers<span class="op">+</span><span class="dv">1</span>):</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>            player <span class="op">=</span> board[(x, y)]</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> player <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>                row.append(<span class="dv">0</span>)</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>            <span class="cf">else</span>:</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>                row.append(player.<span class="bu">id</span>)</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>        image.append(row)</span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>    plt.imshow(image)</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <h2 id="génération-par-couche">Génération par couche</h2>
        <p>On génère pour l’instant toujours un “nombre de points”. Pour
        s’accorder avec <code>show_board</code>, ce serait beaucoup plus
        pratique de donner un nombre de couche. Et vu la façon dont le
        code est en train de s’organiser, je pense que ça devrait être
        la responsabilité de <code>board</code> de se remplir. Mettons
        le générateur de points de la spirale dans un fichier séparé,
        pour mieux organiser les choses:</p>
        <div class="sourceCode" id="cb7"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co"># generators.py</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Generator</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> itertools</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>LEFT <span class="op">=</span> (<span class="op">-</span><span class="dv">1</span>, <span class="dv">0</span>)</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>RIGHT <span class="op">=</span> (<span class="dv">1</span>, <span class="dv">0</span>)</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>UP <span class="op">=</span> (<span class="dv">0</span>, <span class="dv">1</span>)</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>DOWN <span class="op">=</span> (<span class="dv">0</span>, <span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>SPIRAL_CYCLE <span class="op">=</span> [(RIGHT, <span class="dv">1</span>), (UP, <span class="dv">0</span>), (LEFT, <span class="dv">1</span>), (DOWN, <span class="dv">0</span>)]</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> position_generator() <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>    x, y <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>    dx, dy <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>    length <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>    current_pos_in_segment <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>    cycle <span class="op">=</span> itertools.cycle(SPIRAL_CYCLE)</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> <span class="va">True</span>:</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y </span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> current_pos_in_segment <span class="op">==</span> length:</span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a>            direction, dl <span class="op">=</span> <span class="bu">next</span>(cycle)</span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a>            dx, dy <span class="op">=</span> direction</span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a>            length <span class="op">+=</span> dl</span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a>            current_pos_in_segment <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a>        x <span class="op">+=</span> dx</span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a>        y <span class="op">+=</span> dy</span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a>        current_pos_in_segment <span class="op">+=</span> <span class="dv">1</span></span></code></pre></div>
        <p>Et transférons <code>_next_valid_point_for_player</code> dans
        <code>board</code>, pour pouvoir également ajouter une méthode
        <code>fill</code>. On veut s’arrêter dès que tous les
        générateurs des différents joueurs ont dépassé la couche
        maximale que je voudrai afficher. On va donc retenir la “couche
        actuelle” de chaque joueur dans un dictionnaire, et la mettre à
        jour à chaque fois qu’on place un point:</p>
        <div class="sourceCode" id="cb8"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co"># board.py</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Board:</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> _next_valid_point_for_player(<span class="va">self</span>,</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>                                        points: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]],</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>                                        players: Iterable[Player]) <span class="op">-&gt;</span> <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]:</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> x, y <span class="kw">in</span> points:</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="kw">not</span> <span class="va">self</span>.is_available((x, y)): </span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>                <span class="cf">continue</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="bu">any</span>(<span class="va">self</span>.is_visible_from((x, y), player) <span class="cf">for</span> player <span class="kw">in</span> players):</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>                <span class="cf">continue</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> x, y</span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>        </span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> fill(<span class="va">self</span>, max_layers: <span class="bu">int</span>, players: Iterable[Player]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a>        n_points <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>        generators <span class="op">=</span> [(player, position_generator()) <span class="cf">for</span> player <span class="kw">in</span> players]</span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a>        layer_per_player <span class="op">=</span> {}</span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> player <span class="kw">in</span> players:</span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a>            layer_per_player[player.<span class="bu">id</span>] <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="bu">any</span>(layer <span class="op">&lt;=</span> max_layers <span class="cf">for</span> layer <span class="kw">in</span> layer_per_player.values()):</span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a>            <span class="cf">for</span> player, generator <span class="kw">in</span> generators:</span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a>                <span class="cf">if</span> <span class="bu">len</span>(players) <span class="op">==</span> <span class="dv">1</span>:</span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a>                    x, y <span class="op">=</span> <span class="va">self</span>._next_valid_point_for_player(generator, player)</span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a>                <span class="cf">else</span>:</span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true" tabindex="-1"></a>                    x, y <span class="op">=</span> <span class="va">self</span>._next_valid_point_for_player(generator, [p <span class="cf">for</span> p <span class="kw">in</span> players <span class="cf">if</span> p <span class="op">!=</span> player])</span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true" tabindex="-1"></a>                <span class="va">self</span>.put((x, y), player)</span>
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true" tabindex="-1"></a>                n_points <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true" tabindex="-1"></a>                layer_per_player[player.<span class="bu">id</span>] <span class="op">=</span> <span class="bu">max</span>(<span class="bu">abs</span>(x), <span class="bu">abs</span>(y))</span></code></pre></div>
        <p>Ceci nous permet de considérablement simplifier
        <code>spiral.py</code>:</p>
        <div class="sourceCode" id="cb9"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> board <span class="im">import</span> Board</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> player <span class="im">import</span> Player</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> view <span class="im">import</span> show_points, show_board</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    board <span class="op">=</span> Board()</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>    players <span class="op">=</span> [</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>        Player(color<span class="op">=</span><span class="st">&#39;r&#39;</span>, offsets<span class="op">=</span>[(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)]),</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>        Player(color<span class="op">=</span><span class="st">&#39;k&#39;</span>, offsets<span class="op">=</span>[(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)])</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>    ]</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>    board.fill(<span class="dv">20</span>, players)</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a>    show_board(board, <span class="dv">20</span>)</span></code></pre></div>
        <p>Voyons ce que ça donne:</p>
        <figure>
        <img src="./img/spiral_9.png"
        alt="20 premières couches, deux joueurs, cavalier pour les deux" />
        <figcaption aria-hidden="true">20 premières couches, deux
        joueurs, cavalier pour les deux</figcaption>
        </figure>
        <p>Point positif: on a bien des pixels au lieu des croix, le
        rendu est globalement meilleur. Point négatif: on a perdu les
        couleurs qu’on avait définies dans <code>Player</code>:
        <code>plt.imshow</code> utilise une <code>colormap</code> pour
        choisir les couleurs en fonction de la valeur, et pour l’instant
        on est dans la <code>colormap</code> par défaut,
        <code>viridis</code>.</p>
        <h2 id="les-bonnes-couleurs">Les bonnes couleurs</h2>
        <p>Redéfinir une <code>colormap</code> n’est heureusement pas
        compliqué. Petite modification de <code>view.py</code>:</p>
        <div class="sourceCode" id="cb10"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co"># view.py</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> matplotlib.colors <span class="im">import</span> ListedColormap </span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_board(board: Board, max_layers: <span class="bu">int</span>):</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    cmap <span class="op">=</span> ListedColormap([<span class="st">&#39;w&#39;</span>] <span class="op">+</span> [p.color <span class="cf">for</span> p <span class="kw">in</span> board.players]) </span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>    plt.imshow(image, cmap<span class="op">=</span>cmap) </span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <p>Il faut cependant pour ça que <code>Board</code> connaisse la
        liste des <code>players</code>, ce qui n’est pas encore le cas.
        Au passage, on peut aussi lui faire retenir
        <code>max_layers</code>, ce qui va nous permettre de simplifier
        plusieurs choses. En fait, on peut même initialiser tout ça dans
        le constructeur, et laisser le constructeur appeler
        <code>fill</code>:</p>
        <div class="sourceCode" id="cb11"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co"># board.py</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Board:</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, max_layers: <span class="bu">int</span>, players: Iterable[Player]):</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>.values: <span class="bu">dict</span>[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], Player <span class="op">|</span> <span class="va">None</span>] <span class="op">=</span> defaultdict(<span class="kw">lambda</span>: <span class="va">None</span>)</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>.players <span class="op">=</span> players</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>.max_layers <span class="op">=</span> max_layers</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>._fill()</span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> _fill(<span class="va">self</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>        n_points <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>        generators <span class="op">=</span> [(player, position_generator()) <span class="cf">for</span> player <span class="kw">in</span> <span class="va">self</span>.players]</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a>        layer_per_player <span class="op">=</span> {}</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> player <span class="kw">in</span> <span class="va">self</span>.players:</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>            layer_per_player[player.<span class="bu">id</span>] <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="bu">any</span>(layer <span class="op">&lt;=</span> <span class="va">self</span>.max_layers <span class="cf">for</span> layer <span class="kw">in</span> layer_per_player.values()):</span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a>            <span class="cf">for</span> player, generator <span class="kw">in</span> generators:</span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a>                <span class="cf">if</span> <span class="bu">len</span>(<span class="va">self</span>.players) <span class="op">==</span> <span class="dv">1</span>:</span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a>                    x, y <span class="op">=</span> <span class="va">self</span>._next_valid_point_for_player(generator, player)</span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a>                <span class="cf">else</span>:</span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a>                    x, y <span class="op">=</span> <span class="va">self</span>._next_valid_point_for_player(generator, [p <span class="cf">for</span> p <span class="kw">in</span> <span class="va">self</span>.players <span class="cf">if</span> p <span class="op">!=</span> player])</span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a>                <span class="va">self</span>.put((x, y), player)</span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a>                n_points <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a>                layer_per_player[player.<span class="bu">id</span>] <span class="op">=</span> <span class="bu">max</span>(<span class="bu">abs</span>(x), <span class="bu">abs</span>(y))</span></code></pre></div>
        <p>Le <code>main</code> devient alors:</p>
        <div class="sourceCode" id="cb12"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>    players <span class="op">=</span> [</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>        Player(color<span class="op">=</span><span class="st">&#39;r&#39;</span>, offsets<span class="op">=</span>[(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)]),</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>        Player(color<span class="op">=</span><span class="st">&#39;k&#39;</span>, offsets<span class="op">=</span>[(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)])</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>    ]</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>    board <span class="op">=</span> Board(<span class="dv">20</span>, players)</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    show_board(board)</span></code></pre></div>
        <p>Et <code>show_board</code> peut également utiliser le
        <code>max_layers</code> fourni par <code>board</code>:</p>
        <div class="sourceCode" id="cb13"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co"># view.py</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_board(board: Board) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>    image <span class="op">=</span> []</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> y <span class="kw">in</span> <span class="bu">range</span>(<span class="op">-</span>board.max_layers, board.max_layers<span class="op">+</span><span class="dv">1</span>):</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>        row <span class="op">=</span> []</span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> x <span class="kw">in</span> <span class="bu">range</span>(<span class="op">-</span>board.max_layers, board.max_layers<span class="op">+</span><span class="dv">1</span>):</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>            player <span class="op">=</span> board[(x, y)]</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> player <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>                row.append(<span class="dv">0</span>)</span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>            <span class="cf">else</span>:</span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>                row.append(player.<span class="bu">id</span>)</span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a>        image.append(row)</span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a>    cmap <span class="op">=</span> ListedColormap([<span class="st">&#39;w&#39;</span>] <span class="op">+</span> [p.color <span class="cf">for</span> p <span class="kw">in</span> board.players])</span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a>    plt.imshow(image, cmap<span class="op">=</span>cmap)</span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <p>Ce qui nous donne:</p>
        <figure>
        <img src="./img/spiral_10.png"
        alt="20 premières couches, deux joueurs, cavalier pour les deux, avec les bonnes couleurs" />
        <figcaption aria-hidden="true">20 premières couches, deux
        joueurs, cavalier pour les deux, avec les bonnes
        couleurs</figcaption>
        </figure>
        <p>On peut maintenant tester des configurations différentes. Par
        exemple: quatre joueurs, et afficher les 500 premières
        couches:</p>
        <figure>
        <img src="./img/spiral_11.png"
        alt="500 premières couches, quatre joueurs, cavalier pour tout le monde" />
        <figcaption aria-hidden="true">500 premières couches, quatre
        joueurs, cavalier pour tout le monde</figcaption>
        </figure>
        <p>Ca prend quelques secondes à calculer, mais là on arrive dans
        quelque chose d’intéressant!</p>
        <h2 id="image">Image</h2>
        <p>Dernière petite modification avant de marquer une pause:
        plutôt que d’afficher un <code>plot</code>, on peut sauvegarder
        directement l’image du plateau dans un fichier
        <code>png</code>.</p>
        <div class="sourceCode" id="cb14"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="co"># view.py</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pathlib</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_board(board: Board, save_as: pathlib.Path <span class="op">|</span> <span class="va">None</span> <span class="op">=</span> <span class="va">None</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>    image <span class="op">=</span> compute_image_for_board(board)</span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>    cmap <span class="op">=</span> ListedColormap([<span class="st">&#39;w&#39;</span>] <span class="op">+</span> [p.color <span class="cf">for</span> p <span class="kw">in</span> board.players])</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> save_as <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>        plt.imsave(save_as, image, cmap<span class="op">=</span>cmap)</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a>    plt.imshow(image, cmap<span class="op">=</span>cmap)</span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a>    plt.show()</span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pathlib</span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a>    show_board(board, save_as<span class="op">=</span>pathlib.Path(<span class="st">&quot;result.png&quot;</span>))</span></code></pre></div>
        <p>Ce qui donne un résultat un peu plus propre.</p>
        <figure>
        <img src="./img/spiral_12.png"
        alt="500 premières couches, quatre joueurs, cavalier pour tout le monde" />
        <figcaption aria-hidden="true">500 premières couches, quatre
        joueurs, cavalier pour tout le monde</figcaption>
        </figure>
        <p>On y est presque. Reste à pouvoir définir la configuration
        dans un fichier, peut-être encore un peu de refactorisation pour
        la forme, et puis tester ce que ça donne avec des pièces et
        configurations différentes !</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.05.19.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Mon, 18 May 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.05.18 Spirales et chevaliers | 3. deux joueurs, et plus?</title>
    <link>https://adfoucart.be/clavier/2026.05.18.html</link>
    <description>
        <h2><span class='emoji'>♟</span>2026.05.18<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.05.18.html">Spirales
et chevaliers | 3. deux joueurs, et plus?</a></span></h2>
        <p>Dans la <a href="./2026.05.13.html">première partie</a>, nous
        avons dessiné une spirale carrée. Dans la <a
        href="./2026.05.15.html">seconde</a>, nous avons reproduit le
        motif de la première visualisation montrée <a
        href="https://www.youtube.com/watch?v=UiX4CFIiegM">dans la vidéo
        de Numberphile</a> qui a inspiré ce petit projet. Essayons
        maintenant de généraliser notre code pour arriver aux formes
        plus complexes, avec plusieurs couleurs et déplacements.</p>
        <h2 id="plusieurs-joueurs">Plusieurs joueurs</h2>
        <p>Première chose à faire: que notre générateur de points
        accepte une liste de joueurs. On va essayer d’avancer pas à pas,
        en gardant le code fonctionnel à chaque étape, donc on ne change
        pour l’instant rien au reste du code et on ne traite que le
        premier joueur de la liste:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> valid_points_generator(board: Board, max_points: <span class="bu">int</span>, players: Iterable[Player]) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>, Player], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    n_points <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    player <span class="op">=</span> players[<span class="dv">0</span>]</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    board <span class="op">=</span> Board()</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    player <span class="op">=</span> Player(color<span class="op">=</span><span class="st">&#39;b&#39;</span>, offsets<span class="op">=</span>[(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)])</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    show_points(valid_points_generator(board, <span class="dv">1000</span>, [player]))</span></code></pre></div>
        <p>Détachons ensuite la logique de “trouver le prochain point
        <em>pour un joueur</em>”, pour pouvoir itérer sur la liste des
        joueurs dans le générateur. Chaque joueur devra avoir son
        générateur de prochain point potentiel indépendant: ce n’est pas
        parce qu’on a passé un point et qu’il n’était pas valable pour
        le joueur 1 qu’il ne sera pas valable pour les autres.</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _next_valid_point_for_player(points: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]],</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>                                 board: Board,</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>                                 player: Player) <span class="op">-&gt;</span> <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]:</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y <span class="kw">in</span> points:</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">not</span> board.is_available((x, y)): </span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> board.is_visible_from((x, y), player):</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> x, y</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> valid_points_generator(board: Board, max_points: <span class="bu">int</span>, players: Iterable[Player]) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>, Player], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>    n_points <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>    generators <span class="op">=</span> [(player, position_generator()) <span class="cf">for</span> player <span class="kw">in</span> players]</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> <span class="va">True</span>:</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> player, generator <span class="kw">in</span> generators:</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>            x, y <span class="op">=</span> _next_valid_point_for_player(generator, board, player)</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>            board.put((x, y), player)</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>            n_points <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>            <span class="cf">yield</span> x, y, player</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> n_points <span class="op">&gt;=</span> max_points:</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span></span></code></pre></div>
        <p>Tout fonctionne toujours, mais le
        <code>board.is_visible_from((x, y), player)</code> n’est pas
        juste. Ou plutôt, ce ne sera pas juste dans la version avec
        plusieurs joueurs, puisqu’il faudra vérifier si la case est vue
        par l’un des <em>autres</em> joueurs.</p>
        <p>On doit donc passer une liste de joueurs à
        <code>next_valid...</code>, et vérifier si l’on est visible de
        n’importe lequel d’entre eux:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _next_valid_point_for_player(points: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]],</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>                                 board: Board,</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>                                 players: Iterable[Player]) <span class="op">-&gt;</span> <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]:</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y <span class="kw">in</span> points:</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">not</span> board.is_available((x, y)): </span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="bu">any</span>(board.is_visible_from((x, y), player) <span class="cf">for</span> player <span class="kw">in</span> players):</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> x, y</span></code></pre></div>
        <p>Et dans le générateur, on distinguera le cas un ou plusieurs
        joueurs. Dans le premier cas, le point est valide s’il n’est pas
        visible par les cases déjà coloriée par le joueur lui-même. Dans
        le second, s’il n’est pas visible par les cases coloriées par
        l’un des autres.</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> valid_points_generator(board: Board, max_points: <span class="bu">int</span>, players: Iterable[Player]) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>, Player], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    n_points <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    generators <span class="op">=</span> [(player, position_generator()) <span class="cf">for</span> player <span class="kw">in</span> players]</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> <span class="va">True</span>:</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> player, generator <span class="kw">in</span> generators:</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="bu">len</span>(players) <span class="op">==</span> <span class="dv">1</span>:</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>                x, y <span class="op">=</span> _next_valid_point_for_player(generator, board, player)</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>            <span class="cf">else</span>:</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>                x, y <span class="op">=</span> _next_valid_point_for_player(generator, board, [p <span class="cf">for</span> p <span class="kw">in</span> players <span class="cf">if</span> p <span class="op">!=</span> player])</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>            board.put((x, y), player)</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>            n_points <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>            <span class="cf">yield</span> x, y, player</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> n_points <span class="op">&gt;=</span> max_points:</span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span></span></code></pre></div>
        <p>Mettons maintenant deux joueurs:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    board <span class="op">=</span> Board()</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    players <span class="op">=</span> [</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>        Player(color<span class="op">=</span><span class="st">&#39;r&#39;</span>, offsets<span class="op">=</span>[(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)]),</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>        Player(color<span class="op">=</span><span class="st">&#39;k&#39;</span>, offsets<span class="op">=</span>[(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)])</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    ]</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    show_points(valid_points_generator(board, <span class="dv">1000</span>, players))</span></code></pre></div>
        <p>Et on obtient:</p>
        <figure>
        <img src="./img/spiral_8.png"
        alt="1000 premiers points, deux joueurs, cavalier pour les deux" />
        <figcaption aria-hidden="true">1000 premiers points, deux
        joueurs, cavalier pour les deux</figcaption>
        </figure>
        <p>Ce qui, mis à part une inversion des couleurs, me semble bien
        correspondre à l’exemple montré dans la vidéo de Numberphile et
        repris sur le site de l’<a
        href="https://oeis.org/A392178/a392178.png">OEIS</a>.</p>
        <p>Un solide point d’arrêt pour cet article, même s’il reste du
        boulot.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.05.18.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Sun, 17 May 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.05.15 Spirales et chevaliers | 2. plateau et joueur</title>
    <link>https://adfoucart.be/clavier/2026.05.15.html</link>
    <description>
        <h2><span class='emoji'>♟</span>2026.05.15<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.05.15.html">Spirales
et chevaliers | 2. plateau et joueur</a></span></h2>
        <p>Nous sommes capables de <a href="./2026.05.13.html">dessiner
        une spirale carrée</a>. C’est sympa, mais ce n’est pas vraiment
        ce dont on a besoin.</p>
        <p><img src="./img/spiral_5.png" /></p>
        <h2 id="que-faire">Que faire ?</h2>
        <p>On doit pouvoir répondre à la question: “quelle est la
        prochaine case que nous pouvons colorier”. Pour rappel, en
        commençant avec la version “simple” du problème, avec un seul
        joueur:</p>
        <ul>
        <li>La case ne doit pas encore être coloriée.</li>
        <li>La case ne doit pas se trouver à un déplacement de cavalier
        d’échec d’une case coloriée.</li>
        </ul>
        <p>Notre générateur de positions sera (peut-être) utile pour la
        visualisation, mais il n’est certainement pas suffisant pour
        répondre à la question. On doit pouvoir, déjà, retenir si une
        case est coloriée. Vu qu’on veut pouvoir traiter une grille
        “infinie”, le plus pratique sera sans doute d’utiliser un
        dictionnaire avec comme clé la position (ou l’index de la
        spirale?) et comme valeur un identifiant de joueur. Ou une
        couleur directement. On verra ce qui est le plus utile.</p>
        <p>Réfléchissons par l’exemple. Si je me place au début d’un
        tour quelconque, que dois-je faire pour trouver la prochaine
        case à colorier ?</p>
        <ul>
        <li>Parcourir toutes les cases (ça, je peux le faire avec mon
        générateur).</li>
        <li>Si la case est coloriée, je passe.</li>
        <li>Vérifier toutes les cases à un déplacement de cavalier: si
        l’une d’entre elle est coloriée, je passe.</li>
        <li>Si j’arrive ici: je colorie.</li>
        </ul>
        <p>Pour pouvoir facilement vérifier les cases “à un déplacement
        de cavalier”, ce sera clairement plus simple en utilisant la
        position comme clé. Faisons donc cela.</p>
        <h2 id="petite-modification-préalable">Petite modification
        préalable</h2>
        <p>Notre générateur reçoit un nombre maximal de points. Mais je
        ne pense pas que ce sera au générateur de déterminer quand il
        doit s’arrêter. Je préfèrerais que ce soit décidé ailleurs. On
        va donc en faire un générateur infini:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> position_generator() <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    x, y <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    dx, dy <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    length <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    current_pos_in_segment <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    cycle <span class="op">=</span> itertools.cycle(SPIRAL_CYCLE)</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> <span class="va">True</span>:</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y </span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> current_pos_in_segment <span class="op">==</span> length:</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>            direction, dl <span class="op">=</span> <span class="bu">next</span>(cycle)</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>            dx, dy <span class="op">=</span> direction</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>            length <span class="op">+=</span> dl</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>            current_pos_in_segment <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>        x <span class="op">+=</span> dx</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>        y <span class="op">+=</span> dy</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>        current_pos_in_segment <span class="op">+=</span> <span class="dv">1</span></span></code></pre></div>
        <p>Pour l’instant, on modifie le code de visualisation pour
        qu’il s’arrête, lui, après 10 points:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_points(positions: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    i <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> (x1, y1), (x2, y2) <span class="kw">in</span> itertools.pairwise(positions):</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> i <span class="op">&gt;=</span> <span class="dv">10</span>:</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>            <span class="cf">break</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>        plt.plot(x1, y1, <span class="st">&#39;bo&#39;</span>)</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>        plt.plot([x1, x2], [y1, y2], <span class="st">&#39;k-&#39;</span>)</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>        i <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <h2 id="plateau-de-jeu">Plateau de jeu</h2>
        <p>Définissons un plateau de jeu comme un dictionnaire liant un
        tuple (la position) à un entier (l’identifiant de son joueur /
        sa couleur), qui vaudra <code>-1</code> si la case n’est pas
        coloriée. On peut utiliser un <code>defaultdict</code> pour se
        faciliter la vie, qui attribuera la valeur <code>-1</code> par
        défaut si la clé n’existe pas.</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="bu">type</span> Board <span class="op">=</span> <span class="bu">dict</span>[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="bu">int</span>]</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co"># ...</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    board : Board <span class="op">=</span> defaultdict(default_factory<span class="op">=</span><span class="kw">lambda</span>: <span class="op">-</span><span class="dv">1</span>)</span></code></pre></div>
        <p>La fonction qui générera les positions à colorier devrait
        prendre une forme comme ceci, je crois:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> valid_points_generator(board: Board, max_points: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    ...</span></code></pre></div>
        <p>Elle devra faire appel au générateur de points. Le prochain
        point est le premier renvoyé par le générateur qui est libre, et
        qui n’est pas à un déplacement de cavalier d’échec d’un point
        occupé. Essayons de traduire ça en code:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> valid_points_generator(board: Board, max_points: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    offsets <span class="op">=</span> [(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)]</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    n_points <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y <span class="kw">in</span> position_generator():</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> board[(x, y)] <span class="op">!=</span> <span class="op">-</span><span class="dv">1</span>: </span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="bu">any</span>(board[(x<span class="op">+</span>dx, y<span class="op">+</span>dy)] <span class="op">!=</span> <span class="op">-</span><span class="dv">1</span> <span class="cf">for</span> dx, dy <span class="kw">in</span> offsets):</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>        board[(x, y)] <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>        n_points <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> n_points <span class="op">&gt;=</span> max_points:</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>            <span class="cf">break</span></span></code></pre></div>
        <p>On peut faire une affichage similaire à ce qu’on faisait
        avant, sauf qu’on ne veut pas relier les points entre eux. En
        fait, renommons notre ancien <code>show_points</code> en
        <code>show_points_with_line</code>, et réécrivons
        <code>show_points</code>:</p>
        <div class="sourceCode" id="cb6"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_points(positions: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y <span class="kw">in</span> positions:</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>        plt.plot(x, y, <span class="st">&#39;b+&#39;</span>)</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    plt.axis(<span class="st">&#39;equal&#39;</span>)</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <p>Le <code>main</code> devient:</p>
        <div class="sourceCode" id="cb7"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    board : Board <span class="op">=</span> defaultdict(<span class="kw">lambda</span>: <span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    show_points(valid_points_generator(board, <span class="dv">100</span>))</span></code></pre></div>
        <p>Ce qui nous donne l’image:</p>
        <figure>
        <img src="./img/spiral_6.png"
        alt="100 premiers points valides" />
        <figcaption aria-hidden="true">100 premiers points
        valides</figcaption>
        </figure>
        <p>Ce qui ressemble bien à ce qu’on est supposé obtenir. Avec
        1000 points:</p>
        <figure>
        <img src="./img/spiral_7.png"
        alt="1000 premiers points valides" />
        <figcaption aria-hidden="true">1000 premiers points
        valides</figcaption>
        </figure>
        <p>Pas mal du tout! Mais un peu de refactorisation est
        nécessaire, si on veut pouvoir étendre par la suite à
        différentes pièces, joueurs, etc.</p>
        <h2 id="plateau-de-jeu-objet">Plateau de jeu: objet</h2>
        <p>La première chose, c’est que je pense que le plateau de jeu
        mériterait d’être un objet. Je crois que cela permettra de
        rendre le code plus lisible. Le rôle du <code>Board</code>, dans
        un premier temps, sera de dire si une position est libre, ou
        visible à partir d’une série de déplacements.</p>
        <div class="sourceCode" id="cb8"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co"># board.py</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> collections <span class="im">import</span> defaultdict</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Iterable</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Board:</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>):</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>.values <span class="op">=</span> defaultdict(<span class="kw">lambda</span>: <span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> is_available(<span class="va">self</span>, position: <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="va">self</span>.values[position] <span class="op">==</span> <span class="op">-</span><span class="dv">1</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> is_visible_from(<span class="va">self</span>, </span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>                        position: <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], </span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>                        offsets: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a>        x, y <span class="op">=</span> position</span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="bu">any</span>(<span class="va">self</span>.values[x<span class="op">+</span>dx, y<span class="op">+</span>dy] <span class="op">!=</span> <span class="op">-</span><span class="dv">1</span> <span class="cf">for</span> dx, dy <span class="kw">in</span> offsets)</span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> put(<span class="va">self</span>, position: <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], value: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>.values[position] <span class="op">=</span> value</span></code></pre></div>
        <p>Et dans le fichier principal — que j’appelle pour l’instant
        <code>spiral.py</code> — il faut retirer la définition du
        <code>type Board</code> et la remplacer par un
        <code>import</code>:</p>
        <div class="sourceCode" id="cb9"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> board <span class="im">import</span> Board</span></code></pre></div>
        <p>On doit aussi changer le <code>main</code>:</p>
        <div class="sourceCode" id="cb10"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>    board <span class="op">=</span> Board()</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>    <span class="co"># ...</span></span></code></pre></div>
        <p>Et le générateur de points doit utiliser les méthodes de
        <code>Board</code>:</p>
        <div class="sourceCode" id="cb11"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> valid_points_generator(board: Board, max_points: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>    offsets <span class="op">=</span> [(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)]</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>    n_points <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y <span class="kw">in</span> position_generator():</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">not</span> board.is_available((x, y)): </span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> board.is_visible_from((x, y), offsets):</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>        board.put((x, y), <span class="dv">1</span>)</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>        n_points <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> n_points <span class="op">&gt;=</span> max_points:</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>            <span class="cf">break</span></span></code></pre></div>
        <h2 id="le-joueur">Le joueur</h2>
        <p>On peut maintenant introduire le “joueur”, qui aura une
        couleur et une liste de déplacements. Il n’aura, en tout cas
        pour l’instant, pas trop d’autre chose à faire: utilisons
        simplement une <code>dataclass</code>:</p>
        <div class="sourceCode" id="cb12"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co"># player.py</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Iterable</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Player:</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>    color: <span class="bu">str</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    offsets: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]]</span></code></pre></div>
        <p>Cela va nous permettre de créer le joueur dans le
        <code>main</code> et de le passer, avec donc sa “configuration”,
        au générateur:</p>
        <div class="sourceCode" id="cb13"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>    board <span class="op">=</span> Board()</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>    player <span class="op">=</span> Player(color<span class="op">=</span><span class="st">&#39;b&#39;</span>, offsets<span class="op">=</span>[(<span class="dv">1</span>, <span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">2</span>), (<span class="dv">2</span>, <span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">2</span>), (<span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>), (<span class="op">-</span><span class="dv">2</span>, <span class="op">-</span><span class="dv">1</span>)])</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>    show_points(valid_points_generator(board, <span class="dv">1000</span>, player))</span></code></pre></div>
        <p>Le générateur va pouvoir transmettre ce joueur au
        <code>Board</code>: c’est maintenant lui qui sera associé à une
        case remplie. Pour l’affichage, on va aussi renvoyer le
        <code>Player</code> en même temps que la position.</p>
        <div class="sourceCode" id="cb14"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> valid_points_generator(board: Board, max_points: <span class="bu">int</span>, player: Player) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>, Player], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>    n_points <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y <span class="kw">in</span> position_generator():</span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">not</span> board.is_available((x, y)): </span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> board.is_visible_from((x, y), player):</span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>        board.put((x, y), player)</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a>        n_points <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y, player</span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> n_points <span class="op">&gt;=</span> max_points:</span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a>            <span class="cf">break</span></span></code></pre></div>
        <p>Le <code>Board</code> doit maintenant utiliser ce joueur. Le
        dictionnaire associera maintenant un <code>tuple</code> à un
        <code>Player</code>, et pour rester cohérent on utilisera
        <code>None</code> comme valeur vide par défaut.
        <code>is_available</code> est changé en conséquence.
        <code>is_visible_from</code> récupère l’<code>offset</code> à
        partir du <code>player</code>, et vérifie spécifiquement si la
        case est visible par ce joueur. Cela nous permettra, je crois,
        d’étendre plus facilement à plusieurs joueurs par la suite.</p>
        <div class="sourceCode" id="cb15"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co"># board.py</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> collections <span class="im">import</span> defaultdict</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Iterable</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> player <span class="im">import</span> Player</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Board:</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>):</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>.values: <span class="bu">dict</span>[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], Player <span class="op">|</span> <span class="va">None</span>] <span class="op">=</span> defaultdict(<span class="kw">lambda</span>: <span class="va">None</span>)</span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> is_available(<span class="va">self</span>, position: <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="va">self</span>.values[position] <span class="kw">is</span> <span class="va">None</span></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> is_visible_from(<span class="va">self</span>, </span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a>                        position: <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], </span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a>                        player: Player) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true" tabindex="-1"></a>        x, y <span class="op">=</span> position</span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="bu">any</span>(<span class="va">self</span>.values[x<span class="op">+</span>dx, y<span class="op">+</span>dy] <span class="op">==</span> player <span class="cf">for</span> dx, dy <span class="kw">in</span> player.offsets)</span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> put(<span class="va">self</span>, position: <span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], player: Player) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>.values[position] <span class="op">=</span> player</span></code></pre></div>
        <p>Et finalement l’affichage utilisera la couleur du joueur:</p>
        <div class="sourceCode" id="cb16"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co"># spiral.py</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_points(positions: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>, Player]]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y, player <span class="kw">in</span> positions:</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>        plt.plot(x, y, <span class="ss">f&#39;</span><span class="sc">{</span>player<span class="sc">.</span>color<span class="sc">}</span><span class="ss">+&#39;</span>)</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>    plt.axis(<span class="st">&#39;equal&#39;</span>)</span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <p>On relance le code, et tout fonctionne toujours. Ca me parait
        un bon endroit pour s’arrêter.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.05.15.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Thu, 14 May 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.05.13 Spirales et chevaliers | 1</title>
    <link>https://adfoucart.be/clavier/2026.05.13.html</link>
    <description>
        <h2><span class='emoji'>♟</span>2026.05.13<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.05.13.html">Spirales
et chevaliers | 1</a></span></h2>
        <p>Après avoir vu <a
        href="https://www.youtube.com/watch?v=UiX4CFIiegM">cette
        fascinante vidéo</a> sur la chaîne Numberphile avec le
        Professeur Neil Sloane, j’ai eu très envie de faire mon propre
        générateur de visualisations, pour pouvoir tester différentes
        règles et configurations. Et parce que le problème a l’air
        intéressant. Voyons où ça nous mène.</p>
        <h2 id="résumé-de-la-vidéo">Résumé de la vidéo</h2>
        <p>Passez directement à la section suivante si vous avez vu la
        vidéo, je vais juste donner un peu plus de contexte avant de
        commencer!</p>
        <p>La vidéo décrit un “problème” (ou plutôt une curiosité)
        découverte par <a href="https://jonka364.github.io/">Jonas
        Karlsson</a>. On démarre avec une grille de taille infinie, dans
        laquelle une case est marquée de l’indice 0. On attribue ensuite
        des indices croissants en formant une spirale carrée. On a donc,
        schématiquement, pour les dix premiers indices:</p>
        <pre><code>......
.432..
.501..
.6789.
......</code></pre>
        <p>On colore ensuite progressivement la grille en suivant la
        règle suivante:</p>
        <p>Sélectionner la case <strong>avec le plus petit
        indice</strong> qui est:</p>
        <ul>
        <li>Libre (non-coloriée).</li>
        <li>Ne peut pas atteindre une case colorée via un déplacement de
        cavalier d’échec.</li>
        </ul>
        <p>Dans le schéma ci-dessus, je pourrai donc colorier 0, 1, 2, 3
        sans soucis, mais pas 4 (à un déplacement de cavalier de 1), 5
        (vu par 2), 6 (vu par 3 et 1), 7 (vu par 2), 8 (vu par 3) ou 9
        (vu par 0 et 2). La case libre suivante sera la case 20.</p>
        <p>Le site de l’OEIS (l’encyclopédie des séquences entières)
        nous donne les premiers éléments de la séquence: <a
        href="https://oeis.org/A308885">https://oeis.org/A308885</a>.</p>
        <p>On y retrouve aussi le schéma montrant la position des cases
        “coloriées”, qui forme un motif régulier:</p>
        <figure>
        <img src="https://oeis.org/A308885/a308885.png"
        alt="Visualisation des 26 premières “couches” de la spirale" />
        <figcaption aria-hidden="true">Visualisation des 26 premières
        “couches” de la spirale</figcaption>
        </figure>
        <p>Des règles plus complexes permettent de générer des “grilles
        colorées” de plus en plus étranges. Par exemple, la séquence
        présentée dans la vidéo utilise deux “joueurs” (rouge et noir)
        qui colorient à tour de rôle, avec la règle: sélectionner la
        case avec le plus petit indice qui est libre et ne peut pas
        atteindre une case coloriée <em>de l’autre couleur</em>. La
        séquence se trouve également sur <a
        href="https://oeis.org/A392177">OEIS</a>, et donne le schéma
        suivant:</p>
        <p><img src="https://oeis.org/A392177/a392177.png" /></p>
        <p>D’autres exemples sont donnés dans la vidéo, avec plus ou
        moins de joueurs et des pièces différentes. Un tas
        d’illustrations pour des combinaisons de pièces différentes se
        trouve <a
        href="https://jonka364.github.io/stendhal/stendhal.html">sur le
        site de Jonas Karlsson</a>.</p>
        <h2 id="objectif">Objectif</h2>
        <p>On retrouve des liens vers <a
        href="https://oeis.org/A392177/a392177_2.py.txt">le code de
        Jonas Karlsson</a> sur le site de l’OEIS, mais je ne vais pas le
        regarder. Je trouve l’exercice intéressant. Mon objectif sera
        donc de faire un programme permettant de générer des
        visualisations pour:</p>
        <ul>
        <li>Un nombre de joueurs arbitraires.</li>
        <li>Des déplacements arbitraires (pas uniquement cavalier).</li>
        <li>Un nombre donné de “couches” de la spirale.</li>
        </ul>
        <p>Idéalement, j’aimerais bien pouvoir partir d’une fichier de
        configuration simple, et générer l’image. Quelque chose comme (à
        raffiner)</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode json"><code class="sourceCode json"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;n_layers&quot;</span><span class="fu">:</span> <span class="dv">10</span><span class="fu">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;players&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;black&quot;</span><span class="fu">,</span> <span class="dt">&quot;moves&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">]]</span><span class="fu">}</span><span class="ot">,</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>        <span class="fu">{</span><span class="dt">&quot;color&quot;</span><span class="fu">:</span> <span class="st">&quot;red&quot;</span><span class="fu">,</span> <span class="dt">&quot;moves&quot;</span><span class="fu">:</span> <span class="ot">[[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-1</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">],</span> <span class="ot">[</span><span class="dv">-2</span><span class="ot">,</span> <span class="dv">-2</span><span class="ot">]]</span><span class="fu">}</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    <span class="ot">]</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
        <p><code>"moves"</code> décrit les mouvements autorisés pour la
        pièce (ici, j’ai mis ceux du cavalier).</p>
        <h2 id="échauffement">Échauffement</h2>
        <p>Je vais essayer de procéder pas à pas, en restant flexible
        sur la façon dont le code va s’agencer. Je suis beaucoup
        influencé pour l’instant par <a
        href="https://ronjeffries.com/">Ron Jeffries</a> et son approche
        extrêmement fluide de la programmation: des petits pas, un code
        qui reste toujours fonctionnel, qu’on modifie très
        progressivement, avec un focus sur un code très clair, très
        expressif, bien maîtrisé. L’approche opposée du <em>vibe
        coding</em>, en somme.</p>
        <p>Première étape: essayons simplement de dessiner la “spirale
        carrée”, pour commencer à comprendre et cerner le problème.</p>
        <p>Mon premier instinct — qui n’est pas nécessairement bon! —
        est de faire un générateur de position, qui produit les
        coordonnées successives des “positions sur la grille” en suivant
        la spirale. Une fonction qui ferait:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Generator</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> position_generator(max_n: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    ...</span></code></pre></div>
        <p>Qu’on pourrait utiliser ensuite dans un <code>main</code>
        pour afficher les points reliés dans l’ordre. Juste pour tester
        le processus, commençons par simplement mettre les points dans
        l’ordre sur une droite. On fera les virages par la suite.</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Generator</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> position_generator(max_n: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    x, y <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> _ <span class="kw">in</span> <span class="bu">range</span>(max_n):</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>        x <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y <span class="kw">in</span> position_generator(<span class="dv">10</span>):</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(x, y)</span></code></pre></div>
        <p>J’obtient bien:</p>
        <pre><code>0 0
1 0
...
9 0</code></pre>
        <p>L’affichage maintenant:</p>
        <div class="sourceCode" id="cb6"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Generator, Iterable</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> matplotlib <span class="im">import</span> pyplot <span class="im">as</span> plt</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_points(positions: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> x, y <span class="kw">in</span> positions:</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>        plt.plot(x, y, <span class="st">&#39;bo&#39;</span>)</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>    plt.show()</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    show_points(position_generator(<span class="dv">10</span>))</span></code></pre></div>
        <p>Ce qui me donne:</p>
        <figure>
        <img src="./img/spiral_1.png"
        alt="Une spirale pas encore très spirale." />
        <figcaption aria-hidden="true">Une spirale pas encore très
        spirale.</figcaption>
        </figure>
        <p>Bien. Tout à l’air de fonctionner, mis à part que cela ne
        fait pas encore ce qu’il faut !</p>
        <p>Comment faire pour faire “tourner” les points ? Si on regarde
        ce que fait la spirale carrée, on suit le processus suivant, à
        partir de (0, 0) et en imaginant que l’axe x va “à droite” et
        l’axe y va “vers le haut”:</p>
        <ul>
        <li>Avancer une fois vers la droite pour arriver en (1, 0)</li>
        <li>Avancer une fois vers le haut pour arriver en (1, 1)</li>
        <li>Avancer deux fois vers la gauche pour arriver en (-1,
        1)</li>
        <li>Avancer deux fois vers le bas pour arriver en (-1, -1)</li>
        <li>Avancer trois fois vers la droite pour arriver en (2,
        -1)</li>
        <li>…</li>
        </ul>
        <p>On a donc deux “cycles”: “droite”, “haut”, “gauche”, “bas”,
        et une augmentation de la longueur du segment tous les deux
        changements de direction.</p>
        <p>C’est une bonne occasion de ressortir <code>itertools</code>,
        cet outil indispensable de la librairie standard Python. Ici, on
        a <a
        href="https://docs.python.org/3/library/itertools.html#itertools.cycle"><code>itertools.cycle</code></a>
        qui nous permet de tourner entre les différentes directions à
        l’infini:</p>
        <div class="sourceCode" id="cb7"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>LEFT <span class="op">=</span> (<span class="op">-</span><span class="dv">1</span>, <span class="dv">0</span>)</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>RIGHT <span class="op">=</span> (<span class="dv">1</span>, <span class="dv">0</span>)</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>UP <span class="op">=</span> (<span class="dv">0</span>, <span class="dv">1</span>)</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>DOWN <span class="op">=</span> (<span class="dv">0</span>, <span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>DIRECTION_CYCLE <span class="op">=</span> [RIGHT, UP, LEFT, DOWN]</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> position_generator(max_n: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>    x, y <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> i, direction <span class="kw">in</span> <span class="bu">enumerate</span>(itertools.cycle(DIRECTION_CYCLE)):</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> i <span class="op">&gt;=</span> max_n:</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>        dx, dy <span class="op">=</span> direction</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>        x <span class="op">+=</span> dx</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>        y <span class="op">+=</span> dy</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>        i <span class="op">+=</span> <span class="dv">1</span></span></code></pre></div>
        <figure>
        <img src="./img/spiral_2.png" alt="Au moins, ça tourne." />
        <figcaption aria-hidden="true">Au moins, ça tourne.</figcaption>
        </figure>
        <p>Maintenant, le “pas”: lorsqu’on passe de “haut” vers “gauche”
        et lorsqu’on passe de “bas” vers “droit”, on doit augmenter la
        longueur du segment. Renommons <code>DIRECTION_CYCLE</code> pour
        y ajouter cette information, et utilisons-là dans la boucle:</p>
        <div class="sourceCode" id="cb8"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>SPIRAL_CYCLE <span class="op">=</span> [(RIGHT, <span class="dv">1</span>), (UP, <span class="dv">0</span>), (LEFT, <span class="dv">1</span>), (DOWN, <span class="dv">0</span>)]</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> position_generator(max_n: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    x, y <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>    length <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> i, (direction, dl) <span class="kw">in</span> <span class="bu">enumerate</span>(itertools.cycle(SPIRAL_CYCLE)):</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> i <span class="op">&gt;=</span> max_n:</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>        length <span class="op">+=</span> dl</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>        dx, dy <span class="op">=</span> direction</span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>        x <span class="op">+=</span> dx<span class="op">*</span>length</span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>        y <span class="op">+=</span> dy<span class="op">*</span>length</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>        i <span class="op">+=</span> <span class="dv">1</span></span></code></pre></div>
        <figure>
        <img src="./img/spiral_3.png"
        alt="Spirale, pas encore terrible." />
        <figcaption aria-hidden="true">Spirale, pas encore
        terrible.</figcaption>
        </figure>
        <p>On a deux problèmes: c’est difficile de voir l’ordre des
        points, et on ne doit normalement pas “sauter” d’un bout du
        segment à l’autre comme ça. Il manque tous les points
        intermédiaires. Commençons par modifier la vue, toujours à
        l’aide de <code>itertools</code>, mais cette fois-ci avec <a
        href="https://docs.python.org/3/library/itertools.html#itertools.pairwise"><code>itertools.pairwise</code></a>
        pour parcourir les positions deux à deux, ce qui va me permettre
        de relier les points successifs.</p>
        <div class="sourceCode" id="cb9"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> show_points(positions: Iterable[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>]]) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    plt.figure()</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> (x1, y1), (x2, y2) <span class="kw">in</span> itertools.pairwise(positions):</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>        plt.plot(x1, y1, <span class="st">&#39;bo&#39;</span>)</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>        plt.plot([x1, x2], [y1, y2], <span class="st">&#39;k-&#39;</span>)</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    plt.show()</span></code></pre></div>
        <figure>
        <img src="./img/spiral_4.png"
        alt="Spirale, et on voit ce qui se passe." />
        <figcaption aria-hidden="true">Spirale, et on voit ce qui se
        passe.</figcaption>
        </figure>
        <p>Pour afficher tous les points, il faut un tout petit peu
        réorganiser <code>position_generator</code>. Au passage, je me
        rend compte que j’avais un <code>i += 1</code> inutile en fin de
        boucle (puisque <code>i</code> était mis à jour par
        <code>enumerate</code>). De toute façon, le compteur bouge de
        place:</p>
        <div class="sourceCode" id="cb10"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> position_generator(max_n: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    x, y <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>    length <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>    n_points <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">yield</span> x, y</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> _, (direction, dl) <span class="kw">in</span> <span class="bu">enumerate</span>(itertools.cycle(SPIRAL_CYCLE)):</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>        length <span class="op">+=</span> dl</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>        dx, dy <span class="op">=</span> direction</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> _ <span class="kw">in</span> <span class="bu">range</span>(length):</span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> n_points <span class="op">&gt;=</span> max_n:</span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>            n_points <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a>            x <span class="op">+=</span> dx</span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a>            y <span class="op">+=</span> dy</span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a>            <span class="cf">yield</span> x, y</span></code></pre></div>
        <figure>
        <img src="./img/spiral_5.png" alt="Spirale correcte!" />
        <figcaption aria-hidden="true">Spirale correcte!</figcaption>
        </figure>
        <p>On y est. Le code n’est pas hyper lisible, cependant,
        essayons de rendre ça un peu plus clair. Je n’aime pas le fait
        d’avoir deux <code>yield</code>, ni les boucles imbriquées.</p>
        <p>Mettons une seule boucle <code>for</code> sur le nombre de
        points, avec une condition pour détecter le changement de
        position:</p>
        <div class="sourceCode" id="cb11"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> position_generator(max_n: <span class="bu">int</span>) <span class="op">-&gt;</span> Generator[<span class="bu">tuple</span>[<span class="bu">int</span>, <span class="bu">int</span>], <span class="va">None</span>, <span class="va">None</span>]:</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>    x, y <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>    dx, dy <span class="op">=</span> <span class="dv">0</span>, <span class="dv">0</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>    length <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>    current_pos_in_segment <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>    cycle <span class="op">=</span> itertools.cycle(SPIRAL_CYCLE)</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> _ <span class="kw">in</span> <span class="bu">range</span>(max_n):</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>        <span class="cf">yield</span> x, y </span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> current_pos_in_segment <span class="op">==</span> length:</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>            direction, dl <span class="op">=</span> <span class="bu">next</span>(cycle)</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a>            dx, dy <span class="op">=</span> direction</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a>            length <span class="op">+=</span> dl</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>            current_pos_in_segment <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a>        x <span class="op">+=</span> dx</span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a>        y <span class="op">+=</span> dy</span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a>        current_pos_in_segment <span class="op">+=</span> <span class="dv">1</span></span></code></pre></div>
        <p>C’est déjà mieux… et on va s’arrêter ici pour l’instant.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.05.13.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Tue, 12 May 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.04.15 C Orienté Objet | 4</title>
    <link>https://adfoucart.be/clavier/2026.04.15.html</link>
    <description>
        <h2><span class='emoji'>🟨</span>2026.04.15<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.04.15.html">C
Orienté Objet | 4</a></span></h2>
        <p>Pour faire une classe “générique” en C, on a besoin de
        macros. Je vais largement me baser sur l’article de Ian Fisher,
        <a
        href="https://iafisher.com/blog/2020/06/type-safe-generics-in-c">Type-safe
        generic data structures in C</a>, qui explique assez bien l’idée
        de base, sur laquelle on va pouvoir construire notre propre
        solution.</p>
        <h2 id="cas-de-base">Cas de base</h2>
        <p>Prenons un programme très simple avec notre structure-bidon
        <code>Object</code>:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdlib.h&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> Object <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> value<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> Object<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">(){</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    Object<span class="op">*</span> o <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>Object<span class="op">));</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>    o<span class="op">-&gt;</span>value <span class="op">=</span> <span class="dv">10</span><span class="op">;</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%d\n</span><span class="st">&quot;</span><span class="op">,</span> o<span class="op">-&gt;</span>value<span class="op">);</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>o<span class="op">);</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Pour rendre <code>Object</code> générique, on voudrait que
        les <code>int</code> puissent être remplacés par ce qu’on veut.
        On va créer une macro sur base de notre
        <code>typedef</code>:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define GEN_OBJECT</span><span class="op">(</span><span class="pp">type</span><span class="op">,</span><span class="pp"> name</span><span class="op">)\</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="kw">typedef</span><span class="pp"> </span><span class="kw">struct</span><span class="pp"> </span><span class="op">{\</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="pp">        type value</span><span class="op">;\</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="op">}</span><span class="pp"> name</span><span class="op">;</span></span></code></pre></div>
        <p>C’est assez dégueulasse à lire, avec les lignes qui doivent
        se terminer par <code>\</code>, mais globalement, les
        <code>type</code> et <code>name</code> qu’on a mis en paramètre
        de notre macro <code>GEN_OBJECT</code> vont être remplacés
        partout par les valeurs qu’on veut, à la compilation. Ce qui
        veut dire que je peux générer à la volée des structures avec les
        types que je veux.</p>
        <p>Par exemple:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdlib.h&gt;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#define GEN_OBJECT</span><span class="op">(</span><span class="pp">type</span><span class="op">,</span><span class="pp"> name</span><span class="op">)\</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="kw">typedef</span><span class="pp"> </span><span class="kw">struct</span><span class="pp"> </span><span class="op">{\</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="pp">        type value</span><span class="op">;\</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="op">}</span><span class="pp"> name</span><span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>GEN_OBJECT<span class="op">(</span><span class="dt">int</span><span class="op">,</span> IntObject<span class="op">);</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>GEN_OBJECT<span class="op">(</span><span class="dt">char</span><span class="op">,</span> CharObject<span class="op">);</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">(){</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>    IntObject<span class="op">*</span> o <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>IntObject<span class="op">));</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>    o<span class="op">-&gt;</span>value <span class="op">=</span> <span class="dv">10</span><span class="op">;</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%d\n</span><span class="st">&quot;</span><span class="op">,</span> o<span class="op">-&gt;</span>value<span class="op">);</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>    CharObject<span class="op">*</span> o2 <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>CharObject<span class="op">));</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>    o2<span class="op">-&gt;</span>value <span class="op">=</span> <span class="ch">&#39;d&#39;</span><span class="op">;</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%c\n</span><span class="st">&quot;</span><span class="op">,</span> o2<span class="op">-&gt;</span>value<span class="op">);</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>o<span class="op">);</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>o2<span class="op">);</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Les:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>GEN_OBJECT<span class="op">(</span><span class="dt">int</span><span class="op">,</span> IntObject<span class="op">);</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>GEN_OBJECT<span class="op">(</span><span class="dt">char</span><span class="op">,</span> CharObject<span class="op">);</span></span></code></pre></div>
        <p>Vont être remplacés par:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> IntObject <span class="op">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> value<span class="op">;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> IntObject<span class="op">;</span></span></code></pre></div>
        <p>Et:</p>
        <div class="sourceCode" id="cb6"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> CharObject <span class="op">{</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">char</span> value<span class="op">;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> CharObject<span class="op">;</span></span></code></pre></div>
        <p>Dans le reste du programme, on peut donc faire “comme si” on
        les avait définit. Pratique.</p>
        <p>Petite subtilité qui rend la lecture du code encore plus
        immonde: si on veut faire une fonction qui inclut le nom de
        notre “variable de macro”, on doit utiliser <code>##</code> pour
        le “coller” avec le reste du nom. Par exemple:</p>
        <div class="sourceCode" id="cb7"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define GEN_OBJECT</span><span class="op">(</span><span class="pp">type</span><span class="op">,</span><span class="pp"> name</span><span class="op">)\</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="kw">typedef</span><span class="pp"> </span><span class="kw">struct</span><span class="pp"> </span><span class="op">{\</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="pp">        type value</span><span class="op">;\</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="op">}</span><span class="pp"> name</span><span class="op">;\</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="op">\</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="pp">    name</span><span class="op">*</span><span class="pp"> name</span><span class="op">##</span><span class="pp">_make</span><span class="op">(</span><span class="pp">type value</span><span class="op">){\</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="pp">        name</span><span class="op">*</span><span class="pp"> o </span><span class="op">=</span><span class="pp"> malloc</span><span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span><span class="pp">name</span><span class="op">));\</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a><span class="pp">        o</span><span class="op">-&gt;</span><span class="pp">value </span><span class="op">=</span><span class="pp"> value</span><span class="op">;\</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="pp">        </span><span class="cf">return</span><span class="pp"> o</span><span class="op">;\</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="op">}</span></span></code></pre></div>
        <p>Me permettrait de faire ensuite:</p>
        <div class="sourceCode" id="cb8"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>IntObject<span class="op">*</span> o <span class="op">=</span> IntObject_make<span class="op">(</span><span class="dv">10</span><span class="op">);</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%d\n</span><span class="st">&quot;</span><span class="op">,</span> o<span class="op">-&gt;</span>value<span class="op">);</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>CharObject<span class="op">*</span> o2 <span class="op">=</span> CharObject_make<span class="op">(</span><span class="ch">&#39;d&#39;</span><span class="op">);</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%c\n</span><span class="st">&quot;</span><span class="op">,</span> o2<span class="op">-&gt;</span>value<span class="op">);</span></span></code></pre></div>
        <h2 id="retour-à-lensemble">Retour à l’ensemble</h2>
        <p>On doit maintenant récupérer tout ce qu’on avait dans notre
        <code>SetObject</code> de la dernière fois, et transformer ce
        <code>Object</code> en un paramètre. Je vais créer deux macros:
        une pour la déclaration de l’interface publique, et une pour
        l’implémentation. Ainsi, je peux garder le fait de cacher
        l’implémentation aux utilisateurs de la classe.</p>
        <p>Je met la déclaration en entier:</p>
        <div class="sourceCode" id="cb9"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define DECL_SET</span><span class="op">(</span><span class="pp">Object</span><span class="op">)\</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="kw">typedef</span><span class="pp"> </span><span class="kw">struct</span><span class="pp"> Set</span><span class="op">##</span><span class="pp">Object Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">;\</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="pp">    Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">*</span><span class="pp"> Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">##</span><span class="pp">_make</span><span class="op">();\</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="dt">void</span><span class="pp"> Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">##</span><span class="pp">_destroy</span><span class="op">(</span><span class="pp">Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">*</span><span class="pp"> set</span><span class="op">);\</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="dt">bool</span><span class="pp"> Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">##</span><span class="pp">_add</span><span class="op">(</span><span class="pp">Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">*</span><span class="pp"> set</span><span class="op">,</span><span class="pp"> Object</span><span class="op">*</span><span class="pp"> o</span><span class="op">);\</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="dt">bool</span><span class="pp"> Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">##</span><span class="pp">_remove</span><span class="op">(</span><span class="pp">Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">*</span><span class="pp"> set</span><span class="op">,</span><span class="pp"> Object</span><span class="op">*</span><span class="pp"> o</span><span class="op">);\</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="dt">bool</span><span class="pp"> Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">##</span><span class="pp">_contains</span><span class="op">(</span><span class="pp">Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">*</span><span class="pp"> set</span><span class="op">,</span><span class="pp"> Object</span><span class="op">*</span><span class="pp"> o</span><span class="op">);\</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="dt">int</span><span class="pp"> Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">##</span><span class="pp">_size</span><span class="op">(</span><span class="pp">Set</span><span class="op">##</span><span class="pp">Object</span><span class="op">*</span><span class="pp"> set</span><span class="op">);</span></span></code></pre></div>
        <p>Et quelques morceaux choisis de l’implémentation. Tout se
        trouve dans un seul fichier, <code>genericSet.h</code>:</p>
        <div class="sourceCode" id="cb10"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define IMPL_SET</span><span class="op">(</span><span class="pp">Object</span><span class="op">)\</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="kw">typedef</span><span class="pp"> </span><span class="kw">struct</span><span class="pp"> Node</span><span class="op">##</span><span class="pp">Object Node</span><span class="op">##</span><span class="pp">Object</span><span class="op">;\</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="kw">struct</span><span class="pp"> Node</span><span class="op">##</span><span class="pp">Object </span><span class="op">{\</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="pp">        Object</span><span class="op">*</span><span class="pp"> obj</span><span class="op">;\</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="pp">        Node</span><span class="op">##</span><span class="pp">Object</span><span class="op">*</span><span class="pp"> next</span><span class="op">;\</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="pp">    </span><span class="op">};\</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a><span class="pp">    \ </span><span class="co">//...</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">struct</span> Set##Object <span class="op">{</span>\</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>        LinkedList##Object<span class="op">**</span> table<span class="op">;</span>\</span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>        <span class="dt">int</span> capacity<span class="op">;</span>\</span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>        <span class="dt">int</span> size<span class="op">;</span>\</span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">};</span>\</span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a>    \ <span class="co">// ...</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a>    Set##Object<span class="op">*</span> Set##Object##_make<span class="op">()</span> <span class="op">{</span>\</span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a>        Set##Object<span class="op">*</span> set <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>Set##Object<span class="op">));</span>\</span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a>        set<span class="op">-&gt;</span>capacity <span class="op">=</span> <span class="dv">16</span><span class="op">;</span>\</span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a>        set<span class="op">-&gt;</span>size <span class="op">=</span> <span class="dv">0</span><span class="op">;</span>\</span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a>        set<span class="op">-&gt;</span>table <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>LinkedList##Object<span class="op">*)*</span>set<span class="op">-&gt;</span>capacity<span class="op">);</span>\</span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i<span class="op">=</span><span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> set<span class="op">-&gt;</span>capacity<span class="op">;</span> i<span class="op">++){</span>\</span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true" tabindex="-1"></a>            set<span class="op">-&gt;</span>table<span class="op">[</span>i<span class="op">]</span> <span class="op">=</span> LinkedList##Object##_make<span class="op">();</span>\</span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span>\</span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true" tabindex="-1"></a>\</span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> set<span class="op">;</span>\</span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>\</span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true" tabindex="-1"></a>    \ <span class="co">// ...</span></span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true" tabindex="-1"></a>    <span class="dt">bool</span> LinkedList##Object##_contains<span class="op">(</span>LinkedList##Object<span class="op">*</span> ll<span class="op">,</span> Object<span class="op">*</span> o<span class="op">){</span>\</span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="op">(</span>ll<span class="op">-&gt;</span>size <span class="op">==</span> <span class="dv">0</span><span class="op">){</span>\</span>
<span id="cb10-28"><a href="#cb10-28" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span>\</span>
<span id="cb10-29"><a href="#cb10-29" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span>\</span>
<span id="cb10-30"><a href="#cb10-30" aria-hidden="true" tabindex="-1"></a>            Node##Object<span class="op">*</span> node <span class="op">=</span> ll<span class="op">-&gt;</span>first<span class="op">;</span>\</span>
<span id="cb10-31"><a href="#cb10-31" aria-hidden="true" tabindex="-1"></a>            <span class="cf">while</span> <span class="op">(</span>node <span class="op">!=</span> NULL<span class="op">){</span>\</span>
<span id="cb10-32"><a href="#cb10-32" aria-hidden="true" tabindex="-1"></a>                <span class="cf">if</span> <span class="op">(</span>Object##_equals<span class="op">(</span>node<span class="op">-&gt;</span>obj<span class="op">,</span> o<span class="op">)){</span>\</span>
<span id="cb10-33"><a href="#cb10-33" aria-hidden="true" tabindex="-1"></a>                    <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span>\</span>
<span id="cb10-34"><a href="#cb10-34" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span>\</span>
<span id="cb10-35"><a href="#cb10-35" aria-hidden="true" tabindex="-1"></a>                node <span class="op">=</span> node<span class="op">-&gt;</span>next<span class="op">;</span>\</span>
<span id="cb10-36"><a href="#cb10-36" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span>\</span>
<span id="cb10-37"><a href="#cb10-37" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span>\</span>
<span id="cb10-38"><a href="#cb10-38" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span>\</span>
<span id="cb10-39"><a href="#cb10-39" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>\</span>
<span id="cb10-40"><a href="#cb10-40" aria-hidden="true" tabindex="-1"></a><span class="co">// ...</span></span></code></pre></div>
        <p>Toute cette complexité n’est pas en vain, cependant, car elle
        me permet maintenant dans <code>set.h</code> de faire:</p>
        <div class="sourceCode" id="cb11"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdbool.h&gt;</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;genericSet.h&quot;</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> Object Object<span class="op">;</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>Object<span class="op">*</span> Object_make<span class="op">(</span><span class="dt">int</span> value<span class="op">);</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> Object_destroy<span class="op">(</span>Object<span class="op">*</span> o<span class="op">);</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> Object_hash<span class="op">(</span>Object<span class="op">*</span> o<span class="op">);</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> Object_equals<span class="op">(</span>Object<span class="op">*</span> o<span class="op">,</span> Object<span class="op">*</span> other<span class="op">);</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>DECL_SET<span class="op">(</span>Object<span class="op">)</span></span></code></pre></div>
        <p>Et dans <code>set.c</code> de faire:</p>
        <div class="sourceCode" id="cb12"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdbool.h&gt;</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdlib.h&gt;</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;set.h&quot;</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="co">// for testing</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Object <span class="op">{</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> value<span class="op">;</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>Object<span class="op">*</span> Object_make<span class="op">(</span><span class="dt">int</span> value<span class="op">){</span> </span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>    Object<span class="op">*</span> o <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>Object<span class="op">));</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>    o<span class="op">-&gt;</span>value <span class="op">=</span> value<span class="op">;</span></span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> o<span class="op">;</span></span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> Object_destroy<span class="op">(</span>Object<span class="op">*</span> o<span class="op">){</span> free<span class="op">(</span>o<span class="op">);</span> <span class="op">}</span></span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> Object_hash<span class="op">(</span>Object<span class="op">*</span> o<span class="op">){</span> <span class="cf">return</span> o<span class="op">-&gt;</span>value<span class="op">;</span> <span class="op">}</span></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> Object_equals<span class="op">(</span>Object<span class="op">*</span> o<span class="op">,</span> Object<span class="op">*</span> other<span class="op">){</span> <span class="cf">return</span> o<span class="op">-&gt;</span>value <span class="op">==</span> other<span class="op">-&gt;</span>value<span class="op">;</span> <span class="op">}</span></span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true" tabindex="-1"></a>IMPL_SET<span class="op">(</span>Object<span class="op">)</span></span></code></pre></div>
        <p>Pour que tous mes tests précédents fonctionnent. Dans mon
        fichier de test, je peux inclure <code>set.h</code> et avoir
        accès à toutes les méthodes de <code>SetObject</code>.</p>
        <h2 id="un-set-de-personnes">Un set de personnes?</h2>
        <p>Voyons si on peut grâce à tout ce travail se faire, enfin, un
        ensemble de <code>Person</code>. J’aurai besoin de créer une
        méthode <code>Person_hash</code> pour que ça fonctionne. Vu que
        <code>Person</code> est composé de chaînes de caractères, je
        vais utiliser une méthode de hachage appropriée. J’ai trouvé <a
        href="https://cp-algorithms.com/string/string-hashing.html">cet
        article</a> qui traduit un article d’un site russe que mon
        navigateur me déconseille d’ouvrir, donc ça doit être de la
        qualité. On nous dit que, pour hacher une chaîne de caractères,
        on doit faire:</p>
        <p><code>s[0] + s[1]*p + s[2]*(p^2) + ... + s[n-1]*(p^{n-1}) % m</code></p>
        <p>Avec une valeur de <code>p</code> conseillée de
        <code>31</code>. OK, essayons. Modulo une valeur arbitraire.
        L’article propose un “nombre premier large”, comme
        <code>10^9 + 9</code>. Why not.</p>
        <p>On a:</p>
        <div class="sourceCode" id="cb13"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">// person.c</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define HASH_P </span><span class="dv">31</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#define HASH_MOD pow</span><span class="op">(</span><span class="dv">10</span><span class="op">,</span><span class="pp"> </span><span class="dv">9</span><span class="op">)+</span><span class="dv">9</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="co">// ...</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> Person_hash<span class="op">(</span>Person<span class="op">*</span> p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> h <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> strlen<span class="op">(</span>p<span class="op">-&gt;</span>first_name<span class="op">);</span> i<span class="op">++){</span></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>        h <span class="op">+=</span> p<span class="op">-&gt;</span>first_name<span class="op">[</span>i<span class="op">]*</span>pow<span class="op">(</span>HASH_P<span class="op">,</span> i<span class="op">);</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> strlen<span class="op">(</span>p<span class="op">-&gt;</span>last_name<span class="op">);</span> i<span class="op">++){</span></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a>        h <span class="op">+=</span> <span class="op">(</span>p<span class="op">-&gt;</span>first_name<span class="op">[</span>i<span class="op">]+</span>strlen<span class="op">(</span>p<span class="op">-&gt;</span>first_name<span class="op">))*</span>pow<span class="op">(</span>HASH_P<span class="op">,</span> i<span class="op">);</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> h <span class="op">%</span> HASH_MOD<span class="op">;</span></span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Rajoutons dans notre fichier de test un test très simple:</p>
        <div class="sourceCode" id="cb14"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> test_person_hash<span class="op">(){</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> p <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;abcd&quot;</span><span class="op">,</span> <span class="st">&quot;efgh&quot;</span><span class="op">);</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> p2 <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;efgh&quot;</span><span class="op">,</span> <span class="st">&quot;abcd&quot;</span><span class="op">);</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> p3 <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;abcd&quot;</span><span class="op">,</span> <span class="st">&quot;efgh&quot;</span><span class="op">);</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">bool</span> result <span class="op">=</span> <span class="op">!(</span>Person_hash<span class="op">(</span>p<span class="op">)==</span>Person_hash<span class="op">(</span>p2<span class="op">))</span> <span class="op">&amp;&amp;</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>                   <span class="op">(</span>Person_hash<span class="op">(</span>p<span class="op">)==</span>Person_hash<span class="op">(</span>p3<span class="op">));</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Je m’attends à des erreurs de typage, je ne me suis pas trop
        préoccupé des castings dans toutes mes opérations. J’en ai une,
        sur le modulo.</p>
        <div class="sourceCode" id="cb15"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ...</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> h <span class="op">%</span> <span class="op">((</span><span class="dt">int</span><span class="op">)</span> HASH_MOD<span class="op">);</span></span></code></pre></div>
        <p>Le test passe. Je ne suis pas sûr que tout est en ordre, mais
        ce sera suffisant pour le moment.</p>
        <p>L’important, c’est que je devrais pouvoir créer un
        <code>personSet.h</code> avec:</p>
        <div class="sourceCode" id="cb16"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdlib.h&gt;</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;set.h&quot;</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;person.h&quot;</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>DECL_SET<span class="op">(</span>Person<span class="op">)</span></span></code></pre></div>
        <p>Et un fichier <code>personSet.c</code> avec:</p>
        <div class="sourceCode" id="cb17"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;personsSet.h&quot;</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>IMPL_SET<span class="op">(</span>Person<span class="op">);</span></span></code></pre></div>
        <p>Et rajouter un test:</p>
        <div class="sourceCode" id="cb18"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;personsSet.h&quot;</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="co">// ...</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> test_person_set<span class="op">(){</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>    SetPerson<span class="op">*</span> set <span class="op">=</span> SetPerson_make<span class="op">();</span></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> p <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;abcd&quot;</span><span class="op">,</span> <span class="st">&quot;efgh&quot;</span><span class="op">);</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> p2 <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;efgh&quot;</span><span class="op">,</span> <span class="st">&quot;abcd&quot;</span><span class="op">);</span></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a>    SetPerson_add<span class="op">(</span>set<span class="op">,</span> p<span class="op">);</span></span>
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a>    <span class="dt">bool</span> result <span class="op">=</span> SetPerson_contains<span class="op">(</span>set<span class="op">,</span> p<span class="op">)</span> <span class="op">&amp;&amp;</span> <span class="op">!</span>SetPerson_contains<span class="op">(</span>set<span class="op">,</span> p2<span class="op">);</span></span>
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a>    SetPerson_destroy<span class="op">(</span>set<span class="op">);</span></span>
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a>    Person_destroy<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a>    Person_destroy<span class="op">(</span>p2<span class="op">);</span></span>
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Et, mis à part le fait que je me suis un peu emmêlé les
        pinceaux dans mes conventions de nommage et qu’il va falloir que
        je change un peu tout ça entre mes <code>SetPerson</code> et mes
        <code>personSet</code>… ça fonctionne!</p>
        <p>Je suis assez impressionné, je dois dire.</p>
        <p>Renommons donc <code>personSet.h</code> et
        <code>personSet.c</code> en <code>setPerson</code>, et on arrive
        à quelque chose de tout à fait fonctionnel. Je m’attendais à
        beaucoup plus devoir me casser la tête. Tant mieux !</p>
        <h2 id="conclusion">Conclusion</h2>
        <p>Je pense que je vais m’arrêter là, pour le moment en tout
        cas, dans cette exploration du C orienté objet. On pourrait
        évidemment aller beaucoup plus loin. On n’a pas ici d’héritage,
        pas de polymorphisme, pas de pleins de choses que les langages
        orientés objets aiment faire. Mais on pourrait y arriver. Après
        tout, Python permet de faire de l’orienté objet, et le langage
        est écrit en C. Il faut bien que ces fonctionnalités viennent de
        quelque part !</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.04.15.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Tue, 14 Apr 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.04.13 C Orienté Objet | 3</title>
    <link>https://adfoucart.be/clavier/2026.04.13.html</link>
    <description>
        <h2><span class='emoji'>🟨</span>2026.04.13<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.04.13.html">C
Orienté Objet | 3</a></span></h2>
        <p>Avec une <a href="./2026.04.01.html"><code>Person</code></a>
        et une <a
        href="./2026.04.05.html"><code>PersonsTable</code></a>, nous
        avons les prémisses d’un programme “orienté objet”. Peut-on
        aller un peu plus loin? Pour ça, je pense qu’il est temps de
        penser à une petite application qui nous permettra de tester les
        limites de ce qu’on peut faire sans devoir se casser
        déraisonnablement la tête.</p>
        <h2 id="idée-un-annuaire">Idée: un annuaire</h2>
        <p>J’imagine un menu:</p>
        <pre><code>=== Choose option ===

[1] Add new contact
[2] Remove contact
[3] List all contacts
[4] List all groups
[5] List contacts in group
[0] Quit</code></pre>
        <p>On doit pouvoir lister, ajouter et supprimer des “contacts”
        pour des personnes, qui auraient un nom, un prénom, un email et
        un numéro de téléphone, et qu’on peut rassembler en groupes qui,
        eux-même, ont un nom unique. La combinaison
        nom-prénom-email-numéro de téléphone doit, elle aussi, être
        unique. Un contact peut être dans plusieurs groupes, ou dans
        aucun.</p>
        <p>On devrait donc avoir une structure de type:</p>
        <pre><code>AddressBook
    - Groups
    - Contacts

Group
    - Contacts

Contact
    - firstName
    - lastName
    - email
    - phoneNumber
    - Groups</code></pre>
        <p>J’ai mis beaucoup de critères d’unicité dans cette
        description, ce qui signifie qu’on aurait bien besoin d’une
        notion d’ensemble. Ca tombe bien, je viens de voir les ensemble
        dans le cours de structures de données que je donne.
        Voyons-voir: est-ce qu’on peut se créer une “classe ensemble”
        générique, à partir de laquelle on va pouvoir faire des
        ensembles de groupes et des ensembles de contacts ?</p>
        <p>Aucune idée, mais ça se tente. Essayons déjà de faire un
        ensemble d’une chose: des objets bidons.</p>
        <h2 id="ensemble-dobjets">Ensemble d’objets</h2>
        <p>Je définis, juste pour avoir quelque chose à mettre, un
        <code>Object</code> contenant une valeur sous forme
        d’<code>int</code>:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">// set.h</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> Object Object<span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">// set.c</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Object <span class="op">{</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> value<span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
        <p>Et, puisqu’on est avec des objets, on a besoin de ses
        méthodes:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">// set.c</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>Object<span class="op">*</span> Object_make<span class="op">(</span><span class="dt">int</span> value<span class="op">){</span> </span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    Object<span class="op">*</span> o <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>Object<span class="op">));</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    o<span class="op">-&gt;</span>value <span class="op">=</span> value<span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> o<span class="op">;</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> Object_destroy<span class="op">(</span>Object<span class="op">*</span> o<span class="op">){</span> free<span class="op">(</span>o<span class="op">);</span> <span class="op">}</span></span></code></pre></div>
        <p>Jusque là, rien de bien intéressant. Pour gérer des
        ensembles, je vais essayer d’implémenter une “table de hachage”.
        Pour chaque objet, on doit pouvoir calculer un “hash code”, et
        avoir une notion d’égalité:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> Object_hash<span class="op">(</span>Object<span class="op">*</span> o<span class="op">){</span> <span class="cf">return</span> o<span class="op">-&gt;</span>value<span class="op">;</span> <span class="op">}</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> Object_equals<span class="op">(</span>Object<span class="op">*</span> o<span class="op">,</span> Object<span class="op">*</span> other<span class="op">){</span> <span class="cf">return</span> o<span class="op">-&gt;</span>value <span class="op">==</span> other<span class="op">-&gt;</span>value<span class="op">;</span> <span class="op">}</span></span></code></pre></div>
        <p>C’est ce qui va permettre d’avoir un accès aux objets en
        <code>O(1)</code> (si tout va bien) dans l’ensemble. Celui-ci
        devra avoir l’interface suivante:</p>
        <div class="sourceCode" id="cb6"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">// set.h</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> SetObject SetObject<span class="op">;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>SetObject<span class="op">*</span> SetObject_make<span class="op">();</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> SetObject_destroy<span class="op">(</span>SetObject<span class="op">*</span> s<span class="op">);</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> SetObject_add<span class="op">(</span>SetObject<span class="op">*</span> s<span class="op">,</span> Object<span class="op">*</span> p<span class="op">);</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> SetObject_remove<span class="op">(</span>SetObject<span class="op">*</span> s<span class="op">,</span> Object<span class="op">*</span> p<span class="op">);</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> SetObject_contains<span class="op">(</span>SetObject<span class="op">*</span> s<span class="op">,</span> Object<span class="op">*</span> p<span class="op">);</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> SetObject_size<span class="op">(</span>SetObject<span class="op">*</span> s<span class="op">);</span></span></code></pre></div>
        <p>On retrouve tout ce qu’on avait dans les tables avant, mais
        maintenant on va l’implémenter avec une table de hachage
        <em>et</em> on va garantir l’unicité.</p>
        <p>L’idée avec la table de hachage est qu’on garde un
        <strong>tableau</strong> dans lequel on met des <strong>listes
        chaînées</strong> contenant les éléments de l’ensemble. Soit un
        objet ayant le <em>hash code</em> <code>N</code>, si le tableau
        a une taille <code>T</code>, on ajoutera l’élément dans la liste
        liée située en <code>table[N%T]</code>. Imaginons 5 éléments
        avec les <em>hash code</em> 1, 3, 6, 4, 7 dans une table de
        taille 6. On aura:</p>
        <pre><code>table[0] -&gt; [6]
table[1] -&gt; [1] -&gt; [7]
table[2] -&gt; []
table[3] -&gt; [3]
table[4] -&gt; [4]
table[5] -&gt; []</code></pre>
        <p>Pour faire une <strong>liste chaînée</strong>, on doit
        introduire une notion de “noeud”, qui contiennent un élément
        ainsi qu’un pointeur vers l’élément suivant. En complétant
        l’exemple précédent, on a:</p>
        <pre><code>table[0] -&gt; N[6, [/]]
table[1] -&gt; N[1, [-&gt; N[7, [/]]]]
table[2] -&gt; [/]
table[3] -&gt; N[3, [/]]
table[4] -&gt; N[4, [/]]
table[5] -&gt; [/]</code></pre>
        <p>Ou <code>[/]</code> représente un pointeur
        <code>NULL</code>.</p>
        <p>En <code>C</code>, on aura donc des noeuds:</p>
        <div class="sourceCode" id="cb9"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> NodeObject NodeObject<span class="op">;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> NodeObject <span class="op">{</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    Object<span class="op">*</span> obj<span class="op">;</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    NodeObject<span class="op">*</span> next<span class="op">;</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
        <p>Et une liste chaînée:</p>
        <div class="sourceCode" id="cb10"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    NodeObject<span class="op">*</span> first<span class="op">;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> size<span class="op">;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> LinkedListObject<span class="op">;</span></span></code></pre></div>
        <p>Qui nous amène à notre ensemble:</p>
        <div class="sourceCode" id="cb11"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> SetObject <span class="op">{</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>    LinkedListObject<span class="op">**</span> table<span class="op">;</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> capacity<span class="op">;</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> size<span class="op">;</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
        <p><code>SetObject</code> aura donc une liste de pointeurs vers
        des <code>LinkedListObject</code>, qui eux-même contiennent un
        pointeur vers leur premier <code>NodeObject</code>, qui contient
        un pointeur vers le suivant, etc.</p>
        <p>Comment créer notre <code>Set</code>? On doit allouer de la
        mémoire pour le <code>SetObject</code>, fixer sa capacité
        initiale, allouer de la place pour les pointeurs vers des
        <code>LinkedListObject</code> et, pour chacun de ces
        <code>LinkedListObject</code>, on doit allouer de l’espace en
        mémoire et initialiser la taille à <code>0</code> et le pointeur
        de tête à <code>NULL</code>.</p>
        <div class="sourceCode" id="cb12"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>LinkedListObject<span class="op">*</span> LinkedListObject_make<span class="op">(){</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>    LinkedListObject<span class="op">*</span> ll <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>LinkedListObject<span class="op">));</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>    ll<span class="op">-&gt;</span>size <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>    ll<span class="op">-&gt;</span>first <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> ll<span class="op">;</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>SetObject<span class="op">*</span> SetObject_make<span class="op">()</span> <span class="op">{</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>    SetObject<span class="op">*</span> set <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>SetObject<span class="op">));</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>    set<span class="op">-&gt;</span>capacity <span class="op">=</span> <span class="dv">16</span><span class="op">;</span></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>    set<span class="op">-&gt;</span>size <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>    set<span class="op">-&gt;</span>table <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>LinkedListObject<span class="op">*)*</span>set<span class="op">-&gt;</span>capacity<span class="op">);</span></span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i<span class="op">=</span><span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> set<span class="op">-&gt;</span>capacity<span class="op">;</span> i<span class="op">++){</span></span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a>        set<span class="op">-&gt;</span>table<span class="op">[</span>i<span class="op">]</span> <span class="op">=</span> LinkedListObject_make<span class="op">();</span></span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> set<span class="op">;</span></span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Il faut aussi prévoir de pouvoir créer des noeuds:</p>
        <div class="sourceCode" id="cb13"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>NodeObject<span class="op">*</span> NodeObject_make<span class="op">(</span>Object<span class="op">*</span> o<span class="op">,</span> NodeObject<span class="op">*</span> next<span class="op">){</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>    NodeObject<span class="op">*</span> node <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>NodeObject<span class="op">));</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>    node<span class="op">-&gt;</span>obj <span class="op">=</span> o<span class="op">;</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>    node<span class="op">-&gt;</span>next <span class="op">=</span> next<span class="op">;</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> node<span class="op">;</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Lorsqu’on détruit un <code>SetObject</code>, il faudra
        détruire les <code>LinkedListObject</code>, qui elles-même
        devront détruire les <code>NodeObject</code>.</p>
        <div class="sourceCode" id="cb14"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> NodeObject_destroy<span class="op">(</span>NodeObject<span class="op">*</span> node<span class="op">){</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>node<span class="op">-&gt;</span>next <span class="op">!=</span> NULL<span class="op">)</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>        NodeObject_destroy<span class="op">(</span>node<span class="op">-&gt;</span>next<span class="op">);</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>node<span class="op">);</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> LinkedListObject_destroy<span class="op">(</span>LinkedListObject<span class="op">*</span> ll<span class="op">){</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>ll<span class="op">-&gt;</span>size <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>        NodeObject_destroy<span class="op">(</span>ll<span class="op">-&gt;</span>first<span class="op">);</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>ll<span class="op">);</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> SetObject_destroy<span class="op">(</span>SetObject<span class="op">*</span> set<span class="op">){</span></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i<span class="op">=</span><span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> set<span class="op">-&gt;</span>capacity<span class="op">;</span> i<span class="op">++)</span></span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a>        LinkedListObject_destroy<span class="op">(</span>set<span class="op">-&gt;</span>table<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>set<span class="op">);</span></span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Pour rajouter un objet, il faut:</p>
        <ul>
        <li>Calculer son hash code</li>
        <li>En déduire la position dans la table</li>
        <li>Vérifier si la liste liée contient déjà l’élément</li>
        <li>Sinon: le rajouter</li>
        </ul>
        <p>On renvoie un booléen à la fin pour savoir si on a pu ajouter
        l’élément ou non.</p>
        <div class="sourceCode" id="cb15"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> LinkedListObject_add<span class="op">(</span>LinkedListObject<span class="op">*</span> ll<span class="op">,</span> Object<span class="op">*</span> o<span class="op">){</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>ll<span class="op">-&gt;</span>size <span class="op">==</span> <span class="dv">0</span><span class="op">){</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>        <span class="co">// Empty list -&gt; add to the front</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>        ll<span class="op">-&gt;</span>first <span class="op">=</span> NodeObject_make<span class="op">(</span>o<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>        ll<span class="op">-&gt;</span>size<span class="op">++;</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a>        <span class="co">// Go through the list. If we haven&#39;t found it at the end, </span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a>        <span class="co">// we can connect it to the last non-null element we found.</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a>        NodeObject<span class="op">*</span> node <span class="op">=</span> ll<span class="op">-&gt;</span>first<span class="op">;</span></span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a>        NodeObject<span class="op">*</span> prev <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="op">(</span>node <span class="op">!=</span> NULL<span class="op">){</span></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="op">(</span>Object_equals<span class="op">(</span>node<span class="op">-&gt;</span>obj<span class="op">,</span> o<span class="op">))</span></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a>            prev <span class="op">=</span> node<span class="op">;</span></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a>            node <span class="op">=</span> node<span class="op">-&gt;</span>next<span class="op">;</span></span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true" tabindex="-1"></a>        <span class="co">// if we&#39;re here, we are at the end of the list and we </span></span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true" tabindex="-1"></a>        <span class="co">// haven&#39;t found the object, so we can add it.</span></span>
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true" tabindex="-1"></a>        prev<span class="op">-&gt;</span>next <span class="op">=</span> NodeObject_make<span class="op">(</span>o<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true" tabindex="-1"></a>        ll<span class="op">-&gt;</span>size<span class="op">++;</span></span>
<span id="cb15-22"><a href="#cb15-22" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb15-23"><a href="#cb15-23" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb15-24"><a href="#cb15-24" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb15-25"><a href="#cb15-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-26"><a href="#cb15-26" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> SetObject_add<span class="op">(</span>SetObject<span class="op">*</span> set<span class="op">,</span> Object<span class="op">*</span> o<span class="op">){</span></span>
<span id="cb15-27"><a href="#cb15-27" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> h <span class="op">=</span> Object_hash<span class="op">(</span>o<span class="op">)</span> <span class="op">%</span> set<span class="op">-&gt;</span>capacity<span class="op">;</span></span>
<span id="cb15-28"><a href="#cb15-28" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>LinkedListObject_add<span class="op">(</span>set<span class="op">-&gt;</span>table<span class="op">[</span>h<span class="op">],</span> o<span class="op">)){</span></span>
<span id="cb15-29"><a href="#cb15-29" aria-hidden="true" tabindex="-1"></a>        set<span class="op">-&gt;</span>size<span class="op">++;</span></span>
<span id="cb15-30"><a href="#cb15-30" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb15-31"><a href="#cb15-31" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb15-32"><a href="#cb15-32" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb15-33"><a href="#cb15-33" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Le retrait se fait de manière très semblable. S’il n’y a
        rien, on n’a rien à retirer. Sinon, on cherche l’objet. Pour le
        retirer, on connecte le noeud précédent l’objet au noeud suivant
        l’objet. On peut aussi retirer le <code>NodeObject</code> de la
        mémoire. On n’appelle pas <code>NodeObject_destroy</code> car
        celui-ci est prévu pour aussi aller détruire tout ce qui suit de
        manière récursive, ce qu’on ne veut pas voir ici comme
        comportement. Si on n’a pas trouvé l’objet à la fin, c’est qu’on
        ne peut pas le retirer.</p>
        <div class="sourceCode" id="cb16"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> LinkedListObject_remove<span class="op">(</span>LinkedListObject<span class="op">*</span> ll<span class="op">,</span> Object<span class="op">*</span> o<span class="op">){</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>ll<span class="op">-&gt;</span>size <span class="op">==</span> <span class="dv">0</span><span class="op">){</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>        NodeObject<span class="op">*</span> node <span class="op">=</span> ll<span class="op">-&gt;</span>first<span class="op">;</span></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>        NodeObject<span class="op">*</span> prev <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="op">(</span>node <span class="op">!=</span> NULL<span class="op">){</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="op">(</span>Object_equals<span class="op">(</span>node<span class="op">-&gt;</span>obj<span class="op">,</span> o<span class="op">)){</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>                <span class="cf">if</span> <span class="op">(</span>prev <span class="op">==</span> NULL<span class="op">)</span></span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a>                    ll<span class="op">-&gt;</span>first <span class="op">=</span> node<span class="op">-&gt;</span>next<span class="op">;</span></span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a>                <span class="cf">else</span></span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a>                    prev<span class="op">-&gt;</span>next <span class="op">=</span> node<span class="op">-&gt;</span>next<span class="op">;</span></span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a>                free<span class="op">(</span>node<span class="op">);</span></span>
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a>                ll<span class="op">-&gt;</span>size<span class="op">--;</span></span>
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb16-17"><a href="#cb16-17" aria-hidden="true" tabindex="-1"></a>            prev <span class="op">=</span> node<span class="op">;</span></span>
<span id="cb16-18"><a href="#cb16-18" aria-hidden="true" tabindex="-1"></a>            node <span class="op">=</span> node<span class="op">-&gt;</span>next<span class="op">;</span></span>
<span id="cb16-19"><a href="#cb16-19" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb16-20"><a href="#cb16-20" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb16-21"><a href="#cb16-21" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb16-22"><a href="#cb16-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb16-23"><a href="#cb16-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-24"><a href="#cb16-24" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> SetObject_remove<span class="op">(</span>SetObject<span class="op">*</span> set<span class="op">,</span> Object<span class="op">*</span> o<span class="op">){</span></span>
<span id="cb16-25"><a href="#cb16-25" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> h <span class="op">=</span> Object_hash<span class="op">(</span>o<span class="op">)</span> <span class="op">%</span> set<span class="op">-&gt;</span>capacity<span class="op">;</span></span>
<span id="cb16-26"><a href="#cb16-26" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>LinkedListObject_remove<span class="op">(</span>set<span class="op">-&gt;</span>table<span class="op">[</span>h<span class="op">],</span> o<span class="op">)){</span></span>
<span id="cb16-27"><a href="#cb16-27" aria-hidden="true" tabindex="-1"></a>        set<span class="op">-&gt;</span>size<span class="op">--;</span></span>
<span id="cb16-28"><a href="#cb16-28" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb16-29"><a href="#cb16-29" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb16-30"><a href="#cb16-30" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb16-31"><a href="#cb16-31" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Pour savoir si l’ensemble contient un objet, on reste dans la
        même logique:</p>
        <div class="sourceCode" id="cb17"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> LinkedListObject_contains<span class="op">(</span>LinkedListObject<span class="op">*</span> ll<span class="op">,</span> Object<span class="op">*</span> o<span class="op">){</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>ll<span class="op">-&gt;</span>size <span class="op">==</span> <span class="dv">0</span><span class="op">){</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>        NodeObject<span class="op">*</span> node <span class="op">=</span> ll<span class="op">-&gt;</span>first<span class="op">;</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> <span class="op">(</span>node <span class="op">!=</span> NULL<span class="op">){</span></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="op">(</span>Object_equals<span class="op">(</span>node<span class="op">-&gt;</span>obj<span class="op">,</span> o<span class="op">)){</span></span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a>            node <span class="op">=</span> node<span class="op">-&gt;</span>next<span class="op">;</span></span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> SetObject_contains<span class="op">(</span>SetObject<span class="op">*</span> set<span class="op">,</span> Object<span class="op">*</span> o<span class="op">){</span></span>
<span id="cb17-17"><a href="#cb17-17" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> h <span class="op">=</span> Object_hash<span class="op">(</span>o<span class="op">)</span> <span class="op">%</span> set<span class="op">-&gt;</span>capacity<span class="op">;</span></span>
<span id="cb17-18"><a href="#cb17-18" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> LinkedListObject_contains<span class="op">(</span>set<span class="op">-&gt;</span>table<span class="op">[</span>h<span class="op">],</span> o<span class="op">);</span></span>
<span id="cb17-19"><a href="#cb17-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Et renvoyer la taille ne pose pas trop de difficultés:</p>
        <div class="sourceCode" id="cb18"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> SetObject_size<span class="op">(</span>SetObject<span class="op">*</span> set<span class="op">){</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> set<span class="op">-&gt;</span>size<span class="op">;</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <h2 id="conclusion">Conclusion</h2>
        <p>Nous avons maintenant une notion d’un “ensemble” pour un
        objet “bidon”, <code>Object</code>. A priori, adapter cet
        ensemble à n’importe lequel de nos objets (comme
        <code>Person</code>, ou le futur <code>Group</code>) devrait
        être raisonnablement simple. Il faut:</p>
        <ul>
        <li>Modifier toutes les références à <code>Object</code>.</li>
        <li>S’assurer que l’on dispose de méthodes <code>_hash</code> et
        <code>_equals</code> pour l’objet que l’on désire mettre dans un
        ensemble.</li>
        </ul>
        <p>Et si on veut éviter les copier-coller foireux… on va devoir
        s’intéresser aux macros. Je n’en ai jamais fait: il va me
        falloir un peu de lecture d’abord.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.04.13.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Sun, 12 Apr 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.04.03 C Orienté Objet | 2</title>
    <link>https://adfoucart.be/clavier/2026.04.03.html</link>
    <description>
        <h2><span class='emoji'>🟨</span>2026.04.03<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.04.03.html">C
Orienté Objet | 2</a></span></h2>
        <p>Nous avons lors du <a href="./2026.04.01.html">premier
        épisode</a> créé une “classe” <code>Person</code>, mais elle se
        sent bien seule. Les objets sont fait pour communiquer, avoir
        des relations, s’envoyer des messages. Pour l’instant, nous
        n’avons rien de tout ça. Commençons.</p>
        <h2 id="une-table-de-personnes">Une table de personnes</h2>
        <p>Je voudrais avoir un tableau de personnes, avec la
        possibilité d’y ajouter et d’en retirer des éléments, de
        supprimer ou récupérer des éléments par leur index, de savoir si
        la table contient une certaine personne, et d’en afficher le
        contenu. On a du boulot. Commençons par écrire
        <code>personsTable.h</code> pour définir tout ça proprement:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdbool.h&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;person.h&quot;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> PersonsTable PersonsTable<span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>PersonsTable<span class="op">*</span> PersonsTable_make<span class="op">();</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> PersonsTable_destroy<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">);</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> PersonsTable_add_person<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">,</span> Person<span class="op">*</span> p<span class="op">);</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> PersonsTable_remove<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">,</span> <span class="dt">int</span> idx<span class="op">);</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>Person<span class="op">*</span> PersonsTable_get<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">,</span> <span class="dt">int</span> idx<span class="op">);</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> PersonsTable_contains<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">,</span> Person<span class="op">*</span> p<span class="op">);</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> PersonsTable_print<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">);</span></span></code></pre></div>
        <p>Il y a de nombreuses façons d’implémenter ces
        fonctionnalités. L’idée est donc ici que les
        <em>utilisateurs</em> de ma “classe” ne devront pas s’en
        préoccuper. Et si je veux changer, ça ne devrait pas les
        affecter.</p>
        <p>En particulier, j’ai deux grands choix à faire ici:</p>
        <ul>
        <li>Quelle structure de donnée utiliser pour la “table”?</li>
        <li>Quelle est la définition de l’<em>égalité</em> pour mes
        Personnes (c’est-à-dire: comment je détermine si ma table
        contient une certaine Personne).</li>
        </ul>
        <p>Cette seconde question va me faire évoluer la classe
        <code>Person</code>. Commençons par là.</p>
        <h2 id="égalité-des-personnes">Égalité des personnes</h2>
        <p>Je dois rajouter une méthode dans <code>person.h</code>:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> Person_equals<span class="op">(</span>Person<span class="op">*</span> p<span class="op">,</span> Person<span class="op">*</span> other<span class="op">);</span></span></code></pre></div>
        <p>Et évidemment l’implémenter dans <code>person.c</code>. Je
        peux utiliser <a
        href="https://en.cppreference.com/w/c/string/byte/strcmp"><code>strcmp</code></a>
        pour comparer des chaînes de caractères. Je prendrai donc la
        définition de l’égalité ici comme: “a le même nom et le même
        prénom”. J’aurais pu à la place choisir de vérifier si l’adresse
        des deux objets en mémoire était la même, mais ce serait fort
        limitant. Évidemment, dans un vrai système, j’aurais besoin de
        quelque chose de plus “identifiant” que le nom et le prénom,
        mais on va rester simple pour l’instant. <code>strcmp</code>
        compare deux chaînes de caractères (“null-terminated”,
        c’est-à-dire qu’il y a un caractère nul <code>\0</code> pour
        marquer la fin), et renvoie <code>0</code> si tous les
        caractères sont les mêmes.</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> Person_equals<span class="op">(</span>Person<span class="op">*</span> p<span class="op">,</span> Person<span class="op">*</span> other<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="op">(</span>strcmp<span class="op">(</span>p<span class="op">-&gt;</span>first_name<span class="op">,</span> other<span class="op">-&gt;</span>first_name<span class="op">)==</span><span class="dv">0</span> <span class="op">&amp;&amp;</span> </span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>            strcmp<span class="op">(</span>p<span class="op">-&gt;</span>last_name<span class="op">,</span> other<span class="op">-&gt;</span>last_name<span class="op">)==</span><span class="dv">0</span><span class="op">);</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Testons dans le <code>main</code>:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>Person<span class="op">*</span> p <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;Adrien&quot;</span><span class="op">,</span> <span class="st">&quot;Foucart&quot;</span><span class="op">);</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>Person<span class="op">*</span> other <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;Adrien&quot;</span><span class="op">,</span> <span class="st">&quot;Foucart&quot;</span><span class="op">);</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>Person_print<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>Person_print<span class="op">(</span>other<span class="op">);</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>Person_equals<span class="op">(</span>p<span class="op">,</span> other<span class="op">)){</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;Persons are equal.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span> <span class="op">{</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;Persons are not equal.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>Person_destroy<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>Person_destroy<span class="op">(</span>other<span class="op">);</span></span></code></pre></div>
        <p>Je reçois bien <code>Persons are equal</code>. Et si je
        change une lettre à mon nom, j’ai bien <code>unequal</code>.
        Tout va bien! Revenons à notre table.</p>
        <h2 id="tableau-redimensionnable">Tableau redimensionnable</h2>
        <p>Une manière classique de réaliser une table est le principe
        du “tableau redimensionnable”. Il implique de garder en
        parallèle une “taille physique” (espace mémoire occupé par la
        table) et une “taille logique” (nombre d’éléments dans la
        table). Lorsque la taille logique rattrape la taille physique,
        on agrandit la table (généralement en la doublant). On traduit
        ça dans notre <code>struct PersonsTable</code>:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">// personsTable.c</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> PersonsTable <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">**</span> table<span class="op">;</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> capacity<span class="op">;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> size<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
        <p>Le constructeur devra donc donner une capacité initiale à la
        table, et une taille logique (<code>size</code>) de 0. Notons le
        “double pointeur” pour <code>table</code>. Schématiquement,
        j’ai:</p>
        <pre><code>[table] --&gt; Addr: 0x1234 (exemple)
[0x1234] --&gt; [Person a]
[0x1235] --&gt; [Person b]
[0x1236] --&gt; [Person c]</code></pre>
        <p><code>table</code> pointe vers l’adresse du premier élément
        du tableau, qui se trouve quelque part dans la mémoire du
        programme. Ce premier élément est lui-même un pointeur vers une
        <code>Person</code>. L’élément suivant dans la mémoire sera le
        pointeur vers la personne suivante, etc. Au niveau du
        constructeur, j’aurai alors:</p>
        <div class="sourceCode" id="cb7"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>PersonsTable<span class="op">*</span> PersonsTable_make<span class="op">(){</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    PersonsTable<span class="op">*</span> pt <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>PersonsTable<span class="op">));</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    pt<span class="op">-&gt;</span>size <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    pt<span class="op">-&gt;</span>capacity <span class="op">=</span> <span class="dv">16</span><span class="op">;</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>    pt<span class="op">-&gt;</span>table <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>Person<span class="op">*)*</span>pt<span class="op">-&gt;</span>capacity<span class="op">);</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> pt<span class="op">;</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>J’ai deux <a
        href="https://en.cppreference.com/w/c/memory/malloc"><code>malloc</code></a>
        dans le constructeur, j’aurai donc besoin de deux <a
        href="https://en.cppreference.com/w/c/memory/free.html"><code>free</code></a>
        dans le destructeur:</p>
        <div class="sourceCode" id="cb8"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> PersonsTable_destroy<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>t<span class="op">-&gt;</span>table<span class="op">);</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>t<span class="op">);</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Notons un choix fait ici: je ne libère pas les
        <code>Person</code>. Elles n’ont pas été construites par la
        classe table, donc elles peuvent exister ailleurs dans le
        programme. Ce n’est pas la responsabilité de
        <code>PersonsTable</code> de s’en préoccuper.</p>
        <p>Quand on ajoute une personne, on doit d’abord vérifier si on
        ne doit pas allonger la table (avec <a
        href="https://en.cppreference.com/w/c/memory/realloc.html"><code>realloc</code></a>).
        Et il ne faut pas oublier d’incrémenter la taille.</p>
        <div class="sourceCode" id="cb9"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> PersonsTable_add_person<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">,</span> Person<span class="op">*</span> p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>t<span class="op">-&gt;</span>size <span class="op">==</span> t<span class="op">-&gt;</span>capacity<span class="op">){</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>        t<span class="op">-&gt;</span>capacity <span class="op">*=</span> <span class="dv">2</span><span class="op">;</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>        t<span class="op">-&gt;</span>table <span class="op">=</span> realloc<span class="op">(</span>t<span class="op">-&gt;</span>table<span class="op">,</span> t<span class="op">-&gt;</span>capacity<span class="op">);</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    t<span class="op">-&gt;</span>table<span class="op">[</span>t<span class="op">-&gt;</span>size<span class="op">++]</span> <span class="op">=</span> p<span class="op">;</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Pour retirer une personne, il faut s’assurer que l’index est
        valide. On retire une personne en décalant les éléments suivants
        vers la gauche. On renvoie un booléen pour indiquer si
        l’opération a réussit. Et on n’oublie pas de décrémenter la
        taille.</p>
        <div class="sourceCode" id="cb10"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> PersonsTable_remove<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">,</span> <span class="dt">int</span> idx<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>idx <span class="op">&lt;</span> <span class="dv">0</span> <span class="op">||</span> idx <span class="op">&gt;=</span> t<span class="op">-&gt;</span>size<span class="op">)</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i <span class="op">=</span> idx<span class="op">;</span> i <span class="op">&lt;</span> t<span class="op">-&gt;</span>size <span class="op">-</span> <span class="dv">1</span><span class="op">;</span> i<span class="op">++)</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>        t<span class="op">-&gt;</span>table<span class="op">[</span>i<span class="op">]</span> <span class="op">=</span> t<span class="op">-&gt;</span>table<span class="op">[</span>i<span class="op">+</span><span class="dv">1</span><span class="op">];</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>    t<span class="op">-&gt;</span>size<span class="op">--;</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Récupérer un élément est assez simple. On renvoie
        <code>NULL</code> si l’index n’est pas valide:</p>
        <div class="sourceCode" id="cb11"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>Person<span class="op">*</span> PersonsTable_get<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">,</span> <span class="dt">int</span> idx<span class="op">)</span> <span class="op">{</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>idx <span class="op">&lt;</span> <span class="dv">0</span> <span class="op">||</span> idx <span class="op">&gt;=</span> t<span class="op">-&gt;</span>size<span class="op">)</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> NULL<span class="op">;</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> t<span class="op">-&gt;</span>table<span class="op">[</span>idx<span class="op">];</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Pour savoir si notre table contient un élément, on n’a pas
        trop le choix: il faut les parcourir tous. Je ne prétends pas
        ici à une grande efficacité!</p>
        <div class="sourceCode" id="cb12"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> PersonsTable_contains<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">,</span> Person<span class="op">*</span> p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> t<span class="op">-&gt;</span>size<span class="op">;</span> i<span class="op">++){</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="op">(</span>Person_equals<span class="op">(</span>p<span class="op">,</span> t<span class="op">-&gt;</span>table<span class="op">[</span>i<span class="op">]))</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Et finalement on veut pouvoir les afficher:</p>
        <div class="sourceCode" id="cb13"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> PersonsTable_print<span class="op">(</span>PersonsTable<span class="op">*</span> t<span class="op">)</span> <span class="op">{</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;[PersonsTable]:</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i<span class="op">=</span><span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> t<span class="op">-&gt;</span>size<span class="op">;</span> i<span class="op">++){</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>        printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">\t</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>        Person_print<span class="op">(</span>t<span class="op">-&gt;</span>table<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Important ici: pour ces deux dernière méthodes, nous avons
        fait appel aux méthodes de <code>Person</code>. Des objets qui
        s’envoient des messages, comme il faut!</p>
        <p>Reste à tester que ça fonctionne.</p>
        <div class="sourceCode" id="cb14"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">(){</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;Start program</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Create test persons</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> p <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;Adrien&quot;</span><span class="op">,</span> <span class="st">&quot;Foucart&quot;</span><span class="op">);</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> other <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;Leonardo&quot;</span><span class="op">,</span> <span class="st">&quot;Da Vinci&quot;</span><span class="op">);</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Create and fill table</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a>    PersonsTable<span class="op">*</span> pt <span class="op">=</span> PersonsTable_make<span class="op">();</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a>    PersonsTable_add_person<span class="op">(</span>pt<span class="op">,</span> p<span class="op">);</span></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a>    PersonsTable_add_person<span class="op">(</span>pt<span class="op">,</span> other<span class="op">);</span></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a>    PersonsTable_print<span class="op">(</span>pt<span class="op">);</span></span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a>    <span class="co">// check contains method</span></span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>PersonsTable_contains<span class="op">(</span>pt<span class="op">,</span> p<span class="op">)){</span></span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a>        printf<span class="op">(</span><span class="st">&quot;Table contains person: &quot;</span><span class="op">);</span></span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a>        Person_print<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true" tabindex="-1"></a>    <span class="co">// check get method</span></span>
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> found <span class="op">=</span> PersonsTable_get<span class="op">(</span>pt<span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb14-24"><a href="#cb14-24" aria-hidden="true" tabindex="-1"></a>    Person_print<span class="op">(</span>found<span class="op">);</span></span>
<span id="cb14-25"><a href="#cb14-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-26"><a href="#cb14-26" aria-hidden="true" tabindex="-1"></a>    <span class="co">// check remove</span></span>
<span id="cb14-27"><a href="#cb14-27" aria-hidden="true" tabindex="-1"></a>    PersonsTable_remove<span class="op">(</span>pt<span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb14-28"><a href="#cb14-28" aria-hidden="true" tabindex="-1"></a>    PersonsTable_print<span class="op">(</span>pt<span class="op">);</span></span>
<span id="cb14-29"><a href="#cb14-29" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb14-30"><a href="#cb14-30" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(!</span>PersonsTable_contains<span class="op">(</span>pt<span class="op">,</span> p<span class="op">)){</span></span>
<span id="cb14-31"><a href="#cb14-31" aria-hidden="true" tabindex="-1"></a>        printf<span class="op">(</span><span class="st">&quot;Table does not contain person: &quot;</span><span class="op">);</span></span>
<span id="cb14-32"><a href="#cb14-32" aria-hidden="true" tabindex="-1"></a>        Person_print<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb14-33"><a href="#cb14-33" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb14-34"><a href="#cb14-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-35"><a href="#cb14-35" aria-hidden="true" tabindex="-1"></a>    PersonsTable_destroy<span class="op">(</span>pt<span class="op">);</span></span>
<span id="cb14-36"><a href="#cb14-36" aria-hidden="true" tabindex="-1"></a>    Person_destroy<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb14-37"><a href="#cb14-37" aria-hidden="true" tabindex="-1"></a>    Person_destroy<span class="op">(</span>other<span class="op">);</span></span>
<span id="cb14-38"><a href="#cb14-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-39"><a href="#cb14-39" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;End program</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb14-40"><a href="#cb14-40" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb14-41"><a href="#cb14-41" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Ce qui me donne:</p>
        <pre><code>[PersonsTable]:
        [Person]: Adrien Foucart
        [Person]: Leonardo Da Vinci
Table contains person: [Person]: Adrien Foucart
[Person]: Leonardo Da Vinci
[PersonsTable]:
        [Person]: Leonardo Da Vinci
Table does not contain person: [Person]: Adrien Foucart
End program</code></pre>
        <h2 id="un-peu-de-recul">Un peu de recul</h2>
        <p>Qu’avons-nous jusqu’à présent?</p>
        <ul>
        <li>Des objets qui offrent des <em>interfaces publiques</em> et
        disposent d’<em>attributs privés</em>.</li>
        <li>Des objets qui sont responsables des opérations qui les
        concernent, et peuvent déléguer du travail en envoyant des
        messages.</li>
        </ul>
        <p>C’est déjà pas mal, pour une poignée de lignes de code!</p>
        <p>Mais j’ai l’impression qu’on peut aller un peu plus loin. Une
        prochaine fois.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.04.03.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Thu, 02 Apr 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.04.01 C Orienté Objet | 1</title>
    <link>https://adfoucart.be/clavier/2026.04.01.html</link>
    <description>
        <h2><span class='emoji'>🟨</span>2026.04.01<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.04.01.html">C
Orienté Objet | 1</a></span></h2>
        <p>Le C n’est généralement pas un langage associé à la
        programmation orientée objet. Quand j’ai appris le C++, on me
        l’a initialement un peu présenté comme “du C mais avec des
        objets”, et c’est comme ça que j’ai généralement regardé ces
        langages (que j’utilise très peu) jusqu’à présent.</p>
        <p>Mais en “jouant” à résoudre les vieux puzzles <em>Advent of
        Code</em> en C (voir <a
        href="https://notes.adfoucart.be/aocode15">mes notes à ce
        sujet!</a>), je me suis rendu compte que je commençais à
        utiliser naturellement des principes “orienté objet” dans mes
        résolutions. On a parfois je pense (en tout cas moi!) un peu
        tendance à réduire “orienté objet” à “on peut déclarer des
        classes”. Mais on peut avoir des classes et des objets sans
        <em>faire</em> de la programmation orientée objet (beaucoup de
        projets Python en sont un bon exemple!), et on peut faire de la
        programmation orientée objet sans classes. Car au-delà de la
        syntaxe, c’est surtout dans la façon d’organiser et de penser
        son code que les concepts orientés objets apparaissent.</p>
        <p>Et après tout, beaucoup de langages “orientés objets” sont
        <em>écris</em> en C, donc c’est nécessairement possible d’en
        faire en revenant à la source!</p>
        <p>Essayons.</p>
        <h2 id="une-classe-person">Une classe <code>Person</code></h2>
        <p>Commençons par un classique: une classe <code>Person</code>,
        qui contiendrait un nom et un prénom. Nous n’avons pas de
        “classes” en <code>C</code>, mais on peut déclarer des
        structures:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Person <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">char</span><span class="op">*</span> first_name<span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">char</span><span class="op">*</span> last_name<span class="op">;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
        <p>Ici, on a donc bien une <code>Person</code> qui possède un
        <code>first_name</code> et un <code>last_name</code>. Si je veux
        l’utiliser dans mon code, je pourrais faire quelque chose comme
        ceci:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Person<span class="op">*</span> p <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span><span class="kw">struct</span> Person<span class="op">));</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>p<span class="op">-&gt;</span>first_name <span class="op">=</span> <span class="st">&quot;Adrien&quot;</span><span class="op">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>p<span class="op">-&gt;</span>last_name <span class="op">=</span> <span class="st">&quot;Foucart&quot;</span><span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%s</span><span class="st"> </span><span class="sc">%s\n</span><span class="st">&quot;</span><span class="op">,</span> p<span class="op">-&gt;</span>first_name<span class="op">,</span> p<span class="op">-&gt;</span>last_name<span class="op">);</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>free<span class="op">(</span>p<span class="op">);</span></span></code></pre></div>
        <p>Avec <code>malloc</code>, j’alloue dans ma mémoire l’espace
        pour mes deux pointeurs, que je fais pointer vers des chaînes de
        caractère.</p>
        <p>Dans une logique “orientée objet”, cependant, je ne veux pas
        que ces détails d’implémentation de ma classe (comme le nom des
        attributs) soient connus. Je voudrais idéalement ne manipuler
        <em>que</em> des méthodes “publiques”.</p>
        <p>On va donc commencer par créer deux fichiers:
        <code>person.c</code> et <code>person.h</code>. Dans
        <code>person.h</code>, nous allons mettre nos déclarations
        publiques:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">// person.h</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> Person Person<span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>Person<span class="op">*</span> Person_make<span class="op">(</span><span class="dt">char</span><span class="op">*</span> first_name<span class="op">,</span> <span class="dt">char</span><span class="op">*</span> last_name<span class="op">);</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> Person_destroy<span class="op">(</span>Person<span class="op">*</span> p<span class="op">);</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> Person_print<span class="op">(</span>Person<span class="op">*</span> p<span class="op">);</span></span></code></pre></div>
        <p>Je prévois ici trois méthodes: un constructeur, un
        destructeur, et une méthode pour afficher le contenu de l’objet.
        Avec <code>typedef struct Person Person</code>, on permet de ne
        pas retaper <code>struct Person</code> à chaque fois en lui
        donnant un “alias” <code>Person</code>.</p>
        <p>L’implémentation, maintenant, va se trouver dans
        <code>person.c</code>, et sera invisible pour les utilisateurs
        de <code>person.h</code>:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdlib.h&gt;</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;string.h&gt;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;person.h&quot;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Person <span class="op">{</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">char</span><span class="op">*</span> first_name<span class="op">;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="dt">char</span><span class="op">*</span> last_name<span class="op">;</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>Person<span class="op">*</span> Person_make<span class="op">(</span><span class="dt">char</span><span class="op">*</span> first_name<span class="op">,</span> <span class="dt">char</span><span class="op">*</span> last_name<span class="op">){</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> p <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span>Person<span class="op">));</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>    p<span class="op">-&gt;</span>first_name <span class="op">=</span> malloc<span class="op">(</span>strlen<span class="op">(</span>first_name<span class="op">)+</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>    p<span class="op">-&gt;</span>last_name <span class="op">=</span> malloc<span class="op">(</span>strlen<span class="op">(</span>last_name<span class="op">)+</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>    strcpy<span class="op">(</span>p<span class="op">-&gt;</span>first_name<span class="op">,</span> first_name<span class="op">);</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>    strcpy<span class="op">(</span>p<span class="op">-&gt;</span>last_name<span class="op">,</span> last_name<span class="op">);</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> p<span class="op">;</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> Person_destroy<span class="op">(</span>Person<span class="op">*</span> p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>p<span class="op">-&gt;</span>first_name<span class="op">);</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>p<span class="op">-&gt;</span>last_name<span class="op">);</span></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a>    free<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> Person_print<span class="op">(</span>Person<span class="op">*</span> p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;[Person]: </span><span class="sc">%s</span><span class="st"> </span><span class="sc">%s\n</span><span class="st">&quot;</span><span class="op">,</span> p<span class="op">-&gt;</span>first_name<span class="op">,</span> p<span class="op">-&gt;</span>last_name<span class="op">);</span></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Dans le constructeur, on alloue de l’espace pour les chaînes
        de caractères des noms et prénoms, que l’on copie. On ne veut
        pas juste copier les pointeurs, sinon celui qui appelle
        <code>Person_make</code> pourrait ensuite changer la valeur de
        <code>p-&gt;first_name</code>, ou faire un <code>free</code>
        dessus. Dans le destructeur, on fait le ménage.</p>
        <p>Pour utiliser tout ceci, écrivons un <code>main.c</code>:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdlib.h&gt;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;string.h&gt;</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;person.h&quot;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">(){</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;Start program</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>    Person<span class="op">*</span> p <span class="op">=</span> Person_make<span class="op">(</span><span class="st">&quot;Adrien&quot;</span><span class="op">,</span> <span class="st">&quot;Foucart&quot;</span><span class="op">);</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>    Person_print<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>    Person_destroy<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>    printf<span class="op">(</span><span class="st">&quot;End program</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Ce <code>main</code> n’a plus aucune visibilité sur ce que
        contient <code>Person</code>. Si j’essaie d’ajouter
        <code>printf("%s\n", p-&gt;first_name);</code>, j’ai une erreur
        de compilation, car <code>person.h</code> ne contient pas de
        détail sur les “attributs” de Person. Je ne peux les utiliser
        qu’à travers ses méthodes.</p>
        <p>Autrement dit: <code>Person</code> se comporte maintenant
        comme un “objet” qui, par exemple en Java, s’écrirait<a
        href="#fn1" class="footnote-ref" id="fnref1"
        role="doc-noteref"><sup>1</sup></a>:</p>
        <div class="sourceCode" id="cb6"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> Person <span class="op">{</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">private</span> <span class="bu">String</span> first_name<span class="op">;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">private</span> <span class="bu">String</span> last_name<span class="op">;</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="fu">Person</span><span class="op">(</span><span class="bu">String</span> first_name<span class="op">,</span> <span class="bu">String</span> last_name<span class="op">){</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>        <span class="kw">this</span><span class="op">.</span><span class="fu">first_name</span> <span class="op">=</span> first_name<span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>        <span class="kw">this</span><span class="op">.</span><span class="fu">last_name</span> <span class="op">=</span> last_name<span class="op">;</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">void</span> <span class="fu">print</span><span class="op">(){</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>        <span class="bu">System</span><span class="op">.</span><span class="fu">out</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span><span class="st">&quot;[Person]: &quot;</span> <span class="op">+</span> first_name <span class="op">+</span> <span class="st">&quot; &quot;</span> <span class="op">+</span> last_name<span class="op">);</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>On ne va pas encore très loin dans le concept, mais je dirais
        que ça compte, comme premier pas. Pour la suite… on verra dans
        les épisodes suivants!</p>
        <section id="footnotes"
        class="footnotes footnotes-end-of-document" role="doc-endnotes">
        <hr />
        <ol>
        <li id="fn1"><p>Techniquement, on ne fait pas ici la “copie” du
        contenu des <code>String</code> que l’on fait en <code>C</code>.
        Le <code>first_name</code> dans <code>Person</code> pointera
        vers la même adresse que le <code>String</code> dans le scope
        qui a appelé le constructeur. Mais comme en Java les
        <code>String</code> sont des constantes, ça ne risque pas de
        causer un soucis. Si quelque chose essaye de modifier le
        <code>String</code> par après en dehors de la classe, ça y
        créera un nouveau <code>String</code> avec une nouvelle adresse,
        et l’ancienne adresse (retenue par <code>Person</code>)
        contiendra toujours la même information.<a href="#fnref1"
        class="footnote-back" role="doc-backlink">↩︎</a></p></li>
        </ol>
        </section>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.04.01.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Tue, 31 Mar 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.03.30 auto-complétion</title>
    <link>https://adfoucart.be/clavier/2026.03.30.html</link>
    <description>
        <h2><span class='emoji'>⏩</span>2026.03.30<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.03.30.html">auto-complétion</a></span></h2>
        <p>Durant les séances d’exercice, je me retrouve souvent à
        réfléchir au rôle que joue l’auto-complétion, et plus
        généralement les outils de génération de code, dans la pratique
        de la programmation. Car c’est, je pense, l’un des nombreux
        domaines où les dommages causés par l’IA générative se fait
        fortement ressentir.</p>
        <p>L’auto-complétion “traditionnelle” fonctionne est simple et
        largement déterministe: étant donné les éléments présents dans
        le scope et les premières lettres entrées par l’utilisateur,
        déterminer quels sont les possibilités de complétion et (seule
        partie probabiliste) les trier selon leur pertinence. Les outils
        de génération, eux-aussi, sont (étaient) simple: étant donné un
        <em>template</em> paramétrable, et une série de paramètres,
        générer un ou des blocs de code.</p>
        <p>Simple, mais puissant. Il y a plus de dix ans maintenant, je
        l’utilisais pour générer des dizaines de classes à la volée en
        Java sur base d’un schéma de base de données. Tout le
        <em>boilerplate</em> répétitif de la création de classes
        “modèles” et “contrôleurs” était fait, on pouvait se concentrer
        sur la logique de plus haut niveau du programme.</p>
        <p>Et l’auto-complétion classique, en plus d’être un gain de
        temps, est aussi un formidable outil d’apprentissage et de
        découverte. Quelles sont les méthodes disponibles depuis cette
        classe? Tape un <code>.</code>, appuie sur <code>TAB</code> (ou
        <code>Ctrl-Espace</code> selon les IDEs, en général), et fait
        défiler tout l’univers des possibilités. Cette auto-complétion
        fonctionne au rythme du développeur et encourage la réflexion et
        la remise en question. Il m’est régulièrement arrivé de
        m’arrêter, intrigué par une proposition de méthode que je ne
        connaissais pas, de lire rapidement la documentation, et de me
        rendre compte d’une possibilité que je n’avais pas envisagée
        pour remplir les besoins de mon programme.</p>
        <p>Le code généré, quand à lui, ne nécessitait pas vraiment de
        relecture. Une fois qu’on avait compris le principe de comment
        configurer le template, on pouvait être certain que le résultat
        serait toujours exact, et on garde une compréhension aussi
        complète que possible de ce qui se trouve dans le code du
        projet.</p>
        <p>Les IAs génératives nous privent de tous ces bénéfices, et ne
        visent plus qu’à l’objectif de productivité à court terme:
        produire plus de lignes de codes, maintenant, tout de suite.</p>
        <p>L’étudiant.e écrit <code>if</code>, et l’IA propose
        immédiatement un bloc <code>if-else</code> complet qui <em>fait
        quelque chose</em>, quelque chose qui à première vue <em>semble
        toujours logique</em> par rapport à ce qui leur est demandé.
        Parfois, on peut résoudre un exercice complet à coup de
        <code>TAB</code>-<code>TAB</code>-<code>TAB</code> jusqu’à ce
        que tous les tests passent.</p>
        <p>On ne sait pas ce qu’il y a dans le code. On ne sait pas
        pourquoi ça marche. Quand finalement ça ne marche plus, on n’a
        pas la moindre idée de par où commencer.</p>
        <p>Vécu récemment: un.e étudiant.e m’appelle, me montre son
        code.</p>
        <p>– Monsieur, je ne comprends pas ce qu’il se passe.<br />
        – Qu’est-ce qui ne marche pas?<br />
        – Ca marche, mais je ne comprends pas pourquoi il faut écrire
        ça.<br />
        – Pourquoi tu l’as écrit? Qu’est-ce que tu cherchais à
        faire?<br />
        – …</p>
        <p>Je peux alors expliquer le pourquoi, mais la qualité de
        l’apprentissage n’est jamais la même. On apprend en faisant.
        Avec l’IA générative, l’étudiant.e ne fait pas, il relit ce que
        l’IA a fait et pense le comprendre.</p>
        <p>Heureusement, iels sont nombreux.ses à s’en rendre compte.
        Beaucoup m’appellent pour me demander de l’aide pour couper les
        aides IA. Ils voient bien qu’ils n’apprennent pas. Mais ces
        outils reviennent, se réactivent, se réinstallent à chaque mise
        à jour. Ce sont des mauvaises herbes dont ils peinent à se
        débarrasser. Et la tentation est toujours présente, quand la
        charge de travail devient un peu trop élevée, lorsqu’on se sent
        un peu trop largué, lorsqu’on est coincé alors que
        l’enseignant.e est occupé.e avec d’autres et qu’on a envie
        d’avancer.</p>
        <p>L’utilisation des IAs génératives brident l’apprentissage.
        C’est dramatique pour les étudiant.es, c’est problématique même
        pour les développeur.euses aguerri.es. J’ai beaucoup appris
        durant cette première année en tant qu’enseignant, à donner des
        cours dans des matières qui ne sont pas nécessairement les
        miennes. Je suis un meilleur développeur aujourd’hui que je ne
        l’étais il y a un an. Et je le dois, je crois, au fait que je me
        suis forcé à toujours <em>faire</em> les choses moi-même, à
        faire tous (ou presque) les exercices qu’on demande aux
        étudiant.es de faire, à me donner des petits projets pour aller
        plus loin. Et toujours j’ai cherché à avancer à petits pas, en
        comprenant à chaque étape ce que je faisais et pourquoi je le
        faisais.</p>
        <p>Serais-je allé plus vite avec Claude à mes côtés? Peut-être.
        Mais j’en serais sorti diminué. Au fur et à mesure des années,
        je suis persuadé que celleux qui se reposent sur Claude et ses
        amis seront de moins en moins compétents, de moins en moins
        capable de réfléchir, de moins en moins capable de <em>penser
        une solution</em>. Je crois qu’aujourd’hui déjà, et certainement
        demain après l’éclatement de la bulle spéculative des IAs
        génératives, être capable de développer sans <em>agent</em> à
        mes côtés est une compétence qui n’a pas de prix.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.03.30.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Sun, 29 Mar 2026 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.02.07 java test runner</title>
    <link>https://adfoucart.be/clavier/2026.02.07.html</link>
    <description>
        <h2><span class='emoji'>🛑</span>2026.02.07<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.02.07.html">java
test runner</a></span></h2>
        <p>Les tests unitaires en Java semblent assez désagréable à
        mettre en place. Le framework le plus utilisé semble être <a
        href="https://junit.org/">JUnit</a>, et à première vue ça me
        semble un peu “usine à gaz”. Il y a aussi visiblement eu de gros
        changements entre les versions, et les tutoriels que j’ai trouvé
        sont pour la plupart obsolètes.</p>
        <p>Mes besoins en tests unitaires sont assez limités. Je ne
        compte pas faire des tonnes de réflexion, de mocking ou autres
        joyeusetés. Je voudrais juste pouvoir facilement créer des
        classes de test, et avoir un point d’entrée d’où je peux tous
        les lancer automatiquement. Ca ne devrait pas nécessiter de
        faire tout un setup Maven ou Gradle et d’importer des
        <code>jar</code> dans tous les sens.</p>
        <p>On va voir: faisons un petit “test runner” Java en utilisant
        uniquement la librairie standard.</p>
        <h2 id="première-étape-les-tests-à-tester">Première étape: les
        tests à tester</h2>
        <p>Dans mon dossier <code>src</code>, où j’aurais dans un projet
        les “vraies” classes développées, mettons un dossier
        <code>Test</code> et créons une classe <code>TestTest</code> qui
        servira à tester le test runner. Rien de confus jusqu’à
        présent.</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">package</span><span class="im"> Test</span><span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestTest <span class="op">{</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">testSuccess</span><span class="op">(){</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        <span class="cf">assert</span> <span class="kw">true</span> <span class="op">:</span> <span class="st">&quot;if this fails, we are in trouble.&quot;</span><span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">testFailed</span><span class="op">(){</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>        <span class="cf">assert</span> <span class="kw">false</span> <span class="op">:</span> <span class="st">&quot;test has failed successfully!&quot;</span><span class="op">;</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>On met deux tests dedans: le premier devrait réussir, le
        second échouer.</p>
        <h2 id="deuxième-étape-lancer-les-tests">Deuxième étape: lancer
        les tests</h2>
        <p>Ajoutons maintenant une classe <code>TestRunner</code>, avec
        une méthode <code>main</code>. C’est ici que toute la partie
        intéressante doit se passer. Commençons simplement et laissons
        de côté la partie “découverte des classes et méthodes de test”
        pour juste lancer les deux tests et vérifier que tout se passe
        comme prévu.</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">package</span><span class="im"> Test</span><span class="op">;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">main</span><span class="op">(</span><span class="bu">String</span><span class="op">[]</span> args<span class="op">){</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>        TestTest<span class="op">.</span><span class="fu">testSuccess</span><span class="op">();</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>        TestTest<span class="op">.</span><span class="fu">testFailed</span><span class="op">();</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>On lance… et rien ne se passe. Petit moment de recherche: par
        défaut, les assertions en Java <a
        href="https://www.w3schools.com/java/ref_keyword_assert.asp">sont
        désactivées</a>. Pour qu’elles aient de l’effet, il faut aller
        modifier le <a
        href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/ClassLoader.html#setDefaultAssertionStatus(boolean)"><code>DefaultAssertionStatus</code></a>
        du <code>ClassLoader</code>:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">main</span><span class="op">(</span><span class="bu">String</span><span class="op">[]</span> args<span class="op">){</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">ClassLoader</span> loader <span class="op">=</span> <span class="bu">ClassLoader</span><span class="op">.</span><span class="fu">getSystemClassLoader</span><span class="op">();</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>        loader<span class="op">.</span><span class="fu">setDefaultAssertionStatus</span><span class="op">(</span><span class="kw">true</span><span class="op">);</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>        TestTest<span class="op">.</span><span class="fu">testSuccess</span><span class="op">();</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>        TestTest<span class="op">.</span><span class="fu">testFailed</span><span class="op">();</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>On relance:</p>
        <pre><code>Exception in thread &quot;main&quot; java.lang.AssertionError: test has failed successfully!</code></pre>
        <p>Parfait.</p>
        <h2 id="on-continue-trouver-les-méthodes-dans-une-classe">On
        continue: trouver les méthodes dans une classe</h2>
        <p>La classe <code>Class</code> permet de récupérer les méthodes
        qui existent dans une classe donnée. Imaginons donc qu’on a une
        liste de “classes de test”. On peut parcourir ces classes et
        récupérer leurs méthodes:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">private</span> <span class="dt">static</span> <span class="bu">String</span><span class="op">[]</span> testClasses <span class="op">=</span> <span class="op">{</span><span class="st">&quot;TestTest&quot;</span><span class="op">};</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">main</span><span class="op">(</span><span class="bu">String</span><span class="op">[]</span> args<span class="op">){</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>        <span class="co">// ...</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> <span class="op">(</span><span class="bu">String</span> c<span class="op">:</span> testClasses<span class="op">){</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>            <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>                <span class="bu">Class</span> testClass <span class="op">=</span> <span class="bu">Class</span><span class="op">.</span><span class="fu">forName</span><span class="op">(</span>c<span class="op">);</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>                <span class="bu">Method</span><span class="op">[]</span> methods <span class="op">=</span> testClass<span class="op">.</span><span class="fu">getDeclaredMethods</span><span class="op">();</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>                <span class="cf">for</span> <span class="op">(</span><span class="bu">Method</span> m <span class="op">:</span> methods<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>                    <span class="bu">System</span><span class="op">.</span><span class="fu">out</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span>m<span class="op">.</span><span class="fu">getName</span><span class="op">());</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">ClassNotFoundException</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>                <span class="bu">System</span><span class="op">.</span><span class="fu">err</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span><span class="st">&quot;Class not found: &quot;</span> <span class="op">+</span> c<span class="op">);</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span><span class="op">;</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span></code></pre></div>
        <p>Ce qui me donne…</p>
        <pre><code>Class not found: TestTest</code></pre>
        <p>Effectivement, le “nom” de la classe doit inclure le nom du
        package:</p>
        <div class="sourceCode" id="cb7"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">private</span> <span class="dt">static</span> <span class="bu">String</span><span class="op">[]</span> testClasses <span class="op">=</span> <span class="op">{</span><span class="st">&quot;Test.TestTest&quot;</span><span class="op">};</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span></code></pre></div>
        <p>On a maintenant:</p>
        <pre><code>testSuccess
testFailed</code></pre>
        <p>Parfait.</p>
        <h2
        id="invoquer-les-méthodes-et-récupérer-les-exceptions">Invoquer
        les méthodes et récupérer les exceptions</h2>
        <p>Reste à invoquer la méthode, ce qu’on fait avec
        <code>evoke</code>:</p>
        <div class="sourceCode" id="cb9"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">main</span><span class="op">(</span><span class="bu">String</span><span class="op">[]</span> args<span class="op">){</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>        <span class="co">// ...</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>            <span class="cf">for</span> <span class="op">(</span><span class="bu">Method</span> m <span class="op">:</span> methods<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>                m<span class="op">.</span><span class="fu">invoke</span><span class="op">(</span><span class="kw">null</span><span class="op">);</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>        <span class="co">// ...</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span></code></pre></div>
        <p>Mon IDE me signale que je dois traiter des possibles
        <code>IllegalAccessException</code> et
        <code>InvocationTargetException</code>. La première peut arriver
        si la méthode que je cherche à invoquer est privée. Ceci nous
        donne donc gratuitement un filtre sur notre méthode de
        découverte: si la méthode est privée, on ne va pas l’invoquer.
        Dans nos classes Test, on pourra donc utiliser ça pour écrire
        des méthodes “internes” dont on pourrait avoir besoin.</p>
        <p><code>InvocationTargetException</code> arrive si une
        exception a lieu lors de l’exécution de la méthode. Par exemple,
        une <code>AssertionError</code> si un <code>assert</code> est
        <code>false</code>. On a donc notre moyen de détecter si le test
        a échoué.</p>
        <p>Voyons où on en est:</p>
        <div class="sourceCode" id="cb10"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">private</span> <span class="dt">static</span> <span class="bu">String</span><span class="op">[]</span> testClasses <span class="op">=</span> <span class="op">{</span><span class="st">&quot;Test.TestTest&quot;</span><span class="op">};</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">main</span><span class="op">(</span><span class="bu">String</span><span class="op">[]</span> args<span class="op">){</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>        <span class="bu">ClassLoader</span> loader <span class="op">=</span> <span class="bu">ClassLoader</span><span class="op">.</span><span class="fu">getSystemClassLoader</span><span class="op">();</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>        loader<span class="op">.</span><span class="fu">setDefaultAssertionStatus</span><span class="op">(</span><span class="kw">true</span><span class="op">);</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> <span class="op">(</span><span class="bu">String</span> c<span class="op">:</span> testClasses<span class="op">){</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>            <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>                <span class="bu">Class</span> testClass <span class="op">=</span> <span class="bu">Class</span><span class="op">.</span><span class="fu">forName</span><span class="op">(</span>c<span class="op">);</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>                <span class="bu">Method</span><span class="op">[]</span> methods <span class="op">=</span> testClass<span class="op">.</span><span class="fu">getDeclaredMethods</span><span class="op">();</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>                <span class="cf">for</span> <span class="op">(</span><span class="bu">Method</span> m <span class="op">:</span> methods<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a>                    <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a>                        m<span class="op">.</span><span class="fu">invoke</span><span class="op">(</span><span class="kw">null</span><span class="op">);</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a>                    <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">IllegalAccessException</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a>                        <span class="cf">continue</span><span class="op">;</span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a>                    <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">InvocationTargetException</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a>                        <span class="bu">System</span><span class="op">.</span><span class="fu">err</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span><span class="st">&quot;❌ Test &quot;</span> <span class="op">+</span> m<span class="op">.</span><span class="fu">getName</span><span class="op">()</span> <span class="op">+</span> <span class="st">&quot; failed (&quot;</span> <span class="op">+</span> e<span class="op">.</span><span class="fu">getMessage</span><span class="op">()</span> <span class="op">+</span> <span class="st">&quot;)&quot;</span><span class="op">);</span></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a>                        <span class="cf">continue</span><span class="op">;</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true" tabindex="-1"></a>                    <span class="op">}</span></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true" tabindex="-1"></a>                    <span class="bu">System</span><span class="op">.</span><span class="fu">out</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span><span class="st">&quot;✅ Test &quot;</span> <span class="op">+</span> m<span class="op">.</span><span class="fu">getName</span><span class="op">()</span> <span class="op">+</span> <span class="st">&quot; passed&quot;</span><span class="op">);</span></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span></span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">ClassNotFoundException</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true" tabindex="-1"></a>                <span class="bu">System</span><span class="op">.</span><span class="fu">err</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span><span class="st">&quot;Class not found: &quot;</span> <span class="op">+</span> c<span class="op">);</span></span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span><span class="op">;</span></span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb10-28"><a href="#cb10-28" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb10-29"><a href="#cb10-29" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Testons:</p>
        <pre><code>✅ Test testSuccess passed
❌ Test testFailed failed (null)</code></pre>
        <p>Presque bon: le message “test has failed successfully!”
        n’apparaît pas. Pourquoi? <code>InvocationTargetException</code>
        ne contient pas ce message, c’est — me dit la documentation — un
        “wrapper” autour de l’exception dans la méthode invoquée. Je
        dois donc récupérer d’abord la-dite exception, qui sera
        normalement le <code>AssertionError</code>.</p>
        <div class="sourceCode" id="cb12"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="bu">System</span><span class="op">.</span><span class="fu">err</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span><span class="st">&quot;❌ Test &quot;</span> <span class="op">+</span> m<span class="op">.</span><span class="fu">getName</span><span class="op">()</span> <span class="op">+</span> <span class="st">&quot; failed (&quot;</span> <span class="op">+</span> e<span class="op">.</span><span class="fu">getCause</span><span class="op">().</span><span class="fu">getMessage</span><span class="op">()</span> <span class="op">+</span> <span class="st">&quot;)&quot;</span><span class="op">);</span></span></code></pre></div>
        <pre><code>✅ Test testSuccess passed
❌ Test testFailed failed (test has failed successfully!)</code></pre>
        <p>Cette fois, c’est bon!</p>
        <h2 id="découverte-automatique-des-classes">Découverte
        automatique des classes</h2>
        <p>Java ne fournit pas de moyen simple de récupérer toutes les
        classes présentes dans un package, mais on peut s’en sortir
        grâce à notre ami le <code>ClassLoader</code>, celui-là même qui
        nous permet de voir les <code>assert</code>.</p>
        <p>Un certain Victor Tatai <a
        href="https://dzone.com/articles/get-all-classes-within-package">a
        partagé en 2007</a> un bout de code permettant de faire tout ça.
        Créons une sous-classe privée <code>Discovery</code> pour
        encapsuler toute cette logique.</p>
        <div class="sourceCode" id="cb14"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">private</span> <span class="kw">class</span> Discovery <span class="op">{</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>        <span class="kw">private</span> <span class="dt">static</span> <span class="bu">Class</span><span class="op">[]</span> <span class="fu">getClasses</span><span class="op">(</span><span class="bu">ClassLoader</span> classLoader<span class="op">,</span> <span class="bu">String</span> packageName<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>            <span class="co">// ...</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span></code></pre></div>
        <p>On pourra ainsi appeler
        <code>Discovery.getClasses(loader, "Test")</code> pour récupérer
        la liste des classes de test.</p>
        <p>La première étape est de récupérer toutes les “ressources” du
        package:</p>
        <div class="sourceCode" id="cb15"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co">// dans getClasses(...)</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> classLoader <span class="op">!=</span> <span class="kw">null</span><span class="op">;</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="bu">List</span><span class="op">&lt;</span><span class="bu">Class</span><span class="op">&gt;</span> classes <span class="op">=</span> <span class="kw">new</span> <span class="bu">ArrayList</span><span class="op">&lt;&gt;();</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a><span class="cf">try</span> <span class="op">{</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>    resources <span class="op">=</span> classLoader<span class="op">.</span><span class="fu">getResources</span><span class="op">(</span>packageName<span class="op">);</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">IOException</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> classes<span class="op">.</span><span class="fu">toArray</span><span class="op">(</span><span class="kw">new</span> <span class="bu">Class</span><span class="op">[</span><span class="dv">0</span><span class="op">]);</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>On va recevoir une liste d’URLs qui aura un seul élément dans
        notre cas: le chemin vers le dossier du package. Si on n’arrive
        pas à charger les ressources, on renvoie une liste vide. On peut
        parcourir les URLs et récupérer le dossier correspondant dans un
        objet de type <code>File</code>.</p>
        <div class="sourceCode" id="cb16"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ...</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> <span class="op">(</span>resources<span class="op">.</span><span class="fu">hasMoreElements</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>    <span class="bu">URL</span> resource <span class="op">=</span> resources<span class="op">.</span><span class="fu">nextElement</span><span class="op">();</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    <span class="bu">File</span> directory <span class="op">=</span> <span class="kw">new</span> <span class="bu">File</span><span class="op">(</span>resource<span class="op">.</span><span class="fu">getPath</span><span class="op">());</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Ensuite: récupérer la liste des fichiers dans le
        répertoire.</p>
        <div class="sourceCode" id="cb17"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ...</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="bu">File</span><span class="op">[]</span> files <span class="op">=</span> directory<span class="op">.</span><span class="fu">listFiles</span><span class="op">();</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a><span class="cf">assert</span> files <span class="op">!=</span> <span class="kw">null</span><span class="op">;</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> <span class="op">(</span><span class="bu">File</span> file <span class="op">:</span> files<span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>Les fichiers qu’on cherche seront les fichiers
        <code>.class</code>, qui contiennent le code Java compilé. Pour
        ceux-là, on doit récupérer leur nom (sans le
        <code>.class</code>), et on peut ensuite charger les classes
        correspondantes avec <code>Class.forName(...)</code>, en
        n’oubliant pas de rajouter le nom du package devant:</p>
        <div class="sourceCode" id="cb18"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> <span class="op">(</span><span class="bu">File</span> file <span class="op">:</span> files<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>file<span class="op">.</span><span class="fu">getName</span><span class="op">().</span><span class="fu">endsWith</span><span class="op">(</span><span class="st">&quot;.class&quot;</span><span class="op">))</span> <span class="op">{</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">String</span> className <span class="op">=</span> packageName <span class="op">+</span> <span class="st">&quot;.&quot;</span> <span class="op">+</span> file<span class="op">.</span><span class="fu">getName</span><span class="op">().</span><span class="fu">substring</span><span class="op">(</span><span class="dv">0</span><span class="op">,</span> file<span class="op">.</span><span class="fu">getName</span><span class="op">().</span><span class="fu">length</span><span class="op">()-</span><span class="dv">6</span><span class="op">);</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>        classes<span class="op">.</span><span class="fu">add</span><span class="op">(</span><span class="bu">Class</span><span class="op">.</span><span class="fu">forName</span><span class="op">(</span>className<span class="op">));</span></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>C’est à peu près bon, mais il faut maintenant traiter le cas
        où on a une arborescence un peu plus compliquée avec des
        sous-dossiers dans notre répertoire de test. Dans ce cas-là,
        <code>file</code> peut être aussi un dossier, et il faut alors
        chercher les fichiers dans ce sous-dossier. Pour pouvoir
        réaliser ça, on va devoir faire un peu de récursion et extraire
        une méthode <code>findClassesInDirectory</code> de notre méthode
        <code>getClasses</code>. On arrive au final à tout ceci pour la
        classe <code>Discovery</code>:</p>
        <div class="sourceCode" id="cb19"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">private</span> <span class="kw">class</span> Discovery <span class="op">{</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>        <span class="kw">private</span> <span class="dt">static</span> <span class="bu">Class</span><span class="op">[]</span> <span class="fu">getClasses</span><span class="op">(</span><span class="bu">ClassLoader</span> classLoader<span class="op">,</span> <span class="bu">String</span> packageName<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>            <span class="cf">assert</span> classLoader <span class="op">!=</span> <span class="kw">null</span><span class="op">;</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a>            <span class="bu">List</span><span class="op">&lt;</span><span class="bu">Class</span><span class="op">&gt;</span> classes <span class="op">=</span> <span class="kw">new</span> <span class="bu">ArrayList</span><span class="op">&lt;&gt;();</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>            <span class="bu">Enumeration</span><span class="op">&lt;</span><span class="bu">URL</span><span class="op">&gt;</span> resources<span class="op">;</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>                resources <span class="op">=</span> classLoader<span class="op">.</span><span class="fu">getResources</span><span class="op">(</span>packageName<span class="op">);</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">IOException</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span> classes<span class="op">.</span><span class="fu">toArray</span><span class="op">(</span><span class="kw">new</span> <span class="bu">Class</span><span class="op">[</span><span class="dv">0</span><span class="op">]);</span></span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a>            <span class="cf">while</span> <span class="op">(</span>resources<span class="op">.</span><span class="fu">hasMoreElements</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a>                <span class="bu">URL</span> resource <span class="op">=</span> resources<span class="op">.</span><span class="fu">nextElement</span><span class="op">();</span></span>
<span id="cb19-15"><a href="#cb19-15" aria-hidden="true" tabindex="-1"></a>                <span class="bu">File</span> directory <span class="op">=</span> <span class="kw">new</span> <span class="bu">File</span><span class="op">(</span>resource<span class="op">.</span><span class="fu">getPath</span><span class="op">());</span></span>
<span id="cb19-16"><a href="#cb19-16" aria-hidden="true" tabindex="-1"></a>                classes<span class="op">.</span><span class="fu">addAll</span><span class="op">(</span><span class="fu">findClassesInDirectory</span><span class="op">(</span>directory<span class="op">,</span> packageName<span class="op">));</span></span>
<span id="cb19-17"><a href="#cb19-17" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb19-18"><a href="#cb19-18" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> classes<span class="op">.</span><span class="fu">toArray</span><span class="op">(</span><span class="kw">new</span> <span class="bu">Class</span><span class="op">[</span><span class="dv">0</span><span class="op">]);</span></span>
<span id="cb19-19"><a href="#cb19-19" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb19-20"><a href="#cb19-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-21"><a href="#cb19-21" aria-hidden="true" tabindex="-1"></a>        <span class="kw">private</span> <span class="dt">static</span> <span class="bu">List</span><span class="op">&lt;</span><span class="bu">Class</span><span class="op">&gt;</span> <span class="fu">findClassesInDirectory</span><span class="op">(</span><span class="bu">File</span> directory<span class="op">,</span> <span class="bu">String</span> packageName<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-22"><a href="#cb19-22" aria-hidden="true" tabindex="-1"></a>            <span class="bu">List</span><span class="op">&lt;</span><span class="bu">Class</span><span class="op">&gt;</span> classes <span class="op">=</span> <span class="kw">new</span> <span class="bu">ArrayList</span><span class="op">&lt;</span><span class="bu">Class</span><span class="op">&gt;();</span></span>
<span id="cb19-23"><a href="#cb19-23" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="op">(!</span>directory<span class="op">.</span><span class="fu">exists</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb19-24"><a href="#cb19-24" aria-hidden="true" tabindex="-1"></a>                <span class="cf">return</span> classes<span class="op">;</span></span>
<span id="cb19-25"><a href="#cb19-25" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb19-26"><a href="#cb19-26" aria-hidden="true" tabindex="-1"></a>            <span class="bu">File</span><span class="op">[]</span> files <span class="op">=</span> directory<span class="op">.</span><span class="fu">listFiles</span><span class="op">();</span></span>
<span id="cb19-27"><a href="#cb19-27" aria-hidden="true" tabindex="-1"></a>            <span class="cf">assert</span> files <span class="op">!=</span> <span class="kw">null</span><span class="op">;</span></span>
<span id="cb19-28"><a href="#cb19-28" aria-hidden="true" tabindex="-1"></a>            <span class="cf">for</span> <span class="op">(</span><span class="bu">File</span> file <span class="op">:</span> files<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-29"><a href="#cb19-29" aria-hidden="true" tabindex="-1"></a>                <span class="cf">if</span> <span class="op">(</span>file<span class="op">.</span><span class="fu">isDirectory</span><span class="op">()</span> <span class="op">&amp;&amp;</span> <span class="op">!</span>file<span class="op">.</span><span class="fu">getName</span><span class="op">().</span><span class="fu">contains</span><span class="op">(</span><span class="st">&quot;.&quot;</span><span class="op">))</span> <span class="op">{</span></span>
<span id="cb19-30"><a href="#cb19-30" aria-hidden="true" tabindex="-1"></a>                    classes<span class="op">.</span><span class="fu">addAll</span><span class="op">(</span><span class="fu">findClassesInDirectory</span><span class="op">(</span>file<span class="op">,</span> packageName <span class="op">+</span> <span class="st">&quot;.&quot;</span> <span class="op">+</span> file<span class="op">.</span><span class="fu">getName</span><span class="op">()));</span></span>
<span id="cb19-31"><a href="#cb19-31" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>file<span class="op">.</span><span class="fu">getName</span><span class="op">().</span><span class="fu">endsWith</span><span class="op">(</span><span class="st">&quot;.class&quot;</span><span class="op">))</span> <span class="op">{</span></span>
<span id="cb19-32"><a href="#cb19-32" aria-hidden="true" tabindex="-1"></a>                    <span class="bu">String</span> className <span class="op">=</span> packageName <span class="op">+</span> <span class="st">&quot;.&quot;</span> <span class="op">+</span> file<span class="op">.</span><span class="fu">getName</span><span class="op">().</span><span class="fu">substring</span><span class="op">(</span><span class="dv">0</span><span class="op">,</span> file<span class="op">.</span><span class="fu">getName</span><span class="op">().</span><span class="fu">length</span><span class="op">()-</span><span class="dv">6</span><span class="op">);</span></span>
<span id="cb19-33"><a href="#cb19-33" aria-hidden="true" tabindex="-1"></a>                    <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb19-34"><a href="#cb19-34" aria-hidden="true" tabindex="-1"></a>                        classes<span class="op">.</span><span class="fu">add</span><span class="op">(</span><span class="bu">Class</span><span class="op">.</span><span class="fu">forName</span><span class="op">(</span>className<span class="op">));</span></span>
<span id="cb19-35"><a href="#cb19-35" aria-hidden="true" tabindex="-1"></a>                    <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">ClassNotFoundException</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-36"><a href="#cb19-36" aria-hidden="true" tabindex="-1"></a>                        <span class="cf">continue</span><span class="op">;</span></span>
<span id="cb19-37"><a href="#cb19-37" aria-hidden="true" tabindex="-1"></a>                    <span class="op">}</span></span>
<span id="cb19-38"><a href="#cb19-38" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span></span>
<span id="cb19-39"><a href="#cb19-39" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb19-40"><a href="#cb19-40" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> classes<span class="op">;</span></span>
<span id="cb19-41"><a href="#cb19-41" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb19-42"><a href="#cb19-42" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb19-43"><a href="#cb19-43" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <h2 id="utilisation-de-la-découverte-automatique">Utilisation de
        la découverte automatique</h2>
        <p>On peut maintenant retirer notre attribut
        <code>testClasses</code> et utiliser la découverte automatique
        pour récupérer toutes les classes de test. Voyons ce que ça
        donne:</p>
        <div class="sourceCode" id="cb20"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">main</span><span class="op">(</span><span class="bu">String</span><span class="op">[]</span> args<span class="op">){</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">ClassLoader</span> loader <span class="op">=</span> <span class="bu">ClassLoader</span><span class="op">.</span><span class="fu">getSystemClassLoader</span><span class="op">();</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>        loader<span class="op">.</span><span class="fu">setDefaultAssertionStatus</span><span class="op">(</span><span class="kw">true</span><span class="op">);</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>        <span class="bu">Class</span><span class="op">[]</span> classes <span class="op">=</span> Discovery<span class="op">.</span><span class="fu">getClasses</span><span class="op">(</span>loader<span class="op">,</span> <span class="st">&quot;Test&quot;</span><span class="op">);</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> <span class="op">(</span><span class="bu">Class</span> c <span class="op">:</span> classes<span class="op">)</span> <span class="op">{</span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>            <span class="bu">System</span><span class="op">.</span><span class="fu">out</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span>c<span class="op">.</span><span class="fu">getName</span><span class="op">());</span></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>On reçoit:</p>
        <pre><code>Test.TestRunner$Discovery
Test.TestRunner
Test.TestTest</code></pre>
        <p>Évidemment, le <code>TestRunner</code> et la sous-classe
        <code>Discovery</code> sont là aussi. Il va falloir les filtrer,
        puis on peut continuer comme avant.</p>
        <div class="sourceCode" id="cb22"><pre
        class="sourceCode java"><code class="sourceCode java"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> TestRunner <span class="op">{</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">public</span> <span class="dt">static</span> <span class="dt">void</span> <span class="fu">main</span><span class="op">(</span><span class="bu">String</span><span class="op">[]</span> args<span class="op">){</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">ClassLoader</span> loader <span class="op">=</span> <span class="bu">ClassLoader</span><span class="op">.</span><span class="fu">getSystemClassLoader</span><span class="op">();</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a>        loader<span class="op">.</span><span class="fu">setDefaultAssertionStatus</span><span class="op">(</span><span class="kw">true</span><span class="op">);</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a>        <span class="bu">Class</span><span class="op">[]</span> classes <span class="op">=</span> Discovery<span class="op">.</span><span class="fu">getClasses</span><span class="op">(</span>loader<span class="op">,</span> <span class="st">&quot;Test&quot;</span><span class="op">);</span></span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a>        <span class="bu">System</span><span class="op">.</span><span class="fu">out</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span><span class="st">&quot;Found &quot;</span> <span class="op">+</span> <span class="op">(</span>classes<span class="op">.</span><span class="fu">length</span><span class="op">-</span><span class="dv">2</span><span class="op">)</span> <span class="op">+</span> <span class="st">&quot; test classes.&quot;</span><span class="op">);</span></span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> <span class="op">(</span><span class="bu">Class</span> c <span class="op">:</span> classes<span class="op">)</span> <span class="op">{</span></span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="op">(</span>c<span class="op">.</span><span class="fu">getName</span><span class="op">().</span><span class="fu">startsWith</span><span class="op">(</span>TestRunner<span class="op">.</span><span class="fu">class</span><span class="op">.</span><span class="fu">getName</span><span class="op">()))</span></span>
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a>                <span class="cf">continue</span><span class="op">;</span></span>
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a>            <span class="bu">System</span><span class="op">.</span><span class="fu">out</span><span class="op">.</span><span class="fu">println</span><span class="op">(</span><span class="st">&quot;Searching for tests in &quot;</span> <span class="op">+</span> c<span class="op">.</span><span class="fu">getName</span><span class="op">());</span></span>
<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a>            <span class="bu">Method</span><span class="op">[]</span> methods <span class="op">=</span> c<span class="op">.</span><span class="fu">getDeclaredMethods</span><span class="op">();</span></span>
<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a>            <span class="co">// ...</span></span>
<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb22-16"><a href="#cb22-16" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb22-17"><a href="#cb22-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
        <p>On relance:</p>
        <pre><code>Found 1 test classes.
Searching for tests in Test.TestTest
✅ Test testSuccess passed
❌ Test testFailed failed (test has failed successfully!)</code></pre>
        <p>Et c’est bon! J’ai maintenant un dossier Test que je peux
        “dropper” dans d’autres projet pour déployer des tests unitaires
        simples sans me casser la tête.</p>
        <p>Code complet sur Codeberg: <a
        href="https://codeberg.org/adfoucart/JavaTestRunner/">https://codeberg.org/adfoucart/JavaTestRunner/</a></p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.02.07.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Fri, 06 Feb 2026 23:00:00 -0000</pubDate>
</item>
<item>
    <title>2026.01.26 correction assistée par ordinateur</title>
    <link>https://adfoucart.be/clavier/2026.01.26.html</link>
    <description>
        <h2><span class='emoji'>🖋</span>2026.01.26<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2026.01.26.html">correction
assistée par ordinateur</a></span></h2>
        <p>Pourquoi passer dix heures à faire des corrections lorsqu’on
        peut passer cinq heures à faire un système qui automatise
        partiellement les corrections, <em>et puis</em> passer quand
        même dix heures à faire des corrections?</p>
        <p>Bon, il y a effectivement quelques révisions à prévoir pour
        réutiliser “mon” système, mais même si le résultat n’a pas été
        tout à fait à la hauteur de mes attentes cette session, je pense
        que les concepts de base sont raisonnablement sains. Mais
        prenons les choses étape par étape.</p>
        <h2 id="le-besoin">1. Le besoin</h2>
        <p>Une partie de mes corrections était sur papier. Pas
        d’automatisation par là, juste une solide grille de correction
        permettant de ne pas trop agoniser sur chaque demi-point à
        donner.</p>
        <p>Mais deux parties d’examen étaient susceptibles de recevoir
        un peu d’aide technologique: une question de maths en Excel, et
        un examen de machine learning avec des fonctions Python à
        compléter.</p>
        <p>Ce que je voulais éviter, c’est de devoir:</p>
        <ol type="1">
        <li>Ouvrir (et refermer) 300 fichiers Excel pour aller regarder
        les formules écrites par les étudiant.es une à une.<br />
        </li>
        <li>Ouvrir, exécuter et refermer 200 fichiers Python, et
        possiblement les modifier pour tester des fonctions
        individuelles.</li>
        </ol>
        <p>Il faut sans doute le préciser au vu de la situation
        actuelle, mais l’objectif n’est évidemment pas de balancer tout
        ça dans ChatGPT, Copilot ou autre et de lui “demander” de me
        coter tout ça pendant que je bois un café. Je veux me faciliter
        la vie <em>et</em> rester le plus correct possible dans mes
        cotes.</p>
        <p>Non, ce que je veux, c’est pouvoir me faciliter la vie.
        Tester automatiquement ce qui est testable automatiquement, et
        me mettre en avant les informations utiles pour corriger chaque
        étudiant.e sans devoir aller manuellement ouvrir tous les
        fichiers soumis.</p>
        <p>Idéalement, j’aimerais avoir quelque chose qui va me détecter
        facilement quand quelque chose est juste, pour que je puisse
        concentrer mon attention sur les cas où il y a des erreurs pour
        voir si ce sont des erreurs très problématiques ou non. Ou pas
        des erreurs du tout: tout système automatique aura ses faux
        positifs.</p>
        <h2 id="le-plan">2. Le plan</h2>
        <p>Ce que j’avais en tête était, en principe, raisonnablement
        simple:</p>
        <ul>
        <li>Récupérer, pour chaque étudiant.e, le(s) fichier(s)
        remplis.</li>
        <li>Pour Excel:
        <ul>
        <li>Récupérer le contenu des cellules à modifier.</li>
        <li>Comparer ce contenu avec les formules attendues.</li>
        <li>Créer un fichier “rapport” par étudiant.e avec le contenu
        des cellules et une note indiquant si c’est juste ou pas.</li>
        </ul></li>
        <li>Pour Python:
        <ul>
        <li>Copier les fichiers dans un environnement d’évaluation avec
        des fichiers <code>pytest</code> permettant de tester les
        différentes fonctions implémentées.</li>
        <li>Lancer <code>pytest</code> et récupérer le résultat.</li>
        <li>Créer un fichier “rapport” par étudiant avec le résultat des
        tests et une copie du code.</li>
        </ul></li>
        </ul>
        <p>Ensuite, dans les deux cas, créer aussi un fichier “index”
        avec la liste des étudiant.es et un lien vers leur rapport. Tout
        ça idéalement en HTML pour pouvoir facilement naviguer d’un
        rapport à l’autre.</p>
        <p>Python reste mon langage de prédilection, c’est donc de que
        j’utiliserai pour essayer de faire tout ça.</p>
        <h2 id="premier-écueil-la-récupération-des-fichiers">3. Premier
        écueil: la récupération des fichiers</h2>
        <p>Il y a plusieurs configurations possibles pour faire passer
        des examens sur machine à la Haute-École. Dans celle utilisée
        pour le cours de mathématiques, les étudiant.es reçoivent des
        identifiants temporaire pour le temps de l’examen, et doivent
        mettre leurs fichiers à la fin sur un disque réseau. À la fin de
        l’examen, on peut récupérer le contenu de ces disques,
        identifiés par le login temporaire. Les feuilles de login sont
        distribuées avec les énoncés lors de l’examen, et il n’y a donc
        pas de correspondance connue <em>a priori</em> entre les comptes
        et les étudiant.es.</p>
        <p>Par conséquent, les consignes indiquaient clairement que les
        étudiant.es devaient renommer le fichier Excel fourni
        (<code>Probabilites.xlsx</code>) avec le format
        <code>NOM_PRENOM.xlsx</code>. Ils devaient aussi dans le même
        examen faire un projet Java qu’ils devaient également appeler
        <code>NOM_Prenom</code> et mettre à la racine du disque réseau.
        Et on leur demandait également de mettre leur nom et prénom sur
        la feuille de login qu’on récupérait à la fin de l’examen.</p>
        <p>On aurait pu simplement décider que le non respect des
        consignes amènerait un 0, et ne pas corriger si le fichier Excel
        n’était pas correctement renommer, mais on est gentils. On va
        donc essayer de faire un minimum d’effort pour trouver toutes
        les correspondances entre les étudiant.es et leur compte.</p>
        <p>Pour commencer, je parcours tous les dossiers et, dans
        chaque, dossier, j’utilise la méthode <code>glob</code> de <a
        href="https://docs.python.org/3/library/pathlib.html"><code>pathlib</code></a>
        pour rechercher tous les fichiers <code>.xlsx</code> qui se
        trouvent dans le dossier ou ses sous-dossiers:
        <code>xlsx_files = list(f.glob("**/*.xlsx"))</code>. S’il y en a
        un qui ne s’appelle pas <code>Probabilites.xlsx</code>, je
        récupère le nom du fichier et considère que c’est le nom de mon
        étudiant.e. C’est le cas de base, pour celleux qui ont respecté
        les consignes.</p>
        <p>Sinon, je cherche s’il y a un dossier à la racine qui
        correspondrait au projet Java, et je note l’association. Et
        sinon, je note simplement le numéro de session, et espère qu’on
        retrouve une feuille de login avec un nom dessus. Sur un peu
        moins de 300 étudiant.es qui ont passé l’examen, je n’en ai
        qu’un seul au final qui a rempli quelque chose <em>et</em> qui
        n’a laissé aucun moyen de l’identifier.</p>
        <p>La seconde configuration, utilisée pour l’examen en Python,
        rend les choses un peu plus faciles: les étudiant.es uploadent
        leur projet sur une plateforme <a
        href="https://moodle.org/">Moodle</a>, où iels sont connecté.es
        avec leur propre compte. J’ai donc alors un fichier ZIP par
        étudiant.e avec son nom dessus.</p>
        <h2 id="tester-excel-cest-galère">4. Tester Excel, c’est
        galère</h2>
        <p>Il y a plusieurs librairies qui permettent de s’attaquer à un
        fichier Excel en Python. Une des façons les plus simples est
        d’utiliser <a
        href="https://pandas.pydata.org/"><code>pandas</code></a> et sa
        fonction <a
        href="https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html"><code>read_excel</code></a>.
        On peut y faire:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pandas <span class="im">as</span> pd</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>file_content <span class="op">=</span> pd.read_excel(<span class="st">&quot;/path/to/excel/file.xlsx&quot;</span>, header<span class="op">=</span><span class="va">None</span>)</span></code></pre></div>
        <p>Pour récupérer le contenu du fichier dans une DataFrame.
        Problème: on récupère uniquement les <em>valeurs</em> des
        cellules. Mais je ne veux pas juste les valeurs: je veux
        récupérer les formules. En effet, dans l’exercice proposé, une
        bonne partie des valeurs à trouver leur sont déjà données dans
        le fichier, et iels devaient justement trouver la formule
        permettant de les retrouver!</p>
        <p>Pandas utilise <a
        href="https://openpyxl.readthedocs.io/en/stable/"><code>openpyxl</code></a>
        en arrière-fond. Avec <code>openpyxl</code>, on peut ouvrir un
        fichier en récupérant soit les valeurs, soit les formules, et
        ensuite adresser directement les cellules:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> openpyxl <span class="im">import</span> load_workbook</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>wb <span class="op">=</span> load_workbook(<span class="st">&quot;/path/to/excel/file.xlsx&quot;</span>, data_only<span class="op">=</span><span class="va">False</span>)</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>sheet <span class="op">=</span> wb[<span class="st">&quot;Sheet1&quot;</span>]</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>g4 <span class="op">=</span> sheet[<span class="st">&quot;G4&quot;</span>].value <span class="co"># formule dans la cellule G4</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>wb_val <span class="op">=</span> load_workbook(<span class="st">&quot;/path/to/excel/file.xlsx&quot;</span>, data_only<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>sheet_val <span class="op">=</span> wb_val[<span class="st">&quot;Sheet1&quot;</span>]</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>g4_val <span class="op">=</span> sheet_val[<span class="st">&quot;G4&quot;</span>].value <span class="co"># valeur dans la cellule G4</span></span></code></pre></div>
        <p>Et j’ai — malheureusement après avoir choisi de ne pas
        utiliser cette libraire — fini par trouver un moyen d’accéder
        aussi aux graphes présents sur la feuille, et à récupérer
        quelles données ont été utilisées pour réaliser ce graphe, et de
        quel type de graphe il s’agit. Le problème, c’est que c’est
        assez mal documenté, et qu’on doit accéder à des attributs
        “privés” (au sens pythonesque de “marqué par un <code>_</code>
        comme ne faisant pas partie de l’API”) pour pouvoir jouer
        avec:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>all_charts <span class="op">=</span> sheet._charts</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> chart <span class="kw">in</span> all_charts:</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span>(chart.tagname) <span class="co"># donnera le type de graphe</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> ser <span class="kw">in</span> chart.ser: <span class="co"># parcourir les ranges</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(ser.val.numRef.f) <span class="co"># formule du range</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>([pt.v <span class="cf">for</span> pt <span class="kw">in</span> ser.val.numRef.numCache.pt]) <span class="co"># valeurs utilisées dans le graphe</span></span></code></pre></div>
        <p>C’est un peu dégueu, et probablement pas très stable, mais ça
        peut marcher. Malheureusement, je n’ai pas trouvé ça tout de
        suite, et je suis tombé sur des recommandations pour <a
        href="https://www.xlwings.org/"><code>xlwings</code></a>, que
        j’ai finalement utilisé. <code>xlwings</code> permet de
        récupérer le contenu d’un fichier Excel avec ses formules et ses
        valeurs, ainsi que d’extraire les graphes et de les sauvegarder
        en fichiers images. Par contre, s’il y a un moyen de retrouver
        les données utilisées pour créer le graphe, je ne l’ai pas
        trouvé. Et <code>xlwings</code> est leeeeeent. Je pense que la
        prochaine fois, je reviendrai sur <code>openpyxl</code>.
        <code>xlwings</code> propose une API similaire:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> xlwings <span class="im">as</span> xw</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>wb <span class="op">=</span> xw.Book(<span class="st">&quot;/path/to/excel/file.xlsx&quot;</span>)</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>sheet <span class="op">=</span> wb.sheets[<span class="st">&#39;Sheet1&#39;</span>]</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>g4 <span class="op">=</span> sheet[<span class="st">&#39;G4&#39;</span>].formula</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>g4_val <span class="op">=</span> sheet[<span class="st">&#39;G4&#39;</span>].value</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="co"># extraction des graphes</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>charts <span class="op">=</span> sheet.charts</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> chart <span class="kw">in</span> charts:</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    chart.to_png(<span class="st">&quot;/path/to/chart.png&quot;</span>)</span></code></pre></div>
        <p>Avec <code>xlwings</code>, j’ai donc pu faire à peu près ce
        que j’avais en tête:</p>
        <ul>
        <li>Récupérer les formules des cellules qui m’intéressent.</li>
        <li>Récupérer le graphe s’il y en a un, l’extraire et le
        sauvegarder en PNG.</li>
        <li>Comparer les formules à des “formules attendues”.</li>
        <li>Générer un rapport avec tout ça.</li>
        </ul>
        <p>Le rapport obtenu ressemble à ça (anonymisé ici):</p>
        <figure>
        <img src="./img/excel_report.png" width="300" alt="agrandir" />
        <figcaption aria-hidden="true"><a
        href="./img/excel_report.png">agrandir</a></figcaption>
        </figure>
        <p>Le problème principal qui m’a tout de même fait rouvrir une
        bonne partie des fichiers Excels, c’est que le nombre de
        différentes manières équivalentes d’écrire les formules était
        juste trop grand. Entre les parenthèses, les différentes
        manières possibles d’arriver au résultat, ou le choix (tout à
        fait raisonnable!) fait par certain.es d’utiliser des cellules
        “intermédiaires” pour découper les formules en éléments plus
        simples… on ne s’en sort pas trop, et énormément d’étudiant.es
        avaient une réponse correcte à laquelle je n’avais pas
        pensé.</p>
        <h3 id="notes-pour-le-futur">Notes pour le futur</h3>
        <ul>
        <li>Utiliser <code>openpyxl</code> directement.</li>
        <li>Mettre dans le rapport les formules et les valeurs des
        cellules attendus, mais aussi de tout autre cellule non-nulle
        modifiée par l’étudiant.e.</li>
        <li>Tester d’abord sur les valeurs en <em>excluant</em>
        certaines formules “non-autorisées” pour y arriver, plutôt que
        de tester sur les formules. Par exemple ici, certain.es ont
        tenté le coup de “recopier” les valeurs qui étaient déjà données
        (avec par exemple <code>=C4</code> pour récupérer dans la
        cellule où ils devaient mettre la formule la valeur déjà fournie
        correspondante): la valeur serait correcte, mais la formule
        pas.</li>
        <li>Récupérer les ranges et valeurs du graphe et les mettre dans
        le rapport, quitte à recréer le graphe en matplotlib sur base de
        ces valeurs plutôt que d’essayer de l’extraire depuis Excel si
        je veux l’avoir quand même affiché.</li>
        </ul>
        <h2 id="tester-python-cest-plus-facile">5. Tester Python, c’est
        plus facile?</h2>
        <p>J’avais mis toutes les chances de mon côté avec l’énoncé de
        mon examen de Machine Learning en Python: les étudiant.es
        avaient des fonctions à compléter dont iels n’étaient pas
        supposé.es changer le nom ou la signature, et avec des
        spécifications très claires sur les entrées et sorties
        attendues. Le scénario idéal pour pouvoir déployer des tests
        unitaires. J’avais donc préparé une batterie de tests avec
        <code>pytest</code>. Mon plan était donc: pour chaque
        étudiant.e, récupérer les deux fichiers de code qu’ils devaient
        modifier, les mettre dans un environnement de test avec mes
        fichiers <code>pytest</code> prêts à l’emploi, lancer les tests,
        récupérer les résultats, et mettre ces résultats avec le code
        dans un fichier “rapport” par étudiant.e.</p>
        <p>La correction devait ainsi être raisonnablement rapide: si
        les tests d’une fonction sont passés, c’est juste; sinon, on
        jette un coup d’oeil au code pour voir si l’étudiant.e a quand
        même compris quelque chose.</p>
        <p>Premier écueil: les étudiant.es ont été un peu trop
        enthousiastes sur l’auto-complétion, et ont souvent inclus des
        libraires imprévues dans leurs imports. Je ne spécifiais nulle
        part dans l’énoncé qu’il fallait limiter les <code>import</code>
        aux librairies nécessaires, donc je n’ai pas voulu pénaliser ça…
        mais je n’avais pas moi-même installé ces librairies dans mon
        environnement de test, donc les tests unitaires foiraient
        complètement. Heureusement, ces imports excessifs n’étaient pas
        utilisés ensuite dans le code, et pouvaient donc être
        automatiquement enlevés avec <a
        href="https://pypi.org/project/autoflake/"><code>autoflake</code></a>.
        J’ai fait les évaluations depuis mon PC-de-la-maison qui a
        encore Windows dessus, donc j’ai fait un bon vieux script
        <code>.bat</code> pour parcourir les dossiers, lancer
        <code>autoflake</code>, lancer les tests et mettre les résultats
        dans un fichier:</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode bat"><code class="sourceCode dosbat"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">REM Run autoflake and pytest in all directories</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> <span class="at">/f</span> <span class="va">%%i</span> <span class="kw">in</span> <span class="kw">(</span>&#39;dir <span class="at">/b/ad</span>&#39;<span class="kw">)</span> <span class="kw">do</span> <span class="kw">(</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> Autoflake in &quot;<span class="sc">%%</span><span class="va">i</span>&quot;</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    autoflake -<span class="at">-in-place</span> -<span class="at">-remove-all-unused-imports</span> <span class="st">&quot;</span><span class="sc">%%</span><span class="va">i</span><span class="st">/src/conseiller_fraude.py&quot;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    autoflake -<span class="at">-in-place</span> -<span class="at">-remove-all-unused-imports</span> <span class="st">&quot;</span><span class="sc">%%</span><span class="va">i</span><span class="st">/src/detection_fraude.py&quot;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> pytest in &quot;<span class="sc">%%</span><span class="va">i</span>&quot;</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    pytest <span class="st">&quot;</span><span class="sc">%%</span><span class="va">i</span><span class="st">/tests/test_conseiller_fraude.py&quot;</span> -<span class="at">-no-header</span> <span class="at">-tb</span>=no <span class="kw">&gt;&gt;</span> <span class="st">&quot;</span><span class="sc">%%</span><span class="va">i</span><span class="st">.txt&quot;</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    pytest <span class="st">&quot;</span><span class="sc">%%</span><span class="va">i</span><span class="st">/tests/test_detection_fraude.py&quot;</span> -<span class="at">-no-header</span> <span class="at">-tb</span>=no <span class="kw">&gt;&gt;</span> <span class="st">&quot;</span><span class="sc">%%</span><span class="va">i</span><span class="st">.txt&quot;</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="kw">)</span></span></code></pre></div>
        <p>Le <code>pytest</code> aurait pu être fait en une fois, mais
        le faire séparément sur les deux fichiers permet de tout de même
        avoir les résultats si un des deux fichiers plante complètement,
        par exemple pour cause d’import foireux.</p>
        <p>Les rapport obtenus ressemblent à ceci:</p>
        <figure>
        <img src="./img/python_report.png" width="300" alt="agrandir" />
        <figcaption aria-hidden="true"><a
        href="./img/python_report.png">agrandir</a></figcaption>
        </figure>
        <p>Le problème principal que j’ai eu avec ces tests n’était ici
        pas technique, mais conceptuel: les tests unitaires que j’avais
        prévu ne capturaient pas bien les critères sur lesquels je
        voulais les évaluer. Du coup, à part si tous les tests d’une
        méthode passaient (ce qui n’arrivait malheureusement pas très
        souvent à part pour les méthodes les plus simples), je devais
        tout de même souvent repasser sur le code ligne par ligne avec
        ma grille de critères pour voir ce qui passait ou non. C’était
        un peu bête de ma part: j’ai écrit les tests unitaires comme si
        je développais le programme, au lieu de les écrire avec la
        grille d’évaluation en tête. Probablement parce que je n’avais
        pas encore écrit la grille d’évaluation, ce qui n’aide pas.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2026.01.26.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Sun, 25 Jan 2026 23:00:00 -0000</pubDate>
</item>
<item>
    <title>2025.11.22 idiomatique</title>
    <link>https://adfoucart.be/clavier/2025.11.22.html</link>
    <description>
        <h2><span class='emoji'>🌍</span>2025.11.22<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2025.11.22.html">idiomatique</a></span></h2>
        <p>Mon langage de prédilection en programmation est depuis
        longtemps maintenant le Python. Dans le cursus dans lequel
        j’enseigne, cependant, les langages principalement utilisés sont
        le Java et le Typescript. J’ai fait pas mal de Java. Le
        Typescript est une “surcouche” du Javascript, et j’ai déjà fait
        du Javascript. Donc “ça devrait aller”, en tout cas pour m’en
        sortir dans la <strong>syntaxe</strong>. Et, évidemment, les
        concepts de programmation en général sont transférables d’un
        langage à l’autre.</p>
        <p>Mais ce qui va me manquer, et sur lequel je vais devoir un
        peu travailler, c’est ce qui fait souvent dire à des gens qui ne
        maîtrisent pas un langage particulier que ce langage est nul: la
        connaissance des <strong>idiomes</strong> du langage.</p>
        <p>J’entends souvent des gens qui se plaignent du Python, et à
        chaque fois quand je vois ce qui les bloque, c’est qu’ils n’ont
        “pas compris” comment utiliser Python efficacement. Je râle de
        mon côté sur le Java ou le Javascript chaque fois que je me
        retrouve à en faire, et c’est sans doute pour la même
        raison.</p>
        <p>Les <a
        href="https://fr.wikipedia.org/wiki/Idiome_de_programmation"><strong>idiomes</strong></a>
        ne sont pas seulement liés à la syntaxe ou aux règles formelles
        du langage, mais bien au “bon usage”, les réflexes à avoir qui
        font que les choses fonctionnent sans friction.</p>
        <p>L’exemple typique, en Python, est celui des <a
        href="https://docs.python.org/fr/3/tutorial/datastructures.html#list-comprehensions">listes
        en compréhension</a>. Si on cherche à appliquer une certaine
        fonction sur tous les éléments d’une liste et à stocker les
        résultats dans une nouvelle liste, on pourrait faire:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>nouvelle_liste <span class="op">=</span> []</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="bu">len</span>(ma_liste)):</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    element <span class="op">=</span> ma_liste[i]</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    resultat <span class="op">=</span> ma_fonction(element)</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    nouvelle_liste.append(resultat)</span></code></pre></div>
        <p>Ce serait tout à fait “correct” au niveau de la syntaxe du
        langage, mais ce n’est pas <em>idiomatique</em>. La liste en
        compréhension offre, en Python, une manière plus lisible (pour
        celleux qui sont ont l’accent local!) d’exprimer la même
        opération:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>nouvelle_liste <span class="op">=</span> [ma_fonction(element) <span class="cf">for</span> element <span class="kw">in</span> ma_liste]</span></code></pre></div>
        <p>Et comme dans les langages parlés par les humains, plusieurs
        idiomes peuvent coexister. Dans certaines régions du pays
        Python, on utiliserait plutôt:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>nouvelle_liste <span class="op">=</span> <span class="bu">list</span>(<span class="bu">map</span>(ma_fonction, ma_liste))</span></code></pre></div>
        <p>Avec toujours le même résultat.</p>
        <p>Comprendre non seulement la syntaxe et les règles, mais aussi
        les idiomes du Typescript, c’est ce que je vais chercher à faire
        dans les prochains mois. Je documenterai cette aventure en terre
        moins connue ici: <a
        href="https://notes.adfoucart.be/typescript">Typescript: une
        aventure</a>.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2025.11.22.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Fri, 21 Nov 2025 23:00:00 -0000</pubDate>
</item>
<item>
    <title>2025.11.04 simulation probabilités</title>
    <link>https://adfoucart.be/clavier/2025.11.04.html</link>
    <description>
        <h2><span class='emoji'>🪙</span>2025.11.04<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2025.11.04.html">simulation
probabilités</a></span></h2>
        <p>[<a
        href="https://notes.adfoucart.be/vinci/saint_petersbourg.html">Lien
        vers le simulateur</a>]</p>
        <p>Dans le cours de mathématiques, on utilise le <a
        href="https://fr.wikipedia.org/wiki/Paradoxe_de_Saint-P%C3%A9tersbourg">Paradoxe
        de Saint-Peterbourg</a> comme exemple de calcul de fonction de
        masse et d’espérance d’une variable aléatoire.</p>
        <p>Le paradoxe tourne autour d’un jeu qui se déroule ainsi:</p>
        <ul>
        <li>le joueur mise une certaine somme.</li>
        <li>il fait ensuite une série de jets de pile ou face.</li>
        <li>le jeu s’arrête au premier “pile”.</li>
        <li>le joueur gagne alors <span
        class="math inline">2<sup><em>n</em></sup></span> euros, où
        <span class="math inline"><em>n</em></span> est le nombre de
        “faces” obtenues avant d’avoir un “pile”.</li>
        </ul>
        <p>Donc si, par exemple, j’ai la série: Face-Face-Pile, le jeu
        s’arrête après trois lancers et je reçois <span
        class="math inline">2<sup>2</sup> = 4</span> euros.</p>
        <p>Le <strong>paradoxe</strong> réside dans le fait que,
        théoriquement, l’<strong>espérance</strong> des gains obtenus
        est <strong>infinie</strong>. Pourtant, peu de joueurs seraient
        prêts à miser une grosse somme, vu que les chances de gagner
        plus qu’une poignée d’euros sont extrêmement faibles.</p>
        <p>Notre cours de mathématique ne s’intéresse pas trop au cas
        infini. On ajoute donc une règle supplémentaire: si au bout de
        10 lancers on n’a obtenu <strong>que des faces</strong>, le
        joueur gagne <span
        class="math inline">2<sup>10</sup> = 1024</span> euros et la
        partie s’arrête aussi. Les gains ne sont plus infinis, mais le
        “paradoxe” reste présent. L’espérance du gain dans cette
        configuration est de <span class="math inline">6</span> euros,
        mais les chances pour un joueur de gagner de l’argent en misant
        <span class="math inline">6</span> euros sont faibles (<span
        class="math inline">12.5%</span>, pour être précis).</p>
        <p>On peut calculer toutes ces valeurs, mais dès qu’on fait des
        probabilités, j’aime bien <strong>simuler</strong> les choses,
        pour obtenir une bonne intuition de ce qui se passe. En plus,
        comme on enseigne à des futurs développeurs, approcher une
        solution via une simulation est une compétence qui semble
        intéressante à mettre en avant.</p>
        <p>Le <a
        href="https://notes.adfoucart.be/vinci/saint_petersbourg.html">simulateur</a>
        permet de voir comment l’espérance change avec le nombre maximal
        de lancers autorisé, et comment la précision de notre estimation
        de l’espérance s’améliore avec le nombre de parties
        simulées.</p>
        <p>Pour que tout ça soit accessible sur une page web, on va
        faire un peu de Javascript. Commençons donc par la simulation
        d’une partie:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">jeuSaintPetersbourg</span>(max_n) {</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> n <span class="op">=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> (<span class="bu">Math</span><span class="op">.</span><span class="fu">random</span>() <span class="op">&lt;</span> <span class="fl">0.5</span>) {</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> (n <span class="op">===</span> max_n) <span class="cf">return</span> <span class="dv">2</span><span class="op">**</span>n<span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        n<span class="op">++;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="dv">2</span><span class="op">**</span>(n<span class="op">-</span><span class="dv">1</span>)<span class="op">;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
        <p>On utilise pour “pile ou face” un test
        <code>Math.random() &lt; 0.5</code>, et on considère ici que
        <code>true</code> représente face, et <code>false</code>
        représente pile. La fonction renvoie le gain: si on atteint le
        maximum de lancers, on renvoie <span
        class="math inline">2<sup><em>n</em></sup></span>, sinon on
        s’arrête dès qu’on tombe sur pile et on renvoie <span
        class="math inline">2<sup><em>n</em> − 1</sup></span>.</p>
        <p>Pour estimer une statistique via une simulation, il faut
        pouvoir relancer l’expérience beaucoup de fois. On va donc faire
        ça, en collectant la distribution des résultats:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">lancerSimulation</span>(){</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ... récupération d&#39;inputs</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">const</span> resultats <span class="op">=</span> {}<span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> (<span class="kw">let</span> i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;=</span> maxLancers<span class="op">;</span> i<span class="op">++</span> ){</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>       resultats[<span class="dv">2</span><span class="op">**</span>i] <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> total <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> (<span class="kw">let</span> i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> numJeux<span class="op">;</span> i<span class="op">++</span>) {</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>        <span class="kw">const</span> gains <span class="op">=</span> <span class="fu">jeuSaintPetersbourg</span>(maxLancers)<span class="op">;</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>        resultats[gains]<span class="op">++;</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>        total <span class="op">+=</span> gains<span class="op">;</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>    <span class="kw">const</span> moyenne <span class="op">=</span> (total <span class="op">/</span> numJeux)<span class="op">.</span><span class="fu">toFixed</span>(<span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ... affichage des résultats</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
        <p><code>maxLancers</code> et <code>numJeux</code> sont
        récupérés dans des <code>&lt;input&gt;</code>. On initialise le
        tableau des résultats avec tous les gains possibles, puis on
        lance <code>numJeux</code> parties, et on collecte les gains
        obtenus. On calcule notre estimation de l’espérance en faisant
        la moyenne des gains. L’affichage se fait via un diagramme en
        barres fait dans un <a
        href="https://www.w3schools.com/html/html5_canvas.asp">&lt;canvas&gt;</a>.</p>
        <p>Il est clair que miser beaucoup sur une partie n’est pas très
        avantageux. Mais si on peut miser un peu sur beaucoup de
        parties, par contre, la dynamique change. C’est la deuxième
        partie de la simulation: que ce passe-t-il si on peut
        rejouer?</p>
        <p>On va maintenant calculer le total des gains si on joue une
        série de parties, chacune avec une mise fixe:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">totalSerie</span>(numJeux<span class="op">,</span> prix) {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> total <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span>(<span class="kw">let</span> i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> numJeux<span class="op">;</span> i<span class="op">++</span>){</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>        res <span class="op">=</span> <span class="fu">jeuSaintPetersbourg</span>(<span class="dv">10</span>)<span class="op">;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>        total <span class="op">+=</span> res <span class="op">-</span> prix<span class="op">;</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> total<span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
        <p>Et on va cette fois-ci dans notre simulation regarder une
        variable aléatoire binaire: est-ce qu’on gagne de l’argent à la
        fin? (bon, en vrai je l’ai faite ternaire parce que j’ai séparé
        le cas où on récupérait exactement notre mise).</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">lancerSerieJeux</span>(){</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ... récupération d&#39;inputs</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> partiesGagnees <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> partiesPerdues <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> partiesEgales <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span>( <span class="kw">let</span> i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> numSimulations<span class="op">;</span> i<span class="op">++</span> ){</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>        gains <span class="op">=</span> <span class="fu">totalSerie</span>(numJeux<span class="op">,</span> prix)<span class="op">;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span>(gains <span class="op">&gt;</span> <span class="dv">0</span>){</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>            partiesGagnees <span class="op">+=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>        }</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">else</span> <span class="cf">if</span>( gains <span class="op">&lt;</span> <span class="dv">0</span>){</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>            partiesPerdues <span class="op">+=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>        }</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>        <span class="cf">else</span>{</span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>            partiesEgales <span class="op">+=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>        }</span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ... affichage des résultats</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
        <p>En jouant avec le simulateur, on peut ainsi facilement voir
        que si on mise une fois 1000 euros, on va vraisemblablement les
        perdre (0.1% de victoires). Si par contre je joue 200 parties à
        5 euros, j’ai plus d’une chance sur deux de sortir vainqueur de
        l’échange. Enfin, sauf si on compte le temps perdu à jeter la
        pièce.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2025.11.04.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Mon, 03 Nov 2025 23:00:00 -0000</pubDate>
</item>
<item>
    <title>2025.10.17 tests postgres</title>
    <link>https://adfoucart.be/clavier/2025.10.17.html</link>
    <description>
        <h2><span class='emoji'>🗄️</span>2025.10.17<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2025.10.17.html">tests
postgres</a></span></h2>
        <p>Est-ce que ma requête est correcte?</p>
        <p>Je ne suis pas un grand spécialiste des bases de données
        PostgreSQL. Des trois cours que je donne ce quadrimestre, c’est
        certainement celui dans lequel je suis le moins à l’aise. Je
        sais comprendre et expliquer tous les concepts théoriques, mais
        pouvoir rapidement vérifier si une requête fonctionnne
        <strong>exactement</strong> comme prévu, si elle répond
        <strong>exactement</strong> aux critères de l’énoncé, ce n’est
        pas toujours évident.</p>
        <p>Je me suis demandé: est-ce qu’il y a moyen de faire
        l’équivalent en SQL des “tests unitaires” qu’on peut retrouver
        dans les différents languages de programmation auxquels je suis
        plus habitués? Cela me permettrait de mettre en place des
        procédures relativement simples pour vérifier qu’une solution
        (la mienne ou celle d’un.e étudiant.e) est correcte… ou de
        pointer dans la direction de l’erreur dans le cas contraire.</p>
        <p>Je suis tombé sur un article prometteur: “<a
        href="https://vb-consulting.github.io/blog/unit-testing-postgresql/">Unit
        Testing and TDD With PostgreSQL is Easy</a>”, par un certain
        Vedran Bilopavlović. Nous n’avons pas nécessairement la même
        notion de <em>easy</em>, mais je dois reconnaître que la
        solution est, en tout cas, <em>raisonnable</em>. L’idée est
        d’utiliser une procédure SQL qui va:</p>
        <ul>
        <li>Insérer des données de test.</li>
        <li>Exécuter la requête à tester.</li>
        <li>Utiliser des <code>ASSERT</code> pour vérifier si le
        résultat est celui attendu.</li>
        </ul>
        <p>La bonne nouvelle, c’est que ça marche assez bien. La
        mauvaise, c’est que c’est clairement quand même un peu lourd à
        écrire. Mais c’est sans doute une question d’habitude plus
        qu’autre chose. En tout cas je suis prêt à tenter un peu.
        Prenons un exemple avec quelque chose qui piège pas mal
        d’étudiant.e.s débutant.e.s: la différence entre un
        <code>LEFT JOIN</code> et un <code>INNER JOIN</code>.</p>
        <p>Imaginons un schéma avec trois tables: <code>pizza</code>,
        <code>garniture</code> et <code>garniture_pizza</code>. Une
        <strong>pizza</strong> aura un identifiant, un nom et un prix.
        Une <strong>garniture</strong> aura un identifiant et un nom. Et
        on fera le lien entre les deux pour pouvoir associer des pizzas
        à leurs garniture(s):</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode sql"><code class="sourceCode sql"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="cf">BEGIN</span>;</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> <span class="cf">IF</span> <span class="kw">NOT</span> <span class="kw">EXISTS</span> pizza (</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    id_pizza <span class="dt">int</span>,</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    nom <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">UNIQUE</span>,</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    prix <span class="dt">float</span> <span class="kw">not</span> <span class="kw">null</span>,</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (id_pizza)</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>);</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> <span class="cf">IF</span> <span class="kw">NOT</span> <span class="kw">EXISTS</span> garniture (</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    id_garniture <span class="dt">int</span> <span class="kw">PRIMARY</span> <span class="kw">KEY</span>,</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    nom <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">UNIQUE</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>);</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> <span class="cf">IF</span> <span class="kw">NOT</span> <span class="kw">EXISTS</span> garniture_pizza(</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>    id_pizza <span class="dt">int</span>,</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>    id_garniture <span class="dt">int</span>,</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>    <span class="kw">PRIMARY</span> <span class="kw">KEY</span>(id_pizza, id_garniture)</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>);</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMIT</span>;</span></code></pre></div>
        <p>Pour respecter les principes du “test driven development”, on
        va commencer par écrire les tests avant d’écrire la requête.
        Imaginons qu’on cherche à récupérer le nombre de pizzas dans
        laquelle chaque garniture est utilisée. Quels sont les
        différents cas à tester?</p>
        <ul>
        <li>Une garniture g est utilisée sur n&gt;=1 pizzas -&gt; on
        doit voir aparraître le tuple (g, n) dans la réponse.</li>
        <li>Une garniture g n’est utilisée sur aucune pizza -&gt; on
        doit voir apparaître le tuple (g, 0) dans la réponse.</li>
        <li>Une pizza p n’a aucun ingrédient -&gt; elle ne doit pas
        affecter le résultat de la requête.</li>
        </ul>
        <p>J’ai donc besoin d’insérer dans mes données, au minimum:</p>
        <ul>
        <li>Trois pizzas (1, 2, 3)</li>
        <li>Deux garnitures (1, 2)</li>
        <li>Liens (pizza, garniture): ((1, 1), (2, 1))</li>
        </ul>
        <p>On pourrait ne mettre que deux pizzas, mais le nombre de
        pizzas serait alors égal au nombre de garniture, ce qui peut
        rendre des tests sur le “nombre de résultats obtenus” moins
        évidents.</p>
        <p>Mettons ça dans une procédure, en commençant par supprimer
        toutes les données (pour s’assurer qu’on n’a bien uniquement nos
        données de test), et en terminant par un <code>rollback</code>
        (pour remettre la base de données dans son état initial). Il va
        de soi que je ne mettrais pas cette procédure dans une base de
        données en production, on va éviter de supprimer toutes les
        données par erreur!</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode sql"><code class="sourceCode sql"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">PROCEDURE</span> test_nb_pizzas_par_garniture() <span class="kw">AS</span> $$</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">BEGIN</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- supprimer toutes les données</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>        <span class="kw">TRUNCATE</span> pizza, garniture, garniture_pizza;</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- données minimales pour le test</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>        <span class="kw">INSERT</span> <span class="kw">INTO</span> pizza <span class="kw">VALUES</span> (<span class="dv">1</span>, <span class="st">&#39;pizza 1&#39;</span>, <span class="dv">0</span>),</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>                                 (<span class="dv">2</span>, <span class="st">&#39;pizza 2&#39;</span>, <span class="dv">0</span>),</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>                                 (<span class="dv">3</span>, <span class="st">&#39;pizza 3&#39;</span>, <span class="dv">0</span>);</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>        <span class="kw">INSERT</span> <span class="kw">INTO</span> garniture <span class="kw">VALUES</span> (<span class="dv">1</span>, <span class="st">&#39;garniture 1&#39;</span>),</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>                                     (<span class="dv">2</span>, <span class="st">&#39;garniture_2&#39;</span>);</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>        <span class="kw">INSERT</span> <span class="kw">INTO</span> garniture_pizza <span class="kw">VALUES</span> (<span class="dv">1</span>, <span class="dv">1</span>),</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>                                           (<span class="dv">2</span>, <span class="dv">1</span>);</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>        <span class="kw">ROLLBACK</span>;</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">END</span>;</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>$$ LANGUAGE plpgsql;</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="kw">call</span> test_nb_pizzas_par_garniture();</span></code></pre></div>
        <p>Si on exécute ceci dans une base PostgreSQL, on va: -
        supprimer les données existantes. - ajouter les données de test.
        - annuler tout et remettre la base dans son état
        pré-exécution.</p>
        <p>On doit maintenant ajouter la requête et les tests. Pour
        commencer, on imagine que notre requête se trouve dans une
        fonction <code>nb_pizzas_par_garniture</code>, qu’on définira
        plus tard. On peut alors récupérer les résultats de cette
        requête dans notre procédure de test avec:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode sql"><code class="sourceCode sql"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> TEMP <span class="kw">TABLE</span> result <span class="kw">ON</span> <span class="kw">COMMIT</span> <span class="kw">DROP</span> <span class="kw">AS</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> <span class="op">*</span> <span class="kw">FROM</span> nb_pizzas_par_garniture();</span></code></pre></div>
        <p>On stocke ainsi le résultat dans une table temporaire, qui
        sera éliminée à la fin de la transaction. On peut maintenant
        passer aux tests. La syntaxe des <code>ASSERT</code> est:
        <code>ASSERT [expr][, msg]</code>, où si l’expression est fausse
        on affiche une erreur et, optionnellement, le message. On doit
        tester:</p>
        <ul>
        <li>Que l’on trouve 2 garnitures</li>
        <li>Que la garniture 1 apparaît sur 2 pizzas</li>
        <li>Que la garniture 2 apparaît sur 0 pizzas</li>
        </ul>
        <p>Traduisons cela en SQL:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode sql"><code class="sourceCode sql"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 2 garnitures trouvées</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>ASSERT (<span class="kw">SELECT</span> <span class="fu">COUNT</span>(<span class="op">*</span>) <span class="kw">FROM</span> result) <span class="op">=</span> <span class="dv">2</span>,</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="st">&#39;2 garnitures attendues, reçu &#39;</span> <span class="op">||</span> (<span class="kw">SELECT</span> <span class="fu">COUNT</span>(<span class="op">*</span>) <span class="kw">FROM</span> result):<span class="ch">:text</span>;</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- garniture 1 -&gt; 2 pizzas</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>ASSERT (<span class="kw">SELECT</span> nb_pizzas <span class="kw">FROM</span> result <span class="kw">WHERE</span> id_garniture <span class="op">=</span> <span class="dv">1</span>) <span class="op">=</span> <span class="dv">2</span>,</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="st">&#39;2 pizzas attendues pour la garniture 1, reçu &#39;</span> <span class="op">||</span> (<span class="kw">SELECT</span> nb_pizzas <span class="kw">FROM</span> result <span class="kw">WHERE</span> id_garniture <span class="op">=</span> <span class="dv">1</span>):<span class="ch">:text</span>;</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="co">-- garniture 2 -&gt; 0 pizzas</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>ASSERT (<span class="kw">SELECT</span> nb_pizzas <span class="kw">FROM</span> result <span class="kw">WHERE</span> id_garniture <span class="op">=</span> <span class="dv">2</span>) <span class="op">=</span> <span class="dv">0</span>,</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>    <span class="st">&#39;0 pizzas attendues pour la garniture 2, reçu &#39;</span> <span class="op">||</span> (<span class="kw">SELECT</span> nb_pizzas <span class="kw">FROM</span> result <span class="kw">WHERE</span> id_garniture <span class="op">=</span> <span class="dv">2</span>):<span class="ch">:text</span>;</span></code></pre></div>
        <p>La syntaxe n’est pas la plus lisible, mais ça passe.
        <code>||</code> permet de concaténer des chaînes de caractères,
        et <code>::text</code> de <em>caster</em> les entiers de
        <code>nb_pizzas</code> en chaînes de caractères. Commençons par
        écrire une requête bidon pour vérifier que notre test fonctionne
        (sans oublier de la mettre dans une fonction):</p>
        <div class="sourceCode" id="cb5"><pre
        class="sourceCode sql"><code class="sourceCode sql"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">FUNCTION</span> nb_pizzas_par_garniture()</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>RETURNS <span class="kw">TABLE</span> (</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    id_garniture <span class="dt">int</span>,</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    nb_pizzas <span class="dt">int</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>) language sql <span class="kw">as</span> $$</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- LA REQUÊTE EST ICI:</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> <span class="dv">0</span>, <span class="dv">0</span>;</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="co">-- FIN DE LA REQUÊTE</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>$$;</span></code></pre></div>
        <p>On peut maintenant maintenant appeler la procédure de test:
        <code>call test_nb_pizzas_par_garniture();</code>, qui va bien
        planter dès le premier test en renvoyant le message:
        <code>ERROR: 2 garnitures attendues, reçu 1</code>.</p>
        <p>Imaginons maintenant un.e étudiant.e qui utilise un
        <code>INNER JOIN</code> pour répondre à la question:</p>
        <div class="sourceCode" id="cb6"><pre
        class="sourceCode sql"><code class="sourceCode sql"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Mauvais JOIN</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> g.id_garniture, <span class="fu">COUNT</span>(gp.id_pizza)</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> garniture g</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="kw">INNER</span> <span class="kw">JOIN</span> garniture_pizza gp <span class="kw">ON</span> gp.id_garniture <span class="op">=</span> g.id_garniture</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="kw">GROUP</span> <span class="kw">BY</span> g.id_garniture</span></code></pre></div>
        <p>Il aura bien une erreur:
        <code>ERROR: 2 garnitures attendues, reçu 1</code>, car le
        <code>INNER JOIN</code> aura éliminé la garniture 2. S’iel fait
        un <code>LEFT JOIN</code> en laissant le reste identique, tous
        le tests passeront. S’il se trompe dans le <code>COUNT</code> et
        écrit:</p>
        <div class="sourceCode" id="cb7"><pre
        class="sourceCode sql"><code class="sourceCode sql"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Mauvais COUNT</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> g.id_garniture, <span class="fu">COUNT</span>(g.id_garniture)</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> garniture g</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="kw">LEFT</span> <span class="kw">JOIN</span> garniture_pizza gp <span class="kw">ON</span> gp.id_garniture <span class="op">=</span> g.id_garniture</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="kw">GROUP</span> <span class="kw">BY</span> g.id_garniture</span></code></pre></div>
        <p>Iel aura:
        <code>ERROR: 0 pizzas attendues pour la garniture 2, reçu 1</code>.</p>
        <p>Le code complet avec la requête correcte:</p>
        <div class="sourceCode" id="cb8"><pre
        class="sourceCode sql"><code class="sourceCode sql"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">FUNCTION</span> nb_pizzas_par_garniture()</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>RETURNS <span class="kw">TABLE</span> (</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    id_garniture <span class="dt">int</span>,</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    nb_pizzas <span class="dt">int</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>) language sql <span class="kw">as</span> $$</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- LA REQUÊTE EST ICI:</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> g.id_garniture, <span class="fu">COUNT</span>(gp.id_garniture)</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> garniture g</span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="kw">LEFT</span> <span class="kw">JOIN</span> garniture_pizza gp <span class="kw">ON</span> gp.id_garniture <span class="op">=</span> g.id_garniture</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="kw">GROUP</span> <span class="kw">BY</span> g.id_garniture</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a><span class="co">-- FIN DE LA REQUÊTE</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>$$;</span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">PROCEDURE</span> test_nb_pizzas_par_garniture() <span class="kw">AS</span> $$</span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a>    <span class="cf">BEGIN</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- supprimer toutes les données</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a>        <span class="kw">TRUNCATE</span> pizza, garniture, garniture_pizza;</span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- données minimales pour le test</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a>        <span class="kw">INSERT</span> <span class="kw">INTO</span> pizza <span class="kw">VALUES</span> (<span class="dv">1</span>, <span class="st">&#39;pizza 1&#39;</span>, <span class="dv">0</span>),</span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a>                                 (<span class="dv">2</span>, <span class="st">&#39;pizza 2&#39;</span>, <span class="dv">0</span>),</span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a>                                 (<span class="dv">3</span>, <span class="st">&#39;pizza 3&#39;</span>, <span class="dv">0</span>);</span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a>        <span class="kw">INSERT</span> <span class="kw">INTO</span> garniture <span class="kw">VALUES</span> (<span class="dv">1</span>, <span class="st">&#39;garniture 1&#39;</span>),</span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a>                                     (<span class="dv">2</span>, <span class="st">&#39;garniture_2&#39;</span>);</span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true" tabindex="-1"></a>        <span class="kw">INSERT</span> <span class="kw">INTO</span> garniture_pizza <span class="kw">VALUES</span> (<span class="dv">1</span>, <span class="dv">1</span>),</span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true" tabindex="-1"></a>                                           (<span class="dv">2</span>, <span class="dv">1</span>);</span>
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true" tabindex="-1"></a>        <span class="kw">CREATE</span> TEMP <span class="kw">TABLE</span> result <span class="kw">ON</span> <span class="kw">COMMIT</span> <span class="kw">DROP</span> <span class="kw">AS</span></span>
<span id="cb8-29"><a href="#cb8-29" aria-hidden="true" tabindex="-1"></a>        <span class="kw">SELECT</span> <span class="op">*</span> <span class="kw">FROM</span> nb_pizzas_par_garniture();</span>
<span id="cb8-30"><a href="#cb8-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-31"><a href="#cb8-31" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- 2 garnitures trouvées</span></span>
<span id="cb8-32"><a href="#cb8-32" aria-hidden="true" tabindex="-1"></a>        ASSERT (<span class="kw">SELECT</span> <span class="fu">COUNT</span>(<span class="op">*</span>) <span class="kw">FROM</span> result) <span class="op">=</span> <span class="dv">2</span>,</span>
<span id="cb8-33"><a href="#cb8-33" aria-hidden="true" tabindex="-1"></a>            <span class="st">&#39;2 garnitures attendues, reçu &#39;</span> <span class="op">||</span> (<span class="kw">SELECT</span> <span class="fu">COUNT</span>(<span class="op">*</span>) <span class="kw">FROM</span> result):<span class="ch">:text</span>;</span>
<span id="cb8-34"><a href="#cb8-34" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- garniture 1 -&gt; 2 pizzas</span></span>
<span id="cb8-35"><a href="#cb8-35" aria-hidden="true" tabindex="-1"></a>        ASSERT (<span class="kw">SELECT</span> nb_pizzas <span class="kw">FROM</span> result <span class="kw">WHERE</span> id_garniture <span class="op">=</span> <span class="dv">1</span>) <span class="op">=</span> <span class="dv">2</span>,</span>
<span id="cb8-36"><a href="#cb8-36" aria-hidden="true" tabindex="-1"></a>            <span class="st">&#39;2 pizzas attendues pour la garniture 1, reçu &#39;</span> <span class="op">||</span> (<span class="kw">SELECT</span> nb_pizzas <span class="kw">FROM</span> result <span class="kw">WHERE</span> id_garniture <span class="op">=</span> <span class="dv">1</span>):<span class="ch">:text</span>;</span>
<span id="cb8-37"><a href="#cb8-37" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- garniture 2 -&gt; 0 pizzas</span></span>
<span id="cb8-38"><a href="#cb8-38" aria-hidden="true" tabindex="-1"></a>        ASSERT (<span class="kw">SELECT</span> nb_pizzas <span class="kw">FROM</span> result <span class="kw">WHERE</span> id_garniture <span class="op">=</span> <span class="dv">2</span>) <span class="op">=</span> <span class="dv">0</span>,</span>
<span id="cb8-39"><a href="#cb8-39" aria-hidden="true" tabindex="-1"></a>            <span class="st">&#39;0 pizzas attendues pour la garniture 2, reçu &#39;</span> <span class="op">||</span> (<span class="kw">SELECT</span> nb_pizzas <span class="kw">FROM</span> result <span class="kw">WHERE</span> id_garniture <span class="op">=</span> <span class="dv">2</span>):<span class="ch">:text</span>;</span>
<span id="cb8-40"><a href="#cb8-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-41"><a href="#cb8-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-42"><a href="#cb8-42" aria-hidden="true" tabindex="-1"></a>        <span class="kw">ROLLBACK</span>;</span>
<span id="cb8-43"><a href="#cb8-43" aria-hidden="true" tabindex="-1"></a>    <span class="cf">END</span>;</span>
<span id="cb8-44"><a href="#cb8-44" aria-hidden="true" tabindex="-1"></a>$$ LANGUAGE plpgsql;</span>
<span id="cb8-45"><a href="#cb8-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-46"><a href="#cb8-46" aria-hidden="true" tabindex="-1"></a><span class="kw">call</span> test_nb_pizzas_par_garniture();</span></code></pre></div>
        <p>Est-ce que c’est beaucoup de boulot juste pour tester une
        requête? Oui, mais je peux voir des cas où cela vaudrait la
        peine. Par exemple dans un examen ou une interro, pouvoir
        valider rapidement que les résultats attendus ont été obtenus
        peut éviter des erreurs de correction.</p>
        <p>Évidemment, ces tests ne détecteront pas les codes
        sous-optimaux. Par exemple, je pourrais ici ajouter une jointure
        inutile à <code>pizza</code> et compter sur
        <code>p.id_pizza</code>, et les tests passeront.</p>
        <p>Mais je trouve que l’idée reste intéressante.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2025.10.17.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Thu, 16 Oct 2025 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2025.10.07 les maths, c'est chouette?</title>
    <link>https://adfoucart.be/clavier/2025.10.07.html</link>
    <description>
        <h2><span class='emoji'>➕</span>2025.10.07<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2025.10.07.html">les
maths, c’est chouette?</a></span></h2>
        <p>Donner les exercices de mathématiques n’était pas vraiment la
        tâche qui m’emballait le plus dans mes assignations du
        quadrimestre. Nos étudiant.e.s n’ont pas un profil “maths
        fortes” à la base, et ne viennent certainement pas dans une
        école d’informatique dans le but de faire des maths. Tout
        l’enjeux, c’est donc de leur faire comprendre <em>pourquoi</em>,
        au juste, on fait faire des maths à des futurs
        informaticien.ne.s.</p>
        <p>Parce que les maths qu’on leur fait voir sont, heureusement,
        adaptées à leurs besoins. On ne fait pas d’analyse complexe: on
        fait de la logique, de la théorie des ensembles, des suites. Des
        concepts qui ont un lien direct et raisonnablement facile à
        tracer avec les programmes qu’ils vont devoir développer.</p>
        <p>Et cet exercice-là, trouver les liens, voir les étudiants
        passer d’un désintérêt fataliste à, si pas de l’intérêt, au
        moins une certaine prise de conscience que ce qu’on est en train
        de voir, ce n’est pas juste pour les embêter… je trouve ça
        chouette.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2025.10.07.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Mon, 06 Oct 2025 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2025.10.03 le temps file</title>
    <link>https://adfoucart.be/clavier/2025.10.03.html</link>
    <description>
        <h2><span class='emoji'>⏱️</span>2025.10.03<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2025.10.03.html">le
temps file</a></span></h2>
        <p>Je m’attendais à ce que le rythme soit soutenu, mais s’y
        attendre et le vivre, ce sont deux choses bien différentes.
        Quand on regarde l’horaire, on se dit “ça va”. Après tout, il y
        a “seulement” vingt heures par semaine face aux étudiant.e.s
        dans un temps plein. Certain.e.s expert.e.s semblent juger que,
        pour les enseignant.e.s du secondaire en tout cas, c’est trop
        peu [<a
        href="https://www.rtbf.be/article/reduire-le-deficit-de-la-fwb-est-une-necessite-urgente-et-absolue-appuie-un-rapport-d-experts-11606293">rtbf.be</a>].
        Je ne vois personnellement pas comment on pourrait faire plus en
        gardant un minimum de qualité.</p>
        <p>Cette semaine est passée en un clin d’oeil. Pourtant, j’ai un
        rôle facile. Je suis arrivé tard, tous les cours ou presque sont
        déjà préparés. Je n’ai qu’à être présent pour les étudiant.e.s,
        répondre à leurs questions, faire l’une ou l’autre correction au
        tableau. La matière n’est pas particulièrement compliquée. Mais
        c’est toujours la course. Donner cours, relire les notes, boire
        un café, donner cours, écrire des notes, manger, donner cours,
        rentrer à la maison.</p>
        <p>Je n’ai pas à me plaindre. Mais un prof qui ne se plaint pas
        un peu, est-ce que c’est vraiment un prof?</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2025.10.03.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Thu, 02 Oct 2025 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2025.09.28 rentrée tardive</title>
    <link>https://adfoucart.be/clavier/2025.09.28.html</link>
    <description>
        <h2><span class='emoji'>👨‍🏫</span>2025.09.28<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2025.09.28.html">rentrée
tardive</a></span></h2>
        <p>Il y a déjà beaucoup de dates de rentrée des classes en
        Belgique. Pour ma fille, c’était le 25 août. Pour ses ami·e·s en
        école néerlandophone, c’était le 1er septembre. Pour la plupart
        des étudiant·e·s et enseignant·e·s du supérieur, c’était le 15
        septembre. Cette année, je suis un petit peu en retard: pour
        moi, ce sera le 29 septembre. La transition avec mon <a
        href="https://research.adfoucart.be">ancien travail</a> aurait
        été un peu trop abrupte, mon engagement était un peu trop
        tardif, je ne voulais pas laisser un trop grand chantier
        derrière moi.</p>
        <p>Du coup, j’ai des choses à rattraper.</p>
        <p>Mes collègues connaissent déjà, un peu, la cohorte de jeunes
        qui s’amasse dans nos classes et nos auditoires. Ils sont déjà
        rentrés dans leur routine. Pour ce premier quadrimestre de ma
        nouvelle vie d’enseignant, je viens m’intercaler comme je peux
        dans des cours et des exercices préparés par d’autres. J’espère
        ne pas tomber comme un cheveu dans la soupe, mais plutôt comme
        du parmesan sur un plat de spaghettis: on aurait pu faire sans,
        mais ma présence est appréciée!</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2025.09.28.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Sat, 27 Sep 2025 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2025.09.24 merci Google</title>
    <link>https://adfoucart.be/clavier/2025.09.24.html</link>
    <description>
        <h2><span class='emoji'>🔎</span>2025.09.24<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2025.09.24.html">merci
Google</a></span></h2>
        <p>En développant riem [<a
        href="https://gitlab.ulb.be/lisa/ia/protherwal/riem-project">riem-project
        on gitlab.ulb.be</a>], le projet que je suis en train de
        terminer (enfin, clôturer plutôt) dans mes derniers jours de
        chercheur à l’ULB, j’utilise le “viewer” Napari [<a
        href="https://napari.org/">napari.org</a>] pour visualiser et
        annoter des images médicales. Napari est une excellente libraire
        Python, mais sa documentation est parfois un peu bancale. Je
        dois donc souvent faire appel à Google pour essayer de trouver
        comment certaines fonctionnalités fonctionnent.</p>
        <p>Je cherchais en particulier comment pouvoir intercepter
        l’événement déclenché lorsqu’on ajoute un point dans
        l’interface. Je vous passe les détails du pourquoi. J’ai donc
        ouvert Google, et j’ai lancé la requête
        <code>napari event when point added to layer</code>.</p>
        <p>Google ne donne plus des “résultats de recherche” comme
        première option, c’est dépassé. Au lieu de ça, j’ai évidemment
        eu droit à la fameuse “AI Overview”. En général, j’ignore l’AI
        Overview autant que possible. Mais étant un peu pressé, j’ai
        tout de même voulu tester cette fois-ci la solution qu’il me
        proposait. Le code fourni n’était pas utilisable tel quel, mais
        j’avais les lignes qui m’intéressaient:</p>
        <p>Pour pouvoir intercepter l’événement:</p>
        <div class="sourceCode" id="cb1"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>points_layer.events.data.<span class="ex">connect</span>(on_point_added)</span></code></pre></div>
        <p>Et pour le traiter:</p>
        <div class="sourceCode" id="cb2"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> on_point_added(event):</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> event.<span class="bu">type</span> <span class="op">==</span> <span class="st">&#39;add&#39;</span>:</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(<span class="st">&quot;Point added!&quot;</span>)</span></code></pre></div>
        <p>J’ai ajouté cela à mon programme et, sans grande surprise, je
        reçus en retour une erreur: <code>type</code> n’est pas un
        attribut de <code>event</code>. Zut. Mais où Google a-t-il
        cherché ce <code>event.type</code>? Heureusement, l’AI Overview
        fourni des sources. Dans ce cas-ci, la principale était une
        discussions sur Github [<a
        href="https://github.com/napari/napari/issues/2809">issue#2809
        on github.com/napari</a>], datant de 2021, et proposant de
        modifier le comportement du <em>data event</em> de Napari pour y
        ajouter un type, qui pourrait être “set”, “move”, “add” ou
        “remove”.</p>
        <p>Le problème, évidemment, c’est qu’entre le moment où cette
        proposition a été faite et le moment où la fonctionnalité a été
        rajoutée dans Napari, deux ans se sont écoulés, et l’idée a fait
        son chemin et s’est transformée [<a
        href="https://github.com/napari/napari/pull/5967">pull#5967 on
        github.com/napari</a>]. Dans la description du “pull request”,
        on voit que finalement l’événement n’a pas de type mais est
        associé à une <code>action</code>, qui peut être
        <code>add</code>, <code>remove</code> ou <code>change</code>. Le
        bon code serait donc:</p>
        <div class="sourceCode" id="cb3"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> on_point_added(event):</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> event.action <span class="op">==</span> <span class="st">&#39;add&#39;</span>:</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(<span class="st">&quot;Point added!&quot;</span>)</span></code></pre></div>
        <p>Sauf que ça ne marche toujours pas. Heureusement, une fois
        qu’on sait quels fichiers sont impliqués dans cette
        fonctionnalité [<a
        href="https://github.com/napari/napari/pull/5967/files">pull#5967
        files on github.com/napari</a>], on peut aller voir à quoi ils
        ressemblent aujourd’hui. Et on peut ainsi retrouver que les
        actions ont été modifiées peu de temps après leur ajout [<a
        href="https://github.com/napari/napari/commit/bc3c1e3a3dacb2b94962f90e1e8541892503538b">commit#bc3c1e3a
        on github.com/napari</a>]: dorénavant, on trouve
        <code>adding</code>, <code>removing</code>,
        <code>changing</code>, <code>added</code>, <code>removed</code>
        et <code>changed</code>.</p>
        <p>On peut donc faire:</p>
        <div class="sourceCode" id="cb4"><pre
        class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> on_point_added(event):</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> event.action <span class="op">==</span> <span class="st">&#39;adding&#39;</span>:</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(<span class="st">&quot;Appelé avant ajout du point&quot;</span>)</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">elif</span> event.action <span class="op">==</span> <span class="st">&#39;added&#39;</span>:</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(<span class="st">&quot;Appelé après ajout du point&quot;</span>)</span></code></pre></div>
        <p>Ce qui fonctionne enfin.</p>
        <p>L’AI Overview, une nouvelle fois, n’aura servit qu’à ralentir
        le processus, et à entraver l’apprentissage: quand bien même la
        réponse fournie aurait fonctionné, le fait de lire, même en
        diagonale, les pull requests et les codes des commits est
        extrêmement informatif sur le fonctionnement de la librairie. Si
        Google pouvait faire ce qu’il faisait tellement bien avant,
        c’est-à-dire me diriger tout de suite vers les bonnes sources
        d’information, il serait tellement plus utile !</p>
        <p>Mais nous vivons à l’ère du tout-à-l’IA, et on ne peut pas se
        permettre de bêtement donner des informations correctes ou
        utiles… Par curiosité, j’ai regardé ce que Copilot et ChatGPT me
        proposaient, et aucun des deux n’avaient de bonne solution non
        plus.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2025.09.24.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Tue, 23 Sep 2025 22:00:00 -0000</pubDate>
</item>
<item>
    <title>2025.09.22 des blogs</title>
    <link>https://adfoucart.be/clavier/2025.09.22.html</link>
    <description>
        <h2><span class='emoji'>💬</span>2025.09.22<br /><span class='article-title'><a href="https://adfoucart.be/clavier/2025.09.22.html">des
blogs</a></span></h2>
        <p>J’aime bien faire des blogs. Sans doute un truc d’enfant des
        années Skyblog (même si je ne pense pas que j’en avais un, mais
        ma mémoire me fait peut-être défaut). C’est important pour moi
        d’écrire sur ce que je fais et sur ce que je pense. Sur mon blog
        d’opinions souvent plus politiques [<a
        href="https://blog.adfoucart.be">2xRien</a>], il m’est bien
        souvent arrivé de démarrer un article avec une opinion que je
        voulais partager, puis de me rendre compte en l’écrivant, et en
        cherchant des sources pour vérifier mes a priori, que c’était
        une opinion que je n’étais en réalité pas capable de défendre.
        Ou qu’elle était banale et sans intérêt. Même ces articles
        jamais publiés m’aident à façonner et tester mes idées.</p>
        <p>Mon blog de recherche [<a
        href="https://research.adfoucart.be">Research blog</a>] était
        une extension naturelle: je voulais dissocier mes opinions
        <em>personnelles</em> de mes opinions <em>professionnelles</em>,
        dans lesquelles j’engageais mon expertise (et ma légitimité) de
        chercheur. Et puis, quand je communiquais sur ma recherche, je
        préférais le faire en Anglais.</p>
        <p>Mais à partir du 29 septembre, je ne serai plus chercheur. Il
        va donc être temps de clôturer le blog de recherche, ou en tout
        cas de le mettre de côté (je resterai en contact avec la
        recherche, mais sur mon temps personnel). Je commence une
        nouvelle carrière d’enseignant en informatique. L’occasion de
        repartir à zéro sur un nouveau blog, que voici sous nos yeux
        ébahis. J’y parlerai d’enseignement, d’informatique, de
        technologies, d’intelligence artificielle, de programmation, de
        Python, de Java, de tout ce qui peut piquer mon intérêt et que
        j’ai envie de partager avec ma casquette “enseignant” et qui ne
        rentre pas dans la “ligne éditoriale” de “2xRien”. Le choix de
        publier dans l’un ou l’autre sera arbitraire et jamais justifié.
        On va dire que c’est du “vibe blogging”, sauf que ce sera
        vraiment moi qui écrirai.</p>
        <p>Car si je ne garantis rien sur les contenus (ni sur les
        thèmes, ni sur la qualité!), je garantis que tout ce qui est
        écris ici (ou sur tout autre de mes blogs) l’est entièrement par
        moi. Écrire m’aide à penser: si l’IA écrit pour moi, elle
        pensera aussi pour moi, et vu l’alignement politique de ceux qui
        construisent ces IAs [<a
        href="https://www.whitehouse.gov/articles/2025/09/president-trump-tech-leaders-unite-american-ai-dominance/">whitehouse.gov</a>],
        je n’ai pas trop envie qu’elles aient trop d’influence sur mes
        pensées.</p>
        <p class='permalink'>[<a href="https://adfoucart.be/clavier/2025.09.22.html">lien permanent</a>]</p>
        <div id="contact">Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur <a rel="me" href="https://social.sciences.re/@AFoucart">Mastodon</a> ou par mail (adrien@adfoucart.be)</div>
    </description>
    <author>Adrien Foucart &lt;adrien@adfoucart.be&gt;</author> 
    <pubDate>Sun, 21 Sep 2025 22:00:00 -0000</pubDate>
</item>

</channel>
</rss>