Blog - Timid Roboturn:uuid:88f83d92-95b5-3c2c-bc9c-3ec3a64e8e932023-01-16T06:56:00ZTimid Robot ZehtaPhandelver Probabilities2023-01-16T06:56:00ZTimid Robot Zehtaurn:uuid:1c03a176-e1f4-3358-bcec-17e7c74740fa<p>Let's use the Python programming language to explore some of the dice roll
probabilities in a D&D 5e adventure. I personally find math easier to
understand when it's expressed as Python code and I want to improve my
familiarity with plotting (graphing) using the <code>matplotlib</code> Python library.</p>
<h2 id="setup">Setup</h2><h3 id="d-d-specifics">D&D Specifics</h3><p>We'll be looking at the first encounter of the <em>Lost Mine of Phandelver</em>
adventure using the pregenerated characters (see <strong>Notes and References</strong>,
below).</p>
<h3 id="python-imports-and-globals">Python Imports and Globals</h3><p>First we'll load the Python imports and globals:</p>
<div class="hll"><pre><span></span><span class="c1"># Standard library</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="kn">from</span> <span class="nn">statistics</span> <span class="kn">import</span> <span class="n">fmean</span>
<span class="c1"># Third-party</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plot</span>
<span class="kn">import</span> <span class="nn">matplotlib.ticker</span> <span class="k">as</span> <span class="nn">ticker</span>
<span class="kn">import</span> <span class="nn">numpy</span>
<span class="o">%</span><span class="n">matplotlib</span> <span class="n">inline</span>
<span class="n">plot</span><span class="o">.</span><span class="n">style</span><span class="o">.</span><span class="n">use</span><span class="p">(</span><span class="s2">"ggplot"</span><span class="p">)</span>
</pre></div>
<h3 id="probability">Probability</h3><p>The basic formula for probability is the frequency (identified outcomes)
divided by the total possible outcomes. For example</p>
<div class="hll"><pre><span></span><span class="n">frequency</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1"># any given number on a d20 occurs once</span>
<span class="n">total_possible_outcomes</span> <span class="o">=</span> <span class="mi">20</span> <span class="c1"># there are 20 sides</span>
<span class="nb">print</span><span class="p">(</span>
<span class="s2">"Rolling any given number on a d20 has a probability of:"</span>
<span class="sa">f</span><span class="s2">" </span><span class="si">{</span><span class="n">frequency</span><span class="si">}</span><span class="s2">/</span><span class="si">{</span><span class="n">total_possible_outcomes</span><span class="si">}</span><span class="s2"> or"</span>
<span class="sa">f</span><span class="s2">" </span><span class="si">{</span><span class="nb">int</span><span class="p">(</span><span class="n">frequency</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">total_possible_outcomes</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">100</span><span class="p">)</span><span class="si">}</span><span class="s2">%"</span>
<span class="p">)</span>
</pre></div>
<pre><code>Rolling any given number on a d20 has a probability of: 1/20 or 5%
</code></pre>
<h3 id="dice-numpy-arrays">Dice Numpy Arrays</h3><p>We can use the <code>numpy</code> library to make things easier for ourselves. Its array
objects work very nicely with plotting and we can quickly generate the range of
numbers used by dice using its functions:</p>
<div class="hll"><pre><span></span><span class="k">def</span> <span class="nf">dice</span><span class="p">(</span><span class="n">formula</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""</span>
<span class="sd"> Create numpy array from dice formula (example: 3d6 + 2)</span>
<span class="sd"> """</span>
<span class="n">modifier</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">count</span><span class="p">,</span> <span class="n">faces</span> <span class="o">=</span> <span class="n">formula</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"d"</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">count</span><span class="p">:</span>
<span class="n">count</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">count</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">count</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
<span class="k">if</span> <span class="s2">"+"</span> <span class="ow">in</span> <span class="n">faces</span><span class="p">:</span>
<span class="n">faces</span><span class="p">,</span> <span class="n">modifier</span> <span class="o">=</span> <span class="n">faces</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"+"</span><span class="p">)</span>
<span class="n">modifier</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">modifier</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
<span class="k">elif</span> <span class="s2">"-"</span> <span class="ow">in</span> <span class="n">faces</span><span class="p">:</span>
<span class="n">faces</span><span class="p">,</span> <span class="n">modifier</span> <span class="o">=</span> <span class="n">faces</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"-"</span><span class="p">)</span>
<span class="n">modifier</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="sa">f</span><span class="s2">"-</span><span class="si">{</span><span class="n">modifier</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">faces</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">faces</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span>
<span class="n">array</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span>
<span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="n">count</span><span class="p">)</span> <span class="o">+</span> <span class="n">modifier</span><span class="p">,</span> <span class="p">(</span><span class="n">faces</span> <span class="o">*</span> <span class="n">count</span><span class="p">)</span> <span class="o">+</span> <span class="n">modifier</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">array</span>
<span class="n">d4</span> <span class="o">=</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"d4"</span><span class="p">)</span>
<span class="n">d6</span> <span class="o">=</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"d6"</span><span class="p">)</span>
<span class="n">d8</span> <span class="o">=</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"d8"</span><span class="p">)</span>
<span class="n">d12</span> <span class="o">=</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"d12"</span><span class="p">)</span>
<span class="n">d20</span> <span class="o">=</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"d20"</span><span class="p">)</span>
</pre></div>
<h3 id="simple-plot">Simple Plot</h3><p>The probability plot of a single dice roll is fairly boring:</p>
<div class="hll"><pre><span></span><span class="c1"># d20 is defined above</span>
<span class="c1"># Determine frequency of each number (will return an array contain twenty 1s)</span>
<span class="n">frequency</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">unique</span><span class="p">(</span><span class="n">d20</span><span class="p">,</span> <span class="n">return_counts</span><span class="o">=</span><span class="kc">True</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
<span class="c1"># Create probability array for rolling each number</span>
<span class="n">probability</span> <span class="o">=</span> <span class="p">(</span><span class="n">frequency</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">d20</span><span class="p">))</span> <span class="o">*</span> <span class="mi">100</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># Create bar plot for result probability of a normal d20 roll</span>
<span class="n">ax</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span><span class="n">d20</span><span class="p">,</span> <span class="n">probability</span><span class="p">)</span>
<span class="c1"># Configure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Probability of rolling each number on a d20"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"Die result"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="n">d20</span><span class="p">))</span>
<span class="c1"># Configure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s2">"Probability"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">())</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_minor_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">AutoMinorLocator</span><span class="p">())</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_7_1.png" alt="png"></p>
<h2 id="ability-checks">Ability Checks</h2><p>It's a little more interesting, however, if we plot the chance of exceeding a
number when rolling a d20. In D&D 5e the number that must be matched or
exceeded is the Difficulty Class (DC). It's also worth noting that Armor Class
(AC) is simply a specific type of DC. More interesting still are the
probability plots for rolls with <a href="https://www.dndbeyond.com/sources/basic-rules/using-ability-scores#AdvantageandDisadvantage">advantage and
disadvantage</a>:</p>
<div class="hll"><pre><span></span><span class="c1"># d20 is defined above</span>
<span class="k">def</span> <span class="nf">annotate</span><span class="p">(</span><span class="n">ax</span><span class="p">,</span> <span class="n">index</span><span class="p">,</span> <span class="n">probability</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"white"</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""</span>
<span class="sd"> Annotate top of bar plot with exact probability percentage</span>
<span class="sd"> """</span>
<span class="k">if</span> <span class="n">probability</span> <span class="o">>=</span> <span class="mf">100.0</span><span class="p">:</span>
<span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">probability</span><span class="si">:</span><span class="s2">0.0f</span><span class="si">}</span><span class="s2">%"</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">text</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">probability</span><span class="si">:</span><span class="s2">0.2f</span><span class="si">}</span><span class="s2">%"</span>
<span class="n">ax</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
<span class="n">text</span><span class="p">,</span>
<span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">probability</span> <span class="o">-</span> <span class="mf">0.5</span><span class="p">),</span>
<span class="n">color</span><span class="o">=</span><span class="n">color</span><span class="p">,</span>
<span class="n">fontsize</span><span class="o">=</span><span class="s2">"xx-small"</span><span class="p">,</span>
<span class="n">fontweight</span><span class="o">=</span><span class="s2">"bold"</span><span class="p">,</span>
<span class="n">horizontalalignment</span><span class="o">=</span><span class="s2">"center"</span><span class="p">,</span>
<span class="n">verticalalignment</span><span class="o">=</span><span class="s2">"top"</span><span class="p">,</span>
<span class="n">zorder</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Create probability array for rolling DC or higher with advantage</span>
<span class="n">advantage_roll</span> <span class="o">=</span> <span class="p">((</span><span class="nb">len</span><span class="p">(</span><span class="n">d20</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">-</span> <span class="p">(</span><span class="n">d20</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">d20</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>
<span class="c1"># Create probability array for rolling DC or higher on a d20</span>
<span class="n">normal_roll</span> <span class="o">=</span> <span class="p">((</span><span class="nb">len</span><span class="p">(</span><span class="n">d20</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">d20</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">d20</span><span class="p">))</span> <span class="o">*</span> <span class="mi">100</span>
<span class="c1"># Create probability array for rolling DC or higher with disadvantage</span>
<span class="n">disadvantage_roll</span> <span class="o">=</span> <span class="p">(((</span><span class="nb">len</span><span class="p">(</span><span class="n">d20</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">d20</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">d20</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">axs</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">(</span><span class="n">ax1</span><span class="p">,</span> <span class="n">ax2</span><span class="p">,</span> <span class="n">ax3</span><span class="p">)</span> <span class="o">=</span> <span class="n">axs</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">14</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
<span class="c1"># Create bar plot and annotation for success probability with advantage</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span><span class="n">d20</span><span class="p">,</span> <span class="n">advantage_roll</span> <span class="o">+</span> <span class="mi">5</span><span class="p">,</span> <span class="n">bottom</span><span class="o">=-</span><span class="mi">5</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"C2"</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s2">"with advantage"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">probability</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">advantage_roll</span><span class="p">):</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax1</span><span class="p">,</span> <span class="n">d20</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">probability</span><span class="p">)</span>
<span class="c1"># Create bar plot and annotation for success probability of a normal d20 roll</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span><span class="n">d20</span><span class="p">,</span> <span class="n">normal_roll</span> <span class="o">+</span> <span class="mi">5</span><span class="p">,</span> <span class="n">bottom</span><span class="o">=-</span><span class="mi">5</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"C6"</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s2">"normal d20 roll"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">probability</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">normal_roll</span><span class="p">):</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax2</span><span class="p">,</span> <span class="n">d20</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">probability</span><span class="p">,</span> <span class="s2">"black"</span><span class="p">)</span>
<span class="c1"># Create bar plot and annotation for success probability with disadvantage</span>
<span class="n">ax3</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span>
<span class="n">d20</span><span class="p">,</span>
<span class="n">disadvantage_roll</span> <span class="o">+</span> <span class="mi">5</span><span class="p">,</span>
<span class="n">bottom</span><span class="o">=-</span><span class="mi">5</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="s2">"C1"</span><span class="p">,</span>
<span class="n">label</span><span class="o">=</span><span class="s2">"with disadvantage"</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">probability</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">disadvantage_roll</span><span class="p">):</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax3</span><span class="p">,</span> <span class="n">d20</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">probability</span><span class="p">)</span>
<span class="c1"># Configure Title</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Probability of rolling DC or higher"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="k">for</span> <span class="n">ax</span> <span class="ow">in</span> <span class="n">axs</span><span class="p">:</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="n">d20</span><span class="p">))</span>
<span class="n">ax3</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"DC (Difficulty Class)"</span><span class="p">)</span>
<span class="c1"># Configure Y Axis</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s2">"Probability"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">ax</span> <span class="ow">in</span> <span class="n">axs</span><span class="p">:</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">())</span>
<span class="c1"># Configure Legend for each axis</span>
<span class="k">for</span> <span class="n">ax</span> <span class="ow">in</span> <span class="n">axs</span><span class="p">:</span>
<span class="n">ax</span><span class="o">.</span><span class="n">legend</span><span class="p">()</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_9_0.png" alt="png"></p>
<p>Or all together:</p>
<div class="hll"><pre><span></span><span class="c1"># annotate(), d20, advantage_roll, normal_roll, and disadvantage_roll are defined above</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">14</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
<span class="c1"># Create bar plot for success probability with advantage</span>
<span class="n">ax</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span>
<span class="n">d20</span><span class="p">,</span>
<span class="n">advantage_roll</span> <span class="o">+</span> <span class="mi">5</span><span class="p">,</span>
<span class="n">bottom</span><span class="o">=-</span><span class="mi">5</span><span class="p">,</span>
<span class="n">width</span><span class="o">=</span><span class="mf">0.9</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="s2">"C2"</span><span class="p">,</span>
<span class="n">label</span><span class="o">=</span><span class="s2">"with advantage"</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">probability</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">advantage_roll</span><span class="p">):</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax</span><span class="p">,</span> <span class="n">d20</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">probability</span><span class="p">)</span>
<span class="c1"># Create bar plot for success probability of a normal d20 roll</span>
<span class="n">ax</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span>
<span class="n">d20</span><span class="p">,</span>
<span class="n">normal_roll</span> <span class="o">+</span> <span class="mi">5</span><span class="p">,</span>
<span class="n">bottom</span><span class="o">=-</span><span class="mi">5</span><span class="p">,</span>
<span class="n">width</span><span class="o">=</span><span class="mf">0.8</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="s2">"C6"</span><span class="p">,</span>
<span class="n">label</span><span class="o">=</span><span class="s2">"normal d20 roll"</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">probability</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">normal_roll</span><span class="p">):</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax</span><span class="p">,</span> <span class="n">d20</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">probability</span><span class="p">,</span> <span class="s2">"black"</span><span class="p">)</span>
<span class="c1"># Create bar plot for success probability with disadvantage</span>
<span class="n">ax</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span>
<span class="n">d20</span><span class="p">,</span>
<span class="n">disadvantage_roll</span> <span class="o">+</span> <span class="mi">5</span><span class="p">,</span>
<span class="n">bottom</span><span class="o">=-</span><span class="mi">5</span><span class="p">,</span>
<span class="n">width</span><span class="o">=</span><span class="mf">0.7</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="s2">"C1"</span><span class="p">,</span>
<span class="n">label</span><span class="o">=</span><span class="s2">"with disadvantage"</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">probability</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">disadvantage_roll</span><span class="p">):</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax</span><span class="p">,</span> <span class="n">d20</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">probability</span><span class="p">)</span>
<span class="c1"># Configure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Probability of rolling DC or higher"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"DC (Difficulty Class)"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="n">d20</span><span class="p">))</span>
<span class="c1"># Configure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s2">"Probability"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">())</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">MultipleLocator</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>
<span class="c1"># Configure Legend</span>
<span class="n">ax</span><span class="o">.</span><span class="n">legend</span><span class="p">()</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_11_1.png" alt="png"></p>
<h2 id="goblin-ambush">Goblin Ambush</h2><p>Four <a href="https://www.dndbeyond.com/monsters/16907-goblin">goblins</a> attempt to
ambush the party. Their stealth modifier is +6 vs the party's passive
perception. Will they surprise everyone in the party?</p>
<div class="hll"><pre><span></span><span class="c1"># annotate(), d20, and normal_roll are defined above</span>
<span class="k">def</span> <span class="nf">get_success_chance</span><span class="p">(</span><span class="n">dc</span><span class="p">,</span> <span class="n">modifier</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""</span>
<span class="sd"> Determine probibility of success for a given DC with a modifier d20 roll.</span>
<span class="sd"> """</span>
<span class="c1"># Create probability array for rolling DC or higher on a d20</span>
<span class="n">keys</span> <span class="o">=</span> <span class="n">d20</span> <span class="o">+</span> <span class="n">modifier</span>
<span class="k">if</span> <span class="n">keys</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">></span> <span class="n">dc</span><span class="p">:</span>
<span class="n">chance</span> <span class="o">=</span> <span class="mi">100</span>
<span class="k">elif</span> <span class="n">keys</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o"><</span> <span class="n">dc</span><span class="p">:</span>
<span class="n">chance</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">index</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="n">tolist</span><span class="p">()</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">dc</span><span class="p">)</span>
<span class="k">return</span> <span class="n">normal_roll</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="n">goblin_stealth_modifier</span> <span class="o">=</span> <span class="mi">6</span>
<span class="n">player_names</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"Dwarven Cleric</span><span class="se">\n</span><span class="s2">13 passive perception"</span><span class="p">,</span>
<span class="s2">"Elf Wizard</span><span class="se">\n</span><span class="s2">13 passive perception"</span><span class="p">,</span>
<span class="s2">"Halfling Rogue</span><span class="se">\n</span><span class="s2">10 passive perception"</span><span class="p">,</span>
<span class="s2">"Human Fighter 1</span><span class="se">\n</span><span class="s2">13 passive perception"</span><span class="p">,</span>
<span class="s2">"Human Fighter 2</span><span class="se">\n</span><span class="s2">13 passive perception"</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">players_passive_perception</span> <span class="o">=</span> <span class="p">[</span><span class="mi">13</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">13</span><span class="p">]</span>
<span class="n">players_observation_chance</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">passive_perception</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">players_passive_perception</span><span class="p">):</span>
<span class="n">observation_chance</span> <span class="o">=</span> <span class="mi">100</span> <span class="o">-</span> <span class="n">get_success_chance</span><span class="p">(</span>
<span class="n">passive_perception</span><span class="p">,</span> <span class="n">goblin_stealth_modifier</span>
<span class="p">)</span>
<span class="n">players_observation_chance</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">observation_chance</span><span class="p">)</span>
<span class="n">party_observation_chance</span> <span class="o">=</span> <span class="n">fmean</span><span class="p">(</span><span class="n">players_observation_chance</span><span class="p">)</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">axs</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">width_ratios</span><span class="o">=</span><span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">])</span>
<span class="p">(</span><span class="n">ax1</span><span class="p">,</span> <span class="n">ax2</span><span class="p">)</span> <span class="o">=</span> <span class="n">axs</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="c1"># Create bar plot and annotation for observation probability of each player</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">observation_chance</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">players_observation_chance</span><span class="p">):</span>
<span class="k">if</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">4</span><span class="p">:</span>
<span class="n">color</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"C</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">color</span> <span class="o">=</span> <span class="s2">"C5"</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">observation_chance</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="n">color</span><span class="p">)</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax1</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">observation_chance</span><span class="p">)</span>
<span class="c1"># Create bar plot and annotation for observation probability of entire party</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">party_observation_chance</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"C4"</span><span class="p">)</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">party_observation_chance</span><span class="p">,</span> <span class="s2">"black"</span><span class="p">)</span>
<span class="c1"># Configure Title</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Probability of observing the sneaking goblins"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">player_names</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)))</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedFormatter</span><span class="p">(</span><span class="n">player_names</span><span class="p">))</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_tick_params</span><span class="p">(</span><span class="n">labelrotation</span><span class="o">=</span><span class="mi">45</span><span class="p">,</span> <span class="n">labelsize</span><span class="o">=</span><span class="s2">"small"</span><span class="p">)</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">([</span><span class="mi">0</span><span class="p">]))</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedFormatter</span><span class="p">([</span><span class="s2">"Entire Party"</span><span class="p">]))</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_tick_params</span><span class="p">(</span><span class="n">labelrotation</span><span class="o">=</span><span class="mi">45</span><span class="p">,</span> <span class="n">labelsize</span><span class="o">=</span><span class="s2">"medium"</span><span class="p">)</span>
<span class="c1"># Configure Y Axis</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s2">"Probability"</span><span class="p">)</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">())</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">())</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">set_ylim</span><span class="p">(</span><span class="n">ax1</span><span class="o">.</span><span class="n">get_ylim</span><span class="p">())</span> <span class="c1"># use same scale for both axes</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_13_1.png" alt="png"></p>
<h2 id="initiative-rolls">Initiative Rolls</h2><p>Initiative, for everyone involved in this encounter, uses a single die with a
flat probability plot (boring). However, I think the comparison between
characters is interesting:</p>
<div class="hll"><pre><span></span><span class="c1"># d20 is defined above</span>
<span class="n">initiative</span> <span class="o">=</span> <span class="p">[</span>
<span class="c1"># Character, Initiative</span>
<span class="p">(</span><span class="s2">"Dwarven Cleric</span><span class="se">\n</span><span class="s2">-1 initiative"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"Elf Wizard</span><span class="se">\n</span><span class="s2">+2 initiative"</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"Halfling Rogue</span><span class="se">\n</span><span class="s2">+3 initiative"</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"Human Fighter1</span><span class="se">\n</span><span class="s2">-1 initiative"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"Human Fighter2</span><span class="se">\n</span><span class="s2">+3 initiative"</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"Goblins</span><span class="se">\n</span><span class="s2">+2 initiative"</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
<span class="p">]</span>
<span class="n">initiative_arrays</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">modifier</span> <span class="ow">in</span> <span class="n">initiative</span><span class="p">:</span>
<span class="n">initiative_arrays</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">d20</span> <span class="o">+</span> <span class="n">modifier</span><span class="p">)</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">array</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">initiative_arrays</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">left</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">right</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">average</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span>
<span class="c1"># Create horizontal bar plot for initative roll range for each creature</span>
<span class="n">ax</span><span class="o">.</span><span class="n">barh</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">right</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">left</span><span class="o">=</span><span class="n">left</span><span class="p">)</span>
<span class="c1"># Create average initative roll annotation for each creature</span>
<span class="n">ax</span><span class="o">.</span><span class="n">vlines</span><span class="p">(</span><span class="n">average</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="mf">0.35</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="mf">0.35</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="sa">f</span><span class="s2">"C</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">4.0</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">vlines</span><span class="p">(</span><span class="n">average</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="mf">0.3</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="mf">0.3</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"white"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">average</span><span class="si">}</span><span class="s2"> average"</span><span class="p">,</span>
<span class="p">(</span><span class="n">average</span> <span class="o">+</span> <span class="mf">0.5</span><span class="p">,</span> <span class="n">x</span><span class="p">),</span>
<span class="n">backgroundcolor</span><span class="o">=</span><span class="s2">"white"</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="sa">f</span><span class="s2">"C</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
<span class="n">fontsize</span><span class="o">=</span><span class="s2">"x-small"</span><span class="p">,</span>
<span class="n">fontweight</span><span class="o">=</span><span class="s2">"bold"</span><span class="p">,</span>
<span class="n">horizontalalignment</span><span class="o">=</span><span class="s2">"left"</span><span class="p">,</span>
<span class="n">verticalalignment</span><span class="o">=</span><span class="s2">"bottom"</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Configure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Initiative roll ranges"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"Roll Result</span><span class="se">\n</span><span class="s2">(d20 + modifier)"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_minor_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">AutoMinorLocator</span><span class="p">())</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="mi">26</span><span class="p">)</span>
<span class="c1"># Configure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span>
<span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">initiative_arrays</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>
<span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span>
<span class="n">ticker</span><span class="o">.</span><span class="n">FixedFormatter</span><span class="p">([</span><span class="s2">""</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">item</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">initiative</span><span class="p">])</span>
<span class="p">)</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_15_0.png" alt="png"></p>
<h2 id="damage-rolls">Damage Rolls</h2><p>With initiative order established, let us investigate the more interesting
damage probabilities. We'll only explore the specific damage rolls with
interesting (not flat) plots. Last we'll compare damage ranges and averages.</p>
<h3 id="hafling-rogue">Hafling Rogue</h3><p>The Halfling Rogue can do <a href="https://www.dndbeyond.com/classes/rogue#SneakAttack-343">sneak
attack</a> damage when a
fellow party member is adjacentto their target:</p>
<div class="hll"><pre><span></span><span class="c1"># annotate() and d6 are defined above</span>
<span class="c1"># Create datasets</span>
<span class="n">proficiency_modifier</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">dexterity_modifier</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">modifier</span> <span class="o">=</span> <span class="n">proficiency_modifier</span> <span class="o">+</span> <span class="n">dexterity_modifier</span>
<span class="n">d6_plus_mod</span> <span class="o">=</span> <span class="n">d6</span> <span class="o">+</span> <span class="n">modifier</span>
<span class="n">counts</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">combination</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">d6_plus_mod</span><span class="p">,</span> <span class="n">d6</span><span class="p">):</span>
<span class="n">counts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="n">combination</span><span class="p">))</span>
<span class="n">counts</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
<span class="n">average_damage</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">numpy</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">counts</span><span class="p">)</span><span class="o">.</span><span class="n">mean</span><span class="p">())</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">counts</span><span class="p">))))</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="c1"># Create bar plot for each damage roll result probability</span>
<span class="n">probability_total</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">result</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
<span class="n">damage_probability</span> <span class="o">=</span> <span class="p">(</span><span class="n">counts</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">d6_plus_mod</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>
<span class="n">probability_total</span> <span class="o">+=</span> <span class="n">damage_probability</span>
<span class="n">ax</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">damage_probability</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"maroon"</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mf">0.9</span><span class="p">)</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">damage_probability</span><span class="p">)</span>
<span class="c1"># Create average damage annotation</span>
<span class="n">ax</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">average_damage</span><span class="si">:</span><span class="s2">.0f</span><span class="si">}</span><span class="s2"> average damage"</span><span class="p">,</span>
<span class="p">(</span><span class="n">average_damage</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="n">backgroundcolor</span><span class="o">=</span><span class="s2">"white"</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="s2">"maroon"</span><span class="p">,</span>
<span class="n">fontsize</span><span class="o">=</span><span class="s2">"small"</span><span class="p">,</span>
<span class="n">fontweight</span><span class="o">=</span><span class="s2">"bold"</span><span class="p">,</span>
<span class="n">horizontalalignment</span><span class="o">=</span><span class="s2">"center"</span><span class="p">,</span>
<span class="n">rotation</span><span class="o">=</span><span class="s2">"vertical"</span><span class="p">,</span>
<span class="n">verticalalignment</span><span class="o">=</span><span class="s2">"bottom"</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Configure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Halfling Rogue damage probability</span><span class="se">\n</span><span class="s2">Shortbow + Sneak Attack"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"Damage</span><span class="se">\n</span><span class="s2">(1d6 + 3) + 1d6"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="n">results</span><span class="p">))</span>
<span class="c1"># Confgure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s2">"Probability"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">(</span><span class="n">decimals</span><span class="o">=</span><span class="mi">0</span><span class="p">))</span>
<span class="c1"># check probability for errors</span>
<span class="k">if</span> <span class="n">probability_total</span> <span class="o">></span> <span class="mf">100.001</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"ERROR: total damage probability:"</span><span class="p">,</span> <span class="n">probability_total</span><span class="p">)</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_17_0.png" alt="png"></p>
<h3 id="elf-wizard">Elf Wizard</h3><p>The Elf Wizard has more interesting attacks in terms of probability. First,
we'll investigate the damage probability of the <a href="https://www.dndbeyond.com/spells/magic-missile">Magic
Missile</a> spell at 1st level:</p>
<div class="hll"><pre><span></span><span class="c1"># annotate() and d4 are defined above</span>
<span class="c1"># Create datasets</span>
<span class="n">d4_plus_mod</span> <span class="o">=</span> <span class="n">d4</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">counts</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">combination</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">d4_plus_mod</span><span class="p">,</span> <span class="n">d4_plus_mod</span><span class="p">,</span> <span class="n">d4_plus_mod</span><span class="p">):</span>
<span class="n">counts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="n">combination</span><span class="p">))</span>
<span class="n">counts</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
<span class="n">average_damage</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">counts</span><span class="p">)</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">counts</span><span class="p">))))</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="c1"># Create bar plot for each damage roll result probability</span>
<span class="n">probability_total</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">result</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
<span class="n">damage_probability</span> <span class="o">=</span> <span class="p">(</span><span class="n">counts</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">d4_plus_mod</span><span class="p">)</span> <span class="o">**</span> <span class="mi">3</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>
<span class="n">probability_total</span> <span class="o">+=</span> <span class="n">damage_probability</span>
<span class="n">ax</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="n">damage_probability</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"maroon"</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mf">0.9</span><span class="p">)</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">damage_probability</span><span class="p">)</span>
<span class="c1"># Create average damage annotation</span>
<span class="n">ax</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">average_damage</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2"> average damage"</span><span class="p">,</span>
<span class="p">(</span><span class="n">average_damage</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="n">backgroundcolor</span><span class="o">=</span><span class="s2">"white"</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="s2">"maroon"</span><span class="p">,</span>
<span class="n">fontsize</span><span class="o">=</span><span class="s2">"small"</span><span class="p">,</span>
<span class="n">fontweight</span><span class="o">=</span><span class="s2">"bold"</span><span class="p">,</span>
<span class="n">horizontalalignment</span><span class="o">=</span><span class="s2">"center"</span><span class="p">,</span>
<span class="n">rotation</span><span class="o">=</span><span class="s2">"vertical"</span><span class="p">,</span>
<span class="n">verticalalignment</span><span class="o">=</span><span class="s2">"bottom"</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Configure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span>
<span class="s2">"Elf Wizard damage probability</span><span class="se">\n</span><span class="s2">Magic Missile at 1st level against a"</span>
<span class="s2">" single target"</span>
<span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"Damage</span><span class="se">\n</span><span class="s2">(1d4 + 1) + (1d4 + 1) + (1d4 + 1)"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="n">results</span><span class="p">))</span>
<span class="c1"># Confgure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s2">"Probability"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">(</span><span class="n">decimals</span><span class="o">=</span><span class="mi">0</span><span class="p">))</span>
<span class="c1"># check probability for errors</span>
<span class="k">if</span> <span class="n">probability_total</span> <span class="o">></span> <span class="mf">100.001</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"ERROR: total damage probability:"</span><span class="p">,</span> <span class="n">probability_total</span><span class="p">)</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_19_0.png" alt="png"></p>
<p>Next, we'll investigate the damage probability if the Elf Wizard casts the
<a href="https://www.dndbeyond.com/spells/burning-hands">Burning Hands</a> spell at 1st
level:</p>
<div class="hll"><pre><span></span><span class="c1"># annotate() and d6 are defined above</span>
<span class="c1"># Create datasets</span>
<span class="n">counts</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">combination</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">d6</span><span class="p">,</span> <span class="n">d6</span><span class="p">,</span> <span class="n">d6</span><span class="p">):</span>
<span class="n">counts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="n">combination</span><span class="p">))</span>
<span class="n">counts</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
<span class="n">average_damage</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">counts</span><span class="p">)</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">counts</span><span class="p">))))</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="c1"># Create bar plot for each damage roll result probability</span>
<span class="n">probability_total</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">result</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
<span class="n">damage_probability</span> <span class="o">=</span> <span class="p">(</span><span class="n">counts</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">d6</span><span class="p">)</span> <span class="o">**</span> <span class="mi">3</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>
<span class="n">probability_total</span> <span class="o">+=</span> <span class="n">damage_probability</span>
<span class="n">ax</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span>
<span class="n">result</span><span class="p">,</span> <span class="n">damage_probability</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">bottom</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"maroon"</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mf">0.9</span>
<span class="p">)</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">damage_probability</span><span class="p">)</span>
<span class="c1"># Create average damage annotation</span>
<span class="n">ax</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">average_damage</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2"> average damage"</span><span class="p">,</span>
<span class="p">(</span><span class="n">average_damage</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="n">backgroundcolor</span><span class="o">=</span><span class="s2">"white"</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="s2">"maroon"</span><span class="p">,</span>
<span class="n">fontsize</span><span class="o">=</span><span class="s2">"small"</span><span class="p">,</span>
<span class="n">fontweight</span><span class="o">=</span><span class="s2">"bold"</span><span class="p">,</span>
<span class="n">horizontalalignment</span><span class="o">=</span><span class="s2">"center"</span><span class="p">,</span>
<span class="n">rotation</span><span class="o">=</span><span class="s2">"vertical"</span><span class="p">,</span>
<span class="n">verticalalignment</span><span class="o">=</span><span class="s2">"bottom"</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Confgure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Elf Wizard damage probability</span><span class="se">\n</span><span class="s2">Burning Hands at 1st level"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"Damage</span><span class="se">\n</span><span class="s2">3d6"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="n">results</span><span class="p">))</span>
<span class="c1"># Confgure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s2">"Probability"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">(</span><span class="n">decimals</span><span class="o">=</span><span class="mi">0</span><span class="p">))</span>
<span class="c1"># check probability for errors</span>
<span class="k">if</span> <span class="n">probability_total</span> <span class="o">></span> <span class="mf">100.001</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"ERROR: total damage probability:"</span><span class="p">,</span> <span class="n">probability_total</span><span class="p">)</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_21_0.png" alt="png"></p>
<h3 id="goblins">Goblins</h3><p>The total damage of the four
<a href="https://www.dndbeyond.com/monsters/16907-goblin">goblins</a>:</p>
<div class="hll"><pre><span></span><span class="c1"># annotate() and d6 are defined above</span>
<span class="c1"># Create datasets</span>
<span class="n">dexterity_modifier</span> <span class="o">=</span> <span class="mi">2</span>
<span class="n">d6_plus_mod</span> <span class="o">=</span> <span class="n">d6</span> <span class="o">+</span> <span class="n">dexterity_modifier</span>
<span class="n">counts</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">combination</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">d6_plus_mod</span><span class="p">,</span> <span class="n">d6_plus_mod</span><span class="p">,</span> <span class="n">d6_plus_mod</span><span class="p">,</span> <span class="n">d6_plus_mod</span><span class="p">):</span>
<span class="n">counts</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="n">combination</span><span class="p">))</span>
<span class="n">counts</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
<span class="n">average_damage</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">numpy</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">counts</span><span class="p">)</span><span class="o">.</span><span class="n">mean</span><span class="p">())</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">numpy</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="nb">sorted</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">counts</span><span class="p">))))</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="c1"># Create bar plot for each damage roll result probability</span>
<span class="n">probability_total</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">result</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
<span class="n">damage_probability</span> <span class="o">=</span> <span class="p">(</span><span class="n">counts</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">d6_plus_mod</span><span class="p">)</span> <span class="o">**</span> <span class="mi">4</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>
<span class="n">probability_total</span> <span class="o">+=</span> <span class="n">damage_probability</span>
<span class="n">ax</span><span class="o">.</span><span class="n">bar</span><span class="p">(</span>
<span class="n">result</span><span class="p">,</span> <span class="n">damage_probability</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">bottom</span><span class="o">=-</span><span class="mi">1</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"maroon"</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mf">0.9</span>
<span class="p">)</span>
<span class="n">annotate</span><span class="p">(</span><span class="n">ax</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">damage_probability</span><span class="p">)</span>
<span class="c1"># Create average damage annotation</span>
<span class="n">ax</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">average_damage</span><span class="si">:</span><span class="s2">.0f</span><span class="si">}</span><span class="s2"> average damage"</span><span class="p">,</span>
<span class="p">(</span><span class="n">average_damage</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="n">backgroundcolor</span><span class="o">=</span><span class="s2">"white"</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="s2">"maroon"</span><span class="p">,</span>
<span class="n">fontsize</span><span class="o">=</span><span class="s2">"small"</span><span class="p">,</span>
<span class="n">fontweight</span><span class="o">=</span><span class="s2">"bold"</span><span class="p">,</span>
<span class="n">horizontalalignment</span><span class="o">=</span><span class="s2">"center"</span><span class="p">,</span>
<span class="n">rotation</span><span class="o">=</span><span class="s2">"vertical"</span><span class="p">,</span>
<span class="n">verticalalignment</span><span class="o">=</span><span class="s2">"bottom"</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Confgure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Four Goblins</span><span class="se">\n</span><span class="s2">Scimitars or Shortbows"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"Damage</span><span class="se">\n</span><span class="s2">(1d6 + 2) + (1d6 + 2) + (1d6 + 2) + (1d6 + 2)"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="n">results</span><span class="p">))</span>
<span class="c1"># Confgure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s2">"Probability"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">PercentFormatter</span><span class="p">(</span><span class="n">decimals</span><span class="o">=</span><span class="mi">0</span><span class="p">))</span>
<span class="c1"># check probability for errors</span>
<span class="k">if</span> <span class="n">probability_total</span> <span class="o">></span> <span class="mf">100.001</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"ERROR: total damage probability:"</span><span class="p">,</span> <span class="n">probability_total</span><span class="p">)</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_23_0.png" alt="png"></p>
<h3 id="damage-range-comparisons">Damage Range Comparisons</h3><p>Last, let's look at optimal damage comparisons (ignoring criticals, but
otherwise assuming the best circumstance for each character or monster group):</p>
<div class="hll"><pre><span></span><span class="c1"># dice() is defined above</span>
<span class="c1"># Create dataset: character, damage array</span>
<span class="n">damage</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="s2">"Dwarven Cleric</span><span class="se">\n</span><span class="s2">Warhammer</span><span class="se">\n</span><span class="s2">1d8 + 2"</span><span class="p">,</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"1d8 + 2"</span><span class="p">)),</span>
<span class="p">(</span><span class="s2">"Elf Wizard</span><span class="se">\n</span><span class="s2">Burning Hands</span><span class="se">\n</span><span class="s2">3d6"</span><span class="p">,</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"3d6"</span><span class="p">)),</span>
<span class="p">(</span>
<span class="s2">"Halfling Rogue</span><span class="se">\n</span><span class="s2">Shortbow & Sneak Attack</span><span class="se">\n</span><span class="s2">(1d6 + 3) + 1d6"</span><span class="p">,</span>
<span class="n">dice</span><span class="p">(</span><span class="s2">"2d6 + 3"</span><span class="p">),</span>
<span class="p">),</span>
<span class="p">(</span><span class="s2">"Human Fighter 1</span><span class="se">\n</span><span class="s2">Greataxe</span><span class="se">\n</span><span class="s2">1d12 + 3"</span><span class="p">,</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"1d12 + 3"</span><span class="p">)),</span>
<span class="p">(</span><span class="s2">"Human Fighter 2</span><span class="se">\n</span><span class="s2">Longbow</span><span class="se">\n</span><span class="s2">1d8 + 3"</span><span class="p">,</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"1d8 + 3"</span><span class="p">)),</span>
<span class="p">(</span><span class="s2">"Goblins</span><span class="se">\n</span><span class="s2">Scimatar / Shortbow</span><span class="se">\n</span><span class="s2">1d6 + 2"</span><span class="p">,</span> <span class="n">dice</span><span class="p">(</span><span class="s2">"1d6 + 2"</span><span class="p">)),</span>
<span class="p">]</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">array</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">damage</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">left</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">right</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">average</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span>
<span class="n">ax</span><span class="o">.</span><span class="n">barh</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">right</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">left</span><span class="o">=</span><span class="n">left</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s2">"x"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">vlines</span><span class="p">(</span><span class="n">average</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="mf">0.35</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="mf">0.35</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="sa">f</span><span class="s2">"C</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">4.0</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">vlines</span><span class="p">(</span><span class="n">average</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="mf">0.3</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="mf">0.3</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"white"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">average</span><span class="si">}</span><span class="s2"> average"</span><span class="p">,</span>
<span class="p">(</span><span class="n">average</span> <span class="o">+</span> <span class="mf">0.3</span><span class="p">,</span> <span class="n">x</span><span class="p">),</span>
<span class="n">backgroundcolor</span><span class="o">=</span><span class="s2">"white"</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="sa">f</span><span class="s2">"C</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
<span class="n">fontsize</span><span class="o">=</span><span class="s2">"x-small"</span><span class="p">,</span>
<span class="n">fontweight</span><span class="o">=</span><span class="s2">"bold"</span><span class="p">,</span>
<span class="n">horizontalalignment</span><span class="o">=</span><span class="s2">"left"</span><span class="p">,</span>
<span class="n">verticalalignment</span><span class="o">=</span><span class="s2">"bottom"</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Confgure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Individual damage ranges"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"Damage"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">MaxNLocator</span><span class="p">(</span><span class="n">steps</span><span class="o">=</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">10</span><span class="p">)))</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
<span class="c1"># Confgure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span>
<span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">initiative_arrays</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>
<span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span>
<span class="n">ticker</span><span class="o">.</span><span class="n">FixedFormatter</span><span class="p">([</span><span class="s2">""</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">item</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">damage</span><span class="p">])</span>
<span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_tick_params</span><span class="p">(</span><span class="n">labelsize</span><span class="o">=</span><span class="s2">"small"</span><span class="p">)</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_25_0.png" alt="png"></p>
<p>The players have the benefit of outnumbering the goblins:</p>
<div class="hll"><pre><span></span><span class="c1"># Create dataset: group, damage array</span>
<span class="n">damage</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="s2">"Players"</span><span class="p">,</span> <span class="n">numpy</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">19</span><span class="p">,</span> <span class="mi">74</span><span class="p">)),</span>
<span class="p">(</span><span class="s2">"Goblins"</span><span class="p">,</span> <span class="n">numpy</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">33</span><span class="p">)),</span>
<span class="p">]</span>
<span class="c1"># Initialize plot</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plot</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">set_size_inches</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">array</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">damage</span><span class="p">):</span>
<span class="n">left</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">right</span> <span class="o">=</span> <span class="n">array</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">average</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">array</span><span class="o">.</span><span class="n">mean</span><span class="p">())</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span>
<span class="n">ax</span><span class="o">.</span><span class="n">barh</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">right</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">left</span><span class="o">=</span><span class="n">left</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">vlines</span><span class="p">(</span><span class="n">average</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="mf">0.35</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="mf">0.35</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="sa">f</span><span class="s2">"C</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">4.0</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">vlines</span><span class="p">(</span><span class="n">average</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="mf">0.3</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="mf">0.3</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s2">"white"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span>
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">average</span><span class="si">}</span><span class="s2"> average"</span><span class="p">,</span>
<span class="p">(</span><span class="n">average</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">x</span><span class="p">),</span>
<span class="n">backgroundcolor</span><span class="o">=</span><span class="s2">"white"</span><span class="p">,</span>
<span class="n">color</span><span class="o">=</span><span class="sa">f</span><span class="s2">"C</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span>
<span class="n">fontsize</span><span class="o">=</span><span class="s2">"x-small"</span><span class="p">,</span>
<span class="n">fontweight</span><span class="o">=</span><span class="s2">"bold"</span><span class="p">,</span>
<span class="n">horizontalalignment</span><span class="o">=</span><span class="s2">"left"</span><span class="p">,</span>
<span class="n">verticalalignment</span><span class="o">=</span><span class="s2">"bottom"</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Confgure Title</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="s2">"Combined damage ranges"</span><span class="p">)</span>
<span class="c1"># Confgure X Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s2">"Damage"</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">MaxNLocator</span><span class="p">(</span><span class="n">nbins</span><span class="o">=</span><span class="mi">17</span><span class="p">,</span> <span class="n">steps</span><span class="o">=</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">)))</span>
<span class="n">ax</span><span class="o">.</span><span class="n">xaxis</span><span class="o">.</span><span class="n">set_minor_locator</span><span class="p">(</span><span class="n">ticker</span><span class="o">.</span><span class="n">AutoMinorLocator</span><span class="p">())</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">76</span><span class="p">)</span>
<span class="c1"># Confgure Y Axis</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_locator</span><span class="p">(</span>
<span class="n">ticker</span><span class="o">.</span><span class="n">FixedLocator</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">initiative_arrays</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>
<span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">yaxis</span><span class="o">.</span><span class="n">set_major_formatter</span><span class="p">(</span>
<span class="n">ticker</span><span class="o">.</span><span class="n">FixedFormatter</span><span class="p">([</span><span class="s2">""</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="n">item</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">damage</span><span class="p">])</span>
<span class="p">)</span>
</pre></div>
<p><img src="/2023/01/phandelver-probabilities/output_27_0.png" alt="png"></p>
<h2 id="notes-and-references">Notes and References</h2><ul>
<li>D&D Content<ul>
<li><a href="https://www.dndbeyond.com/sources/lmop">Lost Mine of Phandelver</a> D&D
adventure (free, but requires D&D Beyond signup)<ul>
<li><a href="https://www.dndbeyond.com/sources/lmop/introduction#PregeneratedCharacters">Pregenerated
Characters</a>:<ol>
<li><a href="https://www.dndbeyond.com/file-attachments/0/776/dwarf-cleric.pdf">Dwarven ClericDwarven Cleric (231 KB
PDF)</a></li>
<li><a href="https://www.dndbeyond.com/file-attachments/0/777/elf-wizard.pdf">Elven Wizard (219 KB
PDF)</a></li>
<li><a href="https://www.dndbeyond.com/file-attachments/0/778/halfling-rogue.pdf">Halfling Rogue (202 KB
PDF)</a></li>
<li><a href="https://www.dndbeyond.com/file-attachments/0/779/human-fighter.pdf">Human Fighter 1 (189 KB
PDF)</a></li>
<li><a href="https://www.dndbeyond.com/file-attachments/0/780/human-fighter2.pdf">Human Fighter 2 (188 KB
PDF)</a></li>
</ol>
</li>
</ul>
</li>
</ul>
</li>
<li>D&D Rules<ul>
<li><a href="https://www.dndbeyond.com/sources/basic-rules/introduction#RoundDown">Round Down - Basic Rules for Dungeons and Dragons (D&D) Fifth Edition (5e)
D&D Beyond</a></li>
<li><a href="https://www.dndbeyond.com/sources/basic-rules/using-ability-scores#AdvantageandDisadvantage">Advantage and Disadvantage - Basic Rules for Dungeons and Dragons (D&D)
Fifth Edition (5e) - D&D
Beyond</a></li>
</ul>
</li>
<li>D&D and Probability<ul>
<li><a href="https://www.reddit.com/r/DnD/comments/802zzo/comment/dusrx2d/">[5e] The math and probability behind rolling with advantage! :
DnD</a></li>
<li><a href="https://www.cl.cam.ac.uk/~rmk35/dropbears.html">D&D Probability
distributions</a> (tabular
data is inaccurate--off by 1)</li>
<li><a href="https://www.reddit.com/r/DungeonsAndDragons/comments/uupw5t/i_made_a_probability_calculator_for_dnd_dice_rolls/">I made a probability calculator for DnD dice rolls! :
DungeonsAndDragons</a></li>
<li><a href="https://www.reddit.com/r/DnD/comments/i36b8b/comment/g0a72bv/">Comment - DnD has led me to discover a rule of probability and I feel like
Blaise Pascal :
DnD</a> (more
complex problems may require simulation instead of maths)</li>
</ul>
</li>
<li>JupyterLab<ul>
<li><a href="https://docs.jupyter.org/en/latest/">Jupyter Project Documentation</a></li>
<li><a href="https://github.com/ryantam626/jupyterlab_code_formatter">ryantam626/jupyterlab_code_formatter</a>
(note that the extension is configured in Jupyter Lab, not config files)</li>
</ul>
</li>
<li>Matplotlib<ul>
<li><a href="https://matplotlib.org/stable/index.html">Matplotlib documentation</a></li>
</ul>
</li>
<li>NumPy<ul>
<li><a href="https://numpy.org/doc/stable/user/index.html#user">NumPy user guide — NumPy
Manual</a></li>
</ul>
</li>
<li>Python Documentation<ul>
<li><a href="https://docs.python.org/3.10/library/itertools.html">itertools — Functions creating iterators for efficient looping — Python
3.10.9 documentation</a></li>
</ul>
</li>
</ul>
<h2 id="trademark-notice">Trademark Notice</h2><p>Dungeons & Dragons, D&D, their respective logos, and all Wizards titles and
characters are property of Wizards of the Coast LLC in the U.S.A. and other
countries. All other trademarks referenced are the property of their respective
owners.</p>
<h2 id="source">Source</h2><ul>
<li><a href="https://github.com/TimidRobot/dnd_probabilities">TimidRobot/dnd_probabilities</a></li>
</ul>
Emotional Labor Revisited2022-07-09T19:32:00ZTimid Robot Zehtaurn:uuid:8f38a415-481b-3a4f-b056-a33d51233439<p>Back in 2015, I read an amazing document. It was a digest of a message thread
about Emotional Labor. Before I was done I knew I wanted to share it with
everyone I cared about.</p>
<p>To that end I collaborated with the creator of the digest to
update the formatting of the document. I hope the result is very easy and
comfortable (formatting-wise) to read. Please check it out: <strong><a href="/2015/10/emotional-labor/Emotional_Labor_-_The_MetaFilter_Thread_Condensed.pdf">Emotional Labor:
The MetaFilter Thread Condensed (PDF)</a></strong>.</p>
<p>It's been top of mind lately. Since a Google security change broke the original
link around <code>2022 May 22</code>, I've been replying almost every day to someone
asking for access. As of <code>2024 Mar 15</code> I have replied over 800 times (averaging
1.2 replies per day).</p>
<h2 id="feedback-over-the-years">Feedback Over the Years</h2><p>Since my email address is in the document, I've been fortunate enough to
receive feedback over the years. Most of it simply expresses gratitude. Here
are some anonymized excerpts about the content itself:</p>
<ul>
<li><code>2023 Dec 02</code><blockquote><p>Thank you. I think this paper has helped a lot of people. I truly
appreciate it.</p>
</blockquote>
</li>
<li><code>2023 Aug 24</code><blockquote><p>I'll [...] download a copy for my mum-- she's going to feel so seen!</p>
</blockquote>
</li>
<li><code>2023 Jun 27</code>
from the acknowledgements in <em>The Wife App</em>, by Carolyn Mackler:<blockquote><p>The stories in Emotional Thread: The MetaFilter Thread Condensed edited by
Olivia K. Lima, Josh Millard, and Timid Robot Zehta were hilarious,
heartbreaking, and fully relatable.</p>
</blockquote>
</li>
<li><code>2022 Aug 29</code><blockquote><p>I'm a relationship counselor. A common relationship issue (as you might
expect) is emotional labor, and people often don't have language for what
they're trying to express. A brilliantly written, skillfully edited
document is just what they need.</p>
</blockquote>
</li>
<li><code>2022 Jun 04</code><blockquote><p>Still relevant in 2022</p>
</blockquote>
</li>
<li><code>2019 Jul 05</code><blockquote><p>I know it's a few years on but I actually only very recently found it and
it's been lifechanging to say the least!</p>
</blockquote>
</li>
<li><code>2017 Apr 30</code><blockquote><p>I already shared this with a couple of friends and can't stop talking about
it. All of the women with whom I talked about this had the same feelings
and were shocked to see it was so common and had a name. I think that's
actually the most important thing: this EXISTS and putting words on it
helps so much.</p>
</blockquote>
</li>
<li><code>2016 Jul 07</code><blockquote><p>While reading it, I found aspects of women's struggles within relationships
that I hadn't heard before, and it has given me a lot to think about.</p>
</blockquote>
</li>
<li><code>2016 Feb 09</code><blockquote><p>I read the condensed document [...] and found food for thought and room for
personal improvement. Thanks for taking the time to make it.</p>
</blockquote>
</li>
<li><code>2015 Dec 19</code><blockquote><p>It's just a really remarkable, moving, organized, enlightening,
sanity-restoring thing.</p>
</blockquote>
</li>
</ul>
<h2 id="in-the-media">In the Media</h2><p>Unfortunately, I haven't had any success trying to get the broken PDF links in
the articles updated.</p>
<ul>
<li><code>2018 Apr 30</code>: <a href="https://melmagazine.com/en-us/story/the-stupid-easy-guide-to-emotional-labor">The Stupid-Easy Guide to Emotional Labor</a> (MEL
Magazine)</li>
<li><code>2017 Sep 27</code>: <a href="https://www.harpersbazaar.com/culture/features/a12063822/emotional-labor-gender-equality/">Stop Calling Women Nags — How Emotional Labor is Dragging
Down Gender Equality</a> (Harper's Bazaar)</li>
<li><code>2016 Jan 06</code>: <a href="https://www.themarysue.com/emotional-labor-pdf/">Metafilter's Thread On Emotional Labor, in PDF Form | The
Mary Sue</a></li>
</ul>
<h2 id="article-that-started-it-all">Article That Started It All</h2><ul>
<li><code>2015 Jul 13</code>: <a href="https://the-toast.net/2015/07/13/emotional-labor/">“Where’s My Cut?”: On Unpaid Emotional Labor -The
Toast</a></li>
</ul>
<h2 id="previous-blog-post">Previous Blog Post</h2><ul>
<li><code>2015 Oct 03</code>: <a href="/2015/10/emotional-labor/">Emotional Labor</a></li>
</ul>
<h2 id="updates">Updates</h2><ul>
<li><code>2024 Mar 15</code>: Added feedback and updated reply count</li>
<li><code>2023 Nov 17</code>: Added feedback and reply count</li>
<li><code>2022 Nov 04</code>: Added Article That Started It All and updated In the Media</li>
<li><code>2022 Aug 30</code>: Added feedback</li>
</ul>
Abandoning SaltStack?2022-03-12T12:30:00ZTimid Robot Zehtaurn:uuid:178b34d8-7131-337a-a929-e374a955ab2b<p>I really wanted to use SaltStack to configure my home Linux router. It would be
wonderful to manage that infrastructure as code. Unfortunately, it does not
seem tenable for me.</p>
<h2 id="issues-at-home">Issues at Home</h2><h3 id="does-not-support-python-3-8-on-macos">Does not support Python 3.8+ on macOS</h3><p>My goal was to be able to push configurations using <code>salt-ssh</code> from my macOS
laptop. SaltStack claims macOS is supported. <a href="https://docs.saltproject.io/en/3004/topics/about_salt_project.html">Salt Project</a>
(emphasis added):</p>
<blockquote><p>Salt is tested and packaged to run on CentOS, Debian, RHEL, Ubuntu,
<strong>MacOS</strong>, Windows, and more.</p>
</blockquote>
<p>I originally installed SaltStack via homebrew, but had to switch to a Python
3.7 virtual environment due to:</p>
<ul>
<li><del><code>2020 Jun 21</code>: <a href="https://github.com/saltstack/salt/issues/57742">[BUG] AttributeError: 'Process' object has no attribute
'_args_for_getstate' · Issue #57742 · saltstack/salt</a></del><!-- x_ fix vim markdown highlight issue --></li>
<li><del><code>2022 Jan 09</code>: <a href="https://github.com/saltstack/salt/issues/61435">[BUG] salt-ssh fails due to attempting to pickle
_thread._local object · Issue #61435 · saltstack/salt</a></del><!-- x_ fix vim markdown highlight issue --></li>
</ul>
<p><strong>Update:</strong> These problems existed between the release of Python 3.8 on
<code>2019 Oct 14</code> and the release of Salt version <code>3005</code> on <code>2022 Aug 25</code>.</p>
<h3 id="cannot-use-gitfs-with-salt-ssh">Cannot use gitfs with salt-ssh</h3><p>With SaltStack appearing to work, I wanted to use SaltStack Formulas.
Unfortunately, that option has been unsupported since 2005:</p>
<ul>
<li><del><code>2015 May 12</code>: <a href="https://github.com/saltstack/salt/issues/23576">Cannot use gitfs with salt-ssh · Issue #23576 ·
saltstack/salt</a></del></li>
</ul>
<p>I also tried to symlink git submodules in, but I encountered the same error.</p>
<p><strong>Update:</strong> Maybe I misidentified the problem. The issue above was closed on
<code>2022 Mar 14</code> with a comment indicating there are other issues.</p>
<h3 id="docker-images-unmaintained">Docker images unmaintained</h3><p>I thought I might have fewer issues if I might moved my use case closer to the
SaltStack mainstream by using a SaltStack docker image. <del>Unfortunately, no
<code>3004</code> docker image was available</del> (note update, below). <a href="https://hub.docker.com/r/saltstack/salt">saltstack/salt -
Docker Image | Docker Hub</a>:</p>
<blockquote><p><strong>Supported tags and respective <code>Dockerfile</code> links</strong></p>
<ul>
<li><code>3004rc1</code>, <code>rc</code></li>
<li><code>3003.3</code>, <code>3003.2</code>, <code>3003.1</code>, <code>3003</code>, <code>latest</code></li>
</ul>
</blockquote>
<p>I realize I could create my own docker image, but I encountered this when my
capacity for workarounds was already at its limit.</p>
<p><strong>Update:</strong> At some point after <code>2022 May 03</code>, the docker image creation
pipeline was fixed. The <code>3004</code> image was last pushed on <code>2022 Oct 04</code> (just over
2 years after the release of SaltStack <code>3004</code> on <code>2021 Oct 18</code>).</p>
<h2 id="issues-at-work">Issues at Work</h2><h3 id="lack-of-open-community">Lack of Open Community</h3><p>In learning and using SaltStack, I've frequently come up against a terrible
lack of examples and documentation of SaltStack in use. It seems to be getting
worse as the few examples I did find are becoming outdated.</p>
<p>At work, I've tried to use SaltStack in the open so others can build on my
work. I've blogged about it previously (<a href="/tags/saltstack/">SaltStack tag - Timid
Robot</a>). So far, engagement has been nonexistent.</p>
<p>I spent years working with Puppet, which had a much stronger community.</p>
<h3 id="incomplete-migration-from-python-2-7-to-python-3">Incomplete Migration from Python 2.7 to Python 3</h3><p>I use <code>boto_rds</code> for provisioning:</p>
<ul>
<li><code>2022 Jan 31</code>: <a href="https://github.com/saltstack/salt/issues/61555">[BUG] boto_rds.subnet_group_present broken · Issue #61555 ·
saltstack/salt</a></li>
</ul>
<p><strong>Update:</strong> This issue remains open as of <code>2023 Feb 01</code>.</p>
<h3 id="more-salt-ssh-trouble">More <code>salt-ssh</code> Trouble</h3><p>I use <code>salt-ssh</code> for the initial setup of newly provisioned EC2 instances:</p>
<ul>
<li><code>2022 Jan 27</code>: <a href="https://github.com/saltstack/salt/issues/61535">[BUG] salt-ssh requires the distro python package · Issue
#61535 · saltstack/salt</a></li>
</ul>
<p><strong>Update:</strong> This issue remains open as of <code>2023 Feb 01</code>.</p>
<h3 id="overwhelming-backlog">Overwhelming Backlog</h3><p>The backlog contains many ancient and higher severity issues that deal with the
functionality I am interested in (ex. <code>salt-ssh</code>):</p>
<ul>
<li><a href="https://github.com/saltstack/salt/issues?q=is%3Aissue+is%3Aopen+sort%3Acreated-asc">Issues · saltstack/salt</a></li>
</ul>
<h2 id="conclusion">Conclusion</h2><p>I really wanted to love SaltStack. I love Python. I love a lot of the design
decisions made by SaltStack. It seems like the core use case (avoiding
<code>salt-ssh</code>, orchestration, and provisioning) is viable. SaltStack might work
very very well for you. I respect the developers (especially the volunteer
developers) who are working on a great product. Unfortunately, it doesn't feel
like SaltStack will work well for me.</p>
<p>I expect that next I'll try Ansible at home:</p>
<ul>
<li><a href="https://docs.ansible.com/ansible_community.html">Ansible Community Documentation</a></li>
</ul>
<h2 id="response">Response</h2><p><code>2022 Mar 16</code>: Gareth J. Greenaway provided <a href="https://twitter.com/garethgreenaway/status/1504121699372974082?s=20&t=2sVPB8iytlNVglrK3Lnl5Q">a good response</a>
to this post on Twitter.</p>
<h2 id="ansible-success">Ansible Success</h2><p><code>2022 May 03</code>: Not long after writing this post, my firewall's RAM was damaged
by a blackout and I had to rebuild it. I was able to do fairly quickly, even
though I was learning Ansible at the same time. So far, I've really enjoyed
Ansible. My only complaint is that it is slow (though fast enough for my
needs).</p>
<h2 id="updates">Updates</h2><ul>
<li><code>2023 Feb 01</code>:<ul>
<li>Added update notes throughout</li>
<li>Made date formats consistent</li>
</ul>
</li>
<li><code>2022 May 03</code>:<ul>
<li>Added recheck date to Docker images unmaintained section</li>
<li>Added Response section</li>
<li>Added Ansible Success section</li>
</ul>
</li>
</ul>
Psychological Safety and the Unix Room2021-04-16T00:29:00ZTimid Robot Zehtaurn:uuid:fdf83d6a-da4c-3263-8d22-ba4e08f43ae9<p>Ever since I first read it in 2004, the following quote has stuck with me. I've
added it almost every internal company wiki I've managed.</p>
<blockquote><p>One odd detail that I think was vital to how the group [at Bell Labs]
functioned was a result of the first Unix being run on a clunky minicomputer
with terminals in the machine room.<br>
People working on the system congregated in the room - to use the computer,
you pretty much had to be there. (This idea didn't seem odd back then; it
was a natural evolution of the old hour-at-a-time way of booking machines
like the IBM 7090.) The folks liked working that way, so when the machine was
moved to a different room from the terminals, even when it was possible to
connect from your private office, there was still a `Unix room' with a bunch
of terminals where people would congregate, code, design, and just hang out.
(The coffee machine was there too.) The Unix room still exists, and it may be
the greatest cultural reason for the success of Unix as a technology. More
groups could profit from its lesson, but it's really hard to add a
Unix-room-like space to an existing organization. You need the culture to
encourage people not to hide in their offices, you need a way of using
systems that makes a public machine a viable place to work - typically by
storing the data somewhere other than the 'desktop' - and you need people
[...] hanging out in the room, but if you can make it work, it's magical.</p>
<p>(<a href="https://interviews.slashdot.org/story/04/10/18/1153211/rob-pike-responds">Rob Pike Responds - Slashdot</a>, 2004-10-18, retreived 2021-04-17)</p>
</blockquote>
<p>There's a lot of reasons I like this quote. It offers a historical example of
the importance of socializing and building trust for teams to have brilliant
output (ex. psychological safety).</p>
<p>One thing that it illustrates that I haven't read much about is how often
relationships and collaborations are driven by unrelated circumstances.
The Unix room was created by the reality of terminals in the machine room.
Similarly, in college I observed that most of the long lasting friendships
were based on first year dorm assignments. I've also seen a good amount of
media about long lasting friendships based on traumatic circumstances and
shared adversity.</p>
<p>As I think about remote working, I wonder how people can be pushed together by
circumstances and whether those circumstances can be designed instead of
identified after the fact. I wonder how companies and organizations can create
virtual Unix rooms. I do not hope for awkward Unix room skeuomorphismis, but
intentional constraints that make it more likely for people to be close enough
to offer help and create positive feedback loops of excitement.</p>
<h2 id="related">Related</h2><ul>
<li><a href="https://en.wikipedia.org/wiki/Psychological_safety">Psychological safety - Wikipedia</a></li>
<li><a href="https://en.wikipedia.org/wiki/Rob_Pike">Rob Pike - Wikipedia</a></li>
<li><a href="https://en.wikipedia.org/wiki/Bell_Labs">Bell Labs - Wikipedia</a></li>
</ul>
International Date Format2021-04-14T15:27:00ZTimid Robot Zehtaurn:uuid:732c6dcc-1f53-390d-a602-735c8446d98e<h2 id="please-use-the-international-date-format">Please use the international date format</h2><p><strong>Please consider using the international date format (ex. <code>2021-04-14</code> or
<code>YYYY-MM-DD</code>)</strong> instead of a local date format (ex. <code>4/14/2021</code> or
<code>D/MM/YYYY</code>).</p>
<p>The international date format is not only unambiguous and easier
to sort, it participates in internationalism instead of contributing to
colonization, imperialism, and nationalism.</p>
<p>While it may be less intuitive in regional contexts, it is an educational
opportunity requiring relatively low effort.</p>
<h2 id="xkcd">xkcd</h2><p><img src="/2021/04/int-date-format/iso_8601_2x.png" alt=""xkcd: ISO 8601" by Randall Munroe is licensed under CC-BY-NC
2.5"></p>
<p>"<a href="https://xkcd.com/1179/">xkcd: ISO 8601</a>" by Randall Munroe is licensed under <a href="http://creativecommons.org/licenses/by-nc/2.5/">CC-BY-NC
2.5</a>.</p>
<p>For an explanation of the comic (ex. the meaning of the dates in the gray
area), see <a href="https://www.explainxkcd.com/wiki/index.php/1179:_ISO_8601">1179: ISO 8601 - explain xkcd</a>.</p>
<h2 id="details">Details</h2><p>The international date format is defined by the International Organization for
Standardization (ISO) in IS 8601 (<a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601 - Wikipedia</a>).
However, that standards body does not allow open access to the standards!</p>
<p>Thankfully, <a href="https://tools.ietf.org/html/rfc3339">RFC 3339 - Date and Time on the Internet: Timestamps</a> is
available (in addition to the descriptions in the Wikipedia article above).</p>
<p>The World Wide Web Consortium (W3C) discusses the pros and cons of the
international date format here: <a href="https://www.w3.org/International/questions/qa-date-format">Date formats</a>.</p>
Relationship Anarchy Worksheet2021-03-21T19:47:00ZTimid Robot Zehtaurn:uuid:0b48ee82-65b3-3ba8-a2f3-ca14107be355<p>The <a href="/2021/03/relp-anarchy-sheet/RelpAnarchySheet.pdf">RelpAnarchySheet.pdf</a> (152 KB) is a worksheet
designed with the goal of making relationship negotiatings more successful. I
really liked the various Relationship Anarchy Smorgasbords, but I wanted a more
interactive tool.</p>
<h2 id="changelog">Changelog</h2><ul>
<li>2021-03-21<ul>
<li>Smorgasbord<ul>
<li>added Communication Response: considerate response</li>
<li>renamed Social Partners to Relationship Publicity</li>
<li>replaced Social Partners: events with work</li>
<li>replaced Life Partners: shared goals with long term goals</li>
</ul>
</li>
<li>Instructions<ul>
<li>added HALT and note about definitions to instructions</li>
</ul>
</li>
<li>References and Influences<ul>
<li>improved formatting Worksheets</li>
</ul>
</li>
<li>Added QR code</li>
</ul>
</li>
<li>2021-03-10<ul>
<li>initial version, heavily influenced by:<ul>
<li><a href="https://www.reddit.com/r/polyamory/comments/ehmjaq/i_made_my_own_version_of_the_relationship_anarchy/">I made my own version of the Relationship Anarchy Smorgasbord! -
r/Polyamory</a>, 2019-12-30</li>
<li><a href="https://www.facebook.com/sugarbutch/posts/if-youve-been-to-one-of-my-classes-you-know-that-i-love-chart-and-graphics-to-br/10162392581120714/">Relationship Anarchy Smörgåsbord: a tool for discussion (version 5) -
Sinclair Sexsmith - Posts | Facebook</a>, 2019-03-29</li>
<li><a href="https://www.reddit.com/r/polyamory/comments/5jebwg/relationship_anarchy_smorgasbord_choose_your/">Relationship Anarchy Smorgasbord! Choose your Adventure! -
r/Polyamory</a>, 2016-12-20</li>
<li><a href="https://qpadvice.tumblr.com/post/98302305387/hello-recently-i-was-surprised-by-the-amount-of">Queer Platonic Relationship Request Form - Queerplatonic and Aromantic
Advice</a>, 2014-09-24</li>
</ul>
</li>
</ul>
</li>
</ul>
Air Cleaning and Cat Safe Plants2020-09-24T20:05:00ZTimid Robot Zehtaurn:uuid:1c7972ba-8863-3b26-9829-05c360ca30b8<p>I'm moving to a new apartment soon and it has a good number of windows with
lots of light. I'm excited to buy some plants and I wanted to know which ones
are non-toxic to Mochi cat, have been shown to help clean the air, and are
easier to acquire and care for.</p>
<h2 id="recommended">Recommended</h2><p>I like the following plants:</p>
<ul>
<li><strong>Bamboo palm</strong> (<a href="https://en.wikipedia.org/wiki/Chamaedorea">Chamaedorea seifrizii</a>)<ul>
<li>non-toxic to cats per <a href="https://www.aspca.org/pet-care/animal-poison-control/toxic-and-non-toxic-plants/chamaedorea">ASPCA</a></li>
</ul>
</li>
<li><strong>Boston fern</strong> (<a href="https://en.wikipedia.org/wiki/Nephrolepis_exaltata">Nephrolepis exaltata</a> 'Bostoniensis')<ul>
<li>non-toxic to cats per <a href="https://www.aspca.org/pet-care/animal-poison-control/toxic-and-non-toxic-plants/boston-fern">ASPCA</a></li>
<li>Royal Horticultural Society (RHS): <a href="https://www.rhs.org.uk/Plants/58296/Nephrolepis-exaltata-Bostoniensis/Details">Award of Garden Merit (AGM)</a></li>
</ul>
</li>
<li><strong>Lady palm</strong> (<a href="https://en.wikipedia.org/wiki/Rhapis_excelsa">Rhapis excelsa</a>)<ul>
<li>non-toxic to cats per <a href="https://www.aspca.org/pet-care/animal-poison-control/toxic-and-non-toxic-plants/lady-palm">ASPCA</a></li>
<li>Royal Horticultural Society (RHS): <a href="https://www.rhs.org.uk/Plants/14466/Rhapis-excelsa/Details">Award of Garden Merit (AGM)</a></li>
</ul>
</li>
<li><strong>Parlour palm</strong> (<a href="https://en.wikipedia.org/wiki/Chamaedorea_elegans">Chamaedorea elegans</a>)<ul>
<li>non-toxic to cats per <a href="https://www.aspca.org/pet-care/animal-poison-control/toxic-and-non-toxic-plants/chamaedorea">ASPCA</a></li>
<li>Royal Horticultural Society (RHS): <a href="https://www.rhs.org.uk/Plants/29185/Chamaedorea-elegans/Details">Award of Garden Merit (AGM)</a></li>
</ul>
</li>
<li><strong>Pygmy date palm</strong> (<a href="https://en.wikipedia.org/wiki/Phoenix_roebelenii">Phoenix roebelenii</a>)<ul>
<li>non-toxic to cats per <a href="https://www.aspca.org/pet-care/animal-poison-control/toxic-and-non-toxic-plants/miniature-date-palm">ASPCA</a></li>
<li>Royal Horticultural Society (RHS): <a href="https://www.rhs.org.uk/Plants/12771/Phoenix-roebelenii/Details">Award of Garden Merit (AGM)</a></li>
</ul>
</li>
</ul>
<h2 id="full-list-and-comparison">Full List and Comparison</h2><ul>
<li><a href="/2020/09/cleaning-cat-safe-plants/Air_Cleaning_and_Cat_Safe_Plants.pdf">Air_Cleaning_and_Cat_Safe_Plants.pdf</a>
(110 KB)</li>
</ul>
NVMEe on Debian on AWS2020-04-03T15:06:00ZTimid Robot Zehtaurn:uuid:b71661aa-f732-3498-b07a-887186f7dc5a<h2 id="problem">Problem</h2><p>The current Creative Commons infrastructure buildouts use Debian GNU/Linux AWS
EC2 instances with EBS volumes. Depending on chance (or race conditions), the
mapping of block devices can be different from one host to another or between
reboots.</p>
<blockquote><p><em>Occasionally, devices can respond to discovery in a different order in
subsequent instance starts, which causes the device name to change.</em>
(<a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html" title="Amazon EBS and NVMe on Linux Instances - Amazon Elastic Compute Cloud">Amazon EBS and NVMe on Linux Instances - Amazon Elastic Compute
Cloud</a>)</p>
</blockquote>
<h2 id="our-solution">Our Solution</h2><p>Modern Amazon Linux AMIs resolve this by providing a <code>udev</code> rule, but Debian
GNU/Linux does not yet do this. To ensure our systems are configured correctly,
At Creative Commons, we use the device specified during provisioning (ex.
<code>/dev/xvdf</code>) to identify the correct NVMEe device. We then format it with a
label that can be used mounting during subsequent reboots.</p>
<p>Thankfully, AWS documents the the device specified during provisioning (ex. <code>/dev/xvdf</code>):</p>
<blockquote><p><em>For Nitro-based instances, the block device mappings that are specified in
the Amazon EC2 console when you are attaching an EBS volume or during
AttachVolume or RunInstances API calls are captured in the vendor-specific
data field of the NVMe controller identification.</em>
(<a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html" title="Amazon EBS and NVMe on Linux Instances - Amazon Elastic Compute Cloud">Amazon EBS and NVMe on Linux Instances - Amazon Elastic Compute
Cloud</a>)</p>
</blockquote>
<p>We use SaltStack (<a href="https://github.com/creativecommons/sre-salt-prime" title="creativecommons/sre-salt-prime: Site Reliability Engineering / DevOps SaltStack configuration files"><code>creativecommons/sre-salt-prime</code></a>) to:</p>
<ol>
<li>Install the <code>nvme-cli</code> package</li>
<li>Use the <code>nvme</code> command to detect which <code>/dev/nvme?n?</code> contains <em>spec</em> (ex.
<code>xvdf</code>) in the NVMe vendor specific data</li>
<li>Create a symlink (ex. <code>/dev/xvdf -> /dev/nvme1n1</code>) so that SaltStack can use
<code>/dev/xvdf</code> for the initial setup</li>
<li>Perform the intial setup</li>
<li>Delete the symlink since:<ol>
<li>The initial setup formatted the volume with a label that is used to mount
the filesystem</li>
<li>There is no guarantee the symlink will be accurate on subsequent reboots
and it might cause confusion</li>
</ol>
</li>
</ol>
<p>The <a href="https://github.com/creativecommons/sre-salt-prime/blob/master/states/mount/init.sls"><code>states/mount/init.sls</code></a> state includes a complex shell
command (with Jinja2 variables) that loops through the NVMe devices and finds
the correct one:</p>
<div class="hll"><pre><span></span><span class="k">for</span><span class="w"> </span>n<span class="w"> </span><span class="k">in</span><span class="w"> </span>/dev/nvme?n?
<span class="k">do</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span>nvme<span class="w"> </span>id-ctrl<span class="w"> </span>-v<span class="w"> </span><span class="si">${</span><span class="nv">n</span><span class="si">}</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>-q<span class="w"> </span><span class="s1">'^0000:.*{{ spec_short }}'</span>
<span class="w"> </span><span class="k">then</span>
<span class="w"> </span>ln<span class="w"> </span>-s<span class="w"> </span><span class="si">${</span><span class="nv">n</span><span class="si">}</span><span class="w"> </span><span class="o">{{</span><span class="w"> </span>spec_long<span class="w"> </span><span class="o">}}</span>
<span class="w"> </span><span class="k">fi</span>
<span class="k">done</span>
</pre></div>
<p>Example variable values:</p>
<table>
<thead><tr>
<th>Jinja2 Variable</th>
<th>Example Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>{{ spec_short }}</code></td>
<td><code>xvdf</code></td>
</tr>
<tr>
<td><code>{{ spec_long }}</code></td>
<td><code>/dev/xvdf</code></td>
</tr>
</tbody>
</table>
<h3 id="related-links">Related Links</h3><ul>
<li><a href="https://wiki.debian.org/Cloud/AmazonEC2Image/Buster" title="Cloud/AmazonEC2Image/Buster - Debian Wiki">Cloud/AmazonEC2Image/Buster - Debian Wiki</a></li>
<li><a href="https://packages.debian.org/buster/nvme-cli" title="Debian -- Details of package nvme-cli in buster"><code>nvme-cli</code> package details in Debian buster</a></li>
<li>Debian buster — Debian Manpages<ul>
<li><a href="https://manpages.debian.org/buster/nvme-cli/nvme.1.en.html">nvme(1) — nvme-cli</a><ul>
<li><a href="https://manpages.debian.org/buster/nvme-cli/nvme-id-ctrl.1.en.html">nvme-id-ctrl(1) — nvme-cli</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="other-solutions">Other Solutions</h2><p>While doing additional research for this blog post, I found additional
solutions to the same problem. They're all good, but I apprecite the simplicity
of a temporary symlink for setup versus maintaining custom udev rules (maybe I
can help contribute a udev based solution to Debian or Debian's EC2 image). I
can also easily imagine a more complex solution being a better fit if/when our
infrastructure provisioining become more complex.</p>
<ul>
<li><a href="https://github.com/oogali/ebs-automatic-nvme-mapping" title="oogali/ebs-automatic-nvme-mapping: Automatic mapping of EBS volumes via NVMe block devices to standard block device paths">oogali/ebs-automatic-nvme-mapping</a>: Automatic mapping of EBS volumes via NVMe block devices to standard block device paths<ul>
<li>udev rule that invokes a Bash script to create symlinks</li>
</ul>
</li>
<li>CoreOS<ul>
<li>udev rules that invokes a Bash script to create symlinks<ul>
<li><a href="https://github.com/coreos/init/blob/master/udev/rules.d/90-cloud-storage.rules"><code>udev/rules.d/90-cloud-storage.rules</code></a></li>
<li><a href="https://github.com/coreos/init/blob/master/udev/bin/cloud_aws_ebs_nvme_id"><code>udev/bin/cloud_aws_ebs_nvme_id</code></a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://gist.github.com/jalaziz/c22c8464cb602bc2b8d0a339b013a9c4">AWS EBS NVMe udev rules</a><ul>
<li>udev rule that invokes a Pyton script to create symlinks</li>
<li>this is a copy as Amazon only provides access to the source of Amazon Linux
from within an Amazon Linux AMI: <em>The yumdownloader --source command line
tool provided in the Amazon Linux AMI enables viewing of source code inside
of an Amazon EC2.</em> (<a href="https://aws.amazon.com/amazon-linux-ami/faqs/" title="Amazon Linux AMI FAQs">Amazon Linux AMI FAQs</a>)</li>
</ul>
</li>
</ul>
<h2 id="originally-published">Originally Published</h2><p>Originally published <a href="https://opensource.creativecommons.org/blog/entries/2020-04-03-nvmee-on-debian-on-aws/" title="NVMEe on Debian on AWS — Creative Commons on GitHub">NVMEe on Debian on AWS — Creative Commons on
GitHub</a>.</p>
Recommending a Friendly Space policy for Queering Wikipedia2019-12-26T13:37:00ZTimid Robot Zehtaurn:uuid:cc903ad2-79a6-39b2-a2b1-f9bccc6e5cae<p>For the <a href="https://meta.wikimedia.org/wiki/Queering_Wikipedia">Queering Wikipedia Conference 2020</a>, I recommend using the
<a href="https://www.contributor-covenant.org/">Contributor Covenant</a> with updated examples of unacceptable
behavior.</p>
<p>While the <a href="https://meta.wikimedia.org/wiki/Friendly_space_policies#General_friendly_space_policy_for_Wikimedia_events">General friendly space policy for Wikimedia events</a> is
a good start, it lacks definitions of harrassment that may better protect LGBT+
individuals and it lacks specifics about enforcement.</p>
<h2 id="contributor-covenant">Contributor Covenant</h2><p>The <a href="https://www.contributor-covenant.org/version/2/0/code_of_conduct">Contributor Covenant v2.0</a> is a mature code of conduct that
has been very widely adopted. It is self-contained, well worded, and easy to communicate. It has more robust documentation on enforcement. With a few changes,
it can better protect members of the LGBT+ communities:</p>
<h3 id="our-standards-examples-of-unacceptable-behavior">Our Standards: Examples of unacceptable behavior</h3><ol>
<li>Update the use of sexualized language or imagery to allow relevent content<ul>
<li>Adapted from <a href="https://meta.wikimedia.org/wiki/Wikimedia_New_York_City/Code_of_Conduct">Wikimedia New York City/Code of Conduct - Meta</a></li>
</ul>
</li>
<li>Add deliberate misgendering or use of "dead" or rejected names<ul>
<li>Adapted from <a href="https://lgbtq.technology/coc.html">Code of Conduct - LGBTQ in Technology</a></li>
</ul>
</li>
<li>Add questioning or challenging someone's stated self-identity or chosen
labels<ul>
<li>Adapted from <a href="https://lgbtq.technology/coc.html">Code of Conduct - LGBTQ in Technology</a></li>
</ul>
</li>
<li>Add continued one-on-one communication after requests to cease<ul>
<li>Adapted from <a href="https://lgbtq.technology/coc.html">Code of Conduct - LGBTQ in Technology</a></li>
</ul>
</li>
<li>Update publishing others' private information with additional examples
specific to LGBT+ communities<ul>
<li>Adapted from <a href="https://meta.wikimedia.org/wiki/Wikimedia_New_York_City/Code_of_Conduct">Wikimedia New York City/Code of Conduct - Meta</a></li>
</ul>
</li>
</ol>
<h4 id="updated-stanza">Updated Stanza</h4><blockquote><ul>
<li>The use of sexualized language or imagery not directly related to
conference content (conference content might include: history of gender in
images, HIV awareness, etc.)</li>
<li>Sexual attention or advances of any kind</li>
<li>Trolling, insulting or derogatory comments, and personal or political
attacks</li>
<li>Deliberate misgendering or use of "dead" or rejected names</li>
<li>Questioning or challenging someone's stated self-identity or chosen labels,
even if they conflict with your own views</li>
<li>Making general statements about groups you do not belong to</li>
<li>Continued one-on-one communication after requests to cease</li>
<li>Public or private harassment</li>
<li>Publishing others' private information (including, but not limited to: age,
contact information, gender, legal name, orientation, trans status,
workplace) without their explicit permission</li>
<li>Other conduct which could reasonably be considered inappropriate in a
professional setting</li>
</ul>
</blockquote>
<h4 id="markdown-file-comparison">Markdown File Comparison</h4><div class="hll"><pre><span></span><span class="gd">--- Contributor Convenant 2.0</span>
<span class="gi">+++ Recommended for Queering Wikipedia</span>
<span class="gu">@@ -18,12 +18,20 @@</span>
<span class="w"> </span>Examples of unacceptable behavior include:
<span class="gd">-* The use of sexualized language or imagery, and sexual attention or</span>
<span class="gd">- advances of any kind</span>
<span class="gi">+* The use of sexualized language or imagery not directly related to conference</span>
<span class="gi">+ content (conference content might include: history of gender in images, HIV</span>
<span class="gi">+ awareness, etc.)</span>
<span class="gi">+* Sexual attention or advances of any kind</span>
<span class="w"> </span>* Trolling, insulting or derogatory comments, and personal or political attacks
<span class="gi">+* Deliberate misgendering or use of "dead" or rejected names</span>
<span class="gi">+* Questioning or challenging someone's stated self-identity or chosen labels,</span>
<span class="gi">+ even if they conflict with your own views</span>
<span class="gi">+* Making general statements about groups you do not belong to</span>
<span class="gi">+* Continued one-on-one communication after requests to cease</span>
<span class="w"> </span>* Public or private harassment
<span class="gd">-* Publishing others' private information, such as a physical or email</span>
<span class="gd">- address, without their explicit permission</span>
<span class="gi">+* Publishing others' private information (including, but not limited to: age,</span>
<span class="gi">+ contact information, gender, legal name, orientation, trans status,</span>
<span class="gi">+ workplace) without their explicit permission</span>
<span class="w"> </span>* Other conduct which could reasonably be considered inappropriate in a
<span class="w"> </span> professional setting
</pre></div>
<h3 id="enforcement">Enforcement</h3><blockquote><p><strong>Important!</strong> You must add a contact method to the placeholder in the
document so that people know how to report violations. (<a href="https://www.contributor-covenant.org/">Contributor
Covenant</a>)</p>
</blockquote>
<h2 id="discussion">Discussion</h2><p>The Queering Wikipedia Conference 2020 Friendly Space Policy is discussed on
the <a href="https://meta.wikimedia.org/wiki/Talk%3AWikimedia_LGBT%2B#Friendly_space_policy_for_events">Talk:Wikimedia LGBT+ - Meta</a> page.</p>
New Website2019-12-15T13:37:00ZTimid Robot Zehtaurn:uuid:0aa5b7dd-c6ab-3f58-94e2-98fdb14d09fb<p>I have a new website! It uses the <a href="https://www.getlektor.com/">Lektor</a> static site generator and is
hosted on <a href="https://pages.github.com/">GitHub Pages</a>.</p>
<p>The source code for the website is in the <a href="https://github.com/TimidRobot/zehta.me">TimidRobot/zehta.me</a>
repository. See the README there for additional context and links.</p>
<h2 id="theme">Theme</h2><p>The theme is created using <a href="http://getskeleton.com/">Skeleton</a> (<em>responsive CSS Boilerplate</em>).</p>
<p>The repository includes the non-minified custom CSS:
<a href="https://github.com/TimidRobot/zehta.me/blob/master/source/assets/static/custom.css"><code>source/assets/static/custom.css</code></a>.</p>
<h3 id="colors">Colors</h3><table>
<tr>
<td class="dc Black"><code>#000000</code><br><code>Black</code></td>
<td class="dc MetaBlack"><code>#090909</code><br><em>--MetaBlack</em></td>
</tr>
<tr>
<td class="dc NearBlack"><code>#292929</code><br><em>--NearBlack</em></td>
<td class="dc HeavyGray"><code>#393939</code><br><em>--HeavyGray</em></td>
</tr>
<tr>
<td class="dc MediumGray"><code>#3d3d3d</code><br><em>--MediumGray</em></td>
<td class="dc AnswerGray"><code>#424242</code><br><em>--AnswerGray</em></td>
</tr>
<tr>
<td class="dc LightGray"><code>#d3d3d3</code><br><code>LightGray</code></td>
<td class="dc White"><code>#ffffff</code><br><code>White</code></td>
</tr>
<tr>
<td class="dc BluePlum"><code>#cc99ff</code><br><em>--BluePlum</em></td>
<td class="dc DarkRoyal"><code>#221122</code><br><em>--DarkRoyal</em></td>
</tr>
</table><h3 id="tools-and-references">Tools and References</h3><ul>
<li><a href="https://wismuth.com/webcolors.html">Closest Named Web Colors</a></li>
<li><a href="https://web.dev/measure/">Measure page quality - web.dev</a></li>
</ul>
<h2 id="updates">Updates</h2><ul>
<li><code>2022 Mar 05</code>: Added web.dev and removed martial theme name</li>
<li><code>2020 Sep 24</code>: Added <code>--DarkRoyal</code> and removed <code>--PinkPlum</code></li>
<li><code>2019 Dec 22</code>: Added <code>--PinkPlum</code></li>
</ul>
Empowering Collaboration in the Commons2019-11-25T11:11:00ZTimid Robot Zehtaurn:uuid:43bf76bb-2cfd-324a-9080-b6c3f369f8a2<p>In the past few months I have been privileged to attend <a href="https://wikimania.wikimedia.org/wiki/2019:Program">Wikimania
2019</a> and the Google Summer of Code <a href="https://sites.google.com/view/gsoc-mentorsummit2019/home">2019 Mentor
Summit</a>. At these events I was overwhelmed with the amount of
care and effort people are putting into their projects and communities.</p>
<p>At Wikimania 2019, I was impressed with how much overlap there is between the
Creative Commons and Wikimedia communities. Both communities are actively
engaged in expanding the content and participation in the commons--in the
collection and communities empowered by open content. I have a lot of hope in
what existing and future collaborations will allow. At the conference, I got to
see just the tip of the iceberg representing volunteers’ work across the globe.
I hope we at Creative Commons can continue to keep the demands of our daily
workloads in perspective and increase collaboration with the global effort.</p>
<p>At the GSoC 2019 Mentor Summit I was able to attend a good number of sessions
on increasing and maintaining participation. Based on the session discussions
and the work already being done by other organizations, I expect we’ll pursue
the following strategies:</p>
<ul>
<li>Improved support for first timers--people who are engaging with code
repositories for the first time<ul>
<li>Improve tagging of issues</li>
<li>Dedicated documentation</li>
</ul>
</li>
<li>Improve support for GSoC and Outreachy program applicants<ul>
<li>Provide application templates</li>
</ul>
</li>
<li>Participate in social media networks to actively recruit groups who are
underrepresented in FOSS (free and open source software)</li>
<li>Collect anonymous demographic data </li>
</ul>
<p>A couple of organization examples:</p>
<ul>
<li>Public Lab: <a href="https://code.publiclab.org/">Community toolbox</a></li>
<li>Python Software Foundation: <a href="https://python-gsoc.org/index.html">Python GSoC – Home</a></li>
</ul>
<p>Thank you to the community and the work you're doing <3</p>
<h2 id="originally-published">Originally Published</h2><p>Originally published <a href="https://opensource.creativecommons.org/blog/entries/2019-11-25-empowering-collaboration/">Empowering Collaboration in the Commons — Creative
Commons on GitHub</a>.</p>
Host Classification with SaltStack2019-07-31T11:11:00ZTimid Robot Zehtaurn:uuid:3716fa33-5a08-30d8-834f-aa77eef24d2e<p>Within infrastructure as code, as with all programming, there is a goal to
reduce redundancy as much as possible. With configuration management,
duplicated configurations can quickly lead to confusion and unexpected states.
One of the key ways to reduce configuration duplication is robust host
classification.</p>
<h3 id="minion-classification">Minion Classification</h3><p>As this post is about SaltStack, the terms "minion classification" for "host
classification" and "minion" for "host" are used. With Puppet, "node
classification" and "node" would be used.</p>
<p>Minion classification consists of rules that determine what a minion is and
which states (or configurations) should be applied to it. Good minion
classification removes or minimizes the necessity of explicitly assigning
configurations to a minion. Our goal is for the computer to do as much of the
work as possible.</p>
<h3 id="implementation">Implementation</h3><p>Minions are added and configured from the primary salt server (called the
salt-master) with the following Minion ID schema: <strong><code>HST__POD__LOC</code></strong>:</p>
<ol>
<li><strong><code>HST</code></strong> is the hostname or role. It indications what services are running
on the host or the role that it serves.</li>
<li><strong><code>POD</code></strong> is the pod or group. It indicates the logical grouping of the
host.</li>
<li><strong><code>LOC</code></strong> is the location. It indicates where the host is.</li>
</ol>
<p>Examples:</p>
<ul>
<li><code>wordpress__prod__us-east-2</code></li>
<li><code>wordpress__stage__us-east-2</code></li>
</ul>
<p>The Minion ID parts are matched against the following list. SaltStack pillar
data, like Apache2, uses a last declared wins model. The following list is
organized from least-specific to most-specific:</p>
<ol>
<li><code>1_LOC</code> (location)</li>
<li><code>2_POD</code> (pod/group)</li>
<li><code>3_HST</code> (host/role)</li>
<li><code>4_POD__LOC</code> (pod/group and location)</li>
<li><code>5_HST__POD</code> (host/role and pod/group)</li>
</ol>
<p>Using our Minion ID examples above, this allows configuration data to be
specified for both shared data (ex. WordPress security settings that should be
applied to all WordPress hosts/roles) and specific data (ex. Let's Encrypt
TLS/SSL settings).</p>
<h2 id="security">Security</h2><p><em>The only grain which can be safely used is <code>grains['id']</code> which contains the
Minion ID.</em> (<a href="https://docs.saltstack.com/en/latest/faq.html#is-targeting-using-grain-data-secure">FAQ Q.21</a>)</p>
<p>It is important to rely <em>only</em> on the Minion ID as all other grains can be
manipulated by the client. This means a compromised client could change its
grains to collect secrets if a dedicated grain (ex. <code>role</code>) was used for minion
classification.</p>
<h3 id="imperfect-work-in-progress">Imperfect Work in Progress</h3><p>This implementation has proven to be robust and helpful. However, there is
still room for improvement. For example, I will probably refactor <code>HST</code> to
<code>ROLE</code> and <code>POD</code> to <code>GRP</code> for added clarity.</p>
<p><strong>Feedback and development is welcomed.</strong></p>
<p>The <a href="https://github.com/creativecommons/sre-salt-prime">creativecommons/sre-salt-prime</a> repository is open
source.</p>
<p>This host classification is also documented within it: <a href="https://github.com/creativecommons/sre-salt-prime/blob/master/docs/Host_Classification.md">sre-salt-prime/Host_Classification.md at master</a>.</p>
<h2 id="originally-published">Originally Published</h2><p>Originally published <a href="https://opensource.creativecommons.org/blog/entries/saltstack-host-classification/">Host Classification with SaltStack — Creative Commons on
GitHub</a></p>
Open Development with SaltStack2019-04-17T11:11:00ZTimid Robot Zehtaurn:uuid:25c752f5-a2e3-3c46-8fa2-3bd033612a7b<p>I am excited to publicize the open source repository for our SaltStack
configuration: <a href="https://github.com/creativecommons/sre-salt-prime">creativecommons/sre-salt-prime</a>. This is a
rare offering. Not many configuration management system repositories are open
sourced. Read on for why this is important to me :)</p>
<p>I really like open source software (<a href="https://opensource.org/osd-annotated">The Open Source Definition (Annotated) |
Open Source Initiative</a>). Over the years it has provided me with access
and capabilities I would not normally be able to afford.</p>
<p>I had very little money in college. My first computer was salvaged from a
dumpster and ran entirely on open source software. I was once reduced to
begging a few dollars to satisfy a museum's arbitrarily enforced "suggested
donation". Thankfully, open source image manipulation software powered many of
my art projects.</p>
<p>With the exception of typing class in high school and a few training sessions
at work, I have never received any formal computer training. Instead, I was
able to teach myself by using and investigating open source software. I have
been able to determine exactly why open source software behaves the way it does
by looking at the source code. It is instructive to be able to synthesize
complex systems as a single user and participate in the same communities as
SysAdmins using the same software with thousands or millions of users.</p>
<p>I've been gainfully employed in the technology sector for over a decade so the
free cost of open source software is not as necessary to me. However, open
source software remains very important. As a SysAdmin, I have repeatedly found
open source communities to be a better resource for support than even the most
expensive maintenance contract. The ability to make at-will modifications to a
codebase can save project timelines, and seeing those modifications accepted
upstream is deeply satisfying.</p>
<p>When I started at Creative Commons in the fall of 2018, I began using
<a href="https://en.wikipedia.org/wiki/Salt_(software)">SaltStack</a> full time. As part of my work on developing
infrastructure as code at Creative Commons, I created a new open source
repository for our SaltStack configuration:
<a href="https://github.com/creativecommons/sre-salt-prime">creativecommons/sre-salt-prime</a>. While working on
provisioning of AWS resources, I have had trouble finding examples. I learn
best by example and I know I’m not alone. I hope opening this repository helps
other Site Reliability Engineers (SREs) and Systems Administrators (SysAdmins).</p>
<p>I expect that working in the open will improve the quality of my work. Research
has not only shown that people produce higher quality work when they expect it
to be reviewed or shared, but also that open source allows others to provide
criticism, feedback, and contributions.</p>
<p>Perhaps most of all, I hope to give back to the community. I hope it helps
others learn when they do not have access to proprietary software at home,
school, or work. I hope that my work in the open might lower the resources
required to implement open source for others.</p>
<h2 id="originally-published">Originally Published</h2><p>Originally published <a href="https://opensource.creativecommons.org/blog/entries/open-development-with-saltstack/">Open Development with SaltStack — Creative Commons on
GitHub</a>.</p>
In Support of Selfies2017-04-26T08:55:00ZTimid Robot Zehtaurn:uuid:cea55d17-7f30-3d04-81d5-4c69c7062632<p>A collection of links in support of selfies:</p>
<ul>
<li><a href="https://youtu.be/VaVWvcuy3Cg" title="Kero Kero Bonito - Picture This – YouTube">Kero Kero Bonito - Picture This – YouTube</a> (2015-03-10)</li>
<li><a href="https://www.bustle.com/articles/9421-feministselfie-reinforces-why-selfies-are-empowering" title="#FeministSelfie Reinforces Why Selfies Are Empowering">#FeministSelfie Reinforces Why Selfies Are Empowering</a> (2013-11-21)</li>
<li><a href="http://everydayfeminism.com/2014/04/selfies-as-self-love/" title="Selfies and Misogyny: The Importance of Selfies as Self-Love - Everyday Feminism">Selfies and Misogyny: The Importance of Selfies as Self-Love - Everyday
Feminism</a> (2014-04-28)</li>
<li><a href="http://hellogiggles.com/selfies-act-of-feminism/" title="New research suggests selfies are way more empowering than you think">New research suggests selfies are way more empowering than you
think</a> (2015-07-23)</li>
<li><a href="http://www.ravishly.com/2015/10/12/5-reasons-why-selfie-shaming-anti-feminist" title="5 Reasons Why Selfie Shaming Is Anti-Feminist | Ravishly">5 Reasons Why Selfie Shaming Is Anti-Feminist | Ravishly</a>
(2015-10-12)</li>
<li><a href="https://www.sciencedaily.com/releases/2016/09/160913173436.htm" title="Study links selfies, happiness – ScienceDaily">Study links selfies, happiness – ScienceDaily</a> (2016-09-13)</li>
</ul>
Feelings Thermometer2016-12-11T14:12:00ZTimid Robot Zehtaurn:uuid:950de0dd-4103-35e8-998f-78c5ad1828ef<p>I didn't like the Feelings Thermometer worksheet my therapist was using so I
created my own.</p>
<p>Here it is for your use (<a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>):</p>
<ul>
<li><a href="https://drive.google.com/open?id=0B0UUYL6kaNeBUlh3MXh2WG5PRjA">Feelings Thermometer (Word Document)</a></li>
<li><a href="https://drive.google.com/open?id=0B0UUYL6kaNeBVkY3RUZhZWM2MW8">Feelings Thermometer (PDF)</a></li>
</ul>
Emotional Labor2015-10-03T15:45:00ZTimid Robot Zehtaurn:uuid:ac6a68a7-3f31-318c-9036-b45b26d0e46d<p><strong>(See updated post: <a href="/2022/07/emotional-labor-revisited/">Emotional Labor Revisited</a>.)</strong></p>
<p>I recently read an amazing document. It was a digest of a message thread about
Emotional Labor. Before I was done I knew I wanted to share it with everyone I
cared about.</p>
<p>To that end I collaborated with the creator of the digest to
update the formatting of the document. I hope the result is very easy and
comfortable (formatting-wise) to read. Please check it out: <a href="/2015/10/emotional-labor/Emotional_Labor_-_The_MetaFilter_Thread_Condensed.pdf">Emotional Labor:
The MetaFilter Thread Condensed (PDF)</a>.</p>
<h2 id="updates">Updates</h2><ul>
<li><code>2022 Jul 18</code>: added link to <a href="/2022/07/emotional-labor-revisited/">Emotional Labor Revisited</a></li>
<li><code>2022 Apr 26</code>: updated document URL after Google broke the the previous one</li>
</ul>
Maybe It's Valvoline2015-06-09T22:17:00ZTimid Robot Zehtaurn:uuid:030690a5-5e10-3d68-8086-d302345e3534<p><img src="/2015/06/maybe-its-valvoline/furiosa_maybe_its_valvoline.png" alt="Maybe she's borth with it--maybe it's Valvoline" title="Maybe she's borth with it--maybe it's Valvoline"></p>
Apple and Ubuntu OpenSSH Versions2015-02-24T09:08:00ZTimid Robot Zehtaurn:uuid:93455377-3b05-32e5-969b-215d8a0f8f6e<p>I use and support SSH every working day. I created these tables of OpenSSH
versions to make troubleshooting easier:</p>
<h2 id="apple-macos/os-x">Apple macOS / OS X</h2><table>
<thead><tr>
<th>OS Version</th>
<th>OpenSSH Version</th>
<th>Library Version</th>
</tr>
</thead>
<tbody>
<tr>
<td>10.14 Mojave</td>
<td>7.9p1</td>
<td>LibreSSL 2.7.3</td>
</tr>
<tr>
<td>10.13 High Sierra</td>
<td>7.6p1</td>
<td>LibreSSL 2.6.2</td>
</tr>
<tr>
<td>10.12 Sierra</td>
<td>7.2p2</td>
<td>LibreSSL 2.4.1</td>
</tr>
<tr>
<td>10.11 El Capitan</td>
<td>6.9p1</td>
<td>LibreSSL 2.1.8</td>
</tr>
<tr>
<td>10.10 Yosemite</td>
<td>6.2p2</td>
<td>OSSLShim 0.9.8r 8 Dec 2011</td>
</tr>
<tr>
<td>10.9 Mavericks</td>
<td>6.2p2</td>
<td>OSSLShim 0.9.8r 8 Dec 2011</td>
</tr>
<tr>
<td>10.8 Mountain Lion</td>
<td>5.9p1</td>
<td>OpenSSL 0.9.8zc 15 Oct 2014</td>
</tr>
<tr>
<td>10.7 Lion</td>
<td>5.6</td>
<td>OpenSSL 0.9.8za 5 Jun 2014</td>
</tr>
<tr>
<td>10.6 Snow Leopard</td>
<td>5.5p1</td>
<td>OpenSSL 0.9.8l 5 Nov 2009</td>
</tr>
<tr>
<td>10.5 Leopard</td>
<td>5.1p1</td>
<td>OpenSSL 0.9.7l 28 Sep 2006</td>
</tr>
</tbody>
</table>
<h2 id="ubuntu-lts">Ubuntu LTS</h2><table>
<thead><tr>
<th>OS Version</th>
<th>OpenSSH Version</th>
<th>Library Version</th>
</tr>
</thead>
<tbody>
<tr>
<td>18.04 Bionic</td>
<td>7.6p1</td>
<td>OpenSSL 1.0.2n 7 Dec 2017</td>
</tr>
<tr>
<td>16.04 Xenial</td>
<td>7.2p2</td>
<td>OpenSSL 1.0.2g-fips 1 Mar 2016</td>
</tr>
<tr>
<td>14.04 Trusty</td>
<td>6.6p1</td>
<td>OpenSSL 1.0.1f 6 Jan 2014</td>
</tr>
<tr>
<td>12.04 Precise</td>
<td>5.9p1</td>
<td>OpenSSL 1.0.1 14 Mar 2012</td>
</tr>
<tr>
<td>10.04 Lucid</td>
<td>5.3p1</td>
<td>OpenSSL 0.9.8k 25 Mar 2009</td>
</tr>
<tr>
<td> 8.04 Hardy</td>
<td>4.7p2</td>
<td></td>
</tr>
</tbody>
</table>
<ul>
<li><a href="http://packages.ubuntu.com/search?keywords=ssh&searchon=names&exact=1&suite=all&section=all">Ubuntu - Package Search Results -- ssh</a></li>
</ul>
<h2 id="updates">Updates</h2><ul>
<li>this post has been updated in an effort to keep it current</li>
</ul>
SSH on iOS is abysmal2015-01-08T19:06:00ZTimid Robot Zehtaurn:uuid:3661ea82-4b56-3213-98a0-b10a61647926<h2 id="application-summaries">Application Summaries</h2><p>I'm sorry if you're a SysAdmin with an iOS device. I have some bad news:</p>
<h3 id="issh">iSSH</h3><ul>
<li>Version: 5.7.1</li>
<li>Two factor: Yes!</li>
<li>ProxyCommand: No</li>
<li>Additional Issues:<ul>
<li>too-large fonts (even at their smallest, display is less than 80
characters)</li>
</ul>
</li>
</ul>
<h3 id="prompt-2">Prompt 2</h3><ul>
<li>Version: 2.0.3</li>
<li>Two Factor: No<ul>
<li>shitty custom prompts that make two factor authentication all but
impossible</li>
</ul>
</li>
<li>ProxyCommand: No</li>
</ul>
<h3 id="serverauditor">Serverauditor</h3><ul>
<li>Version: 1.5.4</li>
<li>Two Factor: No</li>
<li>ProxyCommand: No</li>
<li>Additional Issues:<ul>
<li>does not support xterm-256color</li>
</ul>
</li>
</ul>
<h2 id="why-it-matters">Why it matters</h2><h3 id="two-factor-authentication">Two Factor authentication</h3><p>Two factor authentication significantly increases security. This is especially
important in a platform (phones and tablets) that are so easy to misplace.</p>
<h3 id="proxycommand">ProxyCommand</h3><p>ProxyCommand is a SSH option that allows the easy use of bastion hosts. Making
increased security easier is a huge win. For an example, see cmc: Workflow.</p>
Color Coded iTerm2 Windows2014-11-03T21:05:00ZTimid Robot Zehtaurn:uuid:44919e9b-7e06-3732-87c5-73ba3a56d5f9<p>As a systems administrator, I often have many many terminal windows open. To
help ensure I am fully aware of which of them are on Production hosts, I color
code them.</p>
<p>I have finally taken that bit of helpful code and made it as easy for you, dear
reader, to use: <a href="https://github.com/TimidRobot/painted-iterm">TimidRobot/painted-iterm</a>.</p>
<p>Some of the neat things I learned about during the course of this project:</p>
<ul>
<li>256 terminal colors</li>
<li>Bash<ul>
<li>PROMPT_COMMAND</li>
<li>clean/simple way to remove leading zeros</li>
</ul>
</li>
<li>SSH LocalCommand</li>
</ul>
Star Wars: Edge of the Empire Dice Probabilities2014-04-02T16:09:00ZTimid Robot Zehtaurn:uuid:8a972ff7-ec81-3aed-83fb-f08e2d417fde<h2 id="overview">Overview</h2><p>Each of the graphs below was created by simulating 100,000 dice rolls for each
die in each graph (400,000 to 900,000 rolls per graph and 6,000,000 total
rolls). The work was inspired by <a href="http://rpg-design.wikidot.com/evaluation">A Treatise on Different Dice-rolling
Mechanics in RPGs</a>. The graphs were created using <a href="https://github.com/TimidRobot/rpgdice">TimidRobot/rpgdice
· GitHub</a>.</p>
<h2 id="graphs">Graphs</h2><p>The 2nd line of the title is an attempt to describe the dice simulated. They
are shown in the top left corner of the graph.</p>
<p>While Despair and Triumph do not cancel each other out, Despair is graphed to
the left of zero to maintain its sinister meaning.</p>
<p>Probabilties below 0.5% were discarded.</p>
<ol>
<li><img src="/2014/04/empire-dice/sweote00_unskilled_pc_vs_average_difficulty.png#vatexttop" alt="Graph: Unskilled PC vs Averge Difficulty" title="Graph: Unskilled PC vs Averge Difficulty"></li>
<li><img src="/2014/04/empire-dice/sweote01_unskilled_pc_vs_hard_difficulty.png#vatexttop" alt="Graph: Unskilled PC vs Hard Difficulty" title="Graph: Unskilled PC vs Hard Difficulty"></li>
<li><img src="/2014/04/empire-dice/sweote02_unskilled_pc_vs_advesary_npc.png#vatexttop" alt="Graph: Unskilled PC vs Advesary NPC" title="Graph: Unskilled PC vs Advesary NPC"></li>
<li><img src="/2014/04/empire-dice/sweote03_talented_pc_vs_average_difficulty.png#vatexttop" alt="Graph: Talented PC vs Average Difficulty" title="Graph: Talented PC vs Average Difficulty"></li>
<li><img src="/2014/04/empire-dice/sweote04_talented_pc_vs_hard_difficulty.png#vatexttop" alt="Graph: Talented PC vs Hard Difficulty" title="Graph: Talented PC vs Hard Difficulty"></li>
<li><img src="/2014/04/empire-dice/sweote05_talented_pc_vs_advesary.png#vatexttop" alt="Graph: Talented PC vs Advesary NPC" title="Graph: Talented PC vs Advesary NPC"></li>
<li><img src="/2014/04/empire-dice/sweote06_pro_pc_vs_average_difficulty.png#vatexttop" alt="Graph: Pro PC vs Average Difficulty" title="Graph: Pro PC vs Averge Difficulty"></li>
<li><img src="/2014/04/empire-dice/sweote07_pro_pc_vs_hard_difficulty.png#vatexttop" alt="Graph: Pro PC vs Hard Difficulty" title="Graph: Pro PC vs Hard Difficulty"></li>
<li><img src="/2014/04/empire-dice/sweote08_pro_pc_vs_advesary_npc.png#vatexttop" alt="Graph: Pro PC vs Advesary NPC" title="Graph: Pro PC vs Advesary NPC"></li>
</ol>
Troubleshooting collectd / statsd / Graphite2014-03-31T16:09:00ZTimid Robot Zehtaurn:uuid:45ee9e12-65a2-3006-a0ec-d6cd4cb9ceec<p>Troubleshooting <a href="http://graphite.readthedocs.org/en/latest/">Graphite</a> can be a bother. Determining where a
problem lies requires verification of the data at all of the points of
communication. The best tool I've found for this is ngrep:</p>
<div class="hll"><pre><span></span>sudo<span class="w"> </span>ngrep<span class="w"> </span>-lqd<span class="w"> </span>any<span class="w"> </span>ping<span class="w"> </span>tcp<span class="w"> </span>dst<span class="w"> </span>port<span class="w"> </span><span class="m">2003</span>
</pre></div>
<p>The following complex command finds ping metrics submitted over TCP to
Graphite and highlights them using grep:</p>
<div class="hll"><pre><span></span><span class="nv">match</span><span class="o">=</span><span class="s1">'ping'</span><span class="p">;</span><span class="w"> </span>sudo<span class="w"> </span>ngrep<span class="w"> </span>-lqd<span class="w"> </span>any<span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">match</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span>tcp<span class="w"> </span>dst<span class="w"> </span>port<span class="w"> </span><span class="m">2003</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>-E<span class="w"> </span>--line-buffered<span class="w"> </span>--color<span class="o">=</span>tty<span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">match</span><span class="si">}</span><span class="s2">"</span>
</pre></div>
<p>Changing the protocol and port allows easy inspection of metrics sent to a
statsd listener, from carbon-aggregator to carbon-cache, etc.</p>
<p>See the man page for more information:</p>
<ul>
<li><a href="http://manpages.ubuntu.com/manpages/precise/en/man8/ngrep.8.html">man 8 ngrep</a></li>
</ul>
Dungeon World Dice Probabilities2014-03-30T23:16:00ZTimid Robot Zehtaurn:uuid:6412f634-e6db-3cab-9603-13ff2565acf1<h2 id="overview">Overview</h2><p>Each of the graphs below was created by simulating 100,000 dice rolls per
Ability Score (500,000 rolls per graph and 1,500,000 total rolls). The work was
inspired by <a href="http://rpg-design.wikidot.com/evaluation">A Treatise on Different Dice-rolling Mechanics in RPGs</a>.
The graphs were created using <a href="https://github.com/TimidRobot/rpgdice">TimidRobot/rpgdice · GitHub</a>.</p>
<h2 id="graphs">Graphs</h2><ol>
<li><img src="/2014/03/dungeon-dice/dw00_success.png#vatexttop" alt="Graph: Chance of Any Success" title="Graph: Chance of Any Success"></li>
<li><img src="/2014/03/dungeon-dice/dw01_simplified.png#vatexttop" alt="Graph: Simplified Results" title="Graph: Simplified Results"></li>
<li><img src="/2014/03/dungeon-dice/dw02_raw.png#vatexttop" alt="Graph: Raw Results" title="Graph: Raw Result"></li>
</ol>
<h2 id="updates">Updates</h2><ul>
<li><code>2014 Apr 01</code>: updated graph labels</li>
</ul>
RPG Dice2014-03-30T23:09:00ZTimid Robot Zehtaurn:uuid:4d129389-31bf-3c49-a1c8-bd4dea954e5e<h2 id="overview">Overview</h2><p>I wrote a Python program that simulates different RPG dice mechanics (rulesets)
and creates graphs showing the probability of the different outcomes:
<a href="https://github.com/TimidRobot/rpgdice">TimidRobot/rpgdice · GitHub</a>!</p>
<h2 id="matplotlib-install">matplotlib install</h2><p>matplotlib is super painful to install on macOS Mavericks 10.9.2. I ended up
creating an installation script (<a href="https://github.com/TimidRobot/rpgdice/blob/master/install_requirements.sh"><code>install_requirements.sh</code></a>) to help
with debugging the mess and allowing me to reliably reproduce success.</p>
<h2 id="ggplot">ggplot</h2><p>This project was the first time I've worked with <a href="https://github.com/yhat/ggplot/">ggplot</a>. I'm very
glad for the shortcuts it provided. I would love to see it become more well
known and successful.</p>
Dungeon World Monster Cards2014-02-27T20:33:00ZTimid Robot Zehtaurn:uuid:72553ee0-c4ae-3862-a673-383bddd673f3<h2 id="overview">Overview</h2><p>I wrote a Python program that allows the generation of <a href="http://www.dungeon-world.com/">Dungeon World</a>
Monster Cards. They can be made from the source files containing the Dungeon
World monsters or from custom YAML files.</p>
<p>I am pleased with the outcome. The formatting does not match the Dungeon World
text, but the most significant changes are intentional. I think they make the
cards easier to use. I especially like the way I categorized and ordered the
tags.</p>
<h2 id="reportlab">ReportLab</h2><p>This project was the first time that I've worked with the
<a href="https://pypi.python.org/pypi/reportlab/">ReportLab</a> Python module. It was very refreshing to do non-WYSIWYG
layout. Overall my experience was very positive.</p>
<h2 id="results">Results</h2><ul>
<li><a href="https://raw.github.com/TimidRobot/dw-monster-cards/main/monster_cards.pdf">monster_cards.pdf</a> (PDF, 39 pages)</li>
<li><a href="https://raw.github.com/TimidRobot/dw-monster-cards/main/back_example.pdf">back_example.pdf</a> (PDF, 1 page)</li>
<li><a href="https://github.com/TimidRobot/dw-monster-cards">TimidRobot/dw-monster-cards</a> (project on GitHub)</li>
</ul>
Go to Bed2013-01-02T09:41:00ZTimid Robot Zehtaurn:uuid:6fdc5221-1dd3-357d-babe-8c31c1955dc9<p>One of my own troubleshooting or programming rules is to go to bed. Over and
over again, I have discovered that I will fail to solve a problem if I focus
too stubbornly on it. If I go to bed, however, the answer is usually waiting
for me as soon as I wake up or pick up the problem again.</p>
<p>I was reminded of this while reading the highly fascinating article, <a href="http://www.newyorker.com/reporting/2013/01/07/130107fa_fact_green">Adam
Green: The Spectacular Thefts of Apollo Robbins, Pickpocket</a> (linked
from <a href="http://www.schneier.com/blog/archives/2013/01/apollo_robbins.html">Schneier on Security: Apollo Robbins, Pickpocket</a>).</p>
<p>Specifically:</p>
<blockquote><p>"inattentional blindness," the phenomenon of focussing so intently on a
single task that one fails to notice things in plain sight.</p>
</blockquote>
Mystery Writes2012-11-01T13:28:00ZTimid Robot Zehtaurn:uuid:87b274c9-6365-3873-9c86-fa435d149eb9<p>We recently observed constant write activity on our development server, while
watching dstat. With the help of <code>iotop</code> (<a href="http://guichaz.free.fr/iotop/">iotop</a>), we identified the
Apache web server was the culprit.</p>
<p>But why would apache be doing so many writes? That's not normal behaviori
(excluding the the logs). We then used auditd to log writes by apache:</p>
<div class="hll"><pre><span></span>sudo<span class="w"> </span>auditctl<span class="w"> </span>-a<span class="w"> </span>exit,always<span class="w"> </span>-S<span class="w"> </span>write<span class="w"> </span>-F<span class="w"> </span><span class="nv">uid</span><span class="o">=</span><span class="m">33</span>
</pre></div>
<p>We also logged opens that were not O_RDONLY:</p>
<div class="hll"><pre><span></span>sudo<span class="w"> </span>auditctl<span class="w"> </span>-a<span class="w"> </span>exit,always<span class="w"> </span>-S<span class="w"> </span>open<span class="w"> </span>-F<span class="w"> </span><span class="nv">uid</span><span class="o">=</span><span class="m">33</span><span class="w"> </span>-F<span class="w"> </span>a1<span class="s1">'!=0'</span><span class="sb">`</span>
</pre></div>
<p>This resulted in the following rules:</p>
<div class="hll"><pre><span></span>sudo<span class="w"> </span>auditctl<span class="w"> </span>-l<span class="w"> </span>LIST_RULES:<span class="w"> </span>exit,always<span class="w"> </span><span class="nv">uid</span><span class="o">=</span><span class="m">33</span><span class="w"> </span><span class="o">(</span>0x21<span class="o">)</span><span class="w"> </span><span class="nv">syscall</span><span class="o">=</span>write<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>LIST_RULES:<span class="w"> </span>exit,always<span class="w"> </span><span class="nv">uid</span><span class="o">=</span><span class="m">33</span><span class="w"> </span><span class="o">(</span>0x21<span class="o">)</span><span class="w"> </span>a1!<span class="o">=</span><span class="m">0</span><span class="w"> </span><span class="nv">syscall</span><span class="o">=</span>open
</pre></div>
<p>This allowed us to isolate a PHP module that was completely broken (using
<code>aureport</code>).</p>
<h2 id="man-pages">Man Pages</h2><ul>
<li><a href="http://manpages.ubuntu.com/manpages/focal/en/man8/auditctl.8.html">man 8 auditctl</a> - a utility to assist controlling the kernel's
audit system</li>
<li><a href="http://manpages.ubuntu.com/manpages/focal/en/man8/aureport.8.html">man 8 aureport</a> - a tool that produces summary reports of
audit daemon logs</li>
<li><a href="http://manpages.ubuntu.com/manpages/focal/en/man8/aureport.8.html">man 1 dstat</a> - versatile tool for generating system resource
statistics</li>
<li><a href="http://manpages.ubuntu.com/manpages/focal/en/man8/iotop.8.html">man 1 iotop</a> - simple top-like I/O monitor</li>
<li><a href="http://manpages.ubuntu.com/manpages/focal/man2/open.2.html">man 2 open</a> - open and possibly create a file or device</li>
</ul>
The Automation of Networks of Networks2012-10-10T12:58:00ZTimid Robot Zehtaurn:uuid:3b48796c-84e3-34fa-9799-3a1e4857b431<p>For me, at least, systems administration is a difficult field to define and
talk about--especially succinctly. A couple of excellent blog posts suggest the
following definition for Systems Administration: <strong>the automation of networks
of networks.</strong></p>
<p>Automation:</p>
<blockquote><p>Therefore, SREs spend half their time writing code to eliminate what they do
the other half of their day. When they "automate themselves out of a job" it
is cause for celebration and they get to pick a new project. There are
always more projects.</p>
<p>(<a href="http://everythingsysadmin.com/2012/10/whatschangedsre.html">Everything Sysadmin: Has the job of a Google SRE changed over the
years?</a>)</p>
</blockquote>
<p>Networks of Networks:</p>
<blockquote><p>Findings so far suggest that networks of networks pose risks of catastrophic
danger that can exceed the risks in isolated systems. A seemingly benign
disruption can generate rippling negative effects. Those effects can cost
millions of dollars, or even billions, when stock markets crash, half of
India loses power or an Icelandic volcano spews ash into the sky, shutting
down air travel and overwhelming hotels and rental car companies. In other
cases, failure within a network of networks can mean the difference between
a minor disease outbreak or a pandemic, a foiled terrorist attack or one
that kills thousands of people.</p>
<p>(<a href="https://web.archive.org/web/20121011071529/http://www.sciencenews.org/view/feature/id/343939/title/When_Networks_Network">When Networks Network - Science News</a> via <a href="http://www.schneier.com/blog/archives/2012/10/the_insecurity_1.html">Schneier on Security:
The Insecurity of Networks</a>)</p>
</blockquote>
<p>The concept of networks of networks also servers to differentiate system administrators from programmers (not 100% accurate, but helpful none-the-less).</p>
Isolating SSD Firmware Issue2012-10-03T16:10:00ZTimid Robot Zehtaurn:uuid:be2ece61-fb33-328f-a2e4-13ccb3994dad<p>On Friday 2012-09-28, my MacBook Pro started freezing. There had been no
significant software updates that morning (though 10.8.2 came out that week).</p>
<p>I didn't want to deal with it, so I took it to the Apple Store. Unfortunately,
they simply wasted a few days (given the solution found, I'm confident they
never let it run more than an hour).</p>
<p>Once I got it back from the Apple Store without resolution, I knew it would be
up to me to isolate the issue. I was inspired by <a href="http://everythingsysadmin.com/2012/09/seniorsysadmins.html">Everything Sysadmin: What
makes a sysadmin a "senior sysadmin"?</a> to proceed methodically.</p>
<p>It paid off:</p>
<blockquote><p>Correct a condition where an incorrect response to a SMART counter will cause
the m4 drive to become unresponsive after 5184 hours [216 days] of Power-on
time. The drive will recover after a power cycle, however, this failure will
repeat once per hour after reaching this point. The condition will allow the
end user to successfully update firmware, and poses no risk to user or system
data stored on the drive.</p>
<p>(<a href="https://web.archive.org/web/20140917090805/http://forum.crucial.com/t5/Solid-State-Drives-SSD/M4-firmware-0309-is-now-available/td-p/80286">>M4 firmware 0309 is now available - Crucial Community</a>; found
via <a href="https://web.archive.org/web/20120304043412/http://www.j03l.com/2012/02/mac-freezes-after-hour-crucial-ssd.html">j03l: MAC Freezes After An Hour - Crucial SSD Problem</a>)</p>
</blockquote>
<p>Since the installation of firmware 010G, my MacBook Pro has operated without
issue!</p>
<p>Hopefully, this post will be another signpost for those
affected.</p>
<p>Also, I can't overemphasis this post: <a href="http://everythingsysadmin.com/2012/09/seniorsysadmins.html">Everything Sysadmin: What makes a
sysadmin a "senior sysadmin"?</a></p>
Linux TCP/IP Tuning2012-10-03T15:32:00ZTimid Robot Zehtaurn:uuid:b84d44a0-8d9b-3924-9ee2-3d5c76de0f66<p>There is a lot of interesting information in this article and related comments:
<a href="http://news.ycombinator.com/item?id=4582253">Linux TCP/IP Tuning | Hacker News</a>. I look forward to eventually having
systems that benefit from that granularity of tuning.</p>
To Err iz Human2012-09-28T12:13:00ZTimid Robot Zehtaurn:uuid:e659cc43-89a2-362d-89f3-16e4a7be6d55<p>Two articles highlight the potent benefits of errors in AI:</p>
<ul>
<li><del>Nate Silver’s ‘The Signal and the Noise’</del><ul>
<li>(link broken: <code>http://www.washingtonpost.com/blogs/ezra-klein/wp/2012/09/26/nate-silvers-the-signal-and-the-noise</code>)</li>
</ul>
</li>
<li><a href="http://www.eurekalert.org/pub_releases/2012-09/uota-aig092612.php">Artificially intelligent game bots pass the Turing test on Turing's
centenary</a></li>
</ul>
Name Change2012-09-12T08:36:00ZTimid Robot Zehtaurn:uuid:2d4e58ff-b443-3dbb-9c5d-a222c9ec83a8<p>My legal name, <strong>Timid Robot</strong> Zehta, was chosen by me and changed via court
order.</p>
<h2 id="faq">FAQ</h2><h3 id="q-what-do-you-prefer-to-be-called">Q. What do you prefer to be called?</h3><p>Please call me by my nickname (my first and middle name):</p>
<ul>
<li><strong>Nickname:</strong> Timid Robot</li>
<li><strong>Legal Name:</strong> Timid Robot Zehta</li>
<li><strong>Pronouns:</strong> they/them/theirs</li>
<li><strong>Honorific:</strong> Mx</li>
</ul>
<h3 id="q-when-did-you-change-your-name">Q. When did you change your name?</h3><p>2012 September.</p>
<h3 id="q-what-are-your-previous-names">Q. What are your previous names?</h3><p>The name assigned to me at birth was <del>Timothy Robert Wheeler</del>. For a few years
prior to 2012, it was <del>Timothy Robert Baldoni</del>.</p>
<h3 id="q-why-timid">Q. Why Timid?</h3><p>I can be quiet while I observe the world around me. However, I'm not without
courage. It takes a lot of confidence to exist as myself in this world. Like
"Little John" from the animated Robin Hood (1973), it's a bit of a joke.</p>
<p>As someone who currently exists in a large assumed-masculine frame, it is good
to declare myself as un-violent. It was also very important to disassociate
myself with a name that professes belief.</p>
<h3 id="q-why-robot">Q. Why Robot?</h3><p>I really like it. It fills me with delight--especially as part of "Timid
Robot". I have a very small amount of regret that I gave up a name that
reflected my germanic heritage, but Robot fits so well. It is non-binary and is
also a way of me reclaiming the ableism that is too often directed autistics.
<em>Domo arigato!</em></p>
<h3 id="q-why-zehta">Q. Why Zehta?</h3><p>It comes from the SI prefix "zetta-", meaning a <em>sex</em>tillion of something.
That's a big number. It starts with "Z", making it easy to find my name on
lists. It is short and I could secure the domain name. Also see <a href="https://en.wikipedia.org/wiki/Zetta-">Zetta- -
Wikipedia</a>.</p>
<h3 id="q-you-based-your-selection-of-a-surname-on-available-domain-names">Q. You based your selection of a surname on available domain names?</h3><p>Yes, most of my world is connected to the Internet--most of my favorite things.
There are no 4 character domain names left (except expired ones being
resold at a high cost).</p>
<h2 id="updates">Updates</h2><ul>
<li><code>2022 Mar 16</code>: Revised answers</li>
</ul>
Reminder2002-04-04T22:22:22ZTim Wheelerurn:uuid:4825fc5c-63a2-3689-889a-6bdeaaf75afd<p><strong>Tim Wheeler. <em>Reminder.</em> 4 April - 11 April 2002</strong>
<br><strong>Steel, 41 x 37 x 16 inches</strong>
<br><strong>North Side of Westlands Building, Sarah Lawrence College</strong><br>
<img src="/2002/04/reminder/side_back_installed.jpg#floatright" alt="Metal skeleton reminder" title="Metal skeleton reminder"></p>
<p><em>Reminder</em> is my conference project for "Sculpture: Explorations" with Tishan
Hsu. It was positioned next to the Peace Pole on the North Side of Westlands,
the main administrative building at Sarah Lawrence College. I was only able to
receive permission to install the sculpture for a week. I also handed out and
posted a flier which can be downloaded below. The photos were taken by Keith
Perhac and I.</p>
<p>I came up with the idea for this piece last Spring while studying in central
Mexico. It is my attempt to fulfill the promises I have made.</p>
<p>One of the the most startling things about traveling in Guatemala was when
people would tell us, "I don't hate you simply because you are from the U.S...
I have learned to differentiate." This was told to us by mothers and widows of
the Disappeared, ministers, priests and others. I would learn that Guatemala
has been very heavily scarr ed by US foreign policy.</p>
<blockquote><p><em>Anti-communism and the National Security Doctrine (DSN) formed part of the
anti-Soviet strategy of the united States in Latin America. In Guatemala,
these were first expressed as anti-reformist, then anti-democratic policies,
culminating in criminal counterinsurgency.</em> <a href="/2002/04/reminder/#fnote1">(1)</a></p>
</blockquote>
<p>Chiapas was also a crazy stressful place. The human rights worker and nun who
were our contacts there asked that I not wear my name tag <a href="/2002/04/reminder/#fnote2">(2)</a>,
which could not only put me in danger of being deported, but might be traced
back to their communities and used against them. Later I would see the military
illegally photographing us. One of the groups we were able to visit were Las
Abejas (the Bees), a community devoted to non-violence. We visited a tent
village watched over by police with Kevlar helmets and body-armor
complimenting their M16s. Installed: Shadow of left hand is pointing to
Chinese glyph for Peace</p>
<p>In the Abejas community at Acteal, we saw a church they no longer meet in
because of the 1997 December 22 massacre perpetrated by a paramilitary group
(while the police in the area did nothing). We saw the bullet holes left in the
thin wood walls of the church. We saw the graves of the mostly women and
children from the massacre. We wept with the friends and families of the 45
victims.</p>
<p>These short anecdotes are the startling tip of an iceberg. I do not have time
here to talk of neoliberal economics, apathy, globalism, consumerism or dying
slowly. I only hope that this brief background gives some indication to how
overwhelming this was for me and members in my group. I didn’t know what to do
with with my newly realized position as representative of an Oppressor. My
friends and I asked, "what can we do? What can we possible do to help without
meddling, to right the wrongs and stop the killings?" They told us to simply
tell their story—to tell everyone we could the necessity of involvement here in
the U.S., to ask that our government and our corporations to quit killing in
our name.</p>
<h2 id="references">References</h2><ul>
<li><a name="fnote1">(1)</a> Report of the Commission for Historical
Clarification Conclusions and Recommendations (CEH). <a href="/2002/04/reminder/CEHreport-english.pdf"><em>Guatemala Memory of
Silence: Tz'inil Na'Tab'al</em> (PDF)</a>. Guatemala: Arte, Color y Texto,
S.A. page 19.<ul>
<li>Digital copy retreived from <a href="https://hrdag.org/publications-year/1999/">HRDAG – Human Rights Data Analysis Group |
Publications Year | 1999</a>.</li>
</ul>
</li>
<li><a name="fnote2">(2)</a> See <a href="/2002/02/name-tags/">Name Tags</a>.</li>
</ul>
Name Tags2002-02-22T22:22:22ZTim Wheelerurn:uuid:50ece029-7e81-3bc5-8177-fb94af7f5459<p><strong>Tim Wheeler. <em>Name Tags.</em> 1999 Aug - 2002 Feb</strong>
<br><strong>2 Purchased Plastic Name Tags</strong><br>
<img src="/2002/02/name-tags/nametags.jpg#floatright" alt="Tim Wheeler Name Tags" title="Tim Wheeler Name Tags"></p>
<p>These name tags are the objects of a performance piece performed daily over two
and half years. They not only served as the focal point or medium for
conversations, but often modified or dictated how people interacted with me.
They made, me at once, distinct and invisible. They placed me on a specific
socio-economic plane. They positioned me within the spectrum of service
industry jobs. They were a symbols for which the most common meaning seemed to
be "approachable."</p>
<p>The habitual (obsessive?) wearing of the name tags also became the symbols with
which others mapped my identity. The extent to which people not only defined
me, but drew some sort of stability from the ritual, was evident on the
occasions when I happened or appeared not to be wearing them.</p>
<p>I donated the name tags to the Student for Student Scholarship Fund (SSSF).
They were sold in the SSSF auction, 2009 Feb 22, for $30. I no longer wear name
tags for any extend amount of time.</p>
<p>It was interesting to see the extent to which people associated the name tags
with me--the extent to which they formed my identity as it was perceived by
others. There were times when I wasn’t wearing them that people would talk and
point as if I had one on. It took months for people I saw every week (or more)
to realize that I was no longer wearing them.</p>
Jazz Dance1998-10-22T13:45:00ZTim Wheelerurn:uuid:ccfa80e7-c37f-3963-9bac-60db201da4bc<pre><code>As I watched, the upright bassists hands,
Became spiders jumping on silken strands.
The bassist told a primitive tale
That was interrupted by the trumpets wail.
The trumpet argued the complexity
The sax agreed with Melody.
The drums enforced the upright bass.
Rhythms feet stomped a monstrous pace.
That sent up clouds of dust.
The pianist swayed with lust
For the moves of Melody.
Hand in Hand, Rhythm and Melody,
Danced a dance to set us free
To set sparks in the musicians eyes
That reinforced the saxs cries
That gave force to the drummers beat
And set springs beneath our pounding feet.
A solo sprawled on the throat of the sax
It loosened our bodies and let our eyes relax
The guitarist told stories with his hands
Of distant places and caravans
The trumpet let out a powerful plea
Causing us to nod and agree.
A concentrated look on his face,
Hammering his notes into place,
The pianist made a floor
On which Rhythm and Melody danced some more.
They danced above the ground
As the upright bass lent a solid sound
And when the drum found his voice
We were left without choice
But to sway and stomp some more.
So we happily stomped and swayed.
We danced as the jazz band played!
</code></pre>
Will Work for Tuition1998-08-18T22:22:22ZTim Wheelerurn:uuid:f30e4e49-00ce-3ec4-a032-51596b9b8384<p><strong>Tim Wheeler. <em>Will Work for Tuition.</em> 1998 August</strong>
<br><strong>Two hours panhandling with a borrowed briefcase and tie</strong></p>
<p><img src="/1998/08/will-work-for-tuition/panhandling.jpg" alt="Panhandling Collage" title="Panhandling Collage"></p>
<p>My first dollar made while panhandling outside the Los Angeles courthouse. I
made a total of $7.00 USD.</p>