<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Noah's Cool Doggo Bloggo]]></title><description><![CDATA[softcore software by a hardcore hooligan]]></description><link>http://mycode.doesnot.run/</link><generator>Ghost 0.11</generator><lastBuildDate>Fri, 13 Feb 2026 16:10:49 GMT</lastBuildDate><atom:link href="http://mycode.doesnot.run/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Creating a tool to help answer yes/no questions in Irish, a language without words for "yes" or "no"]]></title><description><![CDATA[I explain how Irish has no words for yes or no, how and why this causes an issue in machine translation, and how I wrote a program to approach the problem.]]></description><link>http://mycode.doesnot.run/2018/08/15/yes-no/</link><guid isPermaLink="false">1dab3249-84de-4016-ad19-d4167d0a70b5</guid><category><![CDATA[gaeilge]]></category><category><![CDATA[language]]></category><category><![CDATA[programming]]></category><category><![CDATA[projects]]></category><category><![CDATA[python]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Wed, 15 Aug 2018 16:59:34 GMT</pubDate><media:content url="http://mycode.doesnot.run/content/images/2018/08/DiZE7W0X0AAzs7g.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://mycode.doesnot.run/content/images/2018/08/DiZE7W0X0AAzs7g.jpg" alt="Creating a tool to help answer yes/no questions in Irish, a language without words for "yes" or "no""><p>I had the pleasure of spending a couple of months this summer interning at the <a href="https://www.adaptcentre.ie/">ADAPT Centre</a> in Dublin City University, working with the GaelTech team of <a href="http://www.nclt.dcu.ie/~tlynn/">Dr. Teresa Lynn</a> and PhD candidates <a href="https://twitter.com/AnEihe">Abigail Walsh</a> and <a href="https://twitter.com/ismisemeg">Meghan Dowling</a>, on resources and tools for Irish language technology.</p>

<p><blockquote class="twitter-tweet"><p lang="cy" dir="ltr">Foireann GaelTeic ag an ócáid intéirneachta inniu san <a href="https://twitter.com/AdaptCentre?ref_src=twsrc%5Etfw">@AdaptCentre</a>! Tá sárobair déanta ag Noah maidir leis a chuid taighde ar theicneolaíocht na <a href="https://twitter.com/hashtag/Gaeilge?src=hash&amp;ref_src=twsrc%5Etfw">#Gaeilge</a> <a href="https://t.co/DJA2WlvKqF">pic.twitter.com/DJA2WlvKqF</a></p>&mdash; Meghan Dowling (@ismisemeg) <a href="https://twitter.com/ismisemeg/status/1019578162731257858?ref_src=twsrc%5Etfw">July 18, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p>

<p>I had a lot of fun while I was there, and learned a lot about research and the postgraduate life. I will finish my undergraduate degree soon, and I have no doubt that this experience will be extremely useful when I choose what to do afterwards!</p>

<p>I got to work on a few very interesting things at ADAPT. Below is a photo Meghan took of the poster I procrastinated in making that sums up some of my projects.</p>

<p><blockquote class="twitter-tweet" data-conversation="none"><p lang="cy" dir="ltr">Tá grianghraif agam de, ach seans nach bhfuil sé ró-shoiléir. Is le <a href="https://twitter.com/iandioch?ref_src=twsrc%5Etfw">@iandioch</a> é agus tá <a href="https://twitter.com/AnEihe?ref_src=twsrc%5Etfw">@aneihe</a> ag obair ar an stuif seo chomh maith, beidh tuilleadh eolais acu! <a href="https://t.co/Tjvymx2l1J">pic.twitter.com/Tjvymx2l1J</a></p>&mdash; Meghan Dowling (@ismisemeg) <a href="https://twitter.com/ismisemeg/status/1019595483516493824?ref_src=twsrc%5Etfw">July 18, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p>

<p>However, not mentioned on the poster is my final project, which was to do with programmatically answering yes/no questions in Irish. Below, I explain how "yes" and "no" work in the language,  try to show why this causes an issue in machine translation, before briefly explaining how I tried to get a computer to approach the problem.</p>

<h1 id="yesandnoinirish">"Yes" and "No" in Irish</h1>

<p><a href="https://en.wikipedia.org/wiki/Irish_language">Irish</a> is a <a href="https://en.wikipedia.org/wiki/Celtic_languages">Celtic language</a>, spoken (spoiler alert) on the island of Ireland. It has <a href="https://en.wikipedia.org/wiki/List_of_languages_by_type_of_grammatical_genders#Masculine_and_feminine">2 grammatical genders</a>, <a href="https://en.wikipedia.org/wiki/Irish_declension#Case">4-ish grammatical cases</a>, <a href="https://en.wikipedia.org/wiki/Verb%E2%80%93subject%E2%80%93object#Celtic_languages">VSO word order</a>, and has no words for <code>"yes"</code> or <code>"no"</code>.</p>

<p>The lack of words for <code>"yes"</code> and <code>"no"</code> is surprising to many people I mention it to. If you were to translate the answers to the question <code>"Is she ok?"</code> to Irish, it would look something like this:</p>

<table>  
<tr><td>English</td><td>is she ok?</td><td>yes</td><td>no</td></tr>  
<tr><td>Irish</td><td>an bhfuil sí ceart go leor?</td><td>tá</td><td>níl</td></tr>  
<tr><td>Literal translation</td><td>is she alright?</td><td>is</td><td>isn't</td></tr>  
</table>

<p>As you can see, instead of <code>"yes"</code> or <code>"no"</code>, you reply with <code>"is"</code> or <code>"isn't"</code>. The person answering the question takes the verb from the question and repeats it for the response.</p>

<p>Another example:</p>

<table>  
<tr><td>English</td><td>will you go to the disco tonight?</td><td>yes</td><td>no</td></tr>  
<tr><td>Irish</td><td>an rachaidh tú chuig an dioscó anocht?</td><td>rachaidh</td><td>ní rachaidh</td></tr>  
<tr><td>Literal translation</td><td>will you go to the disco tonight?</td><td>will go</td><td>won't go</td></tr>  
</table>

<p>Machine translation systems struggle with differences in languages like this one. Google Translate is probably the most popular machine translation system in use right now; here is its attempt at translating something along these lines:</p>

<table>  
<tr><td>English</td><td>Will you go to the disco tonight? Yes? No?</td></tr>  
<tr><td>Google Translate's Irish</td><td>An dtéann tú ar an dioscó anocht? Tá? Níl?</td></tr>  
<tr><td>Literal translation of the "Irish"</td><td>Do you go to the disco tonight? Is? Isn't?</td></tr>  
</table>

<p>It is clear to see that this is a bad translation. In Irish, in order to correctly translate a "yes" or "no", you need to reference the question you were asked. You take the main verb, maybe inflect it in some way, and respond with it. You can't just translate the word "yes", in isolation; you need to know what you're saying <code>"yes"</code> to. Google Translate doesn't seem to know how to do this.</p>

<h1 id="languagesareweird">Languages are weird</h1>

<p>The above examples are complicated by how English uses auxiliary verbs <code>"will"</code> and <code>"won't"</code> for its future tense. Also, English word order changes from being SVO (<code>"he is a dog."</code>) in normal phrases to being VSO (<code>"is he a dog?"</code>) in questions. English is a weird language.</p>

<p>See the following for how the structure of a more normal phrase, instead of a question, compares in Irish and English:</p>

<table>  
<tr><td>English</td><td>You will go to the disco tonight.</td></tr>  
<tr><td>Irish</td><td>Rachaidh tú chuig an dioscó anocht.</td></tr>  
<tr><td>Literal translation</td><td>["go" in future tense] you to the disco tonight.</td></tr>  
</table>

<p>Irish doesn't have an auxiliary verb here like English does, and it is consistently VSO in phrases like this.</p>

<p>English is indeed weird in switching word order and using auxiliary verbs, but of course, Irish is weird in its own ways too. For example, try and figure out how the following translation makes sense:</p>

<table>  
<tr><td>English</td><td>do you have a dog?</td><td>yes</td><td>no</td></tr>  
<tr><td>Irish</td><td>an bhfuil madra agat?</td><td>tá</td><td>níl</td></tr>  
<tr><td>Literal translation</td><td>is a dog at you?</td><td>is</td><td>isn't</td></tr>  
</table>

<p>We saw earlier in the <code>"is she ok?"</code> example that <code>"an bhfuil?"</code> means <code>"is?"</code> in Irish. <code>"is &lt;something&gt; &lt;something&gt;?"</code> doesn't seem like it can translate as <code>"do you have a dog?"</code>, but let's see.</p>

<ol>
<li><code>"madra"</code> is the Irish for <code>"dog"</code>. But Irish has no <a href="https://en.wikipedia.org/wiki/Article_(grammar)#Indefinite_article">indefinite article</a>, so <code>"madra"</code> can equally mean <code>"dog"</code> or <code>"a dog"</code>. So now we know <code>"an bhfuil madra agat?"</code> is <code>"is [a] dog &lt;something&gt;?"</code>.  </li>
<li>Irish has <a href="https://en.wikipedia.org/wiki/Inflected_preposition#Insular_Celtic">prepositional pronouns</a>; it combines prepositions (like <code>"ag"</code>, which means <code>"at"</code>) with pronouns (like <code>"tú"</code>, which means <code>"you"</code>), to create one word (like <code>"agat"</code>, which means <code>"at you"</code>). For some more examples: <code>"agam"</code> means <code>"at me"</code>; <code>"aige"</code> means <code>"at him"</code>; <code>"romham"</code> means <code>"before me"</code>. So how does <code>"is a dog at you?"</code> mean <code>"do you have a dog?"</code>?  </li>
<li>The language has no verb "to have" - it uses an <a href="https://en.wikipedia.org/wiki/Existential_clause#Indicating_possession">"existential clause"</a> to indicate possession instead. <code>"I have a dog"</code> is translated as <code>"a dog is at me"</code>. Similarly, <code>"you have a cat"</code> would be <code>"tá cat agat"</code>, and <code>"Seán had bananas"</code> would be <code>"bhí bananaí ag Seán"</code>. </li>
</ol>

<p>Maybe it makes some sense now that the phrase <code>"do you have a dog?"</code> is <code>"an bhfuil madra agat?"</code> (literally <code>"is a dog at you?"</code>) in Irish. I suppose Irish is a weird language too.</p>

<p>Language quirks like these mentioned above further complicate translating a simple <code>"yes"</code> or <code>"no"</code> answer. We saw previously that you need to analyse the verb in the question before you give an answer, but now we see that the question verb might not be the same in the two languages; the main verb of <code>"do you have a dog?"</code> is <code>"have"</code> in English, but "<code>is</code>" in Irish. So even if all you want to do is translate the word <code>"yes"</code>, you'll first have to translate whatever question you are trying to answer!</p>

<h1 id="sidenotethecopula">Sidenote: the copula</h1>

<p>Irish has this thing called <a href="https://en.wikipedia.org/wiki/Irish_syntax#The_copula_is">the copula</a>, which is kind of like a verb but isn't actually a verb. Lots of yes/no questions asked are based on this, and are answered in a different way to verbal questions.</p>

<p>The copula is used to express identity or equivalence, but I'll leave you to read the wikipedia bit about it linked above for a better explanation. I'll just give a few examples of its usage:  </p>

<table>  
<tr><td>English</td><td>is Máire a doctor?</td><td>yes</td><td>no</td></tr>  
<tr><td>Irish</td><td>an dochtúir í Máire?</td><td>'sea</td><td>ní hea</td></tr>  
<tr><td>Literal translation</td><td>is a doctor her Máire?</td><td>are they</td><td>aren't they</td></tr>  
</table>

<p>A slightly different question changes the question and answer quite a bit:</p>

<table>  
<tr><td>English</td><td>is Máire the doctor?</td><td>yes</td><td>no</td></tr>  
<tr><td>Irish</td><td>an í Máire an dochtúir?</td><td>is í</td><td>ní hí</td></tr>  
<tr><td>Literal translation</td><td>is her Máire the doctor?</td><td>is her</td><td>isn't her</td></tr>  
</table>

<p>And here's a more complicated usage:</p>

<table>  
<tr><td>English</td><td>would you like a drink?</td><td>yes</td><td>no</td></tr>  
<tr><td>Irish</td><td>ar mhaith leat deoch?</td><td>ba mhaith</td><td>níor mhaith</td></tr>  
<tr><td>Literal translation</td><td>would be good with-you a drink?</td><td>would be good</td><td>wouldn't be good</td></tr>  
</table>

<h1 id="howcanacomputerdothis">How can a computer do this?</h1>

<p>Fluent Irish speakers can do all of the above without thinking about it. But it's difficult to get a computer to do it.</p>

<p>Guided by Dr. Teresa Lynn, I made progress on <a href="http://github.com/iandioch/adapt/tree/master/yesno">a tool</a> to do this. Following is the steps taken.</p>

<ol>
<li>First, I compiled a small corpus of yes/no questions and answers in English and Irish. I used this to help develop and to test the tool.  </li>
<li>You give the tool a question in Irish that you would like to answer with a "yes" or "no", by eg. running the command <code>echo "an bhfuil sí ceart go leor?" | python3 process.py</code>  </li>
<li><a href="https://www.tcd.ie/slscs/staff/uidhonne">Dr. Elaine Uí Dhonnchadha</a> wrote a <a href="http://doras.dcu.ie/2349/">part-of-speech tagger for Irish</a> that is run by this tool.  </li>
<li>The output of the POS-tagger is converted into <a href="http://universaldependencies.org/format.html">CoNLL-U Format</a>.  </li>
<li>The CoNNL-U data is passed to <a href="http://www.maltparser.org/">Maltparser</a>, trained on <a href="https://github.com/tlynn747/IrishDependencyTreebank">the Irish dependency treebank</a>. This <a href="https://en.wikipedia.org/wiki/Treebank">adds grammatical annotations</a> to the question, showing the relations between words.  </li>
<li>The tool can now analyse the grammar of the question, and decide if it is a verbal question, or is based on the copula<sup>[1]</sup>. <br>
<ul><li>If the question is verbal, the tool tries to gather information about the lemma and tense/aspect/mood of the verb.</li>
<li>If the question is copular, the tool tries to find the copula form used<sup>[2]</sup>, and its lemma. It also tries to find the associated predicate, and its lemma.</li></ul></li>
<li>It outputs a report based on this information, that can be used to generate a response to the question<sup>[3]</sup>.</li>
</ol>

<p>Here's an example output of the tool:</p>

<pre><code>{
    "type": "verb",
    "error": null,
    "verb": {
        "lemma": "buail",
        "surface": "mbuailfidh",
        "tams": [
            "FutInd"
        ]
    },
    "question": "Nach mbuailfidh Eilís an bithiúnach ?"
}
</code></pre>

<p>It tells you that the question, <code>"Nach mbuailfidh Eilís an bithiúnach?"</code> (<code>"Won't Eilís hit the scoundrel?"</code>), is verbal, based on the verb <code>"buail"</code> in the <code>FutInd</code> (future indicative) tense. Your answer should then also be the verb <code>"buail"</code> in the <code>FutInd</code> tense. The tool outputs all this in JSON, so it can be easily parsed by another tool to synthesise the verb response. I really want to put together this second tool soon, so that the full pipeline will be complete.</p>

<p>I learned so much in doing this project, and just as much again in doing the other projects across my internship. More importantly, I got to work with a language I love with some superb people, and I'm very thankful.</p>

<p>There's still no shortage of other work to do in Irish language tech technology (and no shortage of improvements to make to just this tool). Hopefully I'll get to do some more soon!</p>

<iframe src="https://www.linkedin.com/embed/feed/update/urn:li:share:6425655804535283712" height="637" width="504" frameborder="0" allowfullscreen></iframe>

<hr>

<p>[1] Right now, the tool decides if the question is based on the copula by just <a href="https://github.com/iandioch/adapt/blob/e22664b519cc9084e8af062093e704d80febb501/yesno/analyse.py#L56">looking at the root of the dependency tree, and seeing if the word there isn't a verb</a>. Unfortunately, Maltparser sometimes incorrectly parses the question, and picks a verb in the question that shouldn't be the root to be the root (and sometimes giving a false-negative to the question being copula-based), or chooses a non-verb to be the root of the sentence where it should have chosen a verb (and giving a false-positive).
 <em>Edit: I was suggested to change the word "top" here to "root", to maybe make this footnote more easily understood. I also need to note that the inaccuracy mentioned here is by no means the fault of Maltparser, but is due to the fact that the Irish treebank that Maltparser is trained on is very small, at just 1020 trees. Irish is a low-resource language! If/when there is more data, it will be more accurate.</em></p>

<p>[2] The tool tries to walk down the tree, finding the uppermost item tagged as being the copula. However, the part-of-speech tagger sometimes struggles with the copula (<code>"an"</code> is often marked as being a definite article instead of the copula, and <code>"ar"</code> is sometimes marked as a preposition instead of the copula, for example). To try to work around this, there's an <a href="https://github.com/iandioch/adapt/blob/e22664b519cc9084e8af062093e704d80febb501/yesno/analyse.py#L3">explicit list of likely forms of the copula in a question</a> that are also looked for, regardless of the part of speech reported for these words.</p>

<p>[3] This tool gives you the information that your answer should be, for example, the verb <code>"ith"</code> (<code>"to eat"</code>) in the past tense. Actually synthesising this verb, ie. outputting <code>"d'ith"</code> (<code>"ate"</code>) or <code>"níor ith"</code> (<code>"didn't eat"</code>), is a different problem. I've tried my hand at this problem, but it's a fair bit of work.</p>]]></content:encoded></item><item><title><![CDATA[Kattis problem Pivot: Incrementally improving the performance of a python script, until nothing makes sense anymore]]></title><description><![CDATA[<p>I have been doing a lot of competitive programming problems on <a href="https://open.kattis.com">Kattis</a> lately. It is my favourite online judge, and I have been enjoying my climb up the <a href="https://open.kattis.com/countries/IRL">Irish scoreboard</a> in an attempt to regain <a href="https://mycode.doesnot.run/2017/04/15/kattis-score-analysis/">my former glory of first place</a>.</p>

<p>In particular, today I was tackling the problem <a href="https://open.kattis.com/problems/pivot">Pivot</a></p>]]></description><link>http://mycode.doesnot.run/2018/04/11/pivot/</link><guid isPermaLink="false">0619cebb-e0c5-4189-942b-087e4ed2aa85</guid><category><![CDATA[programming]]></category><category><![CDATA[python]]></category><category><![CDATA[kattis]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Wed, 11 Apr 2018 16:54:19 GMT</pubDate><media:content url="http://mycode.doesnot.run/content/images/2018/04/Screen-Shot-2018-04-11-at-18.24.20.png" medium="image"/><content:encoded><![CDATA[<img src="http://mycode.doesnot.run/content/images/2018/04/Screen-Shot-2018-04-11-at-18.24.20.png" alt="Kattis problem Pivot: Incrementally improving the performance of a python script, until nothing makes sense anymore"><p>I have been doing a lot of competitive programming problems on <a href="https://open.kattis.com">Kattis</a> lately. It is my favourite online judge, and I have been enjoying my climb up the <a href="https://open.kattis.com/countries/IRL">Irish scoreboard</a> in an attempt to regain <a href="https://mycode.doesnot.run/2017/04/15/kattis-score-analysis/">my former glory of first place</a>.</p>

<p>In particular, today I was tackling the problem <a href="https://open.kattis.com/problems/pivot">Pivot</a>. It is quite a small, simple, self-contained problem, but in trying to optimise my answer, I learned some interesting things.</p>

<p>Here is the gist of the problem statement:</p>

<blockquote>
  <p>An O(n) Partition algorithm partitions an array A around a pivot element (pivot is a member of A) into three parts: a left sub-array that contains elements that are ≤ pivot, the pivot itself, and a right sub-array that contains elements that are > pivot.</p>
  
  <p>Now the actual job in this problem is this: Starting from an array A that has n distinct integers, we partition A using one of the member of A as pivot to produce a transformed array A’. Given this transformed array A’, your job is to count how many integers that could have been the chosen pivot.</p>
</blockquote>

<p>Simply, we are given the output of a partition, and have to count the number of possible values in the list that could have been used as the pivot.</p>

<p>We are told that the number of elements, <code>n</code>, is in the range <code>3 ≤ n ≤ 100000</code>. We also know that all input numbers are 32-bit signed integers (but this doesn't make that big of a difference when we're working in Python).</p>

<p>Now, let's go down the rabbit hole of solving this problem. And then let's go a little further.</p>

<h1 id="navesolution">Naïve Solution</h1>

<blockquote>
  <p><a href="https://www.youtube.com/watch?v=jkaMiaRLgvY">I'm not saying it's your fault / Although you could have done more</a></p>
</blockquote>

<p>When we break it down, we see that we are trying to find each number in the input list (which I will call <code>D</code>) such that <code>D[i]</code> is greater than or equal to all <code>D[j]</code> where <code>j &lt; i</code> and less than all <code>D[k]</code> where <code>k &gt; i</code>. This idea may be implemented like the following.</p>

<pre><code>n = int(input())

d = list(map(int, input().split()))

ans = 0  
for i in range(n):  
    ok = True
    for j in range(i):
        if d[i] &lt; d[j]:
            ok = False
            break
    for k in range(i+1, n):
        if d[i] &gt; d[k]:
            ok = False
            break
    if ok:
        ans += 1
print(ans)  
</code></pre>

<p>This solution has time complexity <code>O(n^2)</code>, and it exceeds the 1 second time limit on the 4th test case on Kattis. It is simply not fast enough. </p>

<p>This is because, for every one of the (up to) one million input numbers, we are iterating over (in the worst case) every other one of the numbers. That's a lot of comparisons.</p>

<p>There are some minor optimisations you could do, like skipping looping over <code>k</code> if <code>ok</code> is already <code>False</code>, but I doubt these could squeeze it under the time limit. However, probably worth trying some optimisations if you have no other ideas, you're in the squeaky bum time of a programming competition, and you're desperate to get a few extra points.</p>

<h1 id="onfleek">O(n) fleek</h1>

<p>I knew that <code>O(n^2)</code> wouldn't suffice when I read the question - <code>10^12</code> comparisons is too many to do in one second. However, I realised straight away that this problem could be solved in <code>O(n)</code> time.</p>

<p>You don't need to compare each number with every number before it to see if it is greater than or equal to each of them. You only need to compare the number to the maximum number before it. Likewise, you only need to compare the number to the minimum number after it.</p>

<p>You can build a list, which I call <code>left</code> below, of "the largest number to the left of this element", in <code>O(n)</code> time. </p>

<p>You can build an equivalent list <code>right</code> of "the smallest number to the right of this element" in similar fashion.</p>

<p>Reading the code is probably easier to understand than me rambling about it. Here is the first solution I submitted.</p>

<pre><code>n = int(input())

d = list(map(int, input().split()))

left = [0 for _ in range(n)]  
left[0] = 0  
for i in range(1, n):  
    left[i] = max(left[i-1], d[i-1])

right = [2**32 - 1 for _ in range(n)]  
for i in range(n-2, -1, -1):  
    right[i] = min(right[i+1], d[i+1])

ans = 0  
for i in range(n):  
    if d[i] &gt;= left[i] and d[i] &lt; right[i]:
        ans += 1
print(ans)  
</code></pre>

<p>Kattis told me this ran in 0.16 seconds, well below the time limit, and suitable to get full points for this problem. Job done!</p>

<p>Kattis has a nice feature that shows you a scoreboard of the ten fastest solutions to each problem, broken down by language. I often go and check out this table after I solve a problem, to see how my solution compares.</p>

<p>On peeking at <a href="https://open.kattis.com/problems/pivot/statistics">the scoreboard for this problem</a>, I was shooketh. Not only was the fastest Python 3 submission a mere 0.06 seconds, but it was submitted by my classmate/arch-nemesis/competitive programming teammate <a href="http://binarysear.ch">Cian Ruane</a>. The horror! I couldn't live with his submission being a full tenth of a second faster; imagine how big his head would be if he saw this! I must do better.</p>

<h1 id="speedup1">Speedup #1</h1>

<blockquote>
  <p><a href="https://www.youtube.com/watch?v=GLfEU5lelUM">Jealousy's what the cheddar brings, for the cheddar it's anything goes</a></p>
</blockquote>

<p>Quickly (I submitted the new solution just 1 minute 18 seconds later), I realised that I was doing twice the work I needed to. Instead of building the <code>left</code> list, I could just keep a running tally of the largest number seen so far while I'm iterating the list at the end to compute the answer. This will make the solution use around half as much space, will save an entire iteration of the array, and will surely improve my speed.</p>

<p>Maybe this is how Cian got a better result? Following is what I submitted.</p>

<pre><code>n = int(input())

d = list(map(int, input().split()))

right = [2**32 - 1 for _ in range(n)]  
for i in range(n-2, -1, -1):  
    right[i] = min(right[i+1], d[i+1])

ans = 0  
maxn = 0  
for i in range(n):  
    if d[i] &gt;= maxn and d[i] &lt; right[i]:
        ans += 1
    maxn = max(maxn, d[i])
print(ans)  
</code></pre>

<p>It ran in 0.14s. Not even fast enough to appear at 10th place on the scoreboard, never mind first.</p>

<p>From previous experience, I've seen that the most trivial Python problem runs in 0.02 seconds. I assume this is the cost of starting the Python interpreter, parsing the program, etc. Once a problem runs in &lt; 0.05s or so, there is likely little space to speed it up. However, we're not at that limit yet. Besides, I can see someone else did it in 0.06s, and the rest of the scoreboard is at 0.07s - 0.09s, so it must be doable!</p>

<h1 id="4minutesolder004sfaster">4 minutes older, 0.04s faster</h1>

<p>2 minutes and 45 seconds later, I submitted the following:</p>

<pre><code>n = int(input())

d = list(map(int, input().split()))

right = [2**32 - 1 for _ in range(n)]  
for i in range(n-2, -1, -1):  
    right[i] = right[i+1]
    if d[i+1] &lt; right[i]:
        right[i] = d[i+1]

ans = 0  
maxn = 0  
for i in range(n):  
    if d[i] &gt;= maxn and d[i] &lt; right[i]:
        ans += 1
    if d[i] &gt; maxn:
        maxn = d[i]
print(ans)  
</code></pre>

<p>I have seen in several places people avoiding the builtin Python <code>max</code> and <code>min</code>, and looking at this problem, I don't see any other obvious potential speedups. It makes sense to me that these could be slower than simple <code>if</code> statements; not only is there the overhead of calling a function every time, but the functions also support getting the max or minimum values from an iterable, so must be some logic in there to figure out if that is necessary that we can avoid.</p>

<p>This solution ran in 0.12s.</p>

<p>Cian's solution still took half that amount of time.  My curiosity was piqued; how did he do it? I decided to read his solution and find out - it could teach me a lot.</p>

<h1 id="peekingbehindthecurtain">Peeking behind the curtain</h1>

<p>I went to <a href="http://github.com/cianlr/kattis-solutions">the github repo of his Kattis solutions</a> and looked at his solution:</p>

<pre><code>def main():  
    N = int(input())
    a = [int(x) for x in input().split()]
    mx = -1
    max_left = [None] * N
    for i in range(N):
        if a[i] &gt; mx:
            mx = a[i]
        max_left[i] = mx
    mn = mx
    possible = 0
    for i in range(N - 1, -1, -1):
        if a[i] &lt; mn:
            mn = a[i]
        if mn == max_left[i]:
            possible += 1
    print(possible)

if __name__ == '__main__':  
    main()
</code></pre>

<p>His solution is... more or less identical. He does his lists in the opposite order to me, but that shouldn't make a difference?</p>

<p>He has one fewer comparison in his final loop - maybe that's it? He builds his list of input values in a list comprehension instead of a map - that could be slightly more optimal? Maybe I'm missing something in his iteration orders, and there is some secret smart speedup going on?</p>

<p>I stole some of his ideas, and submitted the following.</p>

<pre><code>n = int(input())

d = [int(x) for x in input().split()]

left = [None] * n  
maxn = 0  
for i in range(n):  
    if d[i] &gt; maxn:
        maxn = d[i]
    left[i] = maxn

ans = 0  
minn = maxn  
for i in range(n-1, -1, -1):  
    if d[i] &lt; minn:
        minn = d[i]
    if minn == left[i]:
        ans += 1
print(ans)  
</code></pre>

<p>This ran in 0.09s.</p>

<h1 id="wtf">WTF</h1>

<p>Defeated, I fired him a message.</p>

<blockquote>
  <p><strong>Noah</strong>: <em>@Cian Ruane</em> I have pretty much the exact same solution as you for problem pivot, but yours is 0.03s faster, wtf</p>
  
  <p><strong>Cian</strong>: That's incredible</p>
</blockquote>

<p>...</p>

<blockquote>
  <p><strong>Noah</strong>: do you get a magic speedup from the <code>if __name__ == '__main__':</code> line</p>
  
  <p><strong>Noah</strong>: did you get lucky with the server load or something</p>
</blockquote>

<p>...</p>

<blockquote>
  <p><strong>Cian</strong>: Try putting it into a function</p>
  
  <p><strong>Noah</strong>: surely that'd be slower</p>
  
  <p><strong>Cian</strong>: Maybe it's doing some sort of jit stuff or soemthing</p>
</blockquote>

<p>Sure, why not try? I submitted the following.</p>

<pre><code>def main():  
    n = int(input())

    d = [int(x) for x in input().split()]

    left = [None] * n
    maxn = 0
    for i in range(n):
        if d[i] &gt; maxn:
            maxn = d[i]
        left[i] = maxn

    ans = 0
    minn = maxn
    for i in range(n-1, -1, -1):
        if d[i] &lt; minn:
            minn = d[i]
        if minn == left[i]:
            ans += 1
    print(ans)

if __name__ == '__main__':  
    main()
</code></pre>

<p>This solution ran in 0.06s.</p>

<h1 id="wtf2electricboogaloo">WTF 2: Electric Boogaloo</h1>

<p>Why does putting the code in a function speed it up? Surely the overhead of jumping to a function should make the code slower, and not faster? Is Cian right, and that there's some JIT stuff going on?</p>

<p>No - but <a href="https://stackoverflow.com/questions/11241523/why-does-python-code-run-faster-in-a-function">Stack Overflow has the answer</a>:</p>

<blockquote>
  <p><strong><a href="https://stackoverflow.com/users/395939/scharron">Scharron</a></strong>: Just an intuition, not sure if it's true: I would guess it's because of scopes. In the function case, a new scope is created (i.e. kind of a hash with variable names bound to their value). Without a function, variables are in the global scope, when you can find lot of stuff, hence slowing down the loop</p>
  
  <p><strong><a href="https://stackoverflow.com/users/398968/katrielalex">katrielalex</a></strong>: @Scharron you're half correct. It is about scopes, but the reason it's faster in locals is that local scopes are actually implemented as arrays instead of dictionaries (since their size is known at compile-time).</p>
</blockquote>

<p><a href="https://stackoverflow.com/a/11242447/3536521">katriealex then goes into more interesting details about variable scopes in a full answer</a>.</p>

<p><a href="https://stackoverflow.com/a/11241708/3536521">ecatmur also looks at the bytecode and shows how it comes out in the interpreter</a>.</p>

<p>Super interesting.</p>

<h1 id="postmortem">Postmortem</h1>

<p>Having seen all of this, what have I learned?</p>

<ol>
<li>There is no big downside, but a potential upside, to putting my scripts inside a function, instead of in the global scope. <br>
<ul><li>It is faster, as I saw here. This speedup I guess becomes less obvious when there are fewer variable accesses in the script.</li>
<li><a href="https://stackoverflow.com/questions/5544752/should-i-use-a-main-method-in-a-simple-python-script">Some of the answers here</a> mention benefits of the code not being run on module import. </li>
<li>It creates cleaner code (in my opinion). I was just too lazy to do it for throwaway competitive programming solutions before.</li></ul></li>
<li>I should consider using an <code>if</code> instead of <code>max</code> and <code>min</code> if I am only comparing 2 values, and I am worried about performance. <br>
<ul><li>The code is not as nice, but it could save valuable time if it is run often.</li></ul></li>
<li>I should <a href="https://wiki.python.org/moin/PythonSpeed/PerformanceTips">learn off this page about python performance tips that katriealex linked</a>. There are some great details about the innards of Python there.  </li>
<li>I should keep on Cian's good side, so that he continues to be on my team and not on a competing team. That said, I have done better than him in <a href="https://mycode.doesnot.run/2018/03/28/student-battle-dev-season-11/">some</a>, <a href="http://mycode.doesnot.run/2018/02/03/hacktrinity/">previous</a>, <a href="http://aipo.computing.dcu.ie/irelands-best-young-computer-programmers-win-trip-compete-olympics-programming">competitions</a>, and I will never let him forget those.</li>
</ol>]]></content:encoded></item><item><title><![CDATA[My performance at the Student Battle Dev Season 11]]></title><description><![CDATA[<p>I had a lot of fun yesterday evening competing in the 11th edition of the <a href="https://battledev.blogdumoderateur.com/student-battledev/">#battledev</a> competition. I hadn't taken part before, but decided it might be worth checking out what competitions happen beyond Ireland's shores, and a French friend recommended this one.</p>

<p>The competition is French, but it allows</p>]]></description><link>http://mycode.doesnot.run/2018/03/28/student-battle-dev-season-11/</link><guid isPermaLink="false">6146c899-3428-4770-aa2c-32c3c62abe9b</guid><category><![CDATA[competitions]]></category><category><![CDATA[python]]></category><category><![CDATA[programming]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Wed, 28 Mar 2018 16:14:54 GMT</pubDate><media:content url="http://mycode.doesnot.run/content/images/2018/03/Screen-Shot-2018-03-28-at-18.13.57.png" medium="image"/><content:encoded><![CDATA[<img src="http://mycode.doesnot.run/content/images/2018/03/Screen-Shot-2018-03-28-at-18.13.57.png" alt="My performance at the Student Battle Dev Season 11"><p>I had a lot of fun yesterday evening competing in the 11th edition of the <a href="https://battledev.blogdumoderateur.com/student-battledev/">#battledev</a> competition. I hadn't taken part before, but decided it might be worth checking out what competitions happen beyond Ireland's shores, and a French friend recommended this one.</p>

<p>The competition is French, but it allows anyone from an EEC country to compete. It lasts two hours, and consists of six questions. You must solve the first question before you can see the second question, and so on. The winner is the first person to solve the last question, then the second person to solve it, and so on for everyone who solved the last question, followed by the first person to solve the second last question, etc.</p>

<blockquote class="twitter-tweet" data-lang="ga"><p lang="cs" dir="ltr">tá imní orm <a href="https://twitter.com/hashtag/battledev?src=hash&amp;ref_src=twsrc%5Etfw">#battledev</a> <a href="https://t.co/O5Agy2eA8M">pic.twitter.com/O5Agy2eA8M</a></p>&mdash; noah ö donnaile (@iandioch) <a href="https://twitter.com/iandioch/status/978661302515224579?ref_src=twsrc%5Etfw">27 Márta 2018</a></blockquote>  

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>The first 3 questions were in miscellaneous topics, but were all reasonably easy.</p>

<ul>
<li><a href="https://questionsacm.isograd.com/codecontest/pdf/allyoucaneat.pdf">Problem <code>allyoucaneat</code></a> required adding given %-discounts to orders of given sizes, and outputting the final sum of transactions.</li>
<li><a href="https://questionsacm.isograd.com/codecontest/pdf/tripadvisor.pdf"><code>tripadvisor</code></a> involved finding the location from the input with the highest average rating, based on its rating in three subcategories, and outputting this average.</li>
<li><a href="https://questionsacm.isograd.com/codecontest/pdf/rocky13.pdf">Then <code>rocky13</code></a> asked you to find your <code>k</code> friends who's ratings for previous films most closely matched your own rating (by sorting by their minimum total difference in ratings), and output the average of their ratings for a new film.</li>
</ul>

<p>I did quite well at these.</p>

<blockquote class="twitter-tweet" data-lang="ga"><p lang="en" dir="ltr">Happy with how I did anyway. Some screenshots I took to prove I&#39;m not terrible: <a href="https://t.co/2EkGqHfMn2">pic.twitter.com/2EkGqHfMn2</a></p>&mdash; noah ö donnaile (@iandioch) <a href="https://twitter.com/iandioch/status/978724756177539073?ref_src=twsrc%5Etfw">27 Márta 2018</a></blockquote>  

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p><a href="https://questionsacm.isograd.com/codecontest/pdf/pancakes.pdf">Then came <code>pancakes</code></a>, which was a Towers-of-Hanoi-like problem. The inputs were small enough to brute force the optimal solution with a tiny bit of tree-pruning.</p>

<p><a href="https://questionsacm.isograd.com/codecontest/pdf/tramways12.pdf">Problem #5 was <code>tramways12</code></a>, and it was much more difficult. The input described a circle of vertices <code>0</code> to <code>N-1</code>, and a matrix of the number of people who wanted to get from each vertex to each other vertex. You were asked to output the maximal possible number of people you could take by building edges between nodes, with the following caveats:</p>

<ul>
<li>Each vertex can only touch at most one edge.</li>
<li>No edge connecting vertices A and B can intersect the edge connecting vertices C, and D. The input essentially described a regular <code>N</code>-gon, and you could not draw lines between corners of the <code>N</code>-gon that would intersect.</li>
</ul>

<p>The solution needed to be <code>O(N^3)</code>.</p>

<p>I realised you could use dynamic programming. The optimal arrangement of edges within the bounds of vertices <code>[A, B]</code> was independent of the arrangements of edges at vertices outside of this range. However, I found that the implementation of this dynamic programming was easier said than done.</p>

<p>I tried many non-working solutions, that missed edge cases, or that accidentally didn't try some arrangement of vertices, but in the last minute of the competition, I uploaded my final solution. It evaluated... and evaluated...</p>

<p>Seeing as the submission system was flaky (and went offline a few times during the contest, under heavy load),  I re-submitted.</p>

<p>Around the final second of the contest, it finished evaluating, and told me my solution was correct.</p>

<blockquote class="twitter-tweet" data-lang="ga"><p lang="en" dir="ltr">My <a href="https://twitter.com/hashtag/battledev?src=hash&amp;ref_src=twsrc%5Etfw">#battledev</a> solution to problem 5 finished executing in the last second of the contest, idk if my points will be counted. Suspenseful! <a href="https://t.co/dg5dEil9Rq">pic.twitter.com/dg5dEil9Rq</a></p>&mdash; noah ö donnaile (@iandioch) <a href="https://twitter.com/iandioch/status/978723815353147398?ref_src=twsrc%5Etfw">27 Márta 2018</a></blockquote>  

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>Although I emailed the judges to enquire about this, in the end it seems they decided my solution didn't count. Oh well! It was an exciting end to the competition.</p>

<p>While I didn't get to see it during the competition, afterwards I read <a href="https://questionsacm.isograd.com/codecontest/pdf/ducks7.pdf"><code>ducks7</code>, the sixth and final problem</a>, which appeared to be some form of duck-themed max-flow.</p>

<p>The following day (today), they released the results, <a href="https://www.blogdumoderateur.com/resultats-battledev-mars-2018/">in a blog post</a>, and via email.</p>

<pre><code>Bonjour,

Merci d'avoir participé à la Battle Dev.  
Les résultats définitifs de la saison 11 sont désormais connus.

Vous êtes 41eme sur 3322 au classement général.  
</code></pre>

<p>There were 3322 competitors, of whom 2385 solved at least one problem. Out of that, I came 41st! I'm delighted. Had my final submission been accepted, I would have jumped up to 29th place, but as they say, <em>c'est la vie</em>.</p>

<p>However, it was pointed out in the blog post, that Dublin City University was ranked as the #2 university in the competition! I guess many of the people above me in the rankings were not representing a university.</p>

<blockquote class="twitter-tweet" data-lang="ga"><p lang="tl" dir="ltr">hahahaha, unexpected<a href="https://t.co/SKGkV2KfZZ">https://t.co/SKGkV2KfZZ</a><a href="https://twitter.com/dcucomputing?ref_src=twsrc%5Etfw">@dcucomputing</a> <a href="https://t.co/k0m9FNwEXX">pic.twitter.com/k0m9FNwEXX</a></p>&mdash; noah ö donnaile (@iandioch) <a href="https://twitter.com/iandioch/status/979003169102553093?ref_src=twsrc%5Etfw">28 Márta 2018</a></blockquote>  

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>All in all, it was a fun competition, a little different to most I take part in. I was expecting to need to translate problems from French, but in the end they provided links to PDFs in English.</p>

<p>It seems bizarre to see DCU ranked highly in a French competition; I have saved the screenshot for future laughs. Let's see if we reach #1 next time!</p>]]></content:encoded></item><item><title><![CDATA[In 2018, we finally won the IrlCPC]]></title><description><![CDATA[<p>The <a href="http://acm.ucc.ie/Irish-Collegiate-Programming-Contest-IrlCPC">Irish Collegiate Programming Contest (IrlCPC)</a> is an annual programming competition hosted in University College Cork.</p>

<p>I have attended the IrlCPC before, finishing second in 2016 and third in 2017, delighted both times. This year, there were seven problems on the list, roughly arranged in order of difficulty; the first</p>]]></description><link>http://mycode.doesnot.run/2018/03/11/irlcpc-2018/</link><guid isPermaLink="false">dc0962a6-1385-47db-bb9f-5cd1fee851ea</guid><category><![CDATA[programming]]></category><category><![CDATA[competitions]]></category><category><![CDATA[irlcpc]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Sun, 11 Mar 2018 15:53:27 GMT</pubDate><media:content url="https://i.imgur.com/LGCElQQ.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://i.imgur.com/LGCElQQ.jpg" alt="In 2018, we finally won the IrlCPC"><p>The <a href="http://acm.ucc.ie/Irish-Collegiate-Programming-Contest-IrlCPC">Irish Collegiate Programming Contest (IrlCPC)</a> is an annual programming competition hosted in University College Cork.</p>

<p>I have attended the IrlCPC before, finishing second in 2016 and third in 2017, delighted both times. This year, there were seven problems on the list, roughly arranged in order of difficulty; the first six were worth 10 points each, the final one worth 11. We found the fifth problem, which was based on a version of the Countdown numbers game, the most difficult.</p>

<p>I was on a team called <code>-=[B]ichael [B] [B]iggins=-</code> with my friends <a href="http://binarysear.ch/">Cian Ruane</a> and <a href="https://github.com/xCiaraG">Ciara Godwin</a>. We practised a fair amount over the past few months on <a href="https://open.kattis.com/countries/IRL">Kattis</a>, so we weren't altogether unprepared, although it did feel like that on the morning.</p>

<p>We achieved a perfect score of 71 points 63 minutes or so before the end of the contest, nabbing us first place in the university section.</p>

<blockquote class="twitter-tweet" data-lang="ga"><p lang="en" dir="ltr">Congrats to Ciara Godwin, Cian Ruane, &amp; Noah Ó Donnaile of <a href="https://twitter.com/DublinCityUni?ref_src=twsrc%5Etfw">@DublinCityUni</a>. Winners of the 2018 Irish Collegiate Programming Competition at <a href="https://twitter.com/UCC?ref_src=twsrc%5Etfw">@UCC</a> from a field of 40 teams. DCU also has 2nd place! Event sponsored by <a href="https://twitter.com/Google?ref_src=twsrc%5Etfw">@Google</a>, <a href="https://twitter.com/PoppuloTech?ref_src=twsrc%5Etfw">@PoppuloTech</a> and <a href="https://twitter.com/insight_centre?ref_src=twsrc%5Etfw">@insight_centre</a>. <a href="https://twitter.com/hashtag/IrlCPC?src=hash&amp;ref_src=twsrc%5Etfw">#IrlCPC</a> <a href="https://twitter.com/muirtheimhne?ref_src=twsrc%5Etfw">@muirtheimhne</a> <a href="https://t.co/6IJVnGG2sk">pic.twitter.com/6IJVnGG2sk</a></p>&mdash; Prof. Barry O&#39;Sullivan, MRIA (@BarryOSullivan) <a href="https://twitter.com/BarryOSullivan/status/972537743971639297?ref_src=twsrc%5Etfw">10 Márta 2018</a></blockquote>  

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>We were emblazoned with Swiss flags because I had just arrived the night before from Zürich, where I'm interning at Google. This is the first time I've had a team uniform at a programming competition.</p>

<blockquote class="twitter-tweet" data-lang="ga"><p lang="en" dir="ltr">Congrats to Ciara, Cian &amp; Noah on this great achievement!<br>Congrats also to our 2nd placed team.<br>Very good news for <a href="https://twitter.com/dcucomputing?ref_src=twsrc%5Etfw">@dcucomputing</a>! <a href="https://twitter.com/insight_centre?ref_src=twsrc%5Etfw">@insight_centre</a> Thanks to <a href="https://twitter.com/UCC?ref_src=twsrc%5Etfw">@UCC</a> for hosting. <a href="https://t.co/PKMoCLubL7">https://t.co/PKMoCLubL7</a></p>&mdash; Brian MacC (@muirtheimhne) <a href="https://twitter.com/muirtheimhne/status/972545575471255552?ref_src=twsrc%5Etfw">10 Márta 2018</a></blockquote>  

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>It's worth pointing out that just like in 2016, a secondary school team (ie. in a different bracket) beat us to the mark with a perfect score after only one hour and 45 minutes: more than a full hour before we got there.</p>

<p>Our <a href="https://cpssd.net">CPSSD</a> comrades came second in the university division, also with a perfect score.</p>

<blockquote class="twitter-tweet" data-lang="ga"><p lang="en" dir="ltr">And the winners are...<br> <a href="https://twitter.com/hashtag/tech?src=hash&amp;ref_src=twsrc%5Etfw">#tech</a> <a href="https://twitter.com/hashtag/ireland?src=hash&amp;ref_src=twsrc%5Etfw">#ireland</a> <a href="https://twitter.com/hashtag/cork?src=hash&amp;ref_src=twsrc%5Etfw">#cork</a> <a href="https://twitter.com/hashtag/competition?src=hash&amp;ref_src=twsrc%5Etfw">#competition</a> <a href="https://twitter.com/hashtag/IrlCPC?src=hash&amp;ref_src=twsrc%5Etfw">#IrlCPC</a> <a href="https://t.co/MssAU5Xkeu">pic.twitter.com/MssAU5Xkeu</a></p>&mdash; UCC ACM Student Chap (@uccacm) <a href="https://twitter.com/uccacm/status/972554884393336832?ref_src=twsrc%5Etfw">10 Márta 2018</a></blockquote>  

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>]]></content:encoded></item><item><title><![CDATA[HackTrinity CTF Solutions]]></title><description><![CDATA[<p>This week I took part in the <a href="https://hacktrinity.me">HackTrinity CTF</a>. It was my first CTF, but by the end of the week I managed to get all the flags. I think I may have been the second person to do it :)</p>

<p>Following is my solutions to each of the problems, in</p>]]></description><link>http://mycode.doesnot.run/2018/02/03/hacktrinity/</link><guid isPermaLink="false">7490ae17-a984-4a09-b0c6-2cb93dbfdf91</guid><category><![CDATA[programming]]></category><category><![CDATA[competitions]]></category><category><![CDATA[ctf]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Sat, 03 Feb 2018 10:36:08 GMT</pubDate><content:encoded><![CDATA[<p>This week I took part in the <a href="https://hacktrinity.me">HackTrinity CTF</a>. It was my first CTF, but by the end of the week I managed to get all the flags. I think I may have been the second person to do it :)</p>

<p>Following is my solutions to each of the problems, in the order I solved them. The ones closer to the end may be more interesting...</p>

<hr>

<h1 id="starthere">Start Here</h1>

<p>Welcome to HackTrinity '18.</p>

<p>If you've never played a Capture-The-Flag before, the objective is to obtain flags of the form <code>HackTrinity{&lt;some_text&gt;}</code> by solving various challenges. That might involve exploiting a vulnerability in a website or finding it hidden in a file. Every time you submit a correct flag you get points. You can check your answer in the field below the challenge description.</p>

<p>Enter the flag <code>HackTrinity{beginners_welcome}</code> in the field below for 50 points.</p>

<hr>

<h1 id="netopian">Netopian</h1>

<p><img src="https://i.imgur.com/l5wqmlu.png" alt="problem statement"></p>

<p>I vaguely knew in the back of my head that some Eircom router passwords could be cracked. After some Googling, I confirmed it.</p>

<p>Just input <code>1645 1200</code> into <a href="http://s4dd.yore.ma/eircom/">here</a>, and get the flag <code>C8CBE3D384E8F0F0124978FF00</code> back.</p>

<hr>

<h1 id="notnotpetya">NotNotPetya</h1>

<p><img src="https://i.imgur.com/hGqZI6F.png" alt="problem statement"></p>

<p>If you follow the link at the bottom to <code>tinyurl.com/notnotpetya</code>, it redirects to <code>https://notnotpetyawebsitesupport4youtodayhere2serveyoudecryptedfiles.hacktrinity.me/</code>.</p>

<p>Under the "Pay by Bitcoin" heading, there are the following instructions:</p>

<pre><code>Step 1

Go to coinbase.com and sign up for an account

Step 2

Purchase $3000 BTC

Step 3

Send BTC to 1BYg2ZPPAk4SV251S1aAwq8oEAuVv1ZDUo  
</code></pre>

<p>If you google <code>1BYg2ZPPAk4SV251S1aAwq8oEAuVv1ZDUo</code>, it will surface <a href="https://twitter.com/jonasandresen12/status/956168977784348672">this tweet</a>.</p>

<p>The tweet's author is the flag.</p>

<hr>

<h1 id="lenny">Lenny</h1>

<p>Problem statement:</p>

<pre><code>You're a TA assigned with the task of correcting programming assignments. For this particular assignment the students were allowed to use any language of their choosing. Lenny has decided to use some obscure language and refuses to give details.

See if you can run it.  
</code></pre>

<p>Attached is a file full of various <a href="https://www.lennyfaces.net/">lennys</a>. I straightaway posited that this was a variation of brainfuck, but didn't know how to go about decoding it. Fortunately, if you just google "lenny esolang", <a href="https://esolangs.org/wiki/(_%CD%A1%C2%B0_%CD%9C%CA%96_%CD%A1%C2%B0)fuck">this page</a> is returned. It contains the following table to translate this back to a variant of brainfuck:</p>

<table>  
<tr>  
<th>brainfuck</th>  
<th>lennyfuck</th>  
</tr>  
<tr>  
<td>+</td>  
<td>( ͡° ͜ʖ ͡°)</td>  
</tr>  
<tr>  
<td>-</td>  
<td>(♥ ͜ʖ♥)</td>  
</tr>  
<tr>  
<td>.</td>  
<td>(> ͜ʖ<)< td="">  
</)<></td></tr>  
<tr>  
<td>,</td>  
<td>ᕙ( ͡° ͜ʖ ͡°)ᕗ</td>  
</tr>  
<tr>  
<td>&lt;</td>  
<td>(∩ ͡° ͜ʖ ͡°)⊃━☆ﾟ.*</td>  
</tr>  
<tr>  
<td>&gt;</td>  
<td>ᕦ( ͡°ヮ ͡°)ᕥ</td>  
</tr>  
<tr>  
<td>^</td>  
<td>ᕦ( ͡° ͜ʖ ͡°)ᕥ</td>  
</tr>  
<tr>  
<td>v</td>  
<td>( ͡°╭͜ʖ╮ ͡°)</td>  
</tr>  
<tr>  
<td>x</td>  
<td>ಠ_ಠ</td>  
</tr>  
<tr>  
<td>[</td>  
<td>( ͡°(</td>  
</tr>  
<tr>  
<td>]</td>  
<td>) ͡°)</td>  
</tr>  
</table>

<p>After doing some search-and-replace in TextEdit, I was left with:</p>

<pre><code>-[-------&gt;+&lt;]&gt;-.[---&gt;++++&lt;]&gt;+.++.++++++++.&gt;-[---&gt;+&lt;]&gt;-.-[---&gt;+&lt;]&gt;+.---------.+++++.-----.+++++++++++.+++++.++.--[-&gt;+++&lt;]&gt;-.+.------------.[---&gt;+++++&lt;]&gt;.[-----&gt;+++&lt;]&gt;.+++++.+++++.-----.-----.+++++++++++++++.+.+++++.+[-&gt;+++&lt;]&gt;.+++++.[--&gt;+&lt;]&gt;--.[-&gt;++&lt;]&gt;-.[---&gt;+&lt;]&gt;-.------------.[--&gt;+&lt;]&gt;-.---[-&gt;++&lt;]&gt;-.++.--[---&gt;+&lt;]&gt;--..+++[++&gt;---&lt;]&gt;.+[---&gt;+&lt;]&gt;+.+++++++.-.--------.+++++++++.[--&gt;+&lt;]&gt;.&gt;--[--&gt;+++&lt;]&gt;.
</code></pre>

<p>I ran this in <a href="https://sange.fi/esoteric/brainfuck/impl/interp/i.html">this online brainfuck interpreter</a>, and was returned the flag <code>HackTrinity{jk_I_did_not_d0_th3_ass1gnmen7}</code>.</p>

<hr>

<h1 id="securelocker">SecureLocker</h1>

<p>Problem statement:</p>

<pre><code>Trinity have launched their new SecureLocker™ document storage facility which uses high-tech cryptography (AES-CBC + PBKDFv2) to securely store documents. Can you break into the vault and retrieve the flag?

https://locker.hacktrinity.me  
</code></pre>

<p>If you open up the URL <code>https://locker.hacktrinity.me</code>, you'll see a field to enter a 4-digit PIN.</p>

<p>4 digits is very brute-forceable. Open the site and paste the following into the Javascript console:</p>

<pre><code>for (var i = 0; i &lt; 10000; i++) {  
  var s = ("0000" + i).slice(-4);
  var result = tryPinCode(s);
  console.log(s, result);
}
</code></pre>

<p>After a while you'll see <code>8759 HackTrinity{your_encryption_is_only_as_strong_as_your_key_space}</code> scroll past.</p>

<p>You can also just wait for it to finish, and then search for <code>HackTrinity</code>.</p>

<hr>

<h1 id="whiteboard">Whiteboard</h1>

<p>Problem statement:</p>

<pre><code>Say goodbye to Blackboard.... and hello to Whiteboard, the new Learning Management System for Trinity! All your grades are now available to view in a beautiful responsive interface.

Unfortunately you failed Ethical Hacking, so you don't get a Flag! Although if you manage to hack the system, your professor might give you some bonus marks...

https://whiteboard.hacktrinity.me  
</code></pre>

<p><code>https://whiteboard.hacktrinity.me</code> is the login page to a college website, with the credentials <code>username=student1</code> <code>password=password1</code> listed. There's a "Staff Area" tab at the top of the page. After you login, if you try to navigate to this tab, you'll be told you can't access it because you're not staff. Meanwhile, on the "My Grades" page, you see the notice:</p>

<p><code>Sorry, you have not passed all modules! To earn the flag you must pass all modules (get over 40%!)</code>.</p>

<p>If you look at the network tab in Chrome, you'll see that the cookie <code>auth=student1%3Apassword1%3Astaff%3Dfalse</code> is set when you log in as <code>student1</code>.</p>

<p>If you run the command <code>document.cookie="auth=student1%3Apassword1%3Astaff%3Dtrue"</code> in the Javascript console after logging in, you can access the "Staff Area" page, change your grade in ethical hacking (I changed mine to 101%), and then when you go back to the grades page, you'll see the flag <code>HackTrinity{ThisWontWorkOnBlackboardSoDontEvenThinkAboutIt}</code>.</p>

<hr>

<h1 id="eyetee">EyeTee</h1>

<p>Problem statement:</p>

<pre><code>Trinity EyeTee have launched a new knowledge-base website! But can you get access to the super-secret password?

https://eyeteehelpdesk.hacktrinity.me/

Enter the flag as HackTrinity{&lt;password&gt;}  
</code></pre>

<p>This website is nothing but a search box, and different searches surface documents that contain that queried substring. However, an empty search returns previews of all documents, including the following:</p>

<pre><code>**Super-secret password document** *STAFF*

In accordance with our password policy, the super-secret password is lower-case alphanumeric and 9 characters long. Password: a9j[...]  
</code></pre>

<p>We now know the format and first three characters of the answer, <code>a9j</code>. If search try every possible lowercase letter and number appended to the end of this, you'll find that the next character is <code>b</code>; the answer starts with <code>a9jb</code>. If you continue this process you'll find the full answer is <code>a9jb7ib39</code>.</p>

<p>I did this manually and it didn't take long, but you could easily script it by iterating over all possible next characters for your given answer so far, and continuing this process with whichever resulting page has a larger size in bytes.</p>

<hr>

<h1 id="koinex">Koinex</h1>

<p>Problem statement:</p>

<pre><code>Being a white-hat hacker, you're invited by Koinex a cypto-exchange company to test their security, who knows if you are successful you might get some of that sweet Ethereum that you've been looking for.

https://koinex.hacktrinity.me  
</code></pre>

<p><code>https://koinex.hacktrinity.me</code> is a login page, with javascript username and password checking. The script to do that is as follows.</p>

<pre><code>var _0xad25=["\x23\x75\x73\x65\x72\x6E\x61\x6D\x65","\x71\x75\x65\x72\x79\x53\x65\x6C\x65\x63\x74\x6F\x72","\x23\x70\x61\x73\x73","\x72\x65\x73\x75\x6C\x74\x73","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x61\x75\x74\x68\x2D\x62\x75\x74\x74\x6F\x6E","\x63\x6C\x69\x63\x6B","\x70\x72\x65\x76\x65\x6E\x74\x44\x65\x66\x61\x75\x6C\x74","\x76\x61\x6C\x75\x65","\x61\x64\x64\x45\x76\x65\x6E\x74\x4C\x69\x73\x74\x65\x6E\x65\x72","\x55\x73\x65\x72\x6E\x61\x6D\x65\x20\x6F\x72\x20\x50\x61\x73\x73\x77\x6F\x72\x64\x20\x63\x61\x6E\x6E\x6F\x74\x20\x62\x65\x20\x65\x6D\x70\x74\x79","\x70\x69\x72\x61\x74\x65\x73","\x59\x6F\x75\x27\x72\x65\x20\x6C\x6F\x67\x67\x65\x64\x20\x69\x6E\x21\x0A\x46\x6C\x61\x67\x20\x69\x73\x20\x48\x61\x63\x6B\x54\x72\x69\x6E\x69\x74\x79\x7B\x26\x6C\x74\x3B\x70\x61\x73\x73\x77\x6F\x72\x64\x26\x67\x74\x3B\x7D","\x57\x72\x6F\x6E\x67\x20\x43\x72\x65\x64\x65\x6E\x74\x69\x61\x6C\x73","\x64\x38\x61\x39\x37\x39\x34\x65\x36\x35\x38\x62\x38\x32\x64\x38\x30\x35\x62\x38\x37\x31\x39\x61\x62\x64\x34\x32\x64\x32\x63\x32\x63\x65\x39\x35\x36\x64\x31\x64","\x69\x6E\x6E\x65\x72\x48\x54\x4D\x4C","","\x73\x65\x74\x54\x69\x6D\x65\x6F\x75\x74"];var userField=document[_0xad25[1]](_0xad25[0]);var passField=document[_0xad25[1]](_0xad25[2]);var resultsDiv=document[_0xad25[4]](_0xad25[3]);var submitButton=document[_0xad25[4]](_0xad25[5]);submitButton[_0xad25[9]](_0xad25[6],function(_0xe58cx5){_0xe58cx5[_0xad25[7]]();tryAuth(userField[_0xad25[8]],passField[_0xad25[8]])});function tryAuth(_0xe58cx7,_0xe58cx8){if(!_0xe58cx7&amp;&amp;  !_0xe58cx8){invalidCredentials(_0xad25[10]);return};if(_0xe58cx7=== _0xad25[11]&amp;&amp; isValid(_0xe58cx8)){showSuccess(_0xad25[12])}else {invalidCredentials(_0xad25[13])}}function isValid(_0xe58cx8){return sha1(_0xe58cx8)=== _0xad25[14]}function invalidCredentials(_0xe58cxb){resultsDiv[_0xad25[15]]= _0xad25[16];window[_0xad25[17]](function(){resultsDiv[_0xad25[15]]= `&lt;div class="alert alert-danger"&gt;`+ _0xe58cxb+ `&lt;/div&gt;`},100)}function showSuccess(_0xe58cxd){resultsDiv[_0xad25[15]]= `&lt;div class="alert alert-success"&gt;`+ _0xe58cxd+ `&lt;/div&gt;`}  
</code></pre>

<p>If you put that into <a href="http://jsbeautifier.org/">jsbeautifier</a>, it will show you that the script looks like this:</p>

<pre><code>var userField = document['querySelector']('#username');  
var passField = document['querySelector']('#pass');  
var resultsDiv = document['getElementById']('results');  
var submitButton = document['getElementById']('auth-button');  
submitButton['addEventListener']('click', function(_0xe58cx5) {  
    _0xe58cx5['preventDefault']();
    tryAuth(userField['value'], passField['value'])
});

function tryAuth(_0xe58cx7, _0xe58cx8) {  
    if (!_0xe58cx7 &amp;&amp; !_0xe58cx8) {
        invalidCredentials('Username or Password cannot be empty');
        return
    };
    if (_0xe58cx7 === 'pirates' &amp;&amp; isValid(_0xe58cx8)) {
        showSuccess('You\'re logged in!\x0AFlag is HackTrinity{&amp;lt;password&amp;gt;}')
    } else {
        invalidCredentials('Wrong Credentials')
    }
}

function isValid(_0xe58cx8) {  
    return sha1(_0xe58cx8) === 'd8a9794e658b82d805b8719abd42d2c2ce956d1d'
}

function invalidCredentials(_0xe58cxb) {  
    resultsDiv['innerHTML'] = '';
    window['setTimeout'](function() {
        resultsDiv['innerHTML'] = `&lt;div class="alert alert-danger"&gt;` + _0xe58cxb + `&lt;/div&gt;`
    }, 100)
}

function showSuccess(_0xe58cxd) {  
    resultsDiv['innerHTML'] = `&lt;div class="alert alert-success"&gt;` + _0xe58cxd + `&lt;/div&gt;`
}
</code></pre>

<p>This statement tells us the flag is the password:</p>

<pre><code>if (_0xe58cx7 === 'pirates' &amp;&amp; isValid(_0xe58cx8)) {  
    showSuccess('You\'re logged in!\x0AFlag is HackTrinity{&amp;lt;password&amp;gt;}')
}
</code></pre>

<p>We know from below that the password is some string which has the SHA-1 hash of <code>d8a9794e658b82d805b8719abd42d2c2ce956d1d</code>.</p>

<p>We can simply look this up in a rainbow table. I used <a href="https://hashkiller.co.uk/sha1-decrypter.aspx">this website</a>, as it was the first result in Google. In one fifth of a second, it told me that the string <code>banter</code> fit this critera.</p>

<p>The flag is therefore <code>HackTrinity{banter}</code>.</p>

<hr>

<h1 id="jarofjava">Jar of Java</h1>

<p>Problem statement:</p>

<pre><code>Hey, check out this password checker that I wrote for my programming class! I think it's very secure but I'm not sure. Can you reverse-engineer it to make sure it can't be broken?

You can run the program using the command java -jar password.jar  
</code></pre>

<p>You are given a file <code>password.jar</code>. Uploading it to an <a href="http://www.javadecompilers.com/">online decompiler</a> returns the file <code>PasswordCheck.java</code>, which looks like this:</p>

<pre><code>import java.util.Scanner;

public class PasswordCheck { public PasswordCheck() {}  
  public static String password = "HackTrinity{When_You_Lose_The_Code_You_Lose_Your_Secrets}";

  public static void main(String[] paramArrayOfString) {
    Scanner localScanner = new Scanner(System.in);
    System.out.printf("Enter password: ", new Object[0]);
    String str = localScanner.nextLine();
    if (str.equals(password)) {
      System.out.println("Congrats, that was the flag!");
    } else {
      System.out.println("Oops, that's not the flag!");
    }
  }
}
</code></pre>

<p>The flag is <code>HackTrinity{When_You_Lose_The_Code_You_Lose_Your_Secrets}</code>.</p>

<hr>

<h1 id="lotto">Lotto</h1>

<p>Problem statement:</p>

<pre><code>In an effort to raise funds, Trinity have launched a lottery!

See if you can predict the next lotto numbers and win the jackpot.

https://lotto.hacktrinity.me  
</code></pre>

<p><code>lotto.hacktrinity.me</code> is a site that gives you the previous lotto number, and gives you an input box to guess the next number. If you get it correct, you will be shown the flag. If you get it wrong, it will show you what the next number was. After 3 numbers, it tells you the game is over, and to try again (after a CAPTCHA).</p>

<p>However, it also gives you the implementation of the lotto number generator:</p>

<pre><code>import java.util.Random;

public class GenerateLottoNumbers {  
  /* Output 1st, 2nd and 3rd Lotto numbers to stdout.*/
  public static void main(String[] args) {
    Random r = new Random();
    System.out.println(r.nextInt());
    System.out.println(r.nextInt());
    System.out.println(r.nextInt());
  }
}
</code></pre>

<p><code>java.util.Random</code> is predictable though! After 2 outputs from <code>r.nextInt()</code>, you can guess the seed being used, and from there calculate the next output. <a href="https://crypto.stackexchange.com/a/51690">Lery on StackOverflow wrote a script to do this</a>. I just copied this script into ideone.com, put in the first 2 numbers from the lotto site, and ran it.</p>

<p>It outputted the third number, which when put into the lotto site returned the flag <code>HackTrinity{they_call_it_pseudo_random_for_a_reason}</code>.</p>

<hr>

<h1 id="capturethepacket">Capture the Packet</h1>

<p>Problem statement:</p>

<pre><code>We (an unnamed three-letter agency) have managed to install a wire tap on a target's internet connection and have created the attached Packet Capture (PCAP) file.

Can you retrieve any useful information? It's probably all encrypted nowadays anyways thanks to those dratted tech companies.  
</code></pre>

<p>Attached is a PCAP file. I uploaded this to <a href="https://packettotal.com">packettotal.com</a> to look at it. I looked at the transferred files, and opened the one in format <code>text/plain</code>. It contained the following string:</p>

<pre><code>username=l33th4x0r&amp;password=HackTrinity{this_is_what_the_NSA_sees_when_you_dont_encrypt}  
</code></pre>

<p>The flag is <code>HackTrinity{this_is_what_the_NSA_sees_when_you_dont_encrypt}</code>.</p>

<hr>

<h1 id="biccies">Biccies</h1>

<pre><code>You are an investigative reporter with the Student Tribune and have filed a Freedom of Information Request for details of government spending on biscuits. However, the authorities have redacted the documents, citing National Security concerns. Can you find out how much they spent on Rich Tea?

Enter the flag as  
HackTrinity{&lt;amount&gt;} , including currency and commas where appropriate  
</code></pre>

<p>Attached is a PDF with a table of biscuit types to prices. However, there is a black box over the prices.</p>

<p>There is an easier way, but I was limited to a corp computer, so couldn't install any software. I didn't have any luck with online PDF readers or editors- none were buggy in the way that I hoped, by not showing the black boxes. All either crashed, or correctly displayed/converted the document.</p>

<p>I read some of the PDF spec, figured out the formatting of a PDF document.</p>

<ol>
<li><a href="https://blog.didierstevens.com/2008/04/09/quickpost-about-the-physical-and-logical-structure-of-pdf-files/">This post</a> gives a very quick format of a PDF file.  </li>
<li><a href="https://amccormack.net/2012-01-22-anatomy-of-a-pdf-document.html">This post</a> actually goes line-by-line through an example PDF, explaining everything that is going on.  </li>
<li><a href="https://www.prepressure.com/pdf/basics/page-boxes">This post</a> helped when I assumed a MediaBox was a big black redaction box.</li>
</ol>

<p>The PDF was a mess, but <a href="https://stackoverflow.com/questions/11731425/data-extraction-from-filter-flatedecode-pdf-stream-in-php">from StackOverflow</a> I learned about qpdf. <code>sudo apt-get install qpdf</code> on my VPS nabbed it, and then <code>qpdf --qdf --object-streams=disable biscuitdoc.pdf out.pdf</code>. I downloaded <code>out.pdf</code> back to my laptop, and opened it in TextEdit.</p>

<p>I took a look at it, tried to find where the text would be. There was still some weird stuff going on, but by far the longest object in the PDF started like this:</p>

<pre><code>9 0 obj  
&lt;&lt;  
  /BitsPerComponent 8
  /ColorSpace /DeviceGray
  /Height 3488
  /Subtype /Image
  /Type /XObject
  /Width 2464
  /Length 10 0 R
&gt;&gt;
stream  
</code></pre>

<p>This was followed by a lot of mostly-random characters, most of them being <code>^</code>. I assumed this was the text in the document, and that the <code>^</code>s were empty space or similar. Just to see what would happen, I deleted a few lines of them, saved the document, and opened it in Chrome. It looked like the following:</p>

<p><img src="https://i.imgur.com/ixFtks9.jpg" alt="fixed PDF" title="">.</p>

<p>The text moved, but the black boxes didn't. Still, the values are visible. The flag is <code>HackTrinity{€17,278,289}</code>.</p>

<p>I still amn't totally sure how the whole PDF format works, but I know a lot more than I did before.</p>

<hr>

<h1 id="taytosmugglers">Tayto Smugglers</h1>

<pre><code>In the post-Brexit economic apocalypse of the distant future (2019), Tayto crisps are a highly sought after luxury. They are now subject to high customs and charges when they are exported to the UK. An illegal Tayto smuggling ring has cropped up, and the EU would like to crack down on it.

You've been tasked with searching all digital files at the (hard) border and have come across this innocuous-looking picture of the Campanile. Can you ensure it contains no hidden bag of Tayto's crisps?  
</code></pre>

<p>Attached is a huge (1.2MB!) PNG of with a picture of TCD. This PNG does not load in some programs on my Mac laptop, but <code>file</code> tells me this:</p>

<pre><code>campanile.png: PNG image data, -2555936 x 1067590, 73-bit interlaced  
</code></pre>

<p>I expected there was another file type buried in there, because I knew it was very feasible to hide things in PNGs, and this PNG was such a large file for such a normal picture. I tried <code>unzip</code>-ing it, but to no avail. I opened up the file in <a href="https://hexed.it/">an online hex editor</a>. The words "Created with GIMP" were near the top of the file, quite clearly :)</p>

<p>After reading the hex and googling, I figured out that the file had both a PNG header (with the PNG magic number at the beginning, the PNG header in the middle, and the PNG footer at the end), and a JFIF/JPEG image (with the JFIF/JPEG magic number following the PNG magic number, and the JPEG footer just above the PNG header).</p>

<p>I tried renaming the file as a <code>.jpg</code> and opening it, but it still looked the same. I sat stumped for a long time. <code>file</code> still only recognised the file as a PNG, so I tried deleting the PNG magic bits from the beginning. <code>file</code> now said this:</p>

<pre><code>campanile_clean.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, comment: "Created with GIMP", progressive, precision 8, 1000x1000, frames 3  
</code></pre>

<p>I opened the image again, and there was a packet of Taytos looking at me, with the flag <code>HackTrinity{you_can_pry_my_taytos_from_my_cold_dead_hands}</code> underneath in the image.</p>

<p>I guess my image viewer was originally ignoring the JPG suffix and reading the file as a PNG anyway, and that's why it looked the same. I won't make the same mistake again.</p>

<hr>

<h1 id="tocatchacheater">To Catch a Cheater</h1>

<pre><code>College has been made aware of an exam paper leak. A copy of the printed paper was obtained and IT Services has been tasked with tracking the cheater. They believe it was printed on college printers. Can you help give the culprit a dose of r e a l i t y ?  
</code></pre>

<p>Attached was a zip with 3 directories: <code>Printer_1_228406</code>, <code>Printer_2_318008</code>, and <code>Printer_3_615028</code>. Within each were 3 files, all of the same size, with a list of timestamps and usernames. These were the printer logs for the dates 31st March 2017, 1st April 2017, and 2nd April 2017.</p>

<p>Attached also was a PDF of the exam paper that was found.</p>

<p>After floundering, trying to look up details of the paper to see if someone was mentioning it online, I eventually realised that the PDF attached was not the exam paper, but a <em>scan of the print-out</em> of the exam paper. I looked at it more closely, and noticed <a href="http://seeingyellow.com/">little yellow dots</a>. Ah!</p>

<p><a href="http://www.instructables.com/id/Yellow-Dots-of-Mystery-Is-Your-Printer-Spying-on-/">Instructables</a> had an article on how to make the dots more visible. After converting the PDF to a TIFF online, I downloaded imagemagick and ran <code>convert -channel RG -fx 0 ExamPaper.tiff blue.png</code> on my VPS, then downloaded <code>blue.png</code> to look at on my laptop. The dots were a little clearer.</p>

<p><a href="https://w2.eff.org/Privacy/printers/docucolor/">The EFF has a fantastic online decoder for this pattern</a>, including some explanation of the format. I entered the pattern I saw there (wrongly it turns out, but the tool could use the parity bits to fix it):</p>

<pre><code>Parity mismatch for row 2.  
Parity mismatch for column 7.  
Correctable error at row 2 and col 7  
Making correction and processing corrected matrix:

           111111
  123456789012345
7   oo   oo  o  o  
6 o        o o  
5  o       o   o  
4        o o ooo  
3     o    oo o o  
2 oo  o o  o  oo  
1          o  oo  
0 oo  oo o o  ooo  
Printer serial number: 318008 [or 55318008]

Date: April 1, 2017

Time: 13:37  
</code></pre>

<p>So we know that a printer with code <code>318008</code> or <code>55318008</code> (ie. Printer 2) printed this document on the 1st April 2017 (which we have the log for) at 13:37 (of course). The section of this log looks like this:</p>

<pre><code>[01/Apr/2017:13:35:13] Alloway
[01/Apr/2017:13:35:49] DiFrancesco
[01/Apr/2017:13:37:03] wen-huat
[01/Apr/2017:13:37:40] FerrisBueller
[01/Apr/2017:13:38:12] Baert
[01/Apr/2017:13:38:17] Bdale
[01/Apr/2017:13:38:48] Anamaria
</code></pre>

<p>Only two people printed at this time. I tried <code>wen-huat</code>, which was wrong, but the flag did indeed turn out to be <code>HackTrinity{FerrisBueller}</code>. Gratifying!</p>

<hr>

<h1 id="hugsandkisses">Hugs and Kisses</h1>

<p>Problem statement:</p>

<pre><code>Get some hugs and kisses from this binary file.  
</code></pre>

<p>Attached is a file called <code>haxor</code>. Opening it up in vim, I saw the <a href="https://en.wikipedia.org/wiki/Executable_and_Linkable_Format">ELF magic number</a> straight away at the start, and some vaguely human-readable code near the end of the file. The program doesn't run on my macbook, but it runs fine on my Ubuntu VPS. I tried it:</p>

<pre><code>me@my_vps ~ % ./haxor  
Usage ./haxor &lt;flag&gt;  
</code></pre>

<p>Running it with any arg I tried tells me what's up:</p>

<pre><code>me@my_vps ~ % ./haxor testflag  
This is not the flag :)  
</code></pre>

<p>To a disassembler we go, to try to figure out what is the flag, or how does it check if what we entered is correct.</p>

<p>Playing <a href="https://onlinedisassembler.com/odaweb/4q4n194x">with an online disassembler</a> for a long time, I started to see the layout of the program. The graph view of the <code>main</code> function in that online disassembler really helped figure out the control flow. </p>

<p>This is what <code>main</code> looks like.</p>

<pre><code>0x4006bd &lt;main&gt;                 push   %rbp  
0x4006be &lt;main+1&gt;               mov    %rsp,%rbp  
0x4006c1 &lt;main+4&gt;               push   %rbx  
0x4006c2 &lt;main+5&gt;               sub    $0x18,%rsp  
0x4006c6 &lt;main+9&gt;               mov    %edi,-0x14(%rbp)  
0x4006c9 &lt;main+12&gt;              mov    %rsi,-0x20(%rbp)  
0x4006cd &lt;main+16&gt;              cmpl   $0x2,-0x14(%rbp)  
0x4006d1 &lt;main+20&gt;              je     0x4006f6 &lt;main+57&gt;  
0x4006d3 &lt;main+22&gt;              mov    -0x20(%rbp),%rax  
0x4006d7 &lt;main+26&gt;              mov    (%rax),%rax  
0x4006da &lt;main+29&gt;              mov    %rax,%rsi  
0x4006dd &lt;main+32&gt;              mov    $0x4007d4,%edi  
0x4006e2 &lt;main+37&gt;              mov    $0x0,%eax  
0x4006e7 &lt;main+42&gt;              callq  0x4004f0 &lt;printf@plt&gt;  
0x4006ec &lt;main+47&gt;              mov    $0x1,%edi  
0x4006f1 &lt;main+52&gt;              callq  0x400530 &lt;exit@plt&gt;  
0x4006f6 &lt;main+57&gt;              mov    -0x20(%rbp),%rax  
0x4006fa &lt;main+61&gt;              add    $0x8,%rax  
0x4006fe &lt;main+65&gt;              mov    (%rax),%rbx  
0x400701 &lt;main+68&gt;              mov    $0x0,%eax  
0x400706 &lt;main+73&gt;              callq  0x400646 &lt;computeFlag&gt;  
0x40070b &lt;main+78&gt;              mov    %rbx,%rsi  
0x40070e &lt;main+81&gt;              mov    %rax,%rdi  
0x400711 &lt;main+84&gt;              callq  0x400510 &lt;strcmp@plt&gt;  
0x400716 &lt;main+89&gt;              test   %eax,%eax  
0x400718 &lt;main+91&gt;              jne    0x400726 &lt;main+105&gt;  
0x40071a &lt;main+93&gt;              mov    $0x4007e5,%edi  
0x40071f &lt;main+98&gt;              callq  0x4004e0 &lt;puts@plt&gt;  
0x400724 &lt;main+103&gt;             jmp    0x40073a &lt;main+125&gt;  
0x400726 &lt;main+105&gt;             mov    $0x4007f4,%edi  
0x40072b &lt;main+110&gt;             callq  0x4004e0 &lt;puts@plt&gt;  
0x400730 &lt;main+115&gt;             mov    $0x1,%edi  
0x400735 &lt;main+120&gt;             callq  0x400530 &lt;exit@plt&gt;  
0x40073a &lt;main+125&gt;             mov    $0x0,%eax  
0x40073f &lt;main+130&gt;             add    $0x18,%rsp  
0x400743 &lt;main+134&gt;             pop    %rbx  
0x400744 &lt;main+135&gt;             pop    %rbp  
0x400745 &lt;main+136&gt;             retq  
</code></pre>

<p>After much study, it became clear that <code>computeFlag</code> (called at <code>0x400706</code> above) creates the flag, and then the program jumps (from <code>0x400711</code> above) to <code>0x400510</code> (ie. <code>strcmp</code>) where this flag is compared with the inputted flag. From there, it jumps around and prints different strings depending on whether the input flag was correct or not.</p>

<p>I opened gdb and set a breakpoint at <code>0x400510</code>. I ran the program with the argument <code>testflag</code>, until it hit that breakpoint.</p>

<p>I could see that the arguments were at the locations of registers <code>rsi</code> and <code>rdi</code> (or more correctly <code>rbx</code> and <code>rax</code>), as these are set at <code>0x40070b</code> and  <code>0x40070e</code> above, before <code>strcmp</code> is called at <code>0x400711</code>. I printed the strings at these locations in gdb:</p>

<pre><code>(gdb) x/s $rsi
0x7fffffffe782: "testflag"  
(gdb) x/s $rdi
0x602010:       "OTYDIHMRWBAFKPUZYDINSRWBGLKPUZ"  
</code></pre>

<p><code>OTYDIHMRWBAFKPUZYDINSRWBGLKPUZ</code> looks like our flag. And sure enough:</p>

<pre><code>me@my_vps ~ % ./haxor OTYDIHMRWBAFKPUZYDINSRWBGLKPUZ  
Nice job. XOXO  
</code></pre>

<p><code>HackTrinity{OTYDIHMRWBAFKPUZYDINSRWBGLKPUZ}</code> gets me the points.</p>

<p>This all took much longer than it seems here, including re-learning lots of assembly, translating between AT&amp;T and Intel assembly syntax, and looking at random code locations in both my online disassembler and gdb :)</p>

<hr>

<h1 id="unhackable">Unhackable</h1>

<p>Problem statement:</p>

<pre><code>According to the owner, this site is literally unhackable.

https://unhackable.hacktrinity.me/  
</code></pre>

<p>This web address just returns a page with the following HTML:</p>

<pre><code>&lt;!DOCTYPE html&gt;  
&lt;html lang="en"&gt;  
&lt;body&gt;  
&lt;p style="font-size: 20pt"&gt;This site is literally unhackable. It's deployed as a static site. Try your best punks, but there's no way you're getting my flags.txt.&lt;/p&gt;  
&lt;/body&gt;  
&lt;/html&gt;  
</code></pre>

<p>I spent so long on this! I did subdomain bruteforce searches, portscanned it, URL fuzzed, analysed the header files, looked at my cookies, everything I could think of. After hours, an idea popped into my head. The solution? <code>https://unhackable.hacktrinity.me/.git/</code> is accessible.</p>

<p>For some reason it always redirected me to <code>127.0.0.1/.git/*</code> after downloading whatever was at <code>https://unhackable.hacktrinity.me/.git/*</code>, but that's ok - we have all we need.</p>

<p><a href="https://en.internetwache.org/dont-publicly-expose-git-or-how-we-downloaded-your-websites-sourcecode-an-analysis-of-alexas-1m-28-07-2015/">This excellent blogpost</a> walked me through the steps of nabbing <code>flags.txt</code> from this <code>.git</code>. If we download the <code>refs/heads/master</code> file, we can see the hash of the latest commit (<code>4ae239c8f94d1bee157117a2c74720f51d16a1bf</code>). We can create an empty git repo locally, download this file and put it where it should be (<code>.git/objects/4a/e239c8f94d1bee157117a2c74720f51d16a1bf</code>), then read it.</p>

<pre><code>$ git cat-file -p 4ae239c8f94d1bee157117a2c74720f51d16a1bf
tree 3c8848f3d42b50fd87bdf0045b07c73e9a2cbf69  
parent aebaab81b38bdb74cdc2e111118621d2f7dd001a  
author Rory Flynn &lt;roryflynn@users.noreply.github.com&gt; 1517251354 +0000  
committer Rory Flynn &lt;roryflynn@users.noreply.github.com&gt; 1517251354 +0000

Oops, remove those flags.txt. That was a close one  
</code></pre>

<p>Presumably, this commit deleted <code>flags.txt</code>. We can see the parent's commit hash is <code>aebaab81b38bdb74cdc2e111118621d2f7dd001a</code>. We can download that commit (<code>unhackable.hacktrinity.me/.git/objects/ae/baab81b38bdb74cdc2e111118621d2f7dd001a</code>) and do the same as before.</p>

<pre><code>$ git cat-file -p aebaab81b38bdb74cdc2e111118621d2f7dd001a
tree 0a3725964eaad4d19e4b422a8bbe1cf40e5a451e  
author Rory Flynn &lt;roryflynn@users.noreply.github.com&gt; 1517251255 +0000  
committer Rory Flynn &lt;roryflynn@users.noreply.github.com&gt; 1517251255 +0000

initial commit of flags and index.html  
</code></pre>

<p>Cool! We can download the tree and read it:</p>

<pre><code>git cat-file -p 0a3725964eaad4d19e4b422a8bbe1cf40e5a451e  
100644 blob c2440c908a67b4266b6be5690fab86ee25977d80    flags.txt  
100644 blob be6392988371f35dcc5dd6603e567e8153a70920    index.html  
</code></pre>

<p>And now we can download the flags.txt blob and read it:</p>

<pre><code>$ git cat-file -p c2440c908a67b4266b6be5690fab86ee25977d80
HackTrinity{hidden_git_directorys_gonna_git_ya}  
</code></pre>

<p>Heyo.</p>

<hr>

<h1 id="unfurlme">Unfurlme</h1>

<p>Problem:</p>

<pre><code>Can you reverse engineer this obfuscated PHP source code?

https://unfurlme.hacktrinity.me/  
</code></pre>

<p>This page has the HTML source:</p>

<pre><code>&lt;html&gt;&lt;title&gt;UnfurlMe.php&lt;/title&gt;&lt;body&gt;&lt;form method="post"&gt;Enter the password:&lt;input type="password" name="pass"/&gt;&lt;input type="submit" value="submit"/&gt;&lt;/form&gt;&lt;br/&gt;&lt;a href="?src"&gt;View Source&lt;/a&gt;&lt;/body&gt;&lt;/html&gt;  
</code></pre>

<p>This isn't that interesting. Just a password input box, and a <code>View Source</code> hyperlink. But if you click the <code>View Source</code> link on the page, it appends <code>?src</code> to the URL, and shows you the PHP used to generate the page:</p>

<pre><code>&lt;?php  
if(isset($_GET['src'])){show_source(__FILE__);die();}  
$wowzers="b\x61\x73e\x364_de\x63ode";$pandas="\x63o\x6ev\x65rt_\x75\x75de\x63\x6fde";$jenga="g\x7a\x75\x6ecomp\x72\x65ss";$a=$pandas(strrev($wowzers('CmAKSURCKCg5JzpIXUQzV11SNVMxNzkvClpZRDZBISc5Iik3O0otNTNQTVYzTClTOU9AJCxXODc2VCwmNFNAVzRWWCQyUS02NkElRDVJPVcrVzE1OU0KWl00MDRJRjxGOTQxVTFFOjdRRDAtPSM1QU1CLDoxRjMuTSQ1VTkjNjgxVTglQUUxKylXNUswNS5EUTQ2TQpSRCYtKFlGMjpRVjQsRTU1JyFVLUExIy5SSUY7NyVTOFRBJC4vXSYtTC1DNUQ9RDMwSUcxI0ElM0s0IztNCkYpRS0oMUY8WUBFODNdJjNSVFY6VigzNk8xRzwmNVQrOVE2PEtURjtQMUc9OEVXMUdNQjVYPVQ1UTkzPk0KNEk2PjNBJzlMLSM2Uy03PTY1RTNPSDc4NiU2NTZBRDpPNEc2UEEzPjIxRTBPKVMsUyw0NE0xVzlDKVY7TQooMTcuLkFFMS9NIjRWTFYxU0gkLCZBUyxYNVQ5WDQnM1lERDJUWTQsS0E3Lio9Rjk5PVY9WTlDPkUhJS1NCjJdIjQ5OUQ0Rj1XOSg5My44STYzUik2MFUtVz1LNFM1WClDMEFBVzNWMVYwRSknPFZIND5QTEQtKT0jLU0KTFVWLTUxNDhTLEcxOTVENktBVztXOTU5LD1GMlhANTQ2KUMyOkFGMzIxNjZYSFQ9SzFFPFIwRjxQLUc4TQpZTDQ5USglNE8xRzNLSEc8L0klPlYpNTwnWVQ7TDFUM1BgIzRWTUQ7IVFEPFlJJi5UTUYxMyUzPVEpJjJNCjktNz1TXDQ9R11GMVMsRjFGISU5JklUMFEoVzVCVVYtIU0kNFMsMzhUWEQ9UlRWPU4tRjpIUVQ4UDFEO00KV0FEODBFUzEuJTUzJkFVM0g1Nyw1MUc0RlFEOjpBUzsyLUMsRFk0PkotRTEhRVc6UC1TLC05UypEJUM9TQpSNVU7LEEzO0Q1RDtELTY9VzxTPDpBNjtFJSU9Vyk0OFoxRThCLTc0U0xSNCxNRD5aLTQ+Vzk0OSJdMjZNClM9JDRRRDYxNS0jPFJZRjolQUM8TlE2OFRVVjtUREY5WElGMUU1JS1KVSQ8SF1CNSxdJjQmQVYxJUk1MU0KT0RXLVQ0NT4tNUYxMVk0MVUlJjxYPVY4SDFHOi5NND0kMVMsU0VVOTFZJCxUWTQzWDFVMktMMjpSRFctTQooNTQsOCUjPFM5VSo1TUI7Mz1FOlYsNTU3NSY0MzFWKystJj1ZNFQ8JikzLFAkRDApISMtNz01OyspVSxNCiFNQjw4IUcsT0BUOE8oRzRKOUcsMlUmNEQ1VypSMSYzMylGNiNJJzxLQDMwJ0EjLCtdIi5QQVc9Ii1ENU0KOClHLVUhRTxLSSQtOTFUMkEpNiwyQSMsNi0lLDdFJTkjOVYzVyEzM0ItVy0lNVY6QjFWMFhdVCopJTYxTQpQISUxTDFVMyJBVTQwWTY7TEUmMiElJjpULTUuVjQzPDdBJT06QUMzLEk3PSRBRDpHIVU4Uy1XNTEtVjtNCkk1NTkwWUY+WkE2OFRBRTU3XVIqUD1ELFRVNDRJJVc6UiVTMyQtJjooUTYzJUlUPSlFVDQiVUQtVFw2LU0KLCU1LjhBNjRPSVY7QilTOktZVD0qNUYoSDQmOU8tNjlEXSUtVjRWPEEpJipTLTc5UiE3O08tRjtVSVc5TQ==')));eval("eval($wowzers($jenga($pandas($wowzers($a)))));");
</code></pre>

<p>You can see here that the first line of this program just shows the PHP source code when <code>?src</code> is present, and then halts the program. we can ignore this line.</p>

<p>I first put this script through <a href="https://www.unphp.net/">an online PHP deobfuscator</a>. It returned the following:</p>

<pre><code>$wowzers="base64_decode";
$pandas="convert_uudecode";
$jenga="gzuncompress";
$a=$pandas(strrev($wowzers('CmAKSURCKCg5JzpIXUQzV11SNVMxNzkvClpZRDZBISc5Iik3O0otNTNQTVYzTClTOU9AJCxXODc2VCwmNFNAVzRWWCQyUS02NkElRDVJPVcrVzE1OU0KWl00MDRJRjxGOTQxVTFFOjdRRDAtPSM1QU1CLDoxRjMuTSQ1VTkjNjgxVTglQUUxKylXNUswNS5EUTQ2TQpSRCYtKFlGMjpRVjQsRTU1JyFVLUExIy5SSUY7NyVTOFRBJC4vXSYtTC1DNUQ9RDMwSUcxI0ElM0s0IztNCkYpRS0oMUY8WUBFODNdJjNSVFY6VigzNk8xRzwmNVQrOVE2PEtURjtQMUc9OEVXMUdNQjVYPVQ1UTkzPk0KNEk2PjNBJzlMLSM2Uy03PTY1RTNPSDc4NiU2NTZBRDpPNEc2UEEzPjIxRTBPKVMsUyw0NE0xVzlDKVY7TQooMTcuLkFFMS9NIjRWTFYxU0gkLCZBUyxYNVQ5WDQnM1lERDJUWTQsS0E3Lio9Rjk5PVY9WTlDPkUhJS1NCjJdIjQ5OUQ0Rj1XOSg5My44STYzUik2MFUtVz1LNFM1WClDMEFBVzNWMVYwRSknPFZIND5QTEQtKT0jLU0KTFVWLTUxNDhTLEcxOTVENktBVztXOTU5LD1GMlhANTQ2KUMyOkFGMzIxNjZYSFQ9SzFFPFIwRjxQLUc4TQpZTDQ5USglNE8xRzNLSEc8L0klPlYpNTwnWVQ7TDFUM1BgIzRWTUQ7IVFEPFlJJi5UTUYxMyUzPVEpJjJNCjktNz1TXDQ9R11GMVMsRjFGISU5JklUMFEoVzVCVVYtIU0kNFMsMzhUWEQ9UlRWPU4tRjpIUVQ4UDFEO00KV0FEODBFUzEuJTUzJkFVM0g1Nyw1MUc0RlFEOjpBUzsyLUMsRFk0PkotRTEhRVc6UC1TLC05UypEJUM9TQpSNVU7LEEzO0Q1RDtELTY9VzxTPDpBNjtFJSU9Vyk0OFoxRThCLTc0U0xSNCxNRD5aLTQ+Vzk0OSJdMjZNClM9JDRRRDYxNS0jPFJZRjolQUM8TlE2OFRVVjtUREY5WElGMUU1JS1KVSQ8SF1CNSxdJjQmQVYxJUk1MU0KT0RXLVQ0NT4tNUYxMVk0MVUlJjxYPVY4SDFHOi5NND0kMVMsU0VVOTFZJCxUWTQzWDFVMktMMjpSRFctTQooNTQsOCUjPFM5VSo1TUI7Mz1FOlYsNTU3NSY0MzFWKystJj1ZNFQ8JikzLFAkRDApISMtNz01OyspVSxNCiFNQjw4IUcsT0BUOE8oRzRKOUcsMlUmNEQ1VypSMSYzMylGNiNJJzxLQDMwJ0EjLCtdIi5QQVc9Ii1ENU0KOClHLVUhRTxLSSQtOTFUMkEpNiwyQSMsNi0lLDdFJTkjOVYzVyEzM0ItVy0lNVY6QjFWMFhdVCopJTYxTQpQISUxTDFVMyJBVTQwWTY7TEUmMiElJjpULTUuVjQzPDdBJT06QUMzLEk3PSRBRDpHIVU4Uy1XNTEtVjtNCkk1NTkwWUY+WkE2OFRBRTU3XVIqUD1ELFRVNDRJJVc6UiVTMyQtJjooUTYzJUlUPSlFVDQiVUQtVFw2LU0KLCU1LjhBNjRPSVY7QilTOktZVD0qNUYoSDQmOU8tNjlEXSUtVjRWPEEpJipTLTc5UiE3O08tRjtVSVc5TQ==')));
eval("eval($wowzers($jenga($pandas($wowzers($a)))));");  
</code></pre>

<p>I removed the function variables to see things more clearly:</p>

<pre><code>$a=convert_uudecode(strrev(base64_decode('CmAKSURCKCg5JzpIXUQzV11SNVMxNzkvClpZRDZBISc5Iik3O0otNTNQTVYzTClTOU9AJCxXODc2VCwmNFNAVzRWWCQyUS02NkElRDVJPVcrVzE1OU0KWl00MDRJRjxGOTQxVTFFOjdRRDAtPSM1QU1CLDoxRjMuTSQ1VTkjNjgxVTglQUUxKylXNUswNS5EUTQ2TQpSRCYtKFlGMjpRVjQsRTU1JyFVLUExIy5SSUY7NyVTOFRBJC4vXSYtTC1DNUQ9RDMwSUcxI0ElM0s0IztNCkYpRS0oMUY8WUBFODNdJjNSVFY6VigzNk8xRzwmNVQrOVE2PEtURjtQMUc9OEVXMUdNQjVYPVQ1UTkzPk0KNEk2PjNBJzlMLSM2Uy03PTY1RTNPSDc4NiU2NTZBRDpPNEc2UEEzPjIxRTBPKVMsUyw0NE0xVzlDKVY7TQooMTcuLkFFMS9NIjRWTFYxU0gkLCZBUyxYNVQ5WDQnM1lERDJUWTQsS0E3Lio9Rjk5PVY9WTlDPkUhJS1NCjJdIjQ5OUQ0Rj1XOSg5My44STYzUik2MFUtVz1LNFM1WClDMEFBVzNWMVYwRSknPFZIND5QTEQtKT0jLU0KTFVWLTUxNDhTLEcxOTVENktBVztXOTU5LD1GMlhANTQ2KUMyOkFGMzIxNjZYSFQ9SzFFPFIwRjxQLUc4TQpZTDQ5USglNE8xRzNLSEc8L0klPlYpNTwnWVQ7TDFUM1BgIzRWTUQ7IVFEPFlJJi5UTUYxMyUzPVEpJjJNCjktNz1TXDQ9R11GMVMsRjFGISU5JklUMFEoVzVCVVYtIU0kNFMsMzhUWEQ9UlRWPU4tRjpIUVQ4UDFEO00KV0FEODBFUzEuJTUzJkFVM0g1Nyw1MUc0RlFEOjpBUzsyLUMsRFk0PkotRTEhRVc6UC1TLC05UypEJUM9TQpSNVU7LEEzO0Q1RDtELTY9VzxTPDpBNjtFJSU9Vyk0OFoxRThCLTc0U0xSNCxNRD5aLTQ+Vzk0OSJdMjZNClM9JDRRRDYxNS0jPFJZRjolQUM8TlE2OFRVVjtUREY5WElGMUU1JS1KVSQ8SF1CNSxdJjQmQVYxJUk1MU0KT0RXLVQ0NT4tNUYxMVk0MVUlJjxYPVY4SDFHOi5NND0kMVMsU0VVOTFZJCxUWTQzWDFVMktMMjpSRFctTQooNTQsOCUjPFM5VSo1TUI7Mz1FOlYsNTU3NSY0MzFWKystJj1ZNFQ8JikzLFAkRDApISMtNz01OyspVSxNCiFNQjw4IUcsT0BUOE8oRzRKOUcsMlUmNEQ1VypSMSYzMylGNiNJJzxLQDMwJ0EjLCtdIi5QQVc9Ii1ENU0KOClHLVUhRTxLSSQtOTFUMkEpNiwyQSMsNi0lLDdFJTkjOVYzVyEzM0ItVy0lNVY6QjFWMFhdVCopJTYxTQpQISUxTDFVMyJBVTQwWTY7TEUmMiElJjpULTUuVjQzPDdBJT06QUMzLEk3PSRBRDpHIVU4Uy1XNTEtVjtNCkk1NTkwWUY+WkE2OFRBRTU3XVIqUD1ELFRVNDRJJVc6UiVTMyQtJjooUTYzJUlUPSlFVDQiVUQtVFw2LU0KLCU1LjhBNjRPSVY7QilTOktZVD0qNUYoSDQmOU8tNjlEXSUtVjRWPEEpJipTLTc5UiE3O08tRjtVSVc5TQ==')));
eval("eval(base64_decode(gzuncompress(convert_uudecode(base64_decode($a)))));");  
</code></pre>

<p>I tried <code>echo $a</code>, to see what <code>$a</code> was before it was taken apart and evaluated, and saw that it contained the following:</p>

<pre><code>gzuncompress(base64_decode("eJwNkk2bojoQhX9QL5o46MBSIIwJEMlHhcDO1rkqiQMt2Gp+/WVXtahzznPeUiocQWsscPgjHDuzLN8ZtXWq569SthaAHilmnPSXBOTlDPpEaI+OxCdbkeE7sbM0wOfCdYW0SV08R1baKDY4JkrPu6rXVCBwxp8/K08GA8+pzCZbSLdr+udPmR2vjRr/cH/2pXr+A3RKmWW40IBA012FsE9tcK/dSPeWUS6jWSn+U+Vsp1X1EH7y2i++KTxMNt0NQgYs34DuKNjthcgxpauENQFeMyU47y/EZEGhFPoLV/hpMj4UeFjxfi4omtalnr8Ejnrp3UEi1PGsY/BeFwyCzzKLS+3QsbbTzaBwtQemhZs77ucdnEdm8LoUrv1d+6M33pkyAFSjyNd23Ro8ZjLfRtU1uhOXFMQNG9PbHwnDpcLhjcnwm2vN4a33PKA7mbWr1CJFdPfFc3FoguO3usYHbqu1SFkt8jyrLAnKvP00ODloNGqRvxZOrz+NtoPR1eK9bsprd2rTkwJ8YdRNhZJ2VQX8JgLeVwoxkZEYFs3aDU7ml47I6K0yJ6preCdvOxaB2xW5+wsuAbrMjX96HgwfRFYP/R4Pez6ywgYfgJ9xk1NtJI9Lu8gEx38F0J3Gk6P+OFXN9tHobcgtmQC332oBTRy8pZu/jHVUaVaz/NUVussX3ldxSyjTy6qWGxV+gGyXvtpnm+qlY/EFrtoY26km2LoSbX9rdH6Rfl5+LXCFzPNGdV3l4oO8Htc1Wnjr84a7PGUYLSlZJnH4i2YLd9T+WrKFXEcTXX6uTKNNdZ2+aT7MBLWjTuEFfrjTAOzeTw/wiVAaYcqHN6Sx3Pc4Yv70H/g2lOkpMSjmrBdpaZNzetsW/wNOhhvH"))  
</code></pre>

<p>So let's unravel that by running:</p>

<pre><code>base64_decode(gzuncompress(convert_uudecode(base64_decode(gzuncompress(base64_decode("eJwNkk2bojoQhX9QL5o46MBSIIwJEMlHhcDO1rkqiQMt2Gp+/WVXtahzznPeUiocQWsscPgjHDuzLN8ZtXWq569SthaAHilmnPSXBOTlDPpEaI+OxCdbkeE7sbM0wOfCdYW0SV08R1baKDY4JkrPu6rXVCBwxp8/K08GA8+pzCZbSLdr+udPmR2vjRr/cH/2pXr+A3RKmWW40IBA012FsE9tcK/dSPeWUS6jWSn+U+Vsp1X1EH7y2i++KTxMNt0NQgYs34DuKNjthcgxpauENQFeMyU47y/EZEGhFPoLV/hpMj4UeFjxfi4omtalnr8Ejnrp3UEi1PGsY/BeFwyCzzKLS+3QsbbTzaBwtQemhZs77ucdnEdm8LoUrv1d+6M33pkyAFSjyNd23Ro8ZjLfRtU1uhOXFMQNG9PbHwnDpcLhjcnwm2vN4a33PKA7mbWr1CJFdPfFc3FoguO3usYHbqu1SFkt8jyrLAnKvP00ODloNGqRvxZOrz+NtoPR1eK9bsprd2rTkwJ8YdRNhZJ2VQX8JgLeVwoxkZEYFs3aDU7ml47I6K0yJ6preCdvOxaB2xW5+wsuAbrMjX96HgwfRFYP/R4Pez6ywgYfgJ9xk1NtJI9Lu8gEx38F0J3Gk6P+OFXN9tHobcgtmQC332oBTRy8pZu/jHVUaVaz/NUVussX3ldxSyjTy6qWGxV+gGyXvtpnm+qlY/EFrtoY26km2LoSbX9rdH6Rfl5+LXCFzPNGdV3l4oO8Htc1Wnjr84a7PGUYLSlZJnH4i2YLd9T+WrKFXEcTXX6uTKNNdZ2+aT7MBLWjTuEFfrjTAOzeTw/wiVAaYcqHN6Sx3Pc4Yv70H/g2lOkpMSjmrBdpaZNzetsW/wNOhhvH"))))));  
</code></pre>

<p>This evaluates to:  </p>

<pre><code>if(isset($_REQUEST['src'])){  
    show_source(__FILE__);
    die();
}
if(isset($_REQUEST['pass']) &amp;&amp; $_REQUEST['pass']=="\x75\x6e\x66\x75\x72\x6c\x69\x6e\x67\x61\x6c\x6d\x6f\x73\x74\x63\x6f\x6d\x70\x6c\x65\x74\x65"){  
    die("Congratulations the flag is: ".file_get_contents('/flag/flag.txt'));
}else{
    die('&lt;html&gt;&lt;title&gt;UnfurlMe.php&lt;/title&gt;&lt;body&gt;&lt;form method="post"&gt;Enter the password:&lt;input type="password" name="pass"/&gt;&lt;input type="submit" value="submit"/&gt;&lt;/form&gt;&lt;br/&gt;&lt;a href="?src"&gt;View Source&lt;/a&gt;&lt;/body&gt;&lt;/html&gt;');
}
</code></pre>

<p>We can see the password needed is <code>\x75\x6e\x66\x75\x72\x6c\x69\x6e\x67\x61\x6c\x6d\x6f\x73\x74\x63\x6f\x6d\x70\x6c\x65\x74\x65</code>. I pasted this string into the PHP deobfuscator from before to quickly figure out what those characters were. It returned the string <code>unfurlingalmostcomplete</code>.</p>

<p>If you paste this string into the text box original page <code>unfurlme.hacktrinity.me</code>, it shows you the flag: <code>HackTrinity{Functi0nsW1th1nFunc7i0ns}</code>.</p>

<hr>

<h1 id="pgp">PGP</h1>

<p>The problem statement is as follows:</p>

<pre><code>I received the attached vaguely insulting email from an anonymous sender. Can you help me track them down and exact revenge?

Flag is HackTrinity{&lt;name of sender&gt;}  
</code></pre>

<p>Attached are two files, <code>email.txt</code>:</p>

<pre><code>Hey you. you're stupid.  
</code></pre>

<p>and <code>email.txt.asc</code>:</p>

<pre><code>-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAABAgAGBQJacv0IAAoJEAXbzVnYRgZCvEgIAL39tKSA7xhj/+O47gYX9E+c  
5pmRyD+RZ5Z2cE5gsC4pQ5OtwoViFw4ANzxNBjSh6ObPRjIIMNrVjOc8MlEMqusl  
SiM7PWwzBg8wbioPmZIIR0gvpLFLOL8VCa9Zn9BlqYC/+aRlSN8OCtP2r4CQ2qN3  
UiAhXDx9sXZbCby/yrzNyRsy18eItYTehD1/aL4tt7EIrisyrtr1k6JISzGjc5ic  
/cwsSLS6esPCM7vn/Tu574hCAyTOGYoTZ41f0ljlPwyQ7Bo0qtl4oOf5aLrvJoyR
ylZQKdU0CHIDIPa5AfrSQ1bEes30wHNUiWkQUZoPnsHoXdJ/+CdCxr2W5Wvrye0=  
=SqZ8
-----END PGP SIGNATURE-----
</code></pre>

<p>After some Googling to figure out how PGP signatures work, I came across pretty much exactly what I needed <a href="https://security.stackexchange.com/a/63079">in a security.stackexchange.com post</a>. I can just get <code>gpg</code> to parse the signature file:</p>

<pre><code>% gpg -vv &lt; email.txt.asc 
gpg: armor: BEGIN PGP SIGNATURE  
gpg: armor header: Version: GnuPG v1  
:signature packet: algo 1, keyid 05DBCD59D8460642
        version 4, created 1517485320, md5len 0, sigclass 0x00
        digest algo 2, begin of digest bc 48
        hashed subpkt 2 len 4 (sig created 2018-02-01)
        subpkt 16 len 8 (issuer key ID 05DBCD59D8460642)
        data: [2048 bits]
Detached signature.  
</code></pre>

<p>This then gives me the keyid <code>05DBCD59D8460642</code>, which I can look up:</p>

<pre><code>% gpg --search-keys 05DBCD59D8460642
gpg: searching for "05DBCD59D8460642" from hkp server keys.gnupg.net  
(1)     Eve Heinrichtson &lt;eve@heinrichtson.org&gt;
          2048 bit RSA key D8460642, created: 2018-02-01
</code></pre>

<p><code>HackTrinity{Eve Heinrichtson}</code> is our flag.</p>

<hr>

<h1 id="badssh">BadSSH</h1>

<p>Problem statement:</p>

<pre><code>Our sysadmin isn't very good - he doesn't really understand how crypto works, or how to configure OpenSSH properly.

Show him a lesson by finding the privkey.pem and logging in over SSH to retrieve the flag.

ssh -o IdentitiesOnly=yes -o IdentityFile=privkey.pem -p 8022 badssh@145.239.7.7  
</code></pre>

<p>This was later appended to the problem statement, after (or perhaps, because) I had already tried many approaches:</p>

<pre><code>Note: You should only interact with port 8022 on this host. All other ports are out of scope.

Hint:  
The sysadmin generated his own RSA key using a custom crazy Python script (he's not a professional cryptographer, so this was probably unwise). The public part of the key is:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDbe4wAeBPcaFdSviU1OysvpOMNfdyXZdUwSyTZaB4d4gzny1G5bAVLK5J4M2/nA5nMeh9De/jPjrJiJ6gGIz1uHOAzF2gFcXk4jEBDeFFXd4le3DMyKZ57+WD2rR/D71SCBS44mBumiYSJR7q9tZLi9CTorF+6q19BzAC41bVGJTU0HtsQ10DeoGbghLY6ypKStiXIGYDhFAu0gRvIwjDEGhqPdUWa3ddtg6ml+V5rr40qffeYP2jUxYCjNd/LaF5wUsVOL7ZrWrhOx2BgDM4emIhyyXCHAAjGxFC3yAck+JYBVz8FShXdn/S4liVXyrrsyW6ah32uH6j2yazUJLUP  
Calculate the private part of the key, and use it to login to the box over SSH port 8022.  
</code></pre>

<p>This problem took me way too long.</p>

<p>Firstly, I portscanned the target server, hoping for an open webserver or something. I tried <a href="https://www.exploit-db.com/exploits/40136/">an exploit in the version of OpenSSH (7.2p2) this server was running</a>, but this led nowhere. I looked at the public key, took it apart into its modulus and public exponent.</p>

<p>I searched the modulus in <a href="http://factordb.com">FactorDB</a>, googled parts of the key to see if they were leaked somewhere online, did all sorts of recon.</p>

<p>I converted the key to a more useful format early on:</p>

<pre><code>-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA23uMAHgT3GhXUr4lNTsrL6TjDX3cl2XVMEsk2WgeHeIM58tRuWwF  
SyuSeDNv5wOZzHofQ3v4z46yYieoBiM9bhzgMxdoBXF5OIxAQ3hRV3eJXtwzMime  
e/lg9q0fw+9UggUuOJgbpomEiUe6vbWS4vQk6KxfuqtfQcwAuNW1RiU1NB7bENdA  
3qBm4IS2OsqSkrYlyBmA4RQLtIEbyMIwxBoaj3VFmt3XbYOppflea6+NKn33mD9o  
1MWAozXfy2hecFLFTi+2a1q4TsdgYAzOHpiIcslwhwAIxsRQt8gHJPiWAVc/BUoV  
3Z/0uJYlV8q67Mlumod9rh+o9sms1CS1DwIDAQAB  
-----END RSA PUBLIC KEY-----
</code></pre>

<p>I tried putting this into <a href="https://github.com/Ganapati/RsaCtfTool">RsaCtfTool</a>, which does most of the legwork in checking for common vulnerabilities and flaws in RSA keys. It crashed after hitting a recursion limit trying to get a GCD, so I added the following after the imports:</p>

<pre><code>import sys  
sys.setrecursionlimit(1500)  
</code></pre>

<p>Then it <a href="https://github.com/Ganapati/RsaCtfTool/issues/10">crashed for a dumber reason</a> so I applied the fix, and tried again.</p>

<pre><code> % python RsaCtfTool.py --publickey real_rsa.key --private
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA23uMAHgT3GhXUr4lNTsrL6TjDX3cl2XVMEsk2WgeHeIM58tR  
uWwFSyuSeDNv5wOZzHofQ3v4z46yYieoBiM9bhzgMxdoBXF5OIxAQ3hRV3eJXtwz  
Mimee/lg9q0fw+9UggUuOJgbpomEiUe6vbWS4vQk6KxfuqtfQcwAuNW1RiU1NB7b  
ENdA3qBm4IS2OsqSkrYlyBmA4RQLtIEbyMIwxBoaj3VFmt3XbYOppflea6+NKn33  
mD9o1MWAozXfy2hecFLFTi+2a1q4TsdgYAzOHpiIcslwhwAIxsRQt8gHJPiWAVc/  
BUoV3Z/0uJYlV8q67Mlumod9rh+o9sms1CS1DwIDAQABAoIBAQCVrSJjq3+33nw2  
lC9hptEjs6mXqM6HfM2vGn+tt6BaNq8gX/qCndTaV4OSb1mPzFfVJy8s4V1jhmfG  
Ik8mqF+bORv4G603kRPe6V5l5KZsJLvMshRD0DghSYbDknrILu2NoaTMuYaZwsxc  
pksGVs4t8ds9xVefQIE8qLeRVURC6ZeaQXWHlBQoNt7cWEFtcp4XnlthllWl75no  
pAvwN/GJxREaTdtUflerxCJ5JoxElvlsuJDGO2L/5xy8wowGGe9JnUe9qwxTfrGs  
0wE6Gy4IkR1ffmRxB2gzrl2qOk8eRoLJvETTIYZyyhbAc7W6vrIHSyikSgOlmfll  
3BRAijkhAoGBAO0KBBwqBk+p9ve0LCkjoKb49OySJXy9cHKr+Gc0ZMGQpuiFGIHE  
rpBP+xZghyR5yNHodkKJE9mdccv5vYJEk4Z8ejzJwyaw9ogJhZeH/+sJ3shGZYcR  
XRWgU84kS4x/YMo7Q7RoXSEZZH/t7mNgkPYXA37U1Q71T/Okgr+pPVy/AoGBAO0K  
BBwqBk+p9ve0LCkjoKb49OySJXy9cHKr+Gc0ZMGQpuiFGIHErpBP+xZghyR5yNHo  
dkKJE9mdccv5vYJEk4Z8ejzJwyaw9ogJhZeH/+sJ3shGZYcRXRWgU84kS4x/YMo7  
Q7RoXSEZZH/t7mNgkPYXA37U1Q71T/Okgr+pPKuxAoGBALfleWLQR60uU2fA5DHW  
biCcLIgMqgCoh8CvIjwPLcvuCU+DUov6puNW6ZlmsfHeeapACt97sWa2+z4gNqIF  
yd4gXEUk0r4FtH29xWLWI/mY4rnFw6aSFgFdLSdUiTgq6lB6wgAIp5eyN4H4eWWn  
2U7Sc+fF/rVoI+sFylofVnfJAoGBAM5+nz35zi2wHxmCt8XO57ENyDAe0NFuJnt/  
HJKrreqCHSUKbWL++CN3yYCg7pn0DeHu5Lbpu4UkB3JuSY0mOG48GjDCg2M9Xkb8  
JIjxTRxwKMfHq8KSecjRNrCqJbZrcOI75qtPD3I6MLbRi46/HQmE2uKufjzdr5zM  
f6p/v/7BAoGAVy75wz1UYADMSTl5VB2Ljg35l/lo+ADt1akGunSUUsiE9dg8Cwoy  
BGd5LA4wDnXx84mUeC6zd9FkiA9uqqBVUvCasCE0TvZO54bXETKI8cuQGINOR1DM  
ZKSgGDPbd8jB1ru5Rq+y4Aj46lyw2B9vDPc6E3QweWo+ea0CZcWWznA=  
-----END RSA PRIVATE KEY-----
</code></pre>

<p>I didn't expect that to work! But sure enough, putting that private key into <code>private_key.priv</code> and trying to ssh:</p>

<pre><code> % ssh -o IdentitiesOnly=yes -o IdentityFile=private_key.priv -p 8022 badssh@145.239.7.7
===========================
!Congratulations!

. You solved the challenge, here's your flag:

HackTrinity{fermat_factorisation_is_beautiful}


==========================Connection to 145.239.7.7 closed.
</code></pre>

<p>And <code>HackTrinity{fermat_factorisation_is_beautiful}</code> got me the points.</p>

<p>I'm guessing by the flag that the issue with the key was that it used close primes to create the key, allowing a shortcut (ie. fermat factorisation) to cracking it. But I'll have to read up on this later. Thanks for RsaCtfTool for doing that for me!</p>

<hr>

<blockquote class="twitter-tweet" data-lang="en-gb"><p lang="en" dir="ltr">Have been silently taking part in the <a href="https://t.co/CnWBR5wx7L">https://t.co/CnWBR5wx7L</a> CTF in between meetings and stuff over the past week, had a lot of fun. Just solved the last challenge and got the final flag 🎉<br><br>Sásta.<br><br>Thanks for organising <a href="https://twitter.com/ducss?ref_src=twsrc%5Etfw">@ducss</a> / <a href="https://t.co/JYMsVG8oMQ">https://t.co/JYMsVG8oMQ</a> / <a href="https://twitter.com/netsoc?ref_src=twsrc%5Etfw">@netsoc</a> <a href="https://t.co/XZwRohL5KI">pic.twitter.com/XZwRohL5KI</a></p>&mdash; noah ó donnaile in der schweiz (@iandioch) <a href="https://twitter.com/iandioch/status/959428686780010496?ref_src=twsrc%5Etfw">2 February 2018</a></blockquote>  

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>]]></content:encoded></item><item><title><![CDATA[Hashcode 2017]]></title><description><![CDATA[<p>Long ago, I meant to write about this.</p>

<p>I took part in the Hashcode 2017 online round with <a href="http://binarysear.ch">Cian Ruane</a>. He did a <a href="http://binarysear.ch/competitions/2017/02/25/hashcode2017.html">great write-up of our entry</a>!</p>

<p>We came 1st in Ireland, and 164th globally, and had a lot of fun.</p>

<p>Go read his post about it xo</p>]]></description><link>http://mycode.doesnot.run/2018/01/04/hashcode-2017/</link><guid isPermaLink="false">c617042a-8286-49b5-80c9-cf8d37205778</guid><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Thu, 04 Jan 2018 14:52:41 GMT</pubDate><content:encoded><![CDATA[<p>Long ago, I meant to write about this.</p>

<p>I took part in the Hashcode 2017 online round with <a href="http://binarysear.ch">Cian Ruane</a>. He did a <a href="http://binarysear.ch/competitions/2017/02/25/hashcode2017.html">great write-up of our entry</a>!</p>

<p>We came 1st in Ireland, and 164th globally, and had a lot of fun.</p>

<p>Go read his post about it xo</p>]]></content:encoded></item><item><title><![CDATA[Students in CPSSD Provide 72% of Ireland's Total Score on Kattis]]></title><description><![CDATA[<p><a href="http://open.kattis.com">Kattis</a> is an online programming problem archive that gives you points for each problem you solve in relation to how difficult the problem is. It then aggregates the total points of people within a university, and within a country, based on an adjusted sum of the total points of all</p>]]></description><link>http://mycode.doesnot.run/2017/04/15/kattis-score-analysis/</link><guid isPermaLink="false">31a6a14a-bc7f-4b0e-aaec-9c9ec0800e09</guid><category><![CDATA[programming]]></category><category><![CDATA[competitions]]></category><category><![CDATA[cpssd]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Sat, 15 Apr 2017 03:53:47 GMT</pubDate><content:encoded><![CDATA[<p><a href="http://open.kattis.com">Kattis</a> is an online programming problem archive that gives you points for each problem you solve in relation to how difficult the problem is. It then aggregates the total points of people within a university, and within a country, based on an adjusted sum of the total points of all members of each group.</p>

<p>DCU, the university I attend, is now in the top 50 in the world on Kattis, and Ireland is in the top 30 countries there.</p>

<p>I recently wrote a piece on <a href="http://cpssd.net">cpssd.net</a> about how much of Ireland's score on is contributed by myself and my coursemates. It turns out that more than 33% of the score of Ireland comes from my points 🎺</p>

<p>You should read the piece <a href="https://cpssd.net/blog/2017-kattis/">here</a>.</p>

<p>Noah xoxo</p>]]></content:encoded></item><item><title><![CDATA[30 Day Music Challenge: What music I listen to vs. What I think I do]]></title><description><![CDATA[<p>The following image was floating around the internet this month (in varying degrees of blurriness):</p>

<p><img src="http://i.imgur.com/kBacbs6.jpg" alt="30 Day Music Challenge"></p>

<p>The goal is to share a song on social media for each day of the month of April matching the respecting description. Being the wannabe music fan I am, I was interested in trying this.</p>]]></description><link>http://mycode.doesnot.run/2017/04/09/the-30-day-music-challenge/</link><guid isPermaLink="false">4af8dd32-a0e3-41c7-a66e-dec0f863c320</guid><category><![CDATA[programming]]></category><category><![CDATA[python]]></category><category><![CDATA[data]]></category><category><![CDATA[projects]]></category><category><![CDATA[music]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Sun, 09 Apr 2017 03:11:24 GMT</pubDate><media:content url="http://mycode.doesnot.run/content/images/2017/04/Screenshot-from-2017-04-09-04-09-39.png" medium="image"/><content:encoded><![CDATA[<img src="http://mycode.doesnot.run/content/images/2017/04/Screenshot-from-2017-04-09-04-09-39.png" alt="30 Day Music Challenge: What music I listen to vs. What I think I do"><p>The following image was floating around the internet this month (in varying degrees of blurriness):</p>

<p><img src="http://i.imgur.com/kBacbs6.jpg" alt="30 Day Music Challenge: What music I listen to vs. What I think I do"></p>

<p>The goal is to share a song on social media for each day of the month of April matching the respecting description. Being the wannabe music fan I am, I was interested in trying this. However, I am sitting on what many (I suppose) are not; with the wonderful <a href="http://last.fm">last.fm</a>, I have a record of every song I have listened to (with many gaps, granted) over the past few years. I also have the scripting know-how to look through that data.</p>

<p>With this in mind, following is a look at what song lastfm tells me best suits each category, compared to what song I would actually choose when trying to be a music-intellectual to impress my Twitter followers.</p>

<h1 id="day1asongwithacolourinthetitle">Day 1: A song with a colour in the title</h1>

<p>The programming for this is easy enough - take a sorted list of my most-played songs, and find the first one that mentions a colour in the title. <a href="https://simple.wikipedia.org/wiki/List_of_colors">simple.wikipedia.org keeps a colour list</a> that will suit our needs. I had <a href="https://github.com/iandioch/ceres/blob/master/cards/lastfm/get_top.py">some old code</a> from making <a href="http://noah.needs.money">my homepage</a> that dealt with grabbing data from last.fm, so I cannibalised from there and created <a href="https://github.com/iandioch/songchallenge">this repo of scripts for this blog post</a>. <a href="https://github.com/iandioch/songchallenge/blob/master/day_01.py">This script</a> did the above colour-matching, and spouted the following as my favourite song with a colour in the title:</p>

<pre><code>'Ruby' - Kaiser Chiefs  
</code></pre>

<iframe width="560" height="315" src="https://www.youtube.com/embed/qObzgUfCl28" frameborder="0" allowfullscreen></iframe>

<p>A great song! Alas, one that I have not listened to much lately, so is not on my mind. My human-brain answer, to oppose my computer-brain one, would be <em>Rose Rouge</em> by St. Germain. Much higher-brow, and more matching my recent tastes in music.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/yRpKKBmeqV4" frameborder="0" allowfullscreen></iframe>

<p>The script output some other interesting songs, so here are the following ten in order of plays (and the colour they were flagged with, ftb):</p>

<pre><code>'Just Be Good to Green (feat. Lily Allen)' - Professor Green (green)  
'Gold Digger (feat. Jamie Foxx)' - Kanye West (gold)  
'Out of the Blue' - Julian Casablancas (blue)  
'City Of Gold' - Professor Green (gold)  
'Black &amp; Gold' - Sam Sparro (black)  
'Gold on the Ceiling' - The Black Keys (gold)  
'Black horse and the Cherry Tree' - [unknown] (black)  
'Rose Rouge' - St. Germain (rose)  
'Black Plant' - The Last Shadow Puppets (black)  
'Big Yellow Taxi (feat. Vanessa Carlton)' - Counting Crows (yellow)  
</code></pre>

<p>Before I fixed it to stop recognising partial words as colours (eg. "I Predict A Riot" was flagged as having the colour "red" in it), it also output <em>Greenback Boogie</em>, by Ima Robot. I... I really need to listen to that song again. I guess it counts as having a colour in its title too?</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/Slgj96aXgQk" frameborder="0" allowfullscreen></iframe>

<p>I was worried that <em>Come Out Ye Black and Tans</em> would show up here, but probably for the best, I don't seem to have scrobbled it much.</p>

<h1 id="day2asongwithanumberinthetitle">Day 2: A song with a number in the title</h1>

<p><a href="https://github.com/iandioch/songchallenge/blob/master/day_02.py">The script to magic up an answer for this</a> is very similar to the last one, but with some numbers instead of colours. This list of number-words was hand-compiled. The top result was this:</p>

<pre><code>'Four to the Floor' - Starsailor  
</code></pre>

<iframe width="560" height="315" src="https://www.youtube.com/embed/oeXWyXlGkyg" frameborder="0" allowfullscreen></iframe>

<p>Nice! I hadn't thought of that one. The song in my head was <em>Two Minds</em> by Nero, <a href="https://www.last.fm/user/iandioch/library/artists">one of my most-played artists of all-time</a>.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/KFWFJGfEaNo" frameborder="0" allowfullscreen></iframe>

<p>It appeared very far down the list of other outputs, of which here are the next ten:</p>

<pre><code>'Miami 2 Ibiza' - Tinie Tempah (2)  
'Daft Punk - One More Time' - [unknown] (one)  
'Seven Nation Army' - The White Stripes (seven)  
'One Day Like This' - Elbow (one)  
'Season 2 Episode 3' - Glass Animals (2)  
'Ten Cent Pistol' - The Black Keys (ten)  
'No One Knows' - Queens of the Stone Age (one)  
'One (Your Name) [feat. Pharrell]' - Swedish House Mafia (one)  
'Mozart Piano Concerto, K. 488 (Zoltán Kocsis)  movement 3' - [unknown] (3)  
'Song 2 (2012 Remastered Version)' - Blur (2)  
</code></pre>

<p>They're good tunes, Bront. <br>
Because I have listened to different cuts of <em>Song 2</em>, it has multiple lower-playcount listings, but should maybe be higher? This is the case with a lot of songs undoubtedly. You can also see in the list above that wherever I was playing <em>One More Time</em> by Daft Punk had it mistagged :(</p>

<h1 id="day3asongthatremindsyouofsummertime">Day 3: A song that reminds you of summertime</h1>

<p>This is a harder task to do programmatically. I decided to take a similar approach to the previous task, and compile a list of words I think of when I think of summer - things like "holiday", and weather-related words. It's not perfect - it misses "summery-feeling" songs (like <a href="https://www.youtube.com/watch?v=dqrINSxGj9c">this one</a>, for example, that feels sort of swishy and summery to me, but mentions none of those themes).</p>

<p>The script gave the following results, among others:</p>

<pre><code>'When the Sun Goes Down' - Arctic Monkeys (sun)  
'Sunny Afternoon' - The Kinks (sunny)  
'Staring At The Sun' - Simple Kid (sun)  
'Honey Sun' - Elbow (sun)  
'Crying In The Rain (Funkerman Remix)' - Guru Josh Project (rain)  
</code></pre>

<iframe width="560" height="315" src="https://www.youtube.com/embed/yUatH8zI6Qc" frameborder="0" allowfullscreen></iframe>

<p>My own choice was <em>Sunny Afternoon</em> by The Kinks, which is the second output here. Is that a success then?</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/VLs09J_x6-c" frameborder="0" allowfullscreen></iframe>

<h1 id="day4asongthatremindsyouofsomeoneyouwouldratherforgetabout">Day 4: A song that reminds you of someone you would rather forget about.</h1>

<p>I have no idea how to do this in script form. Perhaps I could look at what songs I listened to intensely during some period in the past, but suddenly stopped listening to?</p>

<p>In any case, my organic answer is <em>Bohemian Like You</em> by Dandy Warhols. That one is still a favourite, tainted though it may be.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/CU3mc0yvRNk" frameborder="0" allowfullscreen></iframe>

<h1 id="day5asongthatneedstobeplayedloud">Day 5: A song that needs to be played loud</h1>

<p>I wasn't quite sure how to figure this out. I came to the conclusion that the loudness of my music is strongly correlated to the speed of it - fast, exciting songs are what make me turn up the dial. As such, I queried the beats-per-minute of my top 250 most-played songs using the <a href="https://getsongbpm.com/api">getsongbpm.com api</a>, that they kindly provide freely. <a href="https://github.com/iandioch/songchallenge/blob/master/get_track_bpms.py">Here</a> is the python-fu to do that. There was a lot of messy data, I only got values for 101 of the 250 songs, and I have no idea how correct the numbers are, but c'est la vie. Here were the quickest 10, supposedly, as per <a href="https://github.com/iandioch/songchallenge/blob/master/day_04.py">this script</a>:</p>

<pre><code>'Hotel Yorba' - The White Stripes (196 bpm)  
'From the Ritz to the Rubble' - Arctic Monkeys (190 bpm)  
'Arabella' - Arctic Monkeys (180 bpm)  
'End Credits' - Chase &amp; Status (176 bpm)  
'Fool Yourself' - Chase &amp; Status (176 bpm)  
'Clint Eastwood' - Gorillaz (168 bpm)  
'Brianstorm' - Arctic Monkeys (166 bpm)  
'Everyday I Love You Less and Less' - Kaiser Chiefs (160 bpm)  
'I Predict a Riot' - Kaiser Chiefs (158 bpm)  
'Never Miss a Beat' - Kaiser Chiefs (156 bpm)  
</code></pre>

<iframe width="560" height="315" src="https://www.youtube.com/embed/DZPEUyiNcjA" frameborder="0" allowfullscreen></iframe>

<p>I'm interested in this result. These are fine songs, but not ones that jump into my head when I think "loud"... <em>Fell in Love with a Girl</em> by The White Stripes is what I would have chosen. It has been my ringtone for many years, and I jam to it that time my phone rings every few months. The script-suggested one was also by The White Stripes, so it mustn't be too far wrong?</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/fTH71AAxXmM" frameborder="0" allowfullscreen></iframe>

<p>I wish that song was longer.</p>

<h1 id="postmortem">Postmortem</h1>

<p>That was interesting! I've been reminded of some good songs, and I always enjoy playing with data, so it was a win-win. The programmatic answers seem to be skewed by my tastes a year ago; there's an abundance of rock in there, while lately I've been into a wider variety of genres. That was expected.</p>

<p>I look forward to continuing this exercise in the future, but it takes a while, so I'll have to stop here.</p>

<p>Feelin' so bohemian like you,</p>

<p>Noah</p>

<p><em>(The picture accompanying this post is a screenshot of some lads a-jammin' in the Bohemian Like You music video)</em></p>]]></content:encoded></item><item><title><![CDATA[123rum123 at the IrlCPC 2017]]></title><description><![CDATA[<p>The <a href="http://acm.ucc.ie/irlcpc">Irish Collegiate Programming Contest</a> (IrlCPC) 2017 was held on Saturday, 25th of March, in sunny University College Cork.</p>

<p>I was competing with two of my housemates, Cian Ruane, and Ross O'Sullivan. Myself, Cian Ruane, and Stefan Kennedy came 2nd place at the IrlCPC last year, so I fancied our</p>]]></description><link>http://mycode.doesnot.run/2017/04/03/irlcpc-2017/</link><guid isPermaLink="false">6b5e92ac-09af-4336-ae2a-a1d3d06b91c7</guid><category><![CDATA[competitions]]></category><category><![CDATA[programming]]></category><category><![CDATA[teamwork]]></category><category><![CDATA[travel]]></category><category><![CDATA[redbrick]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Mon, 03 Apr 2017 20:37:51 GMT</pubDate><content:encoded><![CDATA[<p>The <a href="http://acm.ucc.ie/irlcpc">Irish Collegiate Programming Contest</a> (IrlCPC) 2017 was held on Saturday, 25th of March, in sunny University College Cork.</p>

<p>I was competing with two of my housemates, Cian Ruane, and Ross O'Sullivan. Myself, Cian Ruane, and Stefan Kennedy came 2nd place at the IrlCPC last year, so I fancied our chances this year.</p>

<p>Our team, <code>123rum123</code> (named for the password of our other housemate's boyfriend), flew to the top of the board from the beginning, finishing first in the practice round.</p>

<p>We were hyped for the real competition, and quick off the block, we solved 5 of the 6 problems with less stress than we expected. We worked very well as a team, and had a lot of fun! </p>

<p>However, that last, forsaken problem... It was a topological sort, and my only previous problem I could compare to it was a <a href="https://projecteuler.net/problem=79">Project Euler problem I solved</a>. However, I couldn't find a way to apply any solutions I knew for that problem to this, and any graph-based or set-based solution was too slow.</p>

<p>Alas, we only got 4 points for problem 5. It was the only we didn't get full marks in, leaving us 6 points from a perfect score.</p>

<p>While we had solved the other problems faster (we think), 2 teams from Trinity College passed us out (fair deuce to them) and solved the final problem, leaving us in 3rd place at the end of the competition.</p>

<p>We were (of course) delighted - third isn't bad at all :) We are somewhat familiar with the team that came 1st, and <a href="http://binarysear.ch/competitions/2017/02/25/hashcode2017.html">we beat them in the recent Hashcode</a>, so it wasn't all bad!</p>

<p>Thanks to Google, Newsweaver, and UCC for the good day that was in it. We'll win in future xo</p>]]></content:encoded></item><item><title><![CDATA[Hack the Dot Dublin]]></title><description><![CDATA[<p>On Wednesday the 22nd of February, I took to <a href="https://en.wikipedia.org/wiki/Southside,_Dublin">the southside</a> to take part in <a href="https://www.eventbrite.com/e/hack-the-dot-dublin-tickets-18844886563">Hack the Dot</a>, a domain-name-driven hackathon organised and hosted by <a href="http://rightside.co">Rightside</a>.</p>

<p>I am a big domain name watcher, and a vocal fan of the new generic TLDs. Rightside are a <a href="http://donuts.co">Donuts</a> competitor, and I didn't</p>]]></description><link>http://mycode.doesnot.run/2017/02/26/hack-the-dot-dublin/</link><guid isPermaLink="false">bfab6cee-82e9-45a7-868e-0fbdea44e085</guid><category><![CDATA[hackathon]]></category><category><![CDATA[competitions]]></category><category><![CDATA[domains]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Sun, 26 Feb 2017 03:03:00 GMT</pubDate><content:encoded><![CDATA[<p>On Wednesday the 22nd of February, I took to <a href="https://en.wikipedia.org/wiki/Southside,_Dublin">the southside</a> to take part in <a href="https://www.eventbrite.com/e/hack-the-dot-dublin-tickets-18844886563">Hack the Dot</a>, a domain-name-driven hackathon organised and hosted by <a href="http://rightside.co">Rightside</a>.</p>

<p>I am a big domain name watcher, and a vocal fan of the new generic TLDs. Rightside are a <a href="http://donuts.co">Donuts</a> competitor, and I didn't realise that they owned the TLDs of two of my stellar domains, <code>blurry.video</code> and <code>meme.rehab</code> (I want to host a GNU Social instance at the latter, when I get time). It was intiguing to peek behind the curtain, even if just for an evening! Competitors in Hack the Dot were forewarned of a free domain, along with a few beers, so I was excited.</p>

<p>It was a lot of fun! At the beginning of the competition, the teams are all presented with a domain name, the same for all teams. You then have 90 minutes to come up with a business based on that domain. The inspiration-tickler we were given was <code>hack.live</code>. </p>

<p>I was in cahoots with 3 others, all from UCD. The idea we pitched (to the CEO of Rightside, via GHangouts) was a distraction-killing browser extension that resurfaced old embarrassing Facebook posts if you spent too long on blacklisted websites. If you ignored the warnings, it proceeded to message random friends telling you to get back to work. Rather than hacking anything physical, we aimed to envision a lifehack, and this one sounded fun.</p>

<p>We didn't win, but I was surprised with a free tshirt and a good evening of brainjamming. There was a code distributed to get a free <code>.live</code> domain with <code>name.com</code>, the registrar front of Rightside. Alas, I was too busy working in the following 24 hours to redeem the code in time.</p>

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Was at the Hack the Dot with <a href="https://twitter.com/rightside">@rightside</a> in Dub yesterday eve - was a good bit of craic, fair play (y)</p>&mdash; cash for ash ketchum (@iandioch) <a href="https://twitter.com/iandioch/status/834817828045848576">February 23, 2017</a></blockquote>  

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>I had a lot of fun, and met a few new people, so I'd definitely recommend an event like this. However, it didn't really get me going like a longer product-focused hackathon, where there is the time (and expectation) to build a working prototype. <a href="http://mycode.doesnot.run/2017/02/12/coireacht/">The previous hackathon I was at, HackYourWayDay</a>, had a similar problem, but there was more time there to put a prototype together.</p>

<p>It's nice to forget about proper work for a few hours anyway, and have a bit of <a href="https://en.wikipedia.org/wiki/Craic">craic</a>. I'll keep my ear to the ground for similar happenings over the next few months.</p>

<p><em>Cheers to Hakeem, whose name was on the ticket and who I was clumsily masquerading as for the first 3 minutes of Hack the Dot.</em></p>]]></content:encoded></item><item><title><![CDATA[Coireacht - Our 3-Hour-ish Hackathon Project at HackYourWayDay]]></title><description><![CDATA[My experience at the HackYourWayDay eircode-based hackathon, writing a tool based on historical crime data to estimate the safety of an area.]]></description><link>http://mycode.doesnot.run/2017/02/12/coireacht/</link><guid isPermaLink="false">6c45f7bf-c052-4844-99d4-0ef8d55689c6</guid><category><![CDATA[projects]]></category><category><![CDATA[python]]></category><category><![CDATA[flask]]></category><category><![CDATA[webdev]]></category><category><![CDATA[competitions]]></category><category><![CDATA[programming]]></category><category><![CDATA[jinja]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[teamwork]]></category><category><![CDATA[data]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Sun, 12 Feb 2017 06:13:12 GMT</pubDate><content:encoded><![CDATA[<p>On Friday the 10th of February, myself and my compadre <a href="http://binarysear.ch">Cian Ruane</a> took part in the <a href="https://ti.to/govhack/xgovhackyourwayday">HackYourWayDay hackathon in the NDRC</a> that we had <a href="https://www.dcu.ie/events/2017/feb/s0217a.shtml">seen publicised around the place</a> the previous week. The hackathon was themed around <a href="https://www.eircode.ie/">Eircodes</a>, which are Ireland's new <a href="http://www.independent.ie/irish-news/news/experts-warn-27m-eircode-project-is-not-fit-for-purpose-31374678.html">controversial</a> postcodes. I'm interested in data, mapping, and hacking, so I was pretty interested. Unfortunately, I was late to the safari because I had a hospital appointment that morning, but I was there for the fun bits: pitching, building, and presenting our product, <em>Coireacht</em>. Naming things <a href="https://martinfowler.com/bliki/TwoHardThings.html">is said to be one of the most difficult parts of tech</a>, but I have a cheat: use the Irish word for whatever I'm looking at. <em>Coireacht</em> is the Irish for <em>crime</em>. </p>

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Exploring government data at today&#39;s hackathon <a href="https://twitter.com/hashtag/HackYourWayDay?src=hash">#HackYourWayDay</a> <a href="https://t.co/pOdJ0oLqm0">pic.twitter.com/pOdJ0oLqm0</a></p>&mdash; data.gov.ie (@GovDataIE) <a href="https://twitter.com/GovDataIE/status/830068208719761413">February 10, 2017</a></blockquote>  

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p><em>Above is a tweet with a nice pic of, from left to right: Cian, myself, and blurry Adrian, the CTO at <a href="https://opening.io">Opening.io</a>. Opening was the company where I was lucky enough to have my first job, enter my love affair with machine learning, and even have drinks on a boat with; it also now happens to be based in the building the hackathon was hosted in. On the board in the background of the picture you can also squint at our tight time limits for the hacking.</em></p>

<hr>

<p>In advance of the hackathon, some links to location-related datasets were <a href="https://medium.com/tradecraft-of-transformation/hackyourwayday-what-data-do-we-have-anyway-df6e42bdff37#.khlyghn5l">shared with us</a>. It rubbed me the wrong way how many of the datasets were for the chrome and crime of Dublin City, and how few were countrywide. At the hackathon, the vast majority of people in the room were Dubs or near-Dubs. Three quarters of the people in this country are not Dubliners, and I wanted to put together a project that helped them. My teammate Cian is also a countryboi, and either had the same thoughts, or just didn't mind abiding me. </p>

<p>The idea we came up with was a tool to help students from the countryside, or people about to immigrate, find out more about potential accommodation when they are moving for college or work. Before university when I was seeking accommodation in Dublin, came across a potential place, and mentioned the price and address to my mother (who lived in Dublin's fine city once upon a pale moon), she would share a lot of unsolicited information on whether the price was fair, or if it was a dangerous area. Had she not known these things, I would have been clueless. Her information was also decades old. A tool to accessibly provide modern information like this may help someone. We had pinpointed our problem; following is our enthralling technical solution.</p>

<hr>

<p>Here is what we set out to do:</p>

<ol>
<li>Take an eircode from the user in a nice and friendly way  </li>
<li>Find crime data for the location  </li>
<li>Return a rating for how dangerous it is  </li>
<li>Find recent rental prices for nearby properties  </li>
<li>Tell the user how much to expect to pay in rent here</li>
</ol>

<p>We achieved as far as #3 above, tickling the edge of #4, over the three-ish hours of the hackathon. We opted to whip up a webapp of sorts, that you can <a href="http://coireacht.blurry.video">try out live right here</a>, and <a href="https://github.com/CianLR/coireacht">eyeball our code here</a>.</p>

<p>You can git-blame the repo to see who did what between me and my navigator Messr Ruane. I have a fair amount of experience with <a href="http://flask.pocoo.org">Flask</a>; it is perhaps my favourite library or framework of all time, making sometimes messy webdev as pleasant as a trip to the opera. Cian is not so familiar with Flask (or the web, the old fart), so I threw together a barebones webapp, and shipped it off to him. He bound together APIs to turn an eircode into an address, and from there into a latitude and longitude.</p>

<p>We found an open dataset of the <a href="https://data.gov.ie/dataset/crimes-at-garda-stations-level-2010-2016">crimes within the remit of each Garda station in the country over the past few years</a> (although I am still seeking the exact licence for this data). It was laid out like this:</p>

<table>  
<tr>  
<td>ID</td><td>Garda Station</td><td>Division</td><td>X</td><td>Y</td><td>Number of Occurances of Crime P in Year Q</td>  
</tr>  
<tr>  
<td>20441</td><td>Abbeyfeale</td><td>Limerick Division</td><td>112219</td><td>126928</td><td>insert grizzly crime number here</td>  
</tr>  
</table>

<p>I am not sure what the context of the <code>X</code> and <code>Y</code> was - perhaps an offset to the numbers mentioned for the range in the metadata of this dataset. Instead of fiddling with offsets, I doodled a script that took the name of each Garda station (eg. <code>Abbeyfeale</code>), cheekily appended <code>Garda Station, Ireland</code> to it (to make <code>Abbeyfeale Garda Station, Ireland</code>, and queried Google maps for an exact location, then output all this in a CSV. It worked for all but 8 or 9 stations, which we discarded in the name of time constraints.</p>

<p>We only took the numbers of violent crimes and thefts into account; we don't think that tax fraud makes an area more dangerous. In fact, tax fraud might be negatively correlated with an area's safety...</p>

<p>Cian wrote a wee bitta code that took the coordinates of a location, and the coordinates of each Garda station, found the nearest one, and used it to figure out a crime rating for the property. We pretended the world was flat and used the latitude and longitude as points on a 2D plane to calculate distances, again because of time constraints.</p>

<p><img src="http://i.imgur.com/SYUHpoF.png" alt=""></p>

<p><em>A screenshot of the homepage, which is just a light description of the tool, a box for you to type an eircode into, and a big "go" button.</em></p>

<p>I wrote a frontend! I used <a href="http://tachyons.io/">Tachyons</a>, a CSS toolkit I've used and enjoyed before that makes it a bit less painful to get things looking good (on both desktop and mobile!), along with <a href="http://jinja.pocoo.org/">Jinja2</a> templating to put together the pages in a nice, modular way. I used the vital tool of StackOverflow to help me put a map onto the page, in order to visualise where the Eircode was in the real world. I also used some messy Jinja-ing and CSS-ing to fill 5 stars based on the crime rating of the area, and highlight it red for bad, orange for okay, and green for good.</p>

<p><img src="http://i.imgur.com/E1UGiBo.png" alt=""></p>

<p><em>A screenshot of the results page for an eircode, with the address, coordinates of it, a score out of 5 stars for how safe the area is, and an OpenStreetMap embed showing where the place is.</em></p>

<p>Myself and Cian presented our project to a panel of Dubliners (who didn't relate to our original problem perhaps!). We didn't win any of the 3 prizes, so no 5000€ for us. I was very proud of what we managed to put together however. We were one of only 2 teams that had a live interactive demo, I think, and the only team to have a tool working on mobile. After all of the prizegiving, one of the judges came over to us and told us we were *this close* (pinches fingers) to the top dogs. Throughout the presentations, they pushed the point of how close the competition was. I think they always do that, but it felt nice to know we were maybe almost in the money.</p>

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">The workshopping has begun! Ideas forming <a href="https://twitter.com/hashtag/hackyourwayday?src=hash">#hackyourwayday</a> <a href="https://twitter.com/Version1Tweets">@Version1Tweets</a> <a href="https://t.co/nqPOWVBl9A">pic.twitter.com/nqPOWVBl9A</a></p>&mdash; Enda Diggins (@endadiggins) <a href="https://twitter.com/endadiggins/status/830025728095817728">February 10, 2017</a></blockquote>

<p><em>A tweet with a picture of us looking for help with some data. I don't remember it being taken, but I'm happy enough because for some reason I look okay in it. I am a little suspicious.</em></p>

<hr>

<p>I had a lot of fun on the day, and I think Cian did too - maybe he'll be kind enough to write up how it felt from his <em>point de vue</em>. If we get the time and conjure up some motivation out of our rear ends, Cian and I may augment the project a little bit at some stage. I wasn't expecting the free pizza and beer that appeared in the evening of the event, it was a nice way to finish! I'll ignore the fact that I followed it with <a href="https://www.facebook.com/events/1794062740854987/">more drinks</a>. </p>

<p>This was my first time playing with government data, and it was fun - I look forward to doing it again in the future. I hope someone finds some use in our tool. If you're interested, <a href="http://coireacht.blurry.video">you can play with it here</a>!</p>

<p><em>Bonus points to me for peppering this piece with the names of 8 different web browsers. Try and catch them all!</em></p>]]></content:encoded></item><item><title><![CDATA[Parsing Project Euler Progress with PyOCR]]></title><description><![CDATA[<p>I do a lot of problems on <a href="https://projecteuler.net">Project Euler</a>. It is a really cool site full of mathematically-flavoured programming problems that are too difficult for me. <a href="http://noah.needs.money">My homepage</a> right now keeps some live stats about my progress on Project Euler and some similar sites. Unfortunately, Project Euler doesn't provide any</p>]]></description><link>http://mycode.doesnot.run/2017/01/15/parsing-project-euler-progress/</link><guid isPermaLink="false">8c97fceb-6d0a-4913-9600-881a526e0bda</guid><category><![CDATA[project euler]]></category><category><![CDATA[programming]]></category><category><![CDATA[projects]]></category><category><![CDATA[python]]></category><category><![CDATA[github]]></category><category><![CDATA[ocr]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Sun, 15 Jan 2017 02:14:27 GMT</pubDate><content:encoded><![CDATA[<p>I do a lot of problems on <a href="https://projecteuler.net">Project Euler</a>. It is a really cool site full of mathematically-flavoured programming problems that are too difficult for me. <a href="http://noah.needs.money">My homepage</a> right now keeps some live stats about my progress on Project Euler and some similar sites. Unfortunately, Project Euler doesn't provide any sort of public machine-readable data on how many problems you have solved. However, it does provide an image badge like the following:</p>

<p><img src="https://projecteuler.net/profiles/iandioch.png"></p>

<p>I want to use OCR (<a href="https://en.wikipedia.org/wiki/Optical_character_recognition">optical character recognition</a>) to parse the number of problems I have solved out of the above image, and allow it to be displayed on my homepage.</p>

<p>I'm going to use Python3 for this, because Python is easy, and speed isn't critical for this application (it will probably be run as a daily cronjob or something). </p>

<blockquote>
  <p>The Setup</p>
</blockquote>

<p>There are some nice <a href="https://realpython.com/blog/python/setting-up-a-simple-ocr-server/">articles</a> out there that suggest <a href="https://github.com/tesseract-ocr/tesseract">tesseract-ocr</a> is my best bet for some digit-recognition. <a href="https://github.com/jflesch/pyocr">pyocr</a> is the most actively maintained wrapper for it that I can find as of early 2017. Installing this with <code>pip</code> is trivial, although I had to <a href="http://askubuntu.com/a/687637">play with Pillow installation first</a> on my Ubuntu 14.04 VPS.</p>

<blockquote>
  <p>The Heist</p>
</blockquote>

<p>Let's get coding. You can see all the work <a href="https://github.com/iandioch/euler-foiler">in the <code>euler-foiler</code> github repo</a> (the repo name rhymes if you pronounce Euler correctly xo). There is not much to it. Here is the guts of the <code>parse</code> function in <code>parse.py</code>:</p>

<pre><code class="language-python">def parse(image_loc):  
    tools = pyocr.get_available_tools()
    if len(tools) == 0:
        return "Error: No OCR tool found"

    # should be 'Tesseract (sh)'
    tool = tools[0]

    orig_image = Image.open(image_loc)

    # crop to only the section with the number of problems solved
    cropped_image = orig_image.crop((47, 40, 97, 60))

    # double the size of the image so the OCR has more to go on
    resized_image = cropped_image.resize((100, 40), Image.ANTIALIAS)

    digits = tool.image_to_string(
        resized_image,
        builder=pyocr.tesseract.DigitBuilder())

    return digits
</code></pre>

<p>Most of the code is taken directly from the <a href="https://github.com/jflesch/pyocr/blob/master/README.markdown">README</a> of the <code>pyocr</code> project.  We explicitly declare that the script uses the <code>Tesseract (sh)</code> tool, as it provides us with a model to only recognise digits.</p>

<p>We use a nice Pillow method to crop the Project Euler badge to only give us the part we care about (the number of problems solved). The hard-coded parameters are the left, top, right and bottom of the bounding box, and were figured out in the GIMP.</p>

<p>If we plug this into tesseract straight away, then it will not give us the right answer, as it is looking at a very small image (50x20 pixels). We therefore use Pillow to double the size of the cropped image.</p>

<p>We can then pop this into tesseract, specifying the digit-recognising model. We do not have to specify a language, as whatever the system default is will do for digits.</p>

<blockquote>
  <p>The Getaway</p>
</blockquote>

<p>We're done! That wasn't nearly as complicated as I expected. The most difficult part was installing the dependencies.</p>

<p>If you look at the <a href="https://github.com/iandioch/euler-foiler/blob/master/parse.py">full file</a>, you will see some extra bits to make sure the path the user gives is valid, etc. There is also a very small <a href="https://github.com/iandioch/euler-foiler/blob/master/test.py">test script</a> in the repo.</p>

<p>To use this script, all you need to do is grab your badge (eg. <code>wget https://projecteuler.net/profiles/iandioch.png</code>), run the script with the right path (<code>python3 parse.py iandioch.png</code>), and you're away!</p>

<p>It accurately recognises my 128 problems solved right now. I look forward to it recognising higher numbers in future. I will work now to incorporate the data into <a href="http://noah.needs.money">my homepage</a>, and now that I see OCR isn't so scary when I use nice libraries, I can apply it to a few other problems...</p>]]></content:encoded></item><item><title><![CDATA[dia-log, a diabetes management system]]></title><description><![CDATA[Description of the backend  (flask) and frontend (JS, static site) of the first iteration of dia-log, a system to help you manage your diabetes data.]]></description><link>http://mycode.doesnot.run/2016/12/21/dia-log/</link><guid isPermaLink="false">5fb47a21-6ec3-4fa0-8e6c-f1af366357e2</guid><category><![CDATA[projects]]></category><category><![CDATA[programming]]></category><category><![CDATA[linux]]></category><category><![CDATA[python]]></category><category><![CDATA[flask]]></category><category><![CDATA[diabetes]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Wed, 21 Dec 2016 01:26:06 GMT</pubDate><content:encoded><![CDATA[<h3 id="context">Context</h3>

<p>I have type 1 diabetes. It's not a big deal, there are much worse afflictions that can hang on a person. Diabetes just means the occasional pain in the arse (sometimes literally xo).</p>

<p>I am supposed to get up early, eat 3 steady solid meals, and exercise; as is the case with every university student, none of the above happens more than one day a week. I am also discouraged from excessive alcohol, but that is another scéal. Every once in a blue moon (between once a month and once  every 18 months) I have to go to the hospital to see a nurse or doctor or dietitian, and look at the trends in my blood sugar. </p>

<p>The biggest hurdle with diabetes is tracking my blood sugars. I use <a href="https://www.accu-chek.co.uk/gb/products/metersystems/avivanano.html">a glucometer like this</a> to measure my blood glucose level. This is a device with a pathetically small battery and a sufficient amount of internal memory, that stores a history of my blood sugar readings in <code>mmol/L</code>, next to a timestamp of when that reading was taken.</p>

<p>It is a closed system; I cannot access that data. I have dreamed of writing ML tools to predict <a href="https://en.wikipedia.org/wiki/Hypoglycemia">a hypo</a>, suggest that I should take more insulin at certain meals, or even just graph my sugars. It is fine (but against my libre ideals *sob*) that it is a closed system, I can get past that. But every time I visit the hospital, I am supposed to bring all of my blood sugar data from the previous weeks or months in written form, so the doctor does not have to squint at the tiny screen on the glucometer and scroll through readings one at a time. <em>Ugggghhhh</em>.</p>

<p>I have for years been planning to sit down and write a system to digitise the process of logging blood sugars in an open and friendly format, and this afternoon I finally did it.</p>

<h3 id="project">Project</h3>

<p>Presenting: <a href="http://github.com/iandioch/dia-log">dia-log</a> (and its accompanying <a href="http://github.com/iandioch/dia-log-frontend">frontend</a>);</p>

<p>Dia-log (<code>diabetes logging</code>, perhaps the most boring project name I have ever envisioned) is a small backend+frontend to allow a number of users (or a single one, as in my case) to upload their blood sugar data to <em>the cloud</em> (at least, a VPS in my case). </p>

<p>Right now, it is a lightweight system.</p>

<p>The backend is a JSON-based RESTful server implemented with <a href="http://flask.pocoo.org">Flask</a> (my favourite framework of all time, it's so nice to use). So far there are only two endpoints; one to add a new blood sugar reading, and one to add a new user.</p>

<p>That's right, it's a multi-user system, with some small <a href="https://github.com/iandioch/dia-log/blob/6effc5de5a6b309f3cf4ab5b18f5e46b75274665/auth.py">authentification of those users</a>, based on a username and (bcrypt+salted) password.</p>

<p>The frontend is as yet <a href="https://github.com/iandioch/dia-log-frontend/blob/039bfa52424d5feb6119408d6494c1466f2edb4d/templates/blood/index.html">a simple static form</a> (with a nice date picker based on <a href="https://dbushell.github.io/Pikaday/">Pikaday</a> and <a href="http://momentjs.com">moment.js</a>) that sends things to the back end, built with a python&amp;jinja-based static site generation script. I hope to spice it up with some CSS (<a href="http://tachyons.io">TACHYONS</a> is very nice) at some stage, and add some d3-flavoured visualisations; but baby steps.</p>

<h3 id="designdecisions">Design Decisions</h3>

<p>I was asked why I separated the project into two separate repositories. It seemed to make more sense;</p>

<ul>
<li>The two parts are, from a programming point of view, completely separate. They communicate over a RESTful API but share no code.</li>
<li>I would like to create alternate clients at some stage (eg. a mobile app), or at least leave the option open for it.</li>
<li>The server is a dynamic app but the frontend is a static site better served with nginx or apache than Flask for performance and configuration reasons. Putting them in the same repo would conflate and confuse this.</li>
</ul>

<p>I have configured my nginx to make the frontend available at <code>dialog.blurry.video</code>, and the backend at <code>dialog.blurry.video/api</code>, with some <code>rewrite</code>-fu in the middle to make Flask think it is at a subdomain and not <code>/api</code>. </p>

<p>I wanted dia-log to be very configurable, and if you look at the <code>config.json</code> that comes with each repo, I think you'll see I succeeded reasonably well with that. </p>

<p>All of the blood sugar readings are put into plain old textfiles (of the path <code>&lt;configurable_dir&gt;/&lt;username&gt;/&lt;timestamp&gt;</code>), so a backup is as simple as a <code>cp</code> (or in my case, a <code>git push</code>). I have read lots of praise for plaintext data storage systems, and with how simple this data is, a database would likely be overkill. </p>

<p>I need to document, and add a few more features, configuration options (I ignore timezone woes, and there are other measurement systems than <code>mmol/L</code>, to name a couple) and tests, but who knows; maybe another sorrowful diabetic will find these tools useful someday?</p>]]></content:encoded></item><item><title><![CDATA[RIP CPSSD]]></title><description><![CDATA[<p>I am doing a BSc. in <em>Computational Problem Solving and Software Development</em> (CPSSD) at <a href="https://dcu.ie">Dublin City University</a>; <a href="http://www.careersportal.ie/courses/coursedetail.php?course_id=18543"><em>DC122</em> in CAO talk</a>. It is a small, specialised course for students who already have some programming knowledge. You can learn more about it at <a href="http://cpssd.net">the website I set up for it</a>.</p>

<p>But</p>]]></description><link>http://mycode.doesnot.run/2016/12/12/rip-cpssd/</link><guid isPermaLink="false">9566c690-5faf-485f-af4a-92e41401e7da</guid><category><![CDATA[cpssd]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Mon, 12 Dec 2016 14:13:52 GMT</pubDate><content:encoded><![CDATA[<p>I am doing a BSc. in <em>Computational Problem Solving and Software Development</em> (CPSSD) at <a href="https://dcu.ie">Dublin City University</a>; <a href="http://www.careersportal.ie/courses/coursedetail.php?course_id=18543"><em>DC122</em> in CAO talk</a>. It is a small, specialised course for students who already have some programming knowledge. You can learn more about it at <a href="http://cpssd.net">the website I set up for it</a>.</p>

<p>But alas, <a href="https://web.archive.org/web/20161214003629/http://www2.cao.ie/handbook/handbook/important_changes.php">CPSSD is no more</a>. </p>

<p>Never again will people have to stumble through its long name. It has also been called <a href="http://survivingantidepressants.org/index.php?/topic/52-post-ssri-sexual-dysfunction-pssd/">PSSD</a>, <a href="https://en.wikipedia.org/wiki/Complex_post-traumatic_stress_disorder">CPSTD</a>, and <a href="https://en.wikipedia.org/wiki/Posttraumatic_stress_disorder">PSTD</a>, and a whole host of other letter combinations my mother came up with on the spot. </p>

<p>Never again will there be bright-eyed LC students <a href="http://www.computing.dcu.ie/undergraduate/pssd/portfolio-interview">throwing together portfolios or stuttering through interviews</a> to get in.</p>

<p>Never more will DCU teams <a href="http://www.computing.dcu.ie/news/cpssd-teams-take-top-2-spots-irlcpc-2016">top</a> the <a href="http://www.computing.dcu.ie/news/cpssd-team-go-close-google-hashcode">charts</a> at everyone's favourite programming competitions. Well, we'll still be around for a couple of years yet ;)</p>

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">My uni course was cancelled today. I can finish, but no new first years :(</p>&mdash; snoah 🌌❄ (@iandioch) <a href="https://twitter.com/iandioch/status/799281878498832385">November 17, 2016</a></blockquote>  

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>It turns out that hosting a degree with a high barrier to entry is not economically viable, as there were <a href="https://cpssd.net/people">too few students in each year</a> to let DCU turn a profit. </p>

<p>We were told in quite a terse email that those already in the course can see it through, and finish the degree. However, applications for new students have closed for good.</p>

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">The funeral for my course after it was cancelled is a very emotional affair <a href="https://twitter.com/hashtag/ripcpssd?src=hash">#ripcpssd</a> <a href="https://t.co/TPRb9fO5Ny">pic.twitter.com/TPRb9fO5Ny</a></p>&mdash; snoah 🌌❄ (@iandioch) <a href="https://twitter.com/iandioch/status/802950937497243648">November 27, 2016</a></blockquote>  

<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>I firmly believe that CPSSD was the best IT degree in the country, and filled a well-needed hole in the education system here. You can see, looking at the current students (sin a rá "me and my boyz") with their wealth of enthusiasm about tech, that they would get bored and frustrated in a normal degree. None of Ireland's tech degree offerings are aimed at people who already have experience, or who are independent learners. None of them suit someone who is ahead of the curve as well as CPSSD.</p>

<p>It makes sense that DCU cancelled the degree, from a financial point of view. But it still makes me sigh.</p>]]></content:encoded></item><item><title><![CDATA[It's a Ghost!]]></title><description><![CDATA[<p>Bhí sé ar m'intinn an blag seo a athrú ar feadh cúpla mhí. Bhuel, tá sé déanta anois. Táim ag baint úsáid as <a href="https://ghost.io">Ghost</a> anois - tá sé deas go leor, simplí, agus FLOSSy.</p>

<p>Bhog mé an cuid is mó de na postálacha ón leagan sine anseo. D'fhág mé cúpla</p>]]></description><link>http://mycode.doesnot.run/2016/12/12/ghost-post/</link><guid isPermaLink="false">66a2272d-9ed0-41c9-8ea5-783dfe963838</guid><category><![CDATA[webdev]]></category><category><![CDATA[gaeilge]]></category><category><![CDATA[travel]]></category><dc:creator><![CDATA[Noah Ó Donnaile]]></dc:creator><pubDate>Mon, 12 Dec 2016 13:47:06 GMT</pubDate><content:encoded><![CDATA[<p>Bhí sé ar m'intinn an blag seo a athrú ar feadh cúpla mhí. Bhuel, tá sé déanta anois. Táim ag baint úsáid as <a href="https://ghost.io">Ghost</a> anois - tá sé deas go leor, simplí, agus FLOSSy.</p>

<p>Bhog mé an cuid is mó de na postálacha ón leagan sine anseo. D'fhág mé cúpla cinn níos leadránaí sa void ag <a href="https://github.com/iandioch/betelgeuse">github</a>.</p>

<p>Chaith mé an tsamhradh ag Google Zurich. Bhí sé dochreidte. Níos mó ná sin, chaith mé mí ar fad ag teasteal. Tá súil agam go mbeidh an tsamhradh seo chugainn comh iontach.</p>

<p>Beidh postálacha nua ag teacht am éigin.</p>

<p>😘</p>

<p>Noah</p>]]></content:encoded></item></channel></rss>