<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.josetoro.xyz/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.josetoro.xyz/" rel="alternate" type="text/html" /><updated>2026-08-26T18:13:26+00:00</updated><id>https://blog.josetoro.xyz/feed.xml</id><title type="html">Nibelheim’s Landscapes</title><subtitle>An amazing website.</subtitle><author><name>Jose Toro</name><email>wynkth@gmail.com</email></author><entry><title type="html">Tuenti Challenge 8</title><link href="https://blog.josetoro.xyz/blog/programming-contest/python/tuenti-challenge-8/" rel="alternate" type="text/html" title="Tuenti Challenge 8" /><published>2018-05-03T00:00:00+00:00</published><updated>2018-05-03T00:00:00+00:00</updated><id>https://blog.josetoro.xyz/blog/programming-contest/python/tuenti-challenge-8</id><content type="html" xml:base="https://blog.josetoro.xyz/blog/programming-contest/python/tuenti-challenge-8/"><![CDATA[<p>In this post I'll explain the reasoning behind my solutions to the problems of the Tuenti Challenge 8.</p>

<p>List of challenges (you can check the problem statements <a target="_blank" href="https://contest.tuenti.net">here</a>):</p>
<ol>
    <a href="#c1"><li>Waffle love</li></a>
    <a href="#c2"><li>Hidden numbers</li></a>
    <a href="#c3"><li>Scales</li></a>
    <a href="#c4"><li>Brave Knight</li></a>
    <a href="#c5"><li>DNA Splicer</li></a>
    <a href="#c6"><li>Button Hero</li></a>
    <a href="#c7"><li>Unknown cartridge</li></a>
    <a href="#c8"><li>Security by doorscurity</li></a>
    <a href="#c9"><li>Scrambled Photo</li></a>
    <a href="#c10"><li>Dance</li></a>
    <a href="#c11"><li>Lasers</li></a>
    <a href="#c12"><li>Victoria's Secret</li></a>
</ol>
<hr>
<h2 id="c1">Challenge 1 - Waffle love</h2>
<p>There is not much to explain for this one. If $n$ and $m$ are the numbers of vertical and horizontal lines of the waffle, the number of holes will be $(n - 1) (m - 1)$.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">waffleHoles</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">m</span><span class="p">):</span>
  <span class="k">return</span> <span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">m</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span></code></pre></figure>
<hr>
<h2 id="c2">Challenge 2 - Hidden numbers</h2>
<p>In this problem we deal with the representation of base-$n$ numbers, where $2$ $\le$ $n$ $\le$ $26$. The problem asks to compute the maximum and minimum number that we can represent in this base (starting with a non-zero value) and return $max - min$ as a result.</p>

<p>We'll receive a string as an input, with all the chars different in it. As a first step we'll compute the base just by looking at the length of the string.</p>

<p>For the $max$ number, how do you construct the biggest number in base-10 with the numbers $[0..9]$? $9876543210$, right?. Then, for a base-$n$ number it will be given by the expression: $$max = \sum_{i=1}^{n - 1} i n^i$$</p>

<p>The $min$ case is a little bit tricky. If we assume that there is no leading zero, again, for the base-10 case it would be $1023456789$. We just exchanged the $0$ and the $1$ and voilá!. For our base-$n$ case:
$$min = n^{n - 1} + \sum_{i=0}^{n - 3} (n - i - 1) n^i$$</p>

<p>And now some code implementing the idea:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">hidden</span><span class="p">(</span><span class="n">word</span><span class="p">):</span>
  <span class="n">base</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
  <span class="n">max_value</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">([</span><span class="n">i</span> <span class="o">*</span> <span class="n">base</span> <span class="o">**</span> <span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">base</span><span class="p">)])</span>
  <span class="n">min_value</span> <span class="o">=</span> <span class="n">base</span> <span class="o">**</span> <span class="p">(</span><span class="n">base</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="nb">sum</span><span class="p">([(</span><span class="n">base</span> <span class="o">-</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">base</span> <span class="o">**</span> <span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">base</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)])</span>

  <span class="k">return</span> <span class="n">max_value</span> <span class="o">-</span> <span class="n">min_value</span></code></pre></figure>
<hr>
<h2 id="c3">Challenge 3 - Scales</h2>
<p>So, music time!</p>

<p>Summarizing the problem: We are going to receive a set of musical notes and we have to return all the major and natural minor scales that can match these notes.</p>

<p>To simplify the problem we are going to keep only the following notes: $A, A\#, B, C, C\#, D, D\#, E, F, F\#, G, G\#$. Why would we need $Cb$ if it's equivalent to $F\#$?</p>

<p>Now, let's generate the 24 scales! The first step for this is to know the half-step between notes and the progression of both scales:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">W</span> <span class="o">=</span> <span class="n">whole_step</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">H</span> <span class="o">=</span> <span class="n">half_step</span> <span class="o">=</span> <span class="ow">not</span> <span class="n">whole_step</span>

<span class="n">major_scale</span> <span class="o">=</span> <span class="p">[</span><span class="n">W</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">H</span><span class="p">]</span>
<span class="n">natural_minor_scale</span> <span class="o">=</span> <span class="p">[</span><span class="n">W</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">H</span><span class="p">,</span> <span class="n">W</span><span class="p">,</span> <span class="n">W</span><span class="p">]</span>

<span class="n">step</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'A'</span><span class="p">:</span> <span class="s">'A#'</span><span class="p">,</span>
    <span class="s">'A#'</span><span class="p">:</span> <span class="s">'B'</span><span class="p">,</span>
    <span class="s">'B'</span><span class="p">:</span> <span class="s">'C'</span><span class="p">,</span>
    <span class="s">'C'</span><span class="p">:</span> <span class="s">'C#'</span><span class="p">,</span>
    <span class="s">'C#'</span><span class="p">:</span> <span class="s">'D'</span><span class="p">,</span>
    <span class="s">'D'</span><span class="p">:</span> <span class="s">'D#'</span><span class="p">,</span>
    <span class="s">'D#'</span><span class="p">:</span> <span class="s">'E'</span><span class="p">,</span>
    <span class="s">'E'</span><span class="p">:</span> <span class="s">'F'</span><span class="p">,</span>
    <span class="s">'F'</span><span class="p">:</span> <span class="s">'F#'</span><span class="p">,</span>
    <span class="s">'F#'</span><span class="p">:</span> <span class="s">'G'</span><span class="p">,</span>
    <span class="s">'G'</span><span class="p">:</span> <span class="s">'G#'</span><span class="p">,</span>
    <span class="s">'G#'</span><span class="p">:</span> <span class="s">'A'</span>
<span class="p">}</span></code></pre></figure>

<p>Once we have this, generating the scales is piece of cake:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">generate_scale_set</span><span class="p">(</span><span class="n">note</span><span class="p">,</span> <span class="n">scale</span><span class="p">):</span>
    <span class="n">scale_set</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>

    <span class="k">for</span> <span class="n">step_type</span> <span class="ow">in</span> <span class="n">scale</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">step_type</span> <span class="o">==</span> <span class="n">half_step</span><span class="p">:</span>
            <span class="n">note</span> <span class="o">=</span> <span class="n">step</span><span class="p">[</span><span class="n">note</span><span class="p">]</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">note</span> <span class="o">=</span> <span class="n">step</span><span class="p">[</span><span class="n">step</span><span class="p">[</span><span class="n">note</span><span class="p">]]</span>
        <span class="n">scale_set</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">note</span><span class="p">,))</span>

    <span class="k">return</span> <span class="n">scale_set</span>

<span class="n">all_notes</span> <span class="o">=</span> <span class="p">[</span><span class="s">'A'</span><span class="p">,</span> <span class="s">'A#'</span><span class="p">,</span> <span class="s">'B'</span><span class="p">,</span> <span class="s">'C'</span><span class="p">,</span> <span class="s">'C#'</span><span class="p">,</span> <span class="s">'D'</span><span class="p">,</span> <span class="s">'D#'</span><span class="p">,</span> <span class="s">'E'</span><span class="p">,</span> <span class="s">'F'</span><span class="p">,</span> <span class="s">'F#'</span><span class="p">,</span> <span class="s">'G'</span><span class="p">,</span> <span class="s">'G#'</span><span class="p">]</span>
<span class="n">major_scales</span> <span class="o">=</span> <span class="p">{</span><span class="n">x</span><span class="p">:</span> <span class="n">generate_scale_set</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">major_scale</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">all_notes</span><span class="p">}</span>
<span class="n">natural_minor_scales</span> <span class="o">=</span> <span class="p">{</span><span class="n">x</span><span class="p">:</span> <span class="n">generate_scale_set</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">natural_minor_scale</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">all_notes</span><span class="p">}</span></code></pre></figure>

<p>Now that we have the scales we are all setup to solve our problem. Supposing that $notes$ is a set with the notes of the input (converted to our equivalent set of notes), the solution would be:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">scales</span><span class="p">(</span><span class="n">notes</span><span class="p">):</span>
  <span class="n">possible_scales</span> <span class="o">=</span> <span class="p">[]</span>

  <span class="c1"># major scales
</span>  <span class="k">for</span> <span class="n">note</span> <span class="ow">in</span> <span class="n">all_notes</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">notes</span><span class="p">.</span><span class="n">issubset</span><span class="p">(</span><span class="n">major_scales</span><span class="p">[</span><span class="n">note</span><span class="p">]):</span>
      <span class="n">possible_scales</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="s">'M'</span> <span class="o">+</span> <span class="n">note</span><span class="p">)</span>

  <span class="c1"># minor natural scales
</span>  <span class="k">for</span> <span class="n">note</span> <span class="ow">in</span> <span class="n">all_notes</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">notes</span><span class="p">.</span><span class="n">issubset</span><span class="p">(</span><span class="n">natural_minor_scales</span><span class="p">[</span><span class="n">note</span><span class="p">]):</span>
      <span class="n">possible_scales</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="s">'m'</span> <span class="o">+</span> <span class="n">note</span><span class="p">)</span>

  <span class="k">return</span> <span class="s">'None'</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">possible_scales</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="s">' '</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">possible_scales</span><span class="p">)</span></code></pre></figure>
<hr>

<h2 id="c4">Challenge 4 - Brave Knight</h2>
<p>Ingredients: 2D Grid, one source, two sequential destinations, obstacles, lava, trampolines and horse chess-style jumps!</p>

<p>Well, we can reduce this problem to a double shortest-path problem, one between the knight and the princess, and other between the princess and the destination.</p>

<p>In this case, every jump has the same cost, so a breadth-first search will be fine. Starting with the Knight, we generate the possible jumps. If $(row, col)$ is the position of the knight, we have 8 possible jumps, which are the following:</p>

<p>$(row + 2, col + 1)$,$(row + 2, col - 1)$,$(row - 2, col + 1)$,$(row - 2, col - 1)$,$(row + 1, col + 2)$,$(row + 1, col - 2)$,$(row - 1, col + 2)$,$(row - 1, col - 2)$.</p>

<p>In the case of trampolines the jumps will be of length $\pm 4$ and $\pm 2$. Now, given a position we can generate all the possible new jumps:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">open_nodes</span><span class="p">(</span><span class="n">position</span><span class="p">,</span> <span class="n">double_jumps</span><span class="p">,</span> <span class="n">lava_positions</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="n">cols</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">is_valid</span><span class="p">(</span><span class="n">p</span><span class="p">):</span>
        <span class="k">return</span> <span class="ow">not</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">lava_positions</span> <span class="ow">and</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">rows</span> <span class="ow">and</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">cols</span>

    <span class="n">possibles</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
    <span class="n">big</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="n">small</span> <span class="o">=</span> <span class="mi">1</span>

    <span class="k">if</span> <span class="n">position</span> <span class="ow">in</span> <span class="n">double_jumps</span><span class="p">:</span>
        <span class="n">big</span> <span class="o">*=</span> <span class="mi">2</span>
        <span class="n">small</span> <span class="o">*=</span> <span class="mi">2</span>

    <span class="n">possibles</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">big</span><span class="p">,</span> <span class="n">position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">small</span><span class="p">))</span>
    <span class="n">possibles</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">big</span><span class="p">,</span> <span class="n">position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">small</span><span class="p">))</span>
    <span class="n">possibles</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="n">big</span><span class="p">,</span> <span class="n">position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">small</span><span class="p">))</span>
    <span class="n">possibles</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="n">big</span><span class="p">,</span> <span class="n">position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">small</span><span class="p">))</span>

    <span class="n">possibles</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">small</span><span class="p">,</span> <span class="n">position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">big</span><span class="p">))</span>
    <span class="n">possibles</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">small</span><span class="p">,</span> <span class="n">position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">big</span><span class="p">))</span>
    <span class="n">possibles</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="n">small</span><span class="p">,</span> <span class="n">position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">big</span><span class="p">))</span>
    <span class="n">possibles</span><span class="p">.</span><span class="n">add</span><span class="p">((</span><span class="n">position</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="n">small</span><span class="p">,</span> <span class="n">position</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">big</span><span class="p">))</span>

    <span class="k">return</span> <span class="nb">filter</span><span class="p">(</span><span class="n">is_valid</span><span class="p">,</span> <span class="n">possibles</span><span class="p">)</span></code></pre></figure>

<p>Now we'll use this function to implement our breadth-first search algorithm:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">bfs</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">destination</span><span class="p">,</span> <span class="n">double_jumps</span><span class="p">,</span> <span class="n">lava_positions</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="n">cols</span><span class="p">):</span>
    <span class="n">visited</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
    <span class="n">actual</span> <span class="o">=</span> <span class="n">source</span>
    <span class="n">solution</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="c1"># possible destinations from source position
</span>    <span class="n">to_visit</span> <span class="o">=</span> <span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="n">open_nodes</span><span class="p">(</span><span class="n">actual</span><span class="p">,</span> <span class="n">double_jumps</span><span class="p">,</span> <span class="n">lava_positions</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="n">cols</span><span class="p">))</span>

    <span class="k">while</span> <span class="n">solution</span> <span class="ow">is</span> <span class="bp">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">to_visit</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="c1"># we get the first position and its cost from the queue
</span>        <span class="n">position</span> <span class="o">=</span> <span class="n">to_visit</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">position_cost</span> <span class="o">=</span> <span class="n">to_visit</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span>

        <span class="c1"># remove the head of the queue
</span>        <span class="n">to_visit</span> <span class="o">=</span> <span class="n">to_visit</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>

        <span class="c1"># mark the position as visited
</span>        <span class="n">visited</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">position</span><span class="p">)</span>

        <span class="c1"># reached the destination, return the number of jumps
</span>        <span class="k">if</span> <span class="n">position</span> <span class="o">==</span> <span class="n">destination</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">position_cost</span>

        <span class="c1"># get and add to the queue new positions reachable from the actual position
</span>        <span class="n">new_to_visit</span> <span class="o">=</span> <span class="n">open_nodes</span><span class="p">(</span><span class="n">position</span><span class="p">,</span> <span class="n">double_jumps</span><span class="p">,</span> <span class="n">lava_positions</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="n">cols</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">new_to_visit</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">n</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">visited</span> <span class="ow">and</span> <span class="n">n</span> <span class="ow">not</span> <span class="ow">in</span> <span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">to_visit</span><span class="p">):</span>
                <span class="n">to_visit</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">n</span><span class="p">,</span> <span class="n">position_cost</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>

    <span class="c1"># impossible to reach from source to destination
</span>    <span class="k">return</span> <span class="bp">None</span></code></pre></figure>

<p>And finally, we use our BFS algorithm to solve our problem!</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">brave_knight</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">princess</span><span class="p">,</span> <span class="n">destination</span><span class="p">,</span> <span class="n">double_jumps</span><span class="p">,</span> <span class="n">lava_positions</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="n">cols</span><span class="p">):</span>
  <span class="n">source_to_princess</span> <span class="o">=</span> <span class="n">bfs</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">princess</span><span class="p">,</span> <span class="n">double_jumps</span><span class="p">,</span> <span class="n">invalids</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="n">cols</span><span class="p">)</span>
  <span class="n">princess_to_destination</span> <span class="o">=</span> <span class="bp">None</span>

  <span class="k">if</span> <span class="n">source_to_princess</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
      <span class="n">princess_to_destination</span> <span class="o">=</span> <span class="n">bfs</span><span class="p">(</span><span class="n">princess</span><span class="p">,</span> <span class="n">destination</span><span class="p">,</span> <span class="n">double_jumps</span><span class="p">,</span> <span class="n">invalids</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="n">cols</span><span class="p">)</span>

  <span class="k">if</span> <span class="n">princess_to_destination</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
      <span class="k">return</span> <span class="s">'IMPOSSIBLE'</span>
  <span class="k">else</span><span class="p">:</span>
      <span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">source_to_princess</span> <span class="o">+</span> <span class="n">princess_to_destination</span><span class="p">)</span></code></pre></figure>
<hr>

<h2 id="c5">Challenge 5 - DNA Splicer</h2>

<p>It's bioinformatics time!</p>
<p>We have a bunch of strings and we have to get two identical sequences by aligning some of them, so... OK, we can start with two strings that have the same prefix and then try to concatenate with some of the rest strings.</p>

<p>Fingers crossed... nope. Sometimes we were wrong at the beginning, or in the middle of the process. Let's read the notes again... <i>"There are at most 18 parts"</i>. Somebody said backtracking? Let's go for it:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">backtracking</span><span class="p">(</span><span class="n">sequence_1</span><span class="p">,</span> <span class="n">sequence_2</span><span class="p">,</span> <span class="n">indexes</span><span class="p">,</span> <span class="n">sequences</span><span class="p">):</span>
    <span class="c1"># we keep the shortest sequence in sequence_1
</span>    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sequence_1</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">sequence_2</span><span class="p">):</span>
        <span class="n">temp</span> <span class="o">=</span> <span class="n">sequence_1</span>
        <span class="n">sequence_1</span> <span class="o">=</span> <span class="n">sequence_2</span>
        <span class="n">sequence_2</span> <span class="o">=</span> <span class="n">temp</span>

    <span class="k">for</span> <span class="n">index</span><span class="p">,</span> <span class="n">seq</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">sequences</span><span class="p">):</span>
        <span class="c1"># only not used sequences
</span>        <span class="k">if</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">indexes</span><span class="p">:</span>
            <span class="k">continue</span>

        <span class="c1"># we form a new sequence concatenating
</span>        <span class="n">new_sequence_1</span> <span class="o">=</span> <span class="n">sequence_1</span> <span class="o">+</span> <span class="n">seq</span>

        <span class="c1"># sequence match, we found it!
</span>        <span class="k">if</span> <span class="n">new_sequence_1</span> <span class="o">==</span> <span class="n">sequence_2</span><span class="p">:</span>
            <span class="n">indexes</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
            <span class="n">indexes</span><span class="p">.</span><span class="n">sort</span><span class="p">()</span>
            <span class="k">return</span> <span class="s">','</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="nb">str</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">),</span> <span class="n">indexes</span><span class="p">))</span>

        <span class="c1"># we didn't have a match, but still the bigger sequence starts with the smaller, continue the backtracking
</span>        <span class="k">elif</span> <span class="n">new_sequence_1</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">sequence_2</span><span class="p">)</span> <span class="ow">or</span> <span class="n">sequence_2</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">new_sequence_1</span><span class="p">):</span>
            <span class="n">new_indexes</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">indexes</span><span class="p">)</span>
            <span class="n">new_indexes</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>

            <span class="n">call</span> <span class="o">=</span> <span class="n">aux</span><span class="p">(</span><span class="n">new_sequence_1</span><span class="p">,</span> <span class="n">sequence_2</span><span class="p">,</span> <span class="n">new_indexes</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">call</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
                <span class="k">return</span> <span class="n">call</span>
    <span class="k">return</span> <span class="bp">None</span></code></pre></figure>

<p>Eeeh... but what are the initial arguments of our backtracking function? We'll have to try with all the pairs of strings that have a common prefix:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">dna</span><span class="p">(</span><span class="n">sequences</span><span class="p">):</span>
    <span class="n">solution</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">seq_1</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">sequences</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">solution</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="k">break</span>
        <span class="k">for</span> <span class="n">j</span><span class="p">,</span> <span class="n">seq_2</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">sequences</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="n">j</span><span class="p">:</span>
                <span class="k">continue</span>
            <span class="k">if</span> <span class="n">seq_1</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">seq_2</span><span class="p">)</span> <span class="ow">or</span> <span class="n">seq_2</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">seq_1</span><span class="p">):</span>
                <span class="n">solution</span> <span class="o">=</span> <span class="n">backtracking</span><span class="p">(</span><span class="n">seq_1</span><span class="p">,</span> <span class="n">seq_2</span><span class="p">,</span> <span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">],</span> <span class="n">sequences</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">solution</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
                    <span class="k">return</span> <span class="n">solution</span></code></pre></figure>

<p>Well, ready to go. We only need to connect to the server, retrieve the sequences and solve the cases until we get the key!</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">telnetlib</span> <span class="kn">import</span> <span class="n">Telnet</span>

<span class="n">tn</span> <span class="o">=</span> <span class="n">Telnet</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s">'52.49.91.111'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">3241</span><span class="p">)</span>

<span class="n">tn</span><span class="p">.</span><span class="n">read_until</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
<span class="n">tn</span><span class="p">.</span><span class="n">read_until</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
<span class="n">tn</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="s">'SUBMIT</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>

<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="n">response</span> <span class="o">=</span> <span class="n">tn</span><span class="p">.</span><span class="n">read_until</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
    <span class="k">if</span> <span class="s">'key'</span> <span class="ow">in</span> <span class="n">response</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>
        <span class="k">break</span>

    <span class="n">sequences</span> <span class="o">=</span> <span class="n">tn</span><span class="p">.</span><span class="n">read_until</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">).</span><span class="n">split</span><span class="p">()</span>
    <span class="n">tn</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">dna</span><span class="p">(</span><span class="n">sequences</span><span class="p">)</span> <span class="o">+</span> <span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span></code></pre></figure>
<hr>

<h2 id="c6">Challenge 6 - Button Hero</h2>

<p>Did you say <s>job</s> note scheduling? This is a typical dynamic programming problem. First, let's create a class to compute the start and the end time from the input data:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">class</span> <span class="nc">Note</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">length</span><span class="p">,</span> <span class="n">speed</span><span class="p">,</span> <span class="n">score</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">start</span> <span class="o">=</span> <span class="n">start</span> <span class="o">/</span> <span class="n">speed</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">end</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">start</span> <span class="o">+</span> <span class="n">length</span> <span class="o">/</span> <span class="n">speed</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">score</span> <span class="o">=</span> <span class="n">score</span></code></pre></figure>

<p>The idea is to sort the notes by end time in ascending order and create a table (array) of $n$ elements, where $n$ is the number of notes. Now, we compute the values for each entry of the table in the following way: For each $i \in [0..n-1]$ we compute:</p>
\begin{equation*}table[i] =

\left \{
\begin{array}{lc}
    note_0.score & i = 0\\
    \max \left\{
            \begin{array}{l}
                \text{Maximum by including } note_i\\
                table[i - 1]\\
            \end{array}
        \right . & 1 \leq i \le n
\end{array}
\right .
\end{equation*}

<p>Note that $table[i-1]$ is equivalent to "Maximum by excluding $note_i$". Now, for computing the maximum value including the $i^{th}$ note we will have to go back in the table and find the latest note compatible with $note_i$. Summarizing, we want to find a $j$ such that:</p>

$$ j \mid note_j.end < note_i.start \wedge \not\exists k : k > j \mid note_k.end < note_i.start $$

<p>If there is not such $j$, the maximum value will be just $note_i.score$. On the other hand, if it exists the maximum value will be $note_i.score + table[j]$.</p>

<p>Now it's code time! Let's implement this idea:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">schedule</span><span class="p">(</span><span class="n">notes</span><span class="p">):</span>
    <span class="n">notes</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">notes</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span><span class="p">.</span><span class="n">end</span><span class="p">)</span>

    <span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">notes</span><span class="p">)</span>
    <span class="n">table</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">)]</span>

    <span class="n">table</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">notes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">score</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
        <span class="n">score</span> <span class="o">=</span> <span class="n">notes</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">score</span>
        <span class="n">l</span> <span class="o">=</span> <span class="n">binary_search</span><span class="p">(</span><span class="n">notes</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">l</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
            <span class="n">score</span> <span class="o">+=</span> <span class="n">table</span><span class="p">[</span><span class="n">l</span><span class="p">]</span>

        <span class="n">table</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">score</span><span class="p">,</span> <span class="n">table</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">])</span>

    <span class="k">return</span> <span class="n">table</span><span class="p">[</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span></code></pre></figure>

<p>In this case we use binary search in order to search the latest compatible note, instead of going one by one through the previous notes. This reduces the time complexity of the search ($\mathcal{O}(log\ n)$ vs $\mathcal{O}(n)$).</p>

<p>Also notice that when parsing the notes we can find notes that have the same start and end, so we first combine these notes into a new one with the sum of both scores.</p>

<p>To finish here you have also the implementation of the binary search, just in case ;).</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">binary_search</span><span class="p">(</span><span class="n">notes</span><span class="p">,</span> <span class="n">start_index</span><span class="p">):</span>
    <span class="n">lo</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">hi</span> <span class="o">=</span> <span class="n">start_index</span> <span class="o">-</span> <span class="mi">1</span>

    <span class="k">while</span> <span class="n">lo</span> <span class="o">&lt;=</span> <span class="n">hi</span><span class="p">:</span>
        <span class="n">mid</span> <span class="o">=</span> <span class="p">(</span><span class="n">lo</span> <span class="o">+</span> <span class="n">hi</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>
        <span class="k">if</span> <span class="n">notes</span><span class="p">[</span><span class="n">mid</span><span class="p">].</span><span class="n">end</span> <span class="o">&lt;</span> <span class="n">notes</span><span class="p">[</span><span class="n">start_index</span><span class="p">].</span><span class="n">start</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">notes</span><span class="p">[</span><span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">].</span><span class="n">end</span> <span class="o">&lt;</span> <span class="n">notes</span><span class="p">[</span><span class="n">start_index</span><span class="p">].</span><span class="n">start</span><span class="p">:</span>
                <span class="n">lo</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="k">return</span> <span class="n">mid</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">hi</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span></code></pre></figure>
<hr>

<h2 id="c7">Challenge 7 - Unknown cartridge</h2>

<p>When we click on the game boy cartridge image we'll download an unknown file of 32K. First thing that I tried was...</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">❯ file unknown
unknown: Game Boy ROM image: <span class="s2">"UNKNOWN"</span> <span class="o">(</span>Rev.01<span class="o">)</span> <span class="o">[</span>ROM ONLY], ROM: 256Kbit</code></pre></figure>

<p>Surprise! A game boy ROM. You could load the ROM with a game boy emulator and see a lot of letters appearing in the screen, but we are not going to copy them by hand, isn't it?</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">❯ strings unknown | <span class="nb">head</span> <span class="nt">-n</span> 2
3&gt;UNKNOWN
<span class="nv">cGlrYWNodSBrYSBrYSBrYSBrYSBrYSBwaXBpIHBpa2FjaHUga2Ega2Ega2Ega2Ega2Ega2EgcGlwaSBwaWthY2h1IHBpa2FjaHUga2Ega2Ega2Ega2EgcGljaHUgcGlrYWNodSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaWNodSBwaWthY2h1IGthIGthIGthIGthIGthIHBpcGkgcGlrYWNodSBrYSBrYSBrYSBrYSBrYSBrYSBrYSBwaWthY2h1IHBpIHBpIHBpcGkgcGlrYWNodSBwaWNodSBwaWthY2h1IHBpIHBpcGkgcGlrYWNodSBwaSBwaSBwaSBwaWNodSBwaWthY2h1IGthIGthIGthIGthIGthIHBpcGkgcGlrYWNodSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaWNodSBwaWthY2h1IHBpIHBpIHBpIHBpIHBpIHBpIHBpIHBpIHBpIHBpIHBpcGkgcGlwaSBwaXBpIGNodSBrYSBwaWNodSBwaWNodSBwaWNodSBwaWNodSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaSBwaXBpIHBpIHBpIHBpIHBpIHBpIHBpIHBpIHBpcGkgcGkgcGkgcGkgcGlwaSBwaSBwaXBpIHBpa2EgcGkgcGkgcGkgcGkgcGkgcGkgcGkgcGkgcGkgcGk</span><span class="o">=</span></code></pre></figure>

<p>The second line seems like base64...</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">❯ strings unknown | <span class="nb">head</span> <span class="nt">-n</span> 2 | <span class="nb">tail</span> <span class="nt">-n</span> 1 | <span class="nb">base64</span> <span class="nt">--decode</span>
pikachu ka ka ka ka ka pipi pikachu ka ka ka ka ka ka pipi pikachu pikachu ka ka ka ka pichu pikachu pi pi pi pi pi pi pi pi pi pi pi pichu pikachu ka ka ka ka ka pipi pikachu ka ka ka ka ka ka ka pikachu pi pi pipi pikachu pichu pikachu pi pipi pikachu pi pi pi pichu pikachu ka ka ka ka ka pipi pikachu pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pichu pikachu pi pi pi pi pi pi pi pi pi pi pipi pipi pipi chu ka pichu pichu pichu pichu pi pi pi pi pi pi pi pi pi pi pipi pi pi pi pi pi pi pi pipi pi pi pi pipi pi pipi pika pi pi pi pi pi pi pi pi pi pi</code></pre></figure>

<p>So yep, now we have a Pikachu singing... After searching a little I found <a target="_blank" href="https://github.com/groteworld/pikalang">this</a>. Really? A 1-1 mapping to the brainfuck programming language.</p>

<p>First I tried to run the program with an interpreter, but if you search a little about brainfuck and see the translation of the pikalang program you'll see that it contains a ']' before '[' which doesn't make sense.</p>

<p>Or does it? Do you remember the image of the cartridge? It was backwards... so let's try this.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">❯ strings unknown | <span class="nb">head</span> <span class="nt">-n</span> 2 | <span class="nb">tail</span> <span class="nt">-n</span> 1 | <span class="nb">base64</span> <span class="nt">--decode</span> | <span class="nb">awk</span> <span class="s1">'{ for (i=NF; i&gt;1; i--) printf("%s ", $i); print $1; }'</span>
pi pi pi pi pi pi pi pi pi pi pika pipi pi pipi pi pi pi pipi pi pi pi pi pi pi pi pipi pi pi pi pi pi pi pi pi pi pi pichu pichu pichu pichu ka chu pipi pipi pipi pi pi pi pi pi pi pi pi pi pi pikachu pichu pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pikachu pipi ka ka ka ka ka pikachu pichu pi pi pi pikachu pipi pi pikachu pichu pikachu pipi pi pi pikachu ka ka ka ka ka ka ka pikachu pipi ka ka ka ka ka pikachu pichu pi pi pi pi pi pi pi pi pi pi pi pikachu pichu ka ka ka ka pikachu pikachu pipi ka ka ka ka ka ka pikachu pipi ka ka ka ka ka pikachu</code></pre></figure>

<p>And now let's try to execute this pikalang program... Bingo! P1K4L4NG_R00LZ.</p>
<hr>

<h2 id="c8">Challenge 8 - Security by doorscurity</h2>

<p>If you do a couple of examples by hand, you'll notice that you have to find the instant of time where the first door have 0 seconds left to open, the second one 1 second left to open...</p>

<p>Let's take the first example: first door opens every 7 seconds and 5 remaining for the next opening, and the second door opens every 9 seconds and 6 remaining for the next opening.</p>

<p>If you apply the previous reasoning, you want to find an instant with restrictions: 7 seconds of cycle with 5 remaining and 9 seconds of cycle with (6-1)=5 remaining.</p>

<p>In this case the remaining are the same for both doors, so we have only to wait 5 seconds! Let's take the second example:</p>

<p>5 seconds of cycle with 3 remaining, 9 seconds of cycle with (6-1)=5 remaining, and 7 seconds of cycle with (5-2)=3 remaining. Now let me rewrite this in other way:</p>

\begin{align*}
x & \equiv 3 \mod 5 \\
x & \equiv 5 \mod 9 \\
x & \equiv 3 \mod 7 \\
\end{align*}

<p>This is a congruence system, $x$ represents our instant of time, the remainders indicate the remaining seconds of the doors, and the modulus represent the door cycles. If we solve this congruence system we get $x = 248$, the solution to the second example.</p>

<p>If $p$ and $t$ are the cycle and the time that has passed from the last opening of the $i^{th}$ door, we will add to our system one of the following congruences (both are equivalent):</p>

\begin{align*}
x & \equiv - (t + i) & \mod p \\
x & \equiv p - t - i & \mod p \\
\end{align*}

<p>For solving the congruence system we can use the Chinese remainder theorem, but... if you read about it in wikipedia you'll find <i>"under the condition that the divisors are pairwise coprime"</i>. And guess what? Some of the inputs of the problem don't meet this condition.</p>

<p>But no worries, thanks to the power of Internet I could find an implementation of the Chinese remainder theorem that also works for non-pairwise coprime cases :).</p>

<p>In the implementation below, $a$ represents the list of remainders, and $n$ the list of the modulus.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">fractions</span> <span class="kn">import</span> <span class="n">gcd</span>

<span class="k">def</span> <span class="nf">ext_gcd</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
    <span class="n">v1</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">a</span><span class="p">]</span>
    <span class="n">v2</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">b</span><span class="p">]</span>

    <span class="k">if</span> <span class="n">a</span> <span class="o">&gt;</span> <span class="n">b</span><span class="p">:</span>
        <span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span>
    <span class="k">while</span> <span class="n">v1</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">q</span> <span class="o">=</span> <span class="n">v2</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">/</span> <span class="n">v1</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="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">v1</span><span class="p">)):</span>
            <span class="n">v2</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">v2</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">q</span> <span class="o">*</span> <span class="n">v1</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="n">v1</span><span class="p">,</span> <span class="n">v2</span> <span class="o">=</span> <span class="n">v2</span><span class="p">,</span> <span class="n">v1</span>
    <span class="k">return</span> <span class="n">v2</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">v2</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">chinese_remainder</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">m</span><span class="p">):</span>
    <span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>

    <span class="n">a1</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">m1</span> <span class="o">=</span> <span class="n">m</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
        <span class="n">a2</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="n">m2</span> <span class="o">=</span> <span class="n">m</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>

        <span class="n">g</span> <span class="o">=</span> <span class="n">gcd</span><span class="p">(</span><span class="n">m1</span><span class="p">,</span> <span class="n">m2</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">a1</span> <span class="o">%</span> <span class="n">g</span> <span class="o">!=</span> <span class="n">a2</span> <span class="o">%</span> <span class="n">g</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">None</span>

        <span class="n">p</span><span class="p">,</span> <span class="n">q</span> <span class="o">=</span> <span class="n">ext_gcd</span><span class="p">(</span><span class="n">m1</span> <span class="o">/</span> <span class="n">g</span><span class="p">,</span> <span class="n">m2</span> <span class="o">/</span> <span class="n">g</span><span class="p">)</span>

        <span class="n">mod</span> <span class="o">=</span> <span class="n">m1</span> <span class="o">/</span> <span class="n">g</span> <span class="o">*</span> <span class="n">m2</span>
        <span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">a1</span> <span class="o">*</span> <span class="p">(</span><span class="n">m2</span> <span class="o">/</span> <span class="n">g</span><span class="p">)</span> <span class="o">%</span> <span class="n">mod</span> <span class="o">*</span> <span class="n">q</span> <span class="o">%</span> <span class="n">mod</span> <span class="o">+</span> <span class="n">a2</span> <span class="o">*</span> <span class="p">(</span><span class="n">m1</span> <span class="o">/</span> <span class="n">g</span><span class="p">)</span> <span class="o">%</span> <span class="n">mod</span> <span class="o">*</span> <span class="n">p</span> <span class="o">%</span> <span class="n">mod</span><span class="p">)</span> <span class="o">%</span> <span class="n">mod</span>

        <span class="n">a1</span> <span class="o">=</span> <span class="n">x</span>
        <span class="k">if</span> <span class="n">a1</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">a1</span> <span class="o">+=</span> <span class="n">mod</span>
        <span class="n">m1</span> <span class="o">=</span> <span class="n">mod</span>

    <span class="k">return</span> <span class="n">a1</span></code></pre></figure>
<hr>

<h2 id="c9">Challenge 9 - Scrambled Photo</h2>

<p>Rocket science! Seeing the paper shredder and the image you can imagine that the columns of the picture has been shuffled.</p>

<p>After that I found a good implementation of an image unshredder <a target="_blank" href="https://www.nayuki.io/page/image-unshredder-by-annealing">here</a> by Project Nayuki that works pretty good. I used it, scanned the QR codes on both images, and went for the next challenge!</p>
<hr>

<h2 id="c10">Challenge 10 - Dance</h2>

<p>When I first read about returning the number of different ways modulo $10^9 + 7$, I realized that brute force was not an option for this problem.</p>

<p>Then I thought that maybe this is an opportunity to use a divide and conquer strategy, let me explain:</p>

<p>We pick a person from the group of people, let's say that we have the case 4 from the problem statement, with 8 people. If we pick the upper left person, we have 7 possibilities of joining the person with another one (we'll forget about relationships for now).</p>

<center><img src="https://blog.josetoro.xyz/assets/images/dance.png"/></center>

<p>At both sides of the new couple (marked in red) we'll have new groups of people, that can be transformed into new sub-instances of the problem. Both sub-instances can be combined using the <a target="_blank" href="https://en.wikipedia.org/wiki/Rule_of_product">rule of product</a> to compute the total number of ways of arranging all the people.</p>

<p>Notice that this transformation also have to reassign indexes to the members of the new sub-groups. This will help us in further optimizations.</p>

<p>The solution for the problem of $n$ people would be, picking a person $p_i$:<p>

$$\sum_{\substack{j\\j \ne i}}^{0 \leq j \le n} sub(i, j)\ sub(j, i)$$

<p>Where $sub(i,j)$ denotes the sub-instance of the problem generated with the people from the $i^{th}$ person to the $j^{th}$ person (both excluded).</p>

<p>The problem has also some base cases:</p>

<p>if $n = 0$, then the ways of arranging people is $1$, if $n$ is odd, it's $0$, and in the case of $n = 2$ we'll return $1$ in the case of compatibility between both persons and $0$ in the case of non-compatibility between them.</p>

$$
\left \{
    \begin{array}{l}
        n = 0: 1\\
        n\ mod\ 2\ = 1: 0\\
        n = 2
            \left \{
                \begin{array}{l}
                    1\ \text{if both persons are compatible}\\
                    0\ \text{otherwise}
                \end{array}
            \right .
    \end{array}
\right .
$$

<p>Now optimizations! When we're pairing the person with all the rest of people we have to check if both persons are compatible. If this is not the case, we can skip the whole call of both sub-groups, because it would be invalid.</p>

<p>Another technique that I used to reduce the computation time is memoization. Look at splits 1-7, 2-5, 3-6. When transformed into sub-instances they will be equivalent (assuming we also have the same compatibilities among them).</p>

<p>So, every time we solve an instance, we store the input and their corresponding relationships with the solution, then when we match another sub-instance that is equivalent, we can return the result without computing it again.</p>

<p>Here you have the implementation (I assure you it can be further optimized).</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">MEMO</span> <span class="o">=</span> <span class="p">{}</span>

<span class="k">def</span> <span class="nf">transform</span><span class="p">(</span><span class="n">dancers</span><span class="p">,</span> <span class="n">src</span><span class="p">,</span> <span class="n">dst</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">src</span> <span class="o">&gt;</span> <span class="n">dst</span><span class="p">:</span>
        <span class="n">temp</span> <span class="o">=</span> <span class="n">src</span>
        <span class="n">src</span> <span class="o">=</span> <span class="n">dst</span>
        <span class="n">dst</span> <span class="o">=</span> <span class="n">temp</span>

    <span class="n">right_idx</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">dancers</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">src</span> <span class="o">&lt;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">dst</span><span class="p">:</span>
            <span class="n">right_idx</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>

    <span class="n">left_idx</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">dancers</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">src</span> <span class="ow">and</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">dst</span> <span class="ow">and</span> <span class="n">i</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">right_idx</span><span class="p">:</span>
            <span class="n">left_idx</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">left_idx</span><span class="p">,</span> <span class="n">right_idx</span>


<span class="k">def</span> <span class="nf">divide_and_conquer</span><span class="p">(</span><span class="n">dancers</span><span class="p">,</span> <span class="n">grudges</span><span class="p">):</span>
    <span class="n">size</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">dancers</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">size</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">return</span> <span class="mi">1</span>
    <span class="k">elif</span> <span class="n">size</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">return</span> <span class="mi">0</span>
    <span class="k">elif</span> <span class="n">size</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">dancers</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">in</span> <span class="n">grudges</span><span class="p">[</span><span class="n">dancers</span><span class="p">[</span><span class="mi">0</span><span class="p">]]:</span>
            <span class="k">return</span> <span class="mi">1</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="mi">0</span>

    <span class="n">h</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">dancers</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">MEMO</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">MEMO</span><span class="p">[</span><span class="n">h</span><span class="p">]</span>

    <span class="n">dancer</span> <span class="o">=</span> <span class="n">dancers</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>

    <span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">dancers</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="n">dancer</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="k">if</span> <span class="n">i</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">grudges</span><span class="p">[</span><span class="n">dancer</span><span class="p">]:</span>
            <span class="n">left_dancers</span><span class="p">,</span> <span class="n">right_dancers</span> <span class="o">=</span> <span class="n">transform</span><span class="p">(</span><span class="n">dancers</span><span class="p">,</span> <span class="n">dancer</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>

            <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">left_dancers</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">right_dancers</span><span class="p">):</span>
                <span class="n">first</span> <span class="o">=</span> <span class="n">left_dancers</span>
                <span class="n">second</span> <span class="o">=</span> <span class="n">right_dancers</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">first</span> <span class="o">=</span> <span class="n">right_dancers</span>
                <span class="n">second</span> <span class="o">=</span> <span class="n">left_dancers</span>

            <span class="n">first_sol</span> <span class="o">=</span> <span class="n">divide_and_conquer</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">grudges</span><span class="p">)</span>

            <span class="k">if</span> <span class="n">first_sol</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
                <span class="n">second_sol</span> <span class="o">=</span> <span class="n">divide_and_conquer</span><span class="p">(</span><span class="n">second</span><span class="p">,</span> <span class="n">grudges</span><span class="p">)</span>
                <span class="n">total</span> <span class="o">+=</span> <span class="n">first_sol</span> <span class="o">*</span> <span class="n">second_sol</span>

    <span class="n">MEMO</span><span class="p">[</span><span class="n">h</span><span class="p">]</span> <span class="o">=</span> <span class="n">total</span>
    <span class="k">return</span> <span class="n">total</span></code></pre></figure>
<hr>

<h2 id="c11">Challenge 11 - Lasers</h2>

<p>The only thing I was sure at the beginning is that we could put lasers in all the rows and columns that are totally empty, good! After that, I found the order in which we should process the items to decide which laser to put in order to achieve the optimal solution.</p>

<p>Even with this order, when with one item I had the choice of putting a horizontal or a vertical laser, I couldn't find the heuristic to choose in an optimal way, so I tried using backtracking. For the test case it was fine, but for the submit cases it was way too slow.</p>

<p>So, in the end I opted to use <a target="_blank" href="https://en.wikipedia.org/wiki/Integer_programming">integer linear programming (ILP)</a> for solving the problem. The formalization for this problem is the following:</p>

<p>Let:</p>

<p>$
n = \text{number of columns of the grid}\\
m = \text{number of rows of the grid}
$</p>

<p>$
X = \{x_0, x_1, x_2, ..., x_{n-1}\}\\
Y = \{y_0, y_1, y_2, ..., y_{m-1}\}
$</p>

<p>The expression to maximize is:</p>
$$sum(X) + sum(Y)$$

<p>Subject to:</p>

$$
    \forall (i, j) \in items : x_i + y_j \leq 1
$$
$$
    x_i \in \{0, 1\}\ \forall\ 0 \leq i < n\\
    y_i \in \{0, 1\}\ \forall\ 0 \leq i < m
$$

<p>Now we can use <a href="http://www.cvxpy.org" target="_blank">cvxpy</a> for implementing our problem in an easy way:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">cvxpy</span> <span class="kn">import</span> <span class="o">*</span>

<span class="k">def</span> <span class="nf">lasers</span><span class="p">(</span><span class="n">cols</span><span class="p">,</span> <span class="n">rows</span><span class="p">,</span> <span class="n">items</span><span class="p">):</span>
    <span class="n">X</span> <span class="o">=</span> <span class="n">Bool</span><span class="p">(</span><span class="n">cols</span><span class="p">)</span>
    <span class="n">Y</span> <span class="o">=</span> <span class="n">Bool</span><span class="p">(</span><span class="n">rows</span><span class="p">)</span>
    <span class="n">constraints</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">for</span> <span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">)</span> <span class="ow">in</span> <span class="n">items</span><span class="p">:</span>
        <span class="n">constraints</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">Y</span><span class="p">[</span><span class="n">row</span><span class="p">]</span> <span class="o">+</span> <span class="n">X</span><span class="p">[</span><span class="n">col</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">)</span>

    <span class="n">objective</span> <span class="o">=</span> <span class="n">Maximize</span><span class="p">(</span><span class="n">sum_entries</span><span class="p">(</span><span class="n">X</span><span class="p">)</span> <span class="o">+</span> <span class="n">sum_entries</span><span class="p">(</span><span class="n">Y</span><span class="p">))</span>
    <span class="n">prob</span> <span class="o">=</span> <span class="n">Problem</span><span class="p">(</span><span class="n">objective</span><span class="p">,</span> <span class="n">constraints</span><span class="p">)</span>

    <span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">prob</span><span class="p">.</span><span class="n">solve</span><span class="p">()))</span></code></pre></figure>

<p>Notice that we can optimize the execution time removing variables that are not necessary (empty rows or columns).</p>
<hr>

<h2 id="c12">Challenge 12 - Victoria's Secret</h2>

<p>First of all, IP and port: nothing on telnet, <i>"This is not the protocol you are looking for"</i> with curl, let's try wget:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">❯ wget 52.49.91.111:3240
<span class="nt">--2018-05-04</span> 16:05:25--  http://52.49.91.111:3240/
Connecting to 52.49.91.111:3240... connected.
HTTP request sent, awaiting response... 418 I<span class="s1">'m a keyboard!
2018-05-04 16:05:25 ERROR 418: I'</span>m a keyboard!.</code></pre></figure>

<p><i>I'm a keyboard!</i>.</p>

<p>That answer plus the debian hint and the usb to ethernet connector in the image = usbip. I setup a vm with the latest debian and installed the usbip package.</p>

<p>After that, I binded the usb keyboard and the magic started. Something was writing on my terminal, but it introduced a stty command that hid some of the keystrokes.</p>

<p>I'm not going to tell you what happens if you don't follow the instructions, be my guest and try. If after binding the keyboard you switch to a text editor you'll see the hidden message, follow the instructions and all will be fine.</p>

<p>If you are a morse code master, you'll end seeing something like this, and if you're a vi master, you'll get the solution :).</p>

<center><img src="https://blog.josetoro.xyz/assets/images/usbip.png"/></center>

<p>I hope you enjoyed these solutions, and see you next year!</p>]]></content><author><name>Jose Toro</name><email>wynkth@gmail.com</email></author><category term="blog" /><category term="programming-contest" /><category term="python" /><summary type="html"><![CDATA[The reasoning behind my solutions to the problems.]]></summary></entry></feed>