<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.pwkf.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.pwkf.org/" rel="alternate" type="text/html" /><updated>2026-03-29T18:08:23+00:00</updated><id>https://blog.pwkf.org/feed.xml</id><title type="html">Personal Workflow Blog</title><subtitle>Some thoughts I encountered during my working day in the J2EE land. Originally a blog about PWKF, an easy-to-use workflow solution. Yet, as I have less time to work on PWKF than before, it morphed into a generic blog.</subtitle><author><name>Steve SCHNEPP</name></author><entry><title type="html">Amalgamate C Sources for Online IDE Compilers</title><link href="https://blog.pwkf.org/2026/03/27/amalgamate.html" rel="alternate" type="text/html" title="Amalgamate C Sources for Online IDE Compilers" /><published>2026-03-27T00:00:00+00:00</published><updated>2026-03-27T00:00:00+00:00</updated><id>https://blog.pwkf.org/2026/03/27/amalgamate</id><content type="html" xml:base="https://blog.pwkf.org/2026/03/27/amalgamate.html"><![CDATA[<p>I use <a href="https://godbolt.org/">Compiler Explorer</a> and <a href="https://pgetinker.com/">PGE Tinker</a> regularly. Yet both take only a single C or C++ file as input. Most of the projects do not. This tool closes that gap.</p>

<p>The <a href="https://github.com/steveschnepp/amalgamate/">full source</a> is one C file.</p>

<h2 id="what-it-does">What it does</h2>

<p>It walks a C source tree and inlines all <code class="language-plaintext highlighter-rouge">#include "local.h"</code> directives recursively. System includes (<code class="language-plaintext highlighter-rouge">&lt;stdio.h&gt;</code>) pass through untouched. The result is one <code class="language-plaintext highlighter-rouge">.c</code> file you can paste anywhere.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./amalgamate main.c utils.c <span class="o">&gt;</span> squashed.c
</code></pre></div></div>

<p>Pass every <code class="language-plaintext highlighter-rouge">.c</code> that contributes symbols. Order matters: the compiler sees the file top to bottom, definitions before use.</p>

<p>Build on Linux or MSYS2 UCRT64:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-std</span><span class="o">=</span>c99 <span class="nt">-Wall</span> <span class="nt">-Wextra</span> <span class="nt">-o</span> amalgamate amalgamate.c
</code></pre></div></div>

<h2 id="resolution-order">Resolution order</h2>

<p>For each <code class="language-plaintext highlighter-rouge">#include "name.h"</code> the tool tries, in order: the directory of the including file, each <code class="language-plaintext highlighter-rouge">-I</code> path, then <code class="language-plaintext highlighter-rouge">cwd</code>. System includes (<code class="language-plaintext highlighter-rouge">&lt;name.h&gt;</code>) are never touched.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./amalgamate <span class="nt">-I</span> include/ <span class="nt">-I</span> vendor/include/ main.c utils.c <span class="o">&gt;</span> squashed.c
</code></pre></div></div>

<p>Both <code class="language-plaintext highlighter-rouge">-I dir</code> and <code class="language-plaintext highlighter-rouge">-Idir</code> forms work, matching gcc convention.</p>

<h2 id="what-works">What works</h2>

<p>The typical multi-file C project amalgamates cleanly. Given a layout like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>main.c     -- includes utils.h, math.h
utils.c    -- defines add(), and sub() not declared in utils.h
math.c     -- defines mul(), and square() not declared in math.h
types.h    -- shared typedefs, included by both utils.h and math.h
</code></pre></div></div>

<p>Header guards (<code class="language-plaintext highlighter-rouge">#ifndef</code>) survive intact. <code class="language-plaintext highlighter-rouge">types.h</code> is inlined every time it appears, but the preprocessor deduplicates it. That is the right behavior — the tool doesn’t second-guess the preprocessor.</p>

<p>Symbols not declared in any header (<code class="language-plaintext highlighter-rouge">sub</code>, <code class="language-plaintext highlighter-rouge">square</code> above) are present in the output because their <code class="language-plaintext highlighter-rouge">.c</code> files were passed explicitly. The caller must <code class="language-plaintext highlighter-rouge">extern</code>-declare them. Same constraint as with a normal linker.</p>

<h2 id="what-breaks">What breaks</h2>

<p><strong>Colliding <code class="language-plaintext highlighter-rouge">static</code> names across translation units.</strong></p>

<p><code class="language-plaintext highlighter-rouge">static</code> limits linkage, not scope. In separate TUs the compiler never sees both definitions. In a single TU it does. C99 forbids two definitions of the same name in the same scope, even if both are <code class="language-plaintext highlighter-rouge">static</code>.</p>

<p>This compiles fine as separate TUs:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* utils.c */</span>
<span class="k">static</span> <span class="n">i32</span> <span class="nf">helper</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>

<span class="cm">/* math.c */</span>
<span class="k">static</span> <span class="kt">double</span> <span class="nf">helper</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="mi">3</span><span class="p">.</span><span class="mi">14</span><span class="p">;</span> <span class="p">}</span>  <span class="cm">/* same name, different type */</span>
</code></pre></div></div>

<p>Amalgamated, it fails:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error: conflicting types for 'helper'; have 'double(void)'
note: previous definition of 'helper' with type 'i32(void)'
</code></pre></div></div>

<p>The fix is manual: adopt a prefix convention before amalgamating.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* utils.c */</span>
<span class="k">static</span> <span class="n">i32</span> <span class="nf">utils_helper</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>

<span class="cm">/* math.c */</span>
<span class="k">static</span> <span class="kt">double</span> <span class="nf">math_helper</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="mi">3</span><span class="p">.</span><span class="mi">14</span><span class="p">;</span> <span class="p">}</span>
</code></pre></div></div>

<p class="panel warning">The tool cannot rename symbols. That requires a parser. This tool is not a parser.</p>

<p><strong>Include cycles</strong> are detected and fatal:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>include cycle detected: /src/cycle_a.h
  /src/main.c
  /src/cycle_a.h
  /src/cycle_b.h
</code></pre></div></div>

<p>That is always a bug in the source. Fix the source.</p>

<h2 id="flags">Flags</h2>

<p><code class="language-plaintext highlighter-rouge">--with-line</code> emits <code class="language-plaintext highlighter-rouge">#line</code> directives around each inlined file. Without them, a compiler error in the amalgamated output gives a line number that maps to nothing in your editor. With them, errors point back to the original file.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./amalgamate <span class="nt">--with-line</span> main.c utils.c <span class="o">&gt;</span> squashed.c
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">--once</code> skips a file if already emitted. Useful for projects that use <code class="language-plaintext highlighter-rouge">#pragma once</code> instead of header guards, or to avoid redundant inlining across many TUs.</p>

<p><code class="language-plaintext highlighter-rouge">--list</code> prints the resolved path of every file that would be inlined, without producing output. Useful for auditing the dependency tree.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./amalgamate <span class="nt">--list</span> main.c utils.c
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">-x file</code> excludes a specific file from inlining. The <code class="language-plaintext highlighter-rouge">#include</code> is replaced with a tombstone comment; the compiler sees nothing. Useful when one header must remain external.</p>

<p><code class="language-plaintext highlighter-rouge">-o outfile</code> writes to a file instead of stdout.</p>

<p><code class="language-plaintext highlighter-rouge">-v</code> prints the include tree to stderr, indented by depth.</p>

<p class="panel tip">Keep it small.</p>]]></content><author><name>Steve SCHNEPP</name></author><category term="development" /><category term="c" /><category term="tooling" /><summary type="html"><![CDATA[I use Compiler Explorer and PGE Tinker regularly. Yet both take only a single C or C++ file as input. Most of the projects do not. This tool closes that gap.]]></summary></entry><entry><title type="html">Prompt Engineering Frameworks - A Practical Overview</title><link href="https://blog.pwkf.org/2026/03/17/prompt-engineering-howtos.html" rel="alternate" type="text/html" title="Prompt Engineering Frameworks - A Practical Overview" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://blog.pwkf.org/2026/03/17/prompt-engineering-howtos</id><content type="html" xml:base="https://blog.pwkf.org/2026/03/17/prompt-engineering-howtos.html"><![CDATA[<p>There is a recurrent question when starting to use LLMs seriously :
<em>“How do I structure my prompts ?”</em>.</p>

<p>Several frameworks emerged to answer that question.
Let me go through the most common ones, with practical examples.</p>

<h2 id="why-bother-with-a-framework-">Why bother with a framework ?</h2>

<p>A prompt is just text. Yet, a poorly written prompt yields a poor result.
A well-structured one yields a precise, useful one.</p>

<p>The frameworks are just mnemonics. They are a checklist to avoid forgetting
important context. Nothing more, nothing less.</p>

<p>A good framework <a href="https://blog.pwkf.org/2025/04/17/water-flows-down.html">makes the right path the easy path</a>.
Without one, the path of least resistance is to skip context entirely.</p>

<p>Think of them the same way one might think of <a href="https://blog.pwkf.org/2023/08/17/one-sloc.html">1OPS</a> :
the rule itself is simple, but its value comes from <em>discipline</em> — not from the rule.</p>

<h2 id="craft">CRAFT</h2>

<p><strong>Context, Role, Action, Format, Target</strong></p>

<p>The most widely used one. Easy to remember, covers most cases.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Context: Building a 2D platformer in C99
Role:    Senior game engine developer
Action:  Write an AABB collision detection system
Format:  Annotated C99, Linux kernel style
Target:  Embedded / low-level developers
</code></pre></div></div>

<p>A good default. Start here.</p>

<h2 id="risen">RISEN</h2>

<p><strong>Role, Instructions, Steps, End goal, Narrowing</strong></p>

<p>Better suited for multi-step tasks where the order matters.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Role:         Physics programmer
Instructions: Implement sweep-and-prune broadphase
Steps:        1) Define axis-sorted list  2) Find overlapping pairs  3) Output pair buffer
End goal:     Reusable broadphase module
Narrowing:    C99, no dynamic allocation, &lt; 300 lines
</code></pre></div></div>

<p>The <em>Narrowing</em> part is the most valuable addition over CRAFT.
It is where you exclude what you don’t want — which is often
<a href="https://blog.pwkf.org/2022/09/18/always-optimize-for-dummies.html">as important as what you do want</a>.</p>

<p>Saying <em>“no dynamic allocation”</em> is more useful than any amount of positive
description. The model cannot read your mind, but it can follow a constraint.</p>

<h2 id="create">CREATE</h2>

<p><strong>Character, Request, Extras, Adjustments, Type, Extras</strong><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<p>More creative and generative in orientation.
The double <em>Extras</em> is intentional :
first pass for constraints, second pass for polish.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Character:   Senior graphics programmer
Request:     Implement a sprite batch renderer
Extras:      Must sustain 10k sprites @ 60fps
Adjustments: Vulkan only, no OpenGL
Type:        C99 source file
Extras:      Inline performance notes
</code></pre></div></div>

<p>The distinction between <em>Extras</em> and <em>Adjustments</em> is subtle but useful :
<em>Extras</em> adds, <em>Adjustments</em> redirects.</p>

<h2 id="care">CARE</h2>

<p><strong>Context, Ask, Rules, Examples</strong></p>

<p>Best when you have concrete I/O examples. Closest to a proper specification.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Context:  Tile-based binary map loader
Ask:      Parse format into a struct
Rules:    No malloc, fixed buffers, C99 only
Examples: [binary sample] → [expected struct output]
</code></pre></div></div>

<p>The <em>Examples</em> part is the killer feature here.
An LLM guided by examples makes far fewer wrong assumptions.</p>

<p>The <em>Rules</em> field deserves equal attention.
<a href="https://blog.pwkf.org/post/2010/03/API-Design%3A-Hidden-costs-of-simple-features">What you forbid is as load-bearing as what you permit</a> :
an unconstrained surface invites the wrong solution every time.</p>

<p>This maps directly to how <a href="https://blog.pwkf.org/2022/09/18/always-optimize-for-dummies.html">always optimizing for junior devs</a>
works in code : concrete examples remove ambiguity faster than any amount of prose.</p>

<h2 id="clear">CLEAR</h2>

<p><strong>Concise, Logical, Explicit, Audience, Role</strong></p>

<p>A meta-framework of sorts. Less about structure, more about quality attributes
of each component.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Concise:   "Write a ring buffer"
Logical:   "for audio streaming, SPSC"
Explicit:  "thread-safe via atomics, no locks"
Audience:  Embedded C developers
Role:      DSP systems programmer
</code></pre></div></div>

<p>Useful as a review checklist once the prompt is written rather than as a
writing guide. Run it <em>after</em> CRAFT, not instead of it.</p>

<h2 id="comparison">Comparison</h2>

<table>
  <thead>
    <tr>
      <th>Framework</th>
      <th>Sweet spot</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CRAFT</td>
      <td>General purpose, good default</td>
    </tr>
    <tr>
      <td>RISEN</td>
      <td>Multi-step, ordered tasks</td>
    </tr>
    <tr>
      <td>CREATE</td>
      <td>Creative / generative output</td>
    </tr>
    <tr>
      <td>CARE</td>
      <td>Data transformation, precise I/O</td>
    </tr>
    <tr>
      <td>CLEAR</td>
      <td>Quality review of existing prompt</td>
    </tr>
  </tbody>
</table>

<h2 id="in-practice">In practice</h2>

<p>No need to pick one and stick to it forever.
CRAFT covers ~80% of daily use.
Reach for RISEN when the task has sequencing constraints.
Use CARE when you can supply examples — it gives the most deterministic output.</p>

<p>The frameworks are not mutually exclusive either.
A CRAFT prompt can embed CARE’s <em>Examples</em> without issue.</p>

<p>There is an obvious parallel with <a href="https://blog.pwkf.org/2023/06/28/cool-stuff-versus-useful-stuff.html">cool vs useful</a> :
every new framework looks interesting, but the boring, reliable one does the
actual job. CRAFT is boring. Use CRAFT.</p>

<p>The real takeaway is : <strong>always include what the output is for and who reads it</strong>.
That single addition — <em>Target</em> in CRAFT, <em>Audience</em> in CLEAR — has the
biggest impact on output quality, and is the most commonly forgotten element.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>The second <em>E</em> in CREATE varies by source. Some use <em>Evaluation</em>, some use <em>Extras</em>. The meaning differs slightly but the intent is the same : a second pass for refinements. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>This article was written with <a href="https://claude.ai">Claude</a>. Asking an AI to explain how to talk to an AI is either the most efficient use of a prompt framework, or a remarkably short loop. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Steve SCHNEPP</name></author><category term="ai-authored" /><category term="best-practices" /><category term="development" /><category term="learning" /><summary type="html"><![CDATA[There is a recurrent question when starting to use LLMs seriously : “How do I structure my prompts ?”.]]></summary></entry><entry><title type="html">Optimize for Size with Preprocessor Directives</title><link href="https://blog.pwkf.org/2025/12/19/optimize-for-size-preprocessor.html" rel="alternate" type="text/html" title="Optimize for Size with Preprocessor Directives" /><published>2025-12-19T00:00:00+00:00</published><updated>2025-12-19T00:00:00+00:00</updated><id>https://blog.pwkf.org/2025/12/19/optimize-for-size-preprocessor</id><content type="html" xml:base="https://blog.pwkf.org/2025/12/19/optimize-for-size-preprocessor.html"><![CDATA[<p>When working on embedded systems, optimizing for size can be crucial.
Let’s even go beyond the compiler optimization flags by actually generating different code paths based on whether we are optimizing for size or speed.</p>

<h2 id="explore-standard-preprocessor-directives">Explore Standard Preprocessor Directives</h2>

<p>As the GCC manual tells us, you can know the default preprocessor definitions for your target by running:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-dM</span> <span class="nt">-E</span> - &lt; /dev/null
</code></pre></div></div>

<p>This will output a list of all predefined macros for your target architecture.</p>

<p>Note that it also work with optimisation flags, therefore to know the difference for each optimization level, you can run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gcc <span class="nt">-dM</span> <span class="nt">-E</span> <span class="nt">-O0</span> - &lt; /dev/null <span class="o">&gt;</span> defs_O0.txt
gcc <span class="nt">-dM</span> <span class="nt">-E</span> <span class="nt">-Os</span> - &lt; /dev/null <span class="o">&gt;</span> defs_Os.txt

<span class="nv">$ </span>diff  defs_O0.txt defs_Os.txt
60a61
<span class="o">&gt;</span> <span class="c">#define __OPTIMIZE__ 1</span>
182d182
&lt; <span class="c">#define __NO_INLINE__ 1</span>
</code></pre></div></div>

<p>The flags are documented in the GCC manual <a href="https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html">Common Predefined Macros</a> page.</p>

<p>An excerpt of interest is:</p>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">__OPTIMIZE__</code>
<code class="language-plaintext highlighter-rouge">__OPTIMIZE_SIZE__</code>
<code class="language-plaintext highlighter-rouge">__NO_INLINE__</code></p>

  <p>These macros describe the compilation mode. <code class="language-plaintext highlighter-rouge">__OPTIMIZE__</code> is defined in all optimizing compilations.
<code class="language-plaintext highlighter-rouge">__OPTIMIZE_SIZE__</code> is defined if the compiler is optimizing for size, not speed.
<code class="language-plaintext highlighter-rouge">__NO_INLINE__</code> is defined if no functions will be inlined into their callers (when not optimizing, or when inlining has been specifically disabled by -fno-inline).</p>

  <p>These macros cause certain GNU header files to provide optimized definitions, using macros
or inline functions, of system library functions. You should not use these macros in any way
unless you make sure that programs will execute with the same effect whether or not they are
defined. If they are defined, their value is 1.</p>
</blockquote>

<p class="panel note">A very important information is that as soon as you have 2 code paths, you need to make sure that <strong>both</strong> paths are correct.
Otherwise very subtle bugs may appear, as testing might leverage one of the paths and productive code uses the other one.</p>

<h2 id="leveraging-these-directives-in-our-code">Leveraging These Directives in Our Code</h2>

<p>That means, we can do exactly the same in our code to optimize for size.</p>

<p>For example in order to compute a CRC checksum, we can use a precomputed table for speed, or compute it on the fly for size.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef __OPTIMIZE_SIZE__
</span><span class="c1">// Size optimized version: compute CRC on the fly</span>
<span class="kt">uint8_t</span> <span class="nf">crc8_t</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">crc</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">crc</span> <span class="o">^=</span> <span class="n">data</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">uint8_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">crc</span> <span class="o">&amp;</span> <span class="mh">0x80</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">crc</span> <span class="o">=</span> <span class="p">(</span><span class="n">crc</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x07</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">crc</span> <span class="o">&lt;&lt;=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">crc</span><span class="p">;</span>
<span class="p">}</span>

<span class="cp">#else // __OPTIMIZE_SIZE__
</span>
<span class="c1">// Speed optimized version: use a precomputed table</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">uint8_t</span> <span class="n">crc8_table</span><span class="p">[</span><span class="mi">256</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
    <span class="c1">// Precomputed CRC-8 table values</span>
    <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x07</span><span class="p">,</span> <span class="mh">0x0E</span><span class="p">,</span> <span class="mh">0x09</span><span class="p">,</span> <span class="mh">0x1C</span><span class="p">,</span> <span class="mh">0x1B</span><span class="p">,</span> <span class="mh">0x12</span><span class="p">,</span> <span class="mh">0x15</span><span class="p">,</span>
    <span class="mh">0x48</span><span class="p">,</span> <span class="mh">0x4F</span><span class="p">,</span> <span class="mh">0x46</span><span class="p">,</span> <span class="mh">0x41</span><span class="p">,</span> <span class="mh">0x54</span><span class="p">,</span> <span class="mh">0x53</span><span class="p">,</span> <span class="mh">0x5A</span><span class="p">,</span> <span class="mh">0x5D</span><span class="p">,</span>
    <span class="p">...</span>
<span class="p">};</span>

<span class="kt">uint8_t</span> <span class="nf">crc8_t</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="n">crc</span><span class="p">,</span> <span class="k">const</span> <span class="kt">uint8_t</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">crc8_table</span><span class="p">[</span><span class="n">crc</span> <span class="o">^</span> <span class="n">data</span><span class="p">];</span>
<span class="p">}</span>

<span class="cp">#endif // __OPTIMIZE_SIZE__
</span>
<span class="kt">uint8_t</span> <span class="nf">crc8</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">uint8_t</span> <span class="n">crc</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">len</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">crc</span> <span class="o">=</span> <span class="n">crc8_t</span><span class="p">(</span><span class="n">crc</span><span class="p">,</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">crc</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p class="panel warning">The code is only an illustration, the CRC-8 code is not correct.</p>]]></content><author><name>Steve SCHNEPP</name></author><category term="programming" /><category term="c" /><category term="electronics" /><summary type="html"><![CDATA[When working on embedded systems, optimizing for size can be crucial. Let’s even go beyond the compiler optimization flags by actually generating different code paths based on whether we are optimizing for size or speed.]]></summary></entry><entry><title type="html">Jekyll Remote Theme in Debian 12</title><link href="https://blog.pwkf.org/2025/12/11/remote-theme.html" rel="alternate" type="text/html" title="Jekyll Remote Theme in Debian 12" /><published>2025-12-11T00:00:00+00:00</published><updated>2025-12-11T00:00:00+00:00</updated><id>https://blog.pwkf.org/2025/12/11/remote-theme</id><content type="html" xml:base="https://blog.pwkf.org/2025/12/11/remote-theme.html"><![CDATA[<p>In <a href="/2025/10/25/dark-theme.html">a previous post</a>, we moved from the default minima theme
version to a recent one that supports the dark theme, but I couldn’t test it anymore in my local
Jekyll engine on my Debian 12.</p>

<p>To leverage the recent <a href="https://github.com/jekyll/minima">minima theme</a> version, we had to
leverage the <a href="https://github.com/benbalter/jekyll-remote-theme">jekyll-remote-theme</a> plugin.</p>

<p>The package for Debian is <a href="https://packages.debian.org/bullseye/ruby-jekyll-remote-theme">ruby-jekyll-remote-theme</a> but it has been removed since Debian 12.</p>

<p>That said, the Debian 11 version is still installable on Debian 12, so you can do:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget http://ftp.debian.org/debian/pool/main/r/ruby-jekyll-remote-theme/ruby-jekyll-remote-theme_0.4.2-1_all.deb
<span class="nb">sudo </span>apt <span class="nb">install</span> ./ruby-jekyll-remote-theme_0.4.2-1_all.deb
</code></pre></div></div>

<p>Then, you can run jekyll locally with the plugin as if it is on github pages.</p>]]></content><author><name>Steve SCHNEPP</name></author><category term="jekyll" /><category term="blog" /><summary type="html"><![CDATA[In a previous post, we moved from the default minima theme version to a recent one that supports the dark theme, but I couldn’t test it anymore in my local Jekyll engine on my Debian 12.]]></summary></entry><entry><title type="html">The Serenity Prayer, Revisited</title><link href="https://blog.pwkf.org/2025/serenity-prayer-revisited.html" rel="alternate" type="text/html" title="The Serenity Prayer, Revisited" /><published>2025-12-09T00:00:00+00:00</published><updated>2025-12-09T00:00:00+00:00</updated><id>https://blog.pwkf.org/2025/serenity-prayer-revisited</id><content type="html" xml:base="https://blog.pwkf.org/2025/serenity-prayer-revisited.html"><![CDATA[<p>I keep running into the same mistake on projects: spending energy on things
that won’t move. The Serenity Prayer is older than software, but it maps
surprisingly well onto how to spend your time as a developer.</p>

<h2 id="the-prayer">The Prayer</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>God, grant me the serenity to accept the things I cannot change.
Courage to change the things I can.
And wisdom to know the difference.
</code></pre></div></div>

<p>You don’t need the God part for this to be useful. What matters is the three-way split.</p>

<h2 id="a-secular-version">A Secular Version</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>May I find calm for what I cannot change.
May I find courage for what I can.
And may I know the difference.
</code></pre></div></div>

<h2 id="applied-to-a-codebase">Applied to a Codebase</h2>

<p>Every codebase has things you can change and things you cannot.</p>

<p><strong>Cannot change</strong> — legacy ABI contracts, the OS scheduler, a dependency you don’t own,
the language your team already knows. Pushing against these burns time and goodwill.
The correct response is to accept the constraint and design around it.</p>

<p><strong>Can change</strong> — naming, structure, test coverage, your own habits. These are the
things worth spending energy on. Yet they are often the first to be deprioritized
because they don’t feel urgent.</p>

<p><strong>The difference</strong> — this is the hard part. A surprising number of arguments in code
review, in architecture meetings, and in bug post-mortems are really about
misclassifying something. Someone fighting a constraint that is fixed. Someone
accepting a constraint that is actually movable.</p>

<p>The <a href="https://blog.pwkf.org/2023/06/28/cool-stuff-versus-useful-stuff.html">Cool Stuff versus Useful Stuff</a>
problem is a variant of this. The cool stuff feels changeable because it is new.
The boring stuff feels fixed because it is old. Neither feeling is reliable.</p>

<h2 id="a-simple-test">A Simple Test</h2>

<p>Before spending more than an hour on a problem, ask:</p>

<ol>
  <li>Is this actually in my control?</li>
  <li>If yes, is it worth the cost to change it?</li>
  <li>If no, what is the cheapest design that works <em>given</em> this constraint?</li>
</ol>

<p>That third question is the one most people skip. They either fight the constraint
or give up entirely. Designing <em>around</em> a fixed constraint is a skill, and it is
underrated.</p>

<p>The prayer is just a reminder to ask the question before you start.</p>]]></content><author><name>Steve SCHNEPP</name></author><category term="life" /><category term="philosophy" /><category term="best-practices" /><summary type="html"><![CDATA[I keep running into the same mistake on projects: spending energy on things that won’t move. The Serenity Prayer is older than software, but it maps surprisingly well onto how to spend your time as a developer.]]></summary></entry><entry><title type="html">Upgrading the LM358 OpAmp to be RRIO with a NE555</title><link href="https://blog.pwkf.org/2025/11/12/upgrading-lm358-rrio-opamp-ne555.html" rel="alternate" type="text/html" title="Upgrading the LM358 OpAmp to be RRIO with a NE555" /><published>2025-11-12T00:00:00+00:00</published><updated>2025-11-12T00:00:00+00:00</updated><id>https://blog.pwkf.org/2025/11/12/upgrading-lm358-rrio-opamp-ne555</id><content type="html" xml:base="https://blog.pwkf.org/2025/11/12/upgrading-lm358-rrio-opamp-ne555.html"><![CDATA[<p>This project is an attempt to understand how RRIO opamps work internally, and to replicate one using very common components.
It was originally created in 2023 for <a href="https://hackaday.io/project/190900">Hackaday.io</a>.</p>

<h2 id="goals">Goals</h2>

<p>The main goal is learning, but the end result should be a real, working opamp. The design aims to use components as common as possible: resistors, capacitors, and widely available ICs. The LM358 is the obvious choice for opamps, but the NE555 is also considered, especially for the now-defunct NE555 challenge.</p>

<p>Using another LM358 instead of a NE555 could keep the project within the opamp challenge, but might conflict with the secondary objective.</p>

<h2 id="secondary-objective">Secondary Objective</h2>

<p>A surface-mount (SMD) version should aim to be pin-compatible with the most common dual opamps in DIP-8 format. If DIP-8 is too complex, a pin-compatible quad-op in DIP-14 format is the fallback.</p>

<h2 id="design">Design</h2>

<p>The design is simple. To put the LM358 in RRIO mode, the trick is to supply it with a single voltage source that has slightly more amplitude than the main voltage source. This overcomes the voltage drops inherent to the LM358’s internal construction.</p>

<p>The easiest way to achieve this is with a capacitive charge pump. Many RRIO opamps use this method for input rail-to-rail. Output rail-to-rail is usually done with a CMOS buffer, which limits current draw and fits inside the IC die.</p>

<p>This project skips the CMOS output buffer, instead generating a charge pump powerful enough to drive the opamp load directly.</p>

<h2 id="everything-starts-in-a-simulator">Everything starts in a simulator</h2>

<p>First, I start all my designs in a simulator. I’m using the one from <a href="https://www.falstad.com/circuit/circuitjs.html">Paul Falstad</a>, as it is dead simple to use, and working well enough for most of my needs.</p>

<p>I’m using a NE555 to generate a dual voltage power source from a single voltage one. The pattern is very common : a simple capacitor charge pump for each voltage.</p>

<p>I don’t need to have a very high or low voltage, as I only need to supply a little more that the voltage drops in the opamp IC.</p>

<p>Note, I also leverage the fact that the NE555 has a rather strong output, and it will be able drive both charge pumps directly without needed to use buffers.</p>

<p>The values I’m using are also rather common as it is only power-of-ten. Which are the most easy to source components.</p>

<p>The diodes will be the very common 1N4148, also ubiquitous to find. Using some Schottky ones like the 1N5711 might be more efficient, but they are more difficult to source.</p>

<p>The circuit is available in the <a href="https://www.falstad.com/circuit/circuitjs.html?ctz=CQAgjCAMB0l3BOJyWoSATNAHJAzGAGwYAs2CJArBoWCSJZAw05QKYC0YYAUCZOkKRsIBIRAB2DEzHgo0cUxiQMBeOo0rMPAO4ghI3PuHhFPAErGR3DAyHgwtpnnDTM0Su89KPPPIREDB1sgoiUoPwDJNyJJbHpYpV0rU2imROSpGXEs1MgeAHMQbDxbOnoSsvskgCc00Rz4hqhwOB4iW0rmrvLMEAATNgAzAEMAVwAbABcOCbZ+uXDlXgWMKX16UqZCeltB0cmpwpB-a0cTppsW-NOTqJ2QElKN655V9di1kN2B4fHp5JbF63B75aQSfSUcQPQhQk62egANQAwsieOC7oFNlFnkjUe1YY8ROkMMT9OBoHgqfE8FISGAJBIEKQ8JwEMdemFwCQEhJFBE9AzobtSC98nUyPCiSAOJL0uoeBKRLiRLJ5Qq9JLZJLHMSeABjaW6o18lpgWCaS0qThgHwICQkaQIfAM50qATyOC8Io60065mvd5VJhfTCivZ-Q7HUMYcPrUhLN7w9L2UNciMHAG3W5cp7B14YnP2HMBpEAUTL6MgENh4mNtdMTHLladmOCba5PnUaB7yHcDNKCFt5AIEjw2GJFogvFbIOxZycnvgvd7klgA6phEIpVtY+8U-AgJixaineSsfoDwvjYiliusoXMqM4X4zCWvj0afEoYfAsw61-IUZTlZIgMA85f3yQUIMlSVINA01njAkDoNsDgkJgvU9AkJpZC6WQZw9UMr1FUEl3gCBKGgfhuDoWM+WofAnAPQj0GvdMPmqcivQpIch0ZZl7TwelqH3HjeAAexOR4Wn4BARCwTxCGoloXDU8k8B4KSKlkgRrG2FTKMwMlSWk84SROHggA">Live Simulation</a>.</p>

<h2 id="breadboard-time-for-the-charge-pumps">Breadboard Time for the Charge Pumps</h2>

<p>Simulation works nicely, now let’s try it out for real.</p>

<p>As <a href="https://hackaday.io/retiredfeline">Ken Yap</a> nicely commented, there is a strong voltage drop in the bipolar NE555, typically 1.7V. The usual way is to leverage a CMOS version such as the LMC555, but as I’m aiming for very common components, I’ll use only regular NE555.</p>

<h3 id="building-the-charge-pumps">Building the charge pumps</h3>

<p>The charge pumps are not hard to build. I’m using a simple breadboard, and a very simple DIY USB powering for 5V.</p>

<p><img src="../../../assets/images/rrio/5134171683468220661.jpg" alt="breadboarding" /></p>

<h3 id="testing-the-outputs">Testing the outputs</h3>

<p>Now, it is time to test the 2 voltage outputs.</p>

<p><img src="../../../assets/images/rrio/5466341683468315524.jpg" alt="9V" /></p>

<p><img src="../../../assets/images/rrio/1868621683468335059.jpg" alt="-4V" /></p>

<p>A very nice 9V and -4V.</p>

<p>Issue is that it doesn’t hold the voltage as nicely with some load. I tested with a yellow LED in series with a 1k resistors and it falls to 4.5v with a current of 2.4 mA (2.4V across a 1k resistor).</p>

<p><img src="../../../assets/images/rrio/7920461683468473147.jpg" alt="with some load" /></p>

<p>Anyway, let’s try with the LM358 now.</p>

<h2 id="adding-the-lm358">Adding the LM358</h2>

<p>Let’s finally add the LM358 to the mix.</p>

<h3 id="without-load">Without Load</h3>

<p>It works quite well without any load.</p>

<p><img src="../../../assets/images/rrio/5628081683469685618.jpg" alt="5V w/o load" /></p>

<p><img src="../../../assets/images/rrio/2973311683469698455.jpg" alt="0V w/o load" /></p>

<p>Positive &amp; Negative outputs are within 50mV of the input with a simple voltage follower.</p>

<h3 id="with-load">With Load</h3>

<p>As expected, with some load, the voltage isn’t that great : 3.3V only at 2.4mA, which isn’t surprising, given the output of the charge pump.</p>

<h3 id="conclusion--improvements">Conclusion &amp; Improvements</h3>

<p>My initial idea didn’t work as well as expected. Without load it is perfect, but as soon as some load is added, the voltage drops.</p>

<p>But the good findings are that the charge pump is the weak link here. So, if it is improved it should be fine.</p>

<p>I’m planning to try 2 improvements:</p>

<ul>
  <li>Using a LMC555, which is the CMOS version of the NE555 as suggested in the comments by Ken Yap.</li>
  <li>Using a CD4069 CMOS buffer to achieve the same effect than using the LMC555.</li>
</ul>

<p>Once I’ll manage a stable output even with the max expected load from the LM358, I’ll aim to miniaturize on a PCB.</p>]]></content><author><name>Steve SCHNEPP</name></author><summary type="html"><![CDATA[This project is an attempt to understand how RRIO opamps work internally, and to replicate one using very common components. It was originally created in 2023 for Hackaday.io.]]></summary></entry><entry><title type="html">Create SDL2 Debian Packages for mingw-w64</title><link href="https://blog.pwkf.org/2025/11/10/create-sdl2-minggw-64-debian-package.html" rel="alternate" type="text/html" title="Create SDL2 Debian Packages for mingw-w64" /><published>2025-11-10T00:00:00+00:00</published><updated>2025-11-10T00:00:00+00:00</updated><id>https://blog.pwkf.org/2025/11/10/create-sdl2-minggw-64-debian-package</id><content type="html" xml:base="https://blog.pwkf.org/2025/11/10/create-sdl2-minggw-64-debian-package.html"><![CDATA[<p>SDL2 is an awesome library for a portable game. Yet
cross-compiling with it for Windows on Linux is tricky.</p>

<p>Debian ships with the <a href="https://www.mingw-w64.org/">mingw-w64</a>
<a href="https://packages.debian.org/fr/source/trixie/mingw-w64">package</a> that provide a nice
cross compiler toolchain.</p>

<p>Yet SDL2 and its extensions don’t ship ready-made Debian
packages for the mingw-w64 toolkit.</p>

<p>I therefore created a <a href="https://github.com/steveschnepp/sdl2-mingw-w64-debian/blob/master/build-sdl2-mingw.sh">script that generates debian packages</a>.
It downloads the windows version and installs them in the correct directory so that the mingw-w64 toolkit picks it up.</p>

<p>Usage:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sh build-sdl2-mingw.sh <span class="s2">""</span> 2.30.2           <span class="c"># core SDL2</span>
sh build-sdl2-mingw.sh image 2.8.2         <span class="c"># SDL_image</span>
sh build-sdl2-mingw.sh mixer 2.8.0         <span class="c"># SDL_mixer</span>
sh build-sdl2-mingw.sh ttf 2.22.0          <span class="c"># SDL_ttf</span>
sh build-sdl2-mingw.sh net 2.2.0           <span class="c"># SDL_net</span>

<span class="nb">sudo </span>dpkg <span class="nt">-i</span> sdl2-mingw-w64-i686-dev_2.30.2.deb sdl2-mingw-w64-x86-64-dev_2.30.2.deb <span class="se">\</span>
sdl2image-mingw-w64-i686-dev_2.8.2.deb sdl2image-mingw-w64-x86-64-dev_2.8.2.deb <span class="se">\</span>
sdl2mixer-mingw-w64-i686-dev_2.8.0.deb sdl2mixer-mingw-w64-x86-64-dev_2.8.0.deb <span class="se">\</span>
sdl2net-mingw-w64-i686-dev_2.2.0.deb sdl2net-mingw-w64-x86-64-dev_2.2.0.deb <span class="se">\</span>
sdl2ttf-mingw-w64-i686-dev_2.22.0.deb sdl2ttf-mingw-w64-x86-64-dev_2.22.0.deb
</code></pre></div></div>

<p>Note that for distribution, the <code class="language-plaintext highlighter-rouge">SDL2.dll</code> and friends still
need to be manually copied in the application directory.</p>]]></content><author><name>Steve SCHNEPP</name></author><category term="development" /><category term="c" /><summary type="html"><![CDATA[SDL2 is an awesome library for a portable game. Yet cross-compiling with it for Windows on Linux is tricky.]]></summary></entry><entry><title type="html">A Dark Theme</title><link href="https://blog.pwkf.org/2025/10/25/dark-theme.html" rel="alternate" type="text/html" title="A Dark Theme" /><published>2025-10-25T00:00:00+00:00</published><updated>2025-10-25T00:00:00+00:00</updated><id>https://blog.pwkf.org/2025/10/25/dark-theme</id><content type="html" xml:base="https://blog.pwkf.org/2025/10/25/dark-theme.html"><![CDATA[<p>Almost everyone uses Dark Themes by now, myself included.
Either permanently or just at night.
My blog didn’t have one, and it is actually very easy to setup
on the <code class="language-plaintext highlighter-rouge">minima</code> theme of jekyll.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>I’m using the default GitHub Pages Jekyll engine with a tweaked minima theme.
That one doesn’t support the dark theme yet. But the upstream does.</p>

<p>Thankfully, GH pages supports the <code class="language-plaintext highlighter-rouge">jekyll-remote-theme</code> which is exactly designed
to enable themes from 1a GitHub repository.</p>

<h2 id="installation">Installation</h2>

<ol>
  <li>add the <code class="language-plaintext highlighter-rouge">jekyll-remote-theme</code> plugin to your <code class="language-plaintext highlighter-rouge">_config.yml</code></li>
  <li>replace the <code class="language-plaintext highlighter-rouge">theme: minim1</code> configuration option with <code class="language-plaintext highlighter-rouge">remote_theme: "jekyll/minima</code></li>
</ol>

<p>You can also be specific with <code class="language-plaintext highlighter-rouge">jekyll/minima@a6d4d9ce"</code>. The use of a commit id ensures that changes on the theme don’t randomly break your site. That’s useful when you customize it afterwards.
But beware to always keep it up to date from time to time.</p>

<ol>
  <li>add the <code class="language-plaintext highlighter-rouge">skin</code> to dark or auto</li>
</ol>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">minima</span><span class="pi">:</span>
  <span class="na">skin</span><span class="pi">:</span> <span class="s">auto</span>
</code></pre></div></div>

<ol>
  <li>tweak the modifications you made to adjust to upstream’s changes. Mostly the dynamic css namings, that are enabling the skins.</li>
</ol>

<p>Now, if you chose the <code class="language-plaintext highlighter-rouge">auto</code> skin as I did, the site will automatically adjust depending on the configuration of your brower.</p>

<p>I’ll might add a button in the future, but it already suits my needs.</p>]]></content><author><name>Steve SCHNEPP</name></author><category term="jekyll" /><category term="blog" /><summary type="html"><![CDATA[Almost everyone uses Dark Themes by now, myself included. Either permanently or just at night. My blog didn’t have one, and it is actually very easy to setup on the minima theme of jekyll.]]></summary></entry><entry><title type="html">Kartu Sama - Rules</title><link href="https://blog.pwkf.org/2025/10/19/kartu-sama.html" rel="alternate" type="text/html" title="Kartu Sama - Rules" /><published>2025-10-19T00:00:00+00:00</published><updated>2025-10-19T00:00:00+00:00</updated><id>https://blog.pwkf.org/2025/10/19/kartu-sama</id><content type="html" xml:base="https://blog.pwkf.org/2025/10/19/kartu-sama.html"><![CDATA[<p>Kartu Sama is a card game I invented with my children. It’s similar to UNO but played with a regular 54-card deck.
It’s designed to be quick and fun, but not as hectic as UNO since there are no time-sensitive actions.</p>

<h2 id="setup">Setup</h2>

<ol>
  <li>Shuffle the cards.</li>
  <li>Deal 3 cards to each player, then 2 more.</li>
  <li>Place 1 card face up — this will be the discard pile.</li>
  <li>Deal 3 more cards.</li>
  <li>Place the remaining cards face down — this will be the draw pile.</li>
</ol>

<h2 id="gameplay">Gameplay</h2>

<p>A game can be played by counting the accumlated points or by simply counting the number of rounds won.</p>

<h3 id="start-of-turn">Start of Turn</h3>

<ol>
  <li>Play one card from your hand onto the discard pile, face up.
It must share a common trait with the top discard card — either the same rank or the same suit.</li>
  <li>If the player has no valid card to play, they draw 2 cards and skip their turn.</li>
  <li>If the player had only one card before drawing, they draw <em>nb_players − 1</em> additional cards.</li>
  <li>The round ends when a player must draw but the deck is empty, or when a player plays their last card.</li>
  <li>It ends immediately. Drawn cards still count.</li>
  <li>Players total the points of the cards remaining in their hands.</li>
</ol>

<h3 id="scoring">Scoring</h3>

<ul>
  <li>Number cards score their face value.</li>
  <li>Face cards (J, Q, K) score 1 point.</li>
  <li>Aces score 11 points.</li>
</ul>

<p>In a <strong>points game</strong>, players add up their points each round.
In a <strong>victory game</strong>, the winner of a round is determined as follows:</p>

<ol>
  <li>The player with the lowest total points.</li>
  <li>If tied, the player with the most cards.</li>
  <li>If still tied, the player with the lowest master card.</li>
</ol>

<h2 id="variants">Variants</h2>

<p>The core tenet of rules is rather flexible, and does accomodate nicely with some variants that one can
leverage to adjust the playing experience to its audience.</p>

<h3 id="joker-variant">Joker Variant</h3>

<ul>
  <li>A player may discard a Joker on any card, but must then discard <em>nb_players</em> additional cards that
follow the discard rules. Jokers can be stacked.</li>
  <li>If the player cannot play the required additional cards, they draw <em>nb_players × 2</em> cards.</li>
  <li>Jokers score 20 points</li>
</ul>

<h3 id="using-a-32-card-cards-deck">Using a 32-card cards deck</h3>

<p>Using a 32-card deck has the same rules, but leads to much shorter rounds.
Since only the higher number values are present, so it is a little more intense.</p>

<h3 id="using-multiple-54-card-decks">Using multiple 54-card decks</h3>

<p>One can also use multiple 54-card decks to enable longer rounds, or when there are a significant amount of players.
Ideally the back side of the cards should be identical, but that’s not really required as having different
back coloring gives away some information that can be strategically used.</p>

<h2 id="naming-explanation">Naming explanation</h2>

<p>“Kartu Sama” means “Same Card” in Indonesian, which nicely fits the theme of the game &amp; the blog since
the Java island is in Indonesia.</p>]]></content><author><name>Steve SCHNEPP</name></author><category term="card-game" /><summary type="html"><![CDATA[Kartu Sama is a card game I invented with my children. It’s similar to UNO but played with a regular 54-card deck. It’s designed to be quick and fun, but not as hectic as UNO since there are no time-sensitive actions.]]></summary></entry><entry><title type="html">Workaround for deprecated gets()</title><link href="https://blog.pwkf.org/2025/08/19/gets-is-deprecated.html" rel="alternate" type="text/html" title="Workaround for deprecated gets()" /><published>2025-08-19T00:00:00+00:00</published><updated>2025-08-19T00:00:00+00:00</updated><id>https://blog.pwkf.org/2025/08/19/gets-is-deprecated</id><content type="html" xml:base="https://blog.pwkf.org/2025/08/19/gets-is-deprecated.html"><![CDATA[<p><code class="language-plaintext highlighter-rouge">gets()</code> is used a lot in ancien learning material for C. That said, it is unsafe and therefore removed in recent C versions.
A simple <code class="language-plaintext highlighter-rouge">#define</code> can bring it back rather securely.</p>

<p>This is the line you need to add to your C files.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define gets(x) fgets(x, sizeof(x), stdin)
</span></code></pre></div></div>

<p class="panel warning">Don’t add it in a header file, only in each C file.</p>

<p>The <code class="language-plaintext highlighter-rouge">sizeof</code> will only be correct in the case of statically defined buffers.</p>

<p>If it is a dynamic buffer, it is still secure, but incorrect. With a simple pointer, the <code class="language-plaintext highlighter-rouge">sizeof</code> will only be the of the size of a single element.</p>]]></content><author><name>Steve SCHNEPP</name></author><category term="development" /><category term="c" /><summary type="html"><![CDATA[gets() is used a lot in ancien learning material for C. That said, it is unsafe and therefore removed in recent C versions. A simple #define can bring it back rather securely.]]></summary></entry></feed>